aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGav Wood <i@gavwood.com>2015-01-30 07:26:39 +0800
committerGav Wood <i@gavwood.com>2015-01-30 07:26:39 +0800
commita604202f33f1f7dc3abda1080e1bc02b2a2cbcb3 (patch)
treef529a8317ed06e9d7bcd23571cf8366f7b7278b1
parent0d0c47f5889e7d9461c31a86beb88a3698d139a0 (diff)
parent3701543ae8dd8ffbfd58e5648d45699468f10a55 (diff)
downloaddexon-solidity-a604202f33f1f7dc3abda1080e1bc02b2a2cbcb3.tar
dexon-solidity-a604202f33f1f7dc3abda1080e1bc02b2a2cbcb3.tar.gz
dexon-solidity-a604202f33f1f7dc3abda1080e1bc02b2a2cbcb3.tar.bz2
dexon-solidity-a604202f33f1f7dc3abda1080e1bc02b2a2cbcb3.tar.lz
dexon-solidity-a604202f33f1f7dc3abda1080e1bc02b2a2cbcb3.tar.xz
dexon-solidity-a604202f33f1f7dc3abda1080e1bc02b2a2cbcb3.tar.zst
dexon-solidity-a604202f33f1f7dc3abda1080e1bc02b2a2cbcb3.zip
Merge branch 'develop' of github.com:ethereum/cpp-ethereum into develop
-rw-r--r--AST.cpp39
-rwxr-xr-xAST.h95
-rw-r--r--BaseTypes.h4
-rw-r--r--CallGraph.cpp105
-rw-r--r--CallGraph.h69
-rw-r--r--Compiler.cpp165
-rw-r--r--Compiler.h14
-rw-r--r--CompilerContext.cpp79
-rw-r--r--CompilerContext.h31
-rw-r--r--CompilerStack.cpp1
-rw-r--r--CompilerStack.h2
-rw-r--r--ExpressionCompiler.cpp107
-rw-r--r--ExpressionCompiler.h15
-rw-r--r--GlobalContext.cpp8
-rw-r--r--GlobalContext.h2
-rw-r--r--InterfaceHandler.cpp38
-rw-r--r--Parser.cpp6
-rw-r--r--Parser.h2
-rw-r--r--Types.cpp98
-rw-r--r--Types.h44
20 files changed, 469 insertions, 455 deletions
diff --git a/AST.cpp b/AST.cpp
index e5967caa..bc6be600 100644
--- a/AST.cpp
+++ b/AST.cpp
@@ -66,21 +66,25 @@ void ContractDefinition::checkTypeRequirements()
// check for hash collisions in function signatures
set<FixedHash<4>> hashes;
- for (auto const& hashAndFunction: getInterfaceFunctionList())
+ for (auto const& it: getInterfaceFunctionList())
{
- FixedHash<4> const& hash = hashAndFunction.first;
+ FixedHash<4> const& hash = it.first;
if (hashes.count(hash))
- BOOST_THROW_EXCEPTION(createTypeError("Function signature hash collision for " +
- hashAndFunction.second->getCanonicalSignature()));
+ BOOST_THROW_EXCEPTION(createTypeError(
+ std::string("Function signature hash collision for ") +
+ it.second->getCanonicalSignature()));
hashes.insert(hash);
}
}
-map<FixedHash<4>, FunctionDefinition const*> ContractDefinition::getInterfaceFunctions() const
+map<FixedHash<4>, FunctionTypePointer> ContractDefinition::getInterfaceFunctions() const
{
- vector<pair<FixedHash<4>, FunctionDefinition const*>> exportedFunctionList = getInterfaceFunctionList();
- map<FixedHash<4>, FunctionDefinition const*> exportedFunctions(exportedFunctionList.begin(),
- exportedFunctionList.end());
+ auto exportedFunctionList = getInterfaceFunctionList();
+
+ map<FixedHash<4>, FunctionTypePointer> exportedFunctions;
+ for (auto const& it: exportedFunctionList)
+ exportedFunctions.insert(it);
+
solAssert(exportedFunctionList.size() == exportedFunctions.size(),
"Hash collision at Function Definition Hash calculation");
@@ -134,20 +138,31 @@ void ContractDefinition::checkIllegalOverrides() const
}
}
-vector<pair<FixedHash<4>, FunctionDefinition const*>> const& ContractDefinition::getInterfaceFunctionList() const
+vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::getInterfaceFunctionList() const
{
if (!m_interfaceFunctionList)
{
set<string> functionsSeen;
- m_interfaceFunctionList.reset(new vector<pair<FixedHash<4>, FunctionDefinition const*>>());
+ m_interfaceFunctionList.reset(new vector<pair<FixedHash<4>, FunctionTypePointer>>());
for (ContractDefinition const* contract: getLinearizedBaseContracts())
+ {
for (ASTPointer<FunctionDefinition> const& f: contract->getDefinedFunctions())
if (f->isPublic() && !f->isConstructor() && functionsSeen.count(f->getName()) == 0)
{
functionsSeen.insert(f->getName());
FixedHash<4> hash(dev::sha3(f->getCanonicalSignature()));
- m_interfaceFunctionList->push_back(make_pair(hash, f.get()));
+ m_interfaceFunctionList->push_back(make_pair(hash, make_shared<FunctionType>(*f, false)));
}
+
+ for (ASTPointer<VariableDeclaration> const& v: contract->getStateVariables())
+ if (v->isPublic() && functionsSeen.count(v->getName()) == 0)
+ {
+ FunctionType ftype(*v);
+ functionsSeen.insert(v->getName());
+ FixedHash<4> hash(dev::sha3(ftype.getCanonicalSignature(v->getName())));
+ m_interfaceFunctionList->push_back(make_pair(hash, make_shared<FunctionType>(*v)));
+ }
+ }
}
return *m_interfaceFunctionList;
}
@@ -219,7 +234,7 @@ void FunctionDefinition::checkTypeRequirements()
string FunctionDefinition::getCanonicalSignature() const
{
- return getName() + FunctionType(*this).getCanonicalSignature();
+ return FunctionType(*this).getCanonicalSignature(getName());
}
Declaration::LValueType VariableDeclaration::getLValueType() const
diff --git a/AST.h b/AST.h
index e047f976..6c207290 100755
--- a/AST.h
+++ b/AST.h
@@ -157,11 +157,42 @@ private:
};
/**
+ * Abstract class that is added to each AST node that can store local variables.
+ */
+class VariableScope
+{
+public:
+ void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); }
+ std::vector<VariableDeclaration const*> const& getLocalVariables() const { return m_localVariables; }
+
+private:
+ std::vector<VariableDeclaration const*> m_localVariables;
+};
+
+/**
+ * Abstract class that is added to each AST node that can receive documentation.
+ */
+class Documented
+{
+public:
+ explicit Documented(ASTPointer<ASTString> const& _documentation): m_documentation(_documentation) {}
+
+ /// @return A shared pointer of an ASTString.
+ /// Can contain a nullptr in which case indicates absence of documentation
+ ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
+
+protected:
+ ASTPointer<ASTString> m_documentation;
+};
+
+/// @}
+
+/**
* Definition of a contract. This is the only AST nodes where child nodes are not visited in
* document order. It first visits all struct declarations, then all variable declarations and
* finally all function declarations.
*/
-class ContractDefinition: public Declaration
+class ContractDefinition: public Declaration, public Documented
{
public:
ContractDefinition(Location const& _location,
@@ -172,13 +203,12 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables,
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions,
std::vector<ASTPointer<ModifierDefinition>> const& _functionModifiers):
- Declaration(_location, _name),
+ Declaration(_location, _name), Documented(_documentation),
m_baseContracts(_baseContracts),
m_definedStructs(_definedStructs),
m_stateVariables(_stateVariables),
m_definedFunctions(_definedFunctions),
- m_functionModifiers(_functionModifiers),
- m_documentation(_documentation)
+ m_functionModifiers(_functionModifiers)
{}
virtual void accept(ASTVisitor& _visitor) override;
@@ -196,13 +226,9 @@ public:
/// and calls checkTypeRequirements on all its functions.
void checkTypeRequirements();
- /// @return A shared pointer of an ASTString.
- /// Can contain a nullptr in which case indicates absence of documentation
- ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
-
/// @returns a map of canonical function signatures to FunctionDefinitions
/// as intended for use by the ABI.
- std::map<FixedHash<4>, FunctionDefinition const*> getInterfaceFunctions() const;
+ std::map<FixedHash<4>, FunctionTypePointer> getInterfaceFunctions() const;
/// List of all (direct and indirect) base contracts in order from derived to base, including
/// the contract itself. Available after name resolution
@@ -215,17 +241,16 @@ public:
private:
void checkIllegalOverrides() const;
- std::vector<std::pair<FixedHash<4>, FunctionDefinition const*>> const& getInterfaceFunctionList() const;
+ std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& getInterfaceFunctionList() const;
std::vector<ASTPointer<InheritanceSpecifier>> m_baseContracts;
std::vector<ASTPointer<StructDefinition>> m_definedStructs;
std::vector<ASTPointer<VariableDeclaration>> m_stateVariables;
std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions;
std::vector<ASTPointer<ModifierDefinition>> m_functionModifiers;
- ASTPointer<ASTString> m_documentation;
std::vector<ContractDefinition const*> m_linearizedBaseContracts;
- mutable std::unique_ptr<std::vector<std::pair<FixedHash<4>, FunctionDefinition const*>>> m_interfaceFunctionList;
+ mutable std::unique_ptr<std::vector<std::pair<FixedHash<4>, FunctionTypePointer>>> m_interfaceFunctionList;
};
class InheritanceSpecifier: public ASTNode
@@ -293,20 +318,7 @@ private:
std::vector<ASTPointer<VariableDeclaration>> m_parameters;
};
-/**
- * Abstract class that is added to each AST node that can store local variables.
- */
-class VariableScope
-{
-public:
- void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); }
- std::vector<VariableDeclaration const*> const& getLocalVariables() const { return m_localVariables; }
-
-private:
- std::vector<VariableDeclaration const*> m_localVariables;
-};
-
-class FunctionDefinition: public Declaration, public VariableScope
+class FunctionDefinition: public Declaration, public VariableScope, public Documented
{
public:
FunctionDefinition(Location const& _location, ASTPointer<ASTString> const& _name,
@@ -318,13 +330,13 @@ public:
std::vector<ASTPointer<ModifierInvocation>> const& _modifiers,
ASTPointer<ParameterList> const& _returnParameters,
ASTPointer<Block> const& _body):
- Declaration(_location, _name), m_isPublic(_isPublic), m_isConstructor(_isConstructor),
+ Declaration(_location, _name), Documented(_documentation),
+ m_isPublic(_isPublic), m_isConstructor(_isConstructor),
m_parameters(_parameters),
m_isDeclaredConst(_isDeclaredConst),
m_functionModifiers(_modifiers),
m_returnParameters(_returnParameters),
- m_body(_body),
- m_documentation(_documentation)
+ m_body(_body)
{}
virtual void accept(ASTVisitor& _visitor) override;
@@ -339,9 +351,6 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& getReturnParameters() const { return m_returnParameters->getParameters(); }
ASTPointer<ParameterList> const& getReturnParameterList() const { return m_returnParameters; }
Block const& getBody() const { return *m_body; }
- /// @return A shared pointer of an ASTString.
- /// Can contain a nullptr in which case indicates absence of documentation
- ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
virtual TypePointer getType(ContractDefinition const*) const override;
@@ -361,7 +370,6 @@ private:
std::vector<ASTPointer<ModifierInvocation>> m_functionModifiers;
ASTPointer<ParameterList> m_returnParameters;
ASTPointer<Block> m_body;
- ASTPointer<ASTString> m_documentation;
};
/**
@@ -372,8 +380,8 @@ class VariableDeclaration: public Declaration
{
public:
VariableDeclaration(Location const& _location, ASTPointer<TypeName> const& _type,
- ASTPointer<ASTString> const& _name):
- Declaration(_location, _name), m_typeName(_type) {}
+ ASTPointer<ASTString> const& _name, bool _isPublic, bool _isStateVar = false):
+ Declaration(_location, _name), m_typeName(_type), m_isPublic(_isPublic), m_isStateVariable(_isStateVar) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
@@ -385,9 +393,15 @@ public:
void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; }
virtual LValueType getLValueType() const override;
+ bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); }
+ bool isPublic() const { return m_isPublic; }
+ bool isStateVariable() const { return m_isStateVariable; }
+
private:
- ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
+ ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
+ bool m_isPublic; ///< Whether there is an accessor for it or not
+ bool m_isStateVariable; ///< Whether or not this is a contract state variable
std::shared_ptr<Type const> m_type; ///< derived type, initially empty
};
@@ -395,7 +409,7 @@ private:
/**
* Definition of a function modifier.
*/
-class ModifierDefinition: public Declaration, public VariableScope
+class ModifierDefinition: public Declaration, public VariableScope, public Documented
{
public:
ModifierDefinition(Location const& _location,
@@ -403,7 +417,7 @@ public:
ASTPointer<ASTString> const& _documentation,
ASTPointer<ParameterList> const& _parameters,
ASTPointer<Block> const& _body):
- Declaration(_location, _name), m_documentation(_documentation),
+ Declaration(_location, _name), Documented(_documentation),
m_parameters(_parameters), m_body(_body) {}
virtual void accept(ASTVisitor& _visitor) override;
@@ -415,14 +429,10 @@ public:
virtual TypePointer getType(ContractDefinition const* = nullptr) const override;
- /// @return A shared pointer of an ASTString.
- /// Can contain a nullptr in which case indicates absence of documentation
- ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
void checkTypeRequirements();
private:
- ASTPointer<ASTString> m_documentation;
ASTPointer<ParameterList> m_parameters;
ASTPointer<Block> m_body;
};
@@ -1076,5 +1086,6 @@ private:
/// @}
+
}
}
diff --git a/BaseTypes.h b/BaseTypes.h
index a8fd77c8..057289ef 100644
--- a/BaseTypes.h
+++ b/BaseTypes.h
@@ -41,6 +41,8 @@ struct Location
start(_start), end(_end), sourceName(_sourceName) { }
Location(): start(-1), end(-1) { }
+ bool isEmpty() const { return start == -1 && end == -1; }
+
int start;
int end;
std::shared_ptr<std::string const> sourceName;
@@ -49,6 +51,8 @@ struct Location
/// Stream output for Location (used e.g. in boost exceptions).
inline std::ostream& operator<<(std::ostream& _out, Location const& _location)
{
+ if (_location.isEmpty())
+ return _out << "NO_LOCATION_SPECIFIED";
return _out << *_location.sourceName << "[" << _location.start << "," << _location.end << ")";
}
diff --git a/CallGraph.cpp b/CallGraph.cpp
deleted file mode 100644
index 5f8fc547..00000000
--- a/CallGraph.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-
-/*
- This file is part of cpp-ethereum.
-
- cpp-ethereum is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- cpp-ethereum is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
-*/
-/**
- * @author Christian <c@ethdev.com>
- * @date 2014
- * Callgraph of functions inside a contract.
- */
-
-#include <libsolidity/AST.h>
-#include <libsolidity/CallGraph.h>
-
-using namespace std;
-
-namespace dev
-{
-namespace solidity
-{
-
-void CallGraph::addNode(ASTNode const& _node)
-{
- if (!m_nodesSeen.count(&_node))
- {
- m_workQueue.push(&_node);
- m_nodesSeen.insert(&_node);
- }
-}
-
-set<FunctionDefinition const*> const& CallGraph::getCalls()
-{
- computeCallGraph();
- return m_functionsSeen;
-}
-
-void CallGraph::computeCallGraph()
-{
- while (!m_workQueue.empty())
- {
- m_workQueue.front()->accept(*this);
- m_workQueue.pop();
- }
-}
-
-bool CallGraph::visit(Identifier const& _identifier)
-{
- if (auto fun = dynamic_cast<FunctionDefinition const*>(_identifier.getReferencedDeclaration()))
- {
- if (m_functionOverrideResolver)
- fun = (*m_functionOverrideResolver)(fun->getName());
- solAssert(fun, "Error finding override for function " + fun->getName());
- addNode(*fun);
- }
- if (auto modifier = dynamic_cast<ModifierDefinition const*>(_identifier.getReferencedDeclaration()))
- {
- if (m_modifierOverrideResolver)
- modifier = (*m_modifierOverrideResolver)(modifier->getName());
- solAssert(modifier, "Error finding override for modifier " + modifier->getName());
- addNode(*modifier);
- }
- return true;
-}
-
-bool CallGraph::visit(FunctionDefinition const& _function)
-{
- m_functionsSeen.insert(&_function);
- return true;
-}
-
-bool CallGraph::visit(MemberAccess const& _memberAccess)
-{
- // used for "BaseContract.baseContractFunction"
- if (_memberAccess.getExpression().getType()->getCategory() == Type::Category::TYPE)
- {
- TypeType const& type = dynamic_cast<TypeType const&>(*_memberAccess.getExpression().getType());
- if (type.getMembers().getMemberType(_memberAccess.getMemberName()))
- {
- ContractDefinition const& contract = dynamic_cast<ContractType const&>(*type.getActualType())
- .getContractDefinition();
- for (ASTPointer<FunctionDefinition> const& function: contract.getDefinedFunctions())
- if (function->getName() == _memberAccess.getMemberName())
- {
- addNode(*function);
- return true;
- }
- }
- }
- return true;
-}
-
-}
-}
diff --git a/CallGraph.h b/CallGraph.h
deleted file mode 100644
index 9af5cdf9..00000000
--- a/CallGraph.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- This file is part of cpp-ethereum.
-
- cpp-ethereum is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- cpp-ethereum is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
-*/
-/**
- * @author Christian <c@ethdev.com>
- * @date 2014
- * Callgraph of functions inside a contract.
- */
-
-#include <set>
-#include <queue>
-#include <functional>
-#include <boost/range/iterator_range.hpp>
-#include <libsolidity/ASTVisitor.h>
-
-namespace dev
-{
-namespace solidity
-{
-
-/**
- * Can be used to compute the graph of calls (or rather references) between functions of the same
- * contract. Current functionality is limited to computing all functions that are directly
- * or indirectly called by some functions.
- */
-class CallGraph: private ASTConstVisitor
-{
-public:
- using FunctionOverrideResolver = std::function<FunctionDefinition const*(std::string const&)>;
- using ModifierOverrideResolver = std::function<ModifierDefinition const*(std::string const&)>;
-
- CallGraph(FunctionOverrideResolver const& _functionOverrideResolver,
- ModifierOverrideResolver const& _modifierOverrideResolver):
- m_functionOverrideResolver(&_functionOverrideResolver),
- m_modifierOverrideResolver(&_modifierOverrideResolver) {}
-
- void addNode(ASTNode const& _node);
-
- std::set<FunctionDefinition const*> const& getCalls();
-
-private:
- virtual bool visit(FunctionDefinition const& _function) override;
- virtual bool visit(Identifier const& _identifier) override;
- virtual bool visit(MemberAccess const& _memberAccess) override;
-
- void computeCallGraph();
-
- FunctionOverrideResolver const* m_functionOverrideResolver;
- ModifierOverrideResolver const* m_modifierOverrideResolver;
- std::set<ASTNode const*> m_nodesSeen;
- std::set<FunctionDefinition const*> m_functionsSeen;
- std::queue<ASTNode const*> m_workQueue;
-};
-
-}
-}
diff --git a/Compiler.cpp b/Compiler.cpp
index 5190f93f..1fa31ce7 100644
--- a/Compiler.cpp
+++ b/Compiler.cpp
@@ -28,7 +28,6 @@
#include <libsolidity/Compiler.h>
#include <libsolidity/ExpressionCompiler.h>
#include <libsolidity/CompilerUtils.h>
-#include <libsolidity/CallGraph.h>
using namespace std;
@@ -40,22 +39,15 @@ void Compiler::compileContract(ContractDefinition const& _contract,
{
m_context = CompilerContext(); // clear it just in case
initializeContext(_contract, _contracts);
-
- for (ContractDefinition const* contract: _contract.getLinearizedBaseContracts())
+ appendFunctionSelector(_contract);
+ set<Declaration const*> functions = m_context.getFunctionsWithoutCode();
+ while (!functions.empty())
{
- for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions())
- if (!function->isConstructor())
- m_context.addFunction(*function);
- for (ASTPointer<ModifierDefinition> const& modifier: contract->getFunctionModifiers())
- m_context.addModifier(*modifier);
+ for (Declaration const* function: functions)
+ function->accept(*this);
+ functions = m_context.getFunctionsWithoutCode();
}
- appendFunctionSelector(_contract);
- for (ContractDefinition const* contract: _contract.getLinearizedBaseContracts())
- for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions())
- if (!function->isConstructor())
- function->accept(*this);
-
// Swap the runtime context with the creation-time context
swap(m_context, m_runtimeContext);
initializeContext(_contract, _contracts);
@@ -66,72 +58,26 @@ void Compiler::initializeContext(ContractDefinition const& _contract,
map<ContractDefinition const*, bytes const*> const& _contracts)
{
m_context.setCompiledContracts(_contracts);
+ m_context.setInheritanceHierarchy(_contract.getLinearizedBaseContracts());
registerStateVariables(_contract);
}
void Compiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext)
{
- std::vector<ContractDefinition const*> const& bases = _contract.getLinearizedBaseContracts();
-
- // Make all modifiers known to the context.
- for (ContractDefinition const* contract: bases)
- for (ASTPointer<ModifierDefinition> const& modifier: contract->getFunctionModifiers())
- m_context.addModifier(*modifier);
-
// arguments for base constructors, filled in derived-to-base order
map<ContractDefinition const*, vector<ASTPointer<Expression>> const*> baseArguments;
- set<FunctionDefinition const*> neededFunctions;
- set<ASTNode const*> nodesUsedInConstructors;
- // Determine the arguments that are used for the base constructors and also which functions
- // are needed at compile time.
+ // Determine the arguments that are used for the base constructors.
+ std::vector<ContractDefinition const*> const& bases = _contract.getLinearizedBaseContracts();
for (ContractDefinition const* contract: bases)
- {
- if (FunctionDefinition const* constructor = contract->getConstructor())
- nodesUsedInConstructors.insert(constructor);
for (ASTPointer<InheritanceSpecifier> const& base: contract->getBaseContracts())
{
ContractDefinition const* baseContract = dynamic_cast<ContractDefinition const*>(
base->getName()->getReferencedDeclaration());
solAssert(baseContract, "");
if (baseArguments.count(baseContract) == 0)
- {
baseArguments[baseContract] = &base->getArguments();
- for (ASTPointer<Expression> const& arg: base->getArguments())
- nodesUsedInConstructors.insert(arg.get());
- }
}
- }
-
- auto functionOverrideResolver = [&](string const& _name) -> FunctionDefinition const*
- {
- for (ContractDefinition const* contract: bases)
- for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions())
- if (!function->isConstructor() && function->getName() == _name)
- return function.get();
- return nullptr;
- };
- auto modifierOverrideResolver = [&](string const& _name) -> ModifierDefinition const*
- {
- return &m_context.getFunctionModifier(_name);
- };
-
- neededFunctions = getFunctionsCalled(nodesUsedInConstructors, functionOverrideResolver,
- modifierOverrideResolver);
-
- // First add all overrides (or the functions themselves if there is no override)
- for (FunctionDefinition const* fun: neededFunctions)
- {
- FunctionDefinition const* override = nullptr;
- if (!fun->isConstructor())
- override = functionOverrideResolver(fun->getName());
- if (!!override && neededFunctions.count(override))
- m_context.addFunction(*override);
- }
- // now add the rest
- for (FunctionDefinition const* fun: neededFunctions)
- if (fun->isConstructor() || functionOverrideResolver(fun->getName()) != fun)
- m_context.addFunction(*fun);
// Call constructors in base-to-derived order.
// The Constructor for the most derived contract is called later.
@@ -153,10 +99,14 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract, Comp
m_context << eth::Instruction::DUP1 << sub << u256(0) << eth::Instruction::CODECOPY;
m_context << u256(0) << eth::Instruction::RETURN;
- // note that we have to explicitly include all used functions because of absolute jump
- // labels
- for (FunctionDefinition const* fun: neededFunctions)
- fun->accept(*this);
+ // note that we have to include the functions again because of absolute jump labels
+ set<Declaration const*> functions = m_context.getFunctionsWithoutCode();
+ while (!functions.empty())
+ {
+ for (Declaration const* function: functions)
+ function->accept(*this);
+ functions = m_context.getFunctionsWithoutCode();
+ }
}
void Compiler::appendBaseConstructorCall(FunctionDefinition const& _constructor,
@@ -177,31 +127,22 @@ void Compiler::appendConstructorCall(FunctionDefinition const& _constructor)
unsigned argumentSize = 0;
for (ASTPointer<VariableDeclaration> const& var: _constructor.getParameters())
argumentSize += CompilerUtils::getPaddedSize(var->getType()->getCalldataEncodedSize());
+
if (argumentSize > 0)
{
m_context << u256(argumentSize);
m_context.appendProgramSize();
m_context << u256(CompilerUtils::dataStartOffset); // copy it to byte four as expected for ABI calls
m_context << eth::Instruction::CODECOPY;
- appendCalldataUnpacker(_constructor, true);
+ appendCalldataUnpacker(FunctionType(_constructor).getParameterTypes(), true);
}
m_context.appendJumpTo(m_context.getFunctionEntryLabel(_constructor));
m_context << returnTag;
}
-set<FunctionDefinition const*> Compiler::getFunctionsCalled(set<ASTNode const*> const& _nodes,
- function<FunctionDefinition const*(string const&)> const& _resolveFunctionOverrides,
- function<ModifierDefinition const*(string const&)> const& _resolveModifierOverrides)
-{
- CallGraph callgraph(_resolveFunctionOverrides, _resolveModifierOverrides);
- for (ASTNode const* node: _nodes)
- callgraph.addNode(*node);
- return callgraph.getCalls();
-}
-
void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
{
- map<FixedHash<4>, FunctionDefinition const*> interfaceFunctions = _contract.getInterfaceFunctions();
+ map<FixedHash<4>, FunctionTypePointer> interfaceFunctions = _contract.getInterfaceFunctions();
map<FixedHash<4>, const eth::AssemblyItem> callDataUnpackerEntryPoints;
// retrieve the function signature hash from the calldata
@@ -209,7 +150,6 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
CompilerUtils(m_context).loadFromMemory(0, 4, false, true);
// stack now is: 1 0 <funhash>
- // for (auto it = interfaceFunctions.cbegin(); it != interfaceFunctions.cend(); ++it)
for (auto const& it: interfaceFunctions)
{
callDataUnpackerEntryPoints.insert(std::make_pair(it.first, m_context.newTag()));
@@ -220,29 +160,28 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
for (auto const& it: interfaceFunctions)
{
- FunctionDefinition const& function = *it.second;
+ FunctionTypePointer const& functionType = it.second;
m_context << callDataUnpackerEntryPoints.at(it.first);
eth::AssemblyItem returnTag = m_context.pushNewTag();
- appendCalldataUnpacker(function);
- m_context.appendJumpTo(m_context.getFunctionEntryLabel(function));
+ appendCalldataUnpacker(functionType->getParameterTypes());
+ m_context.appendJumpTo(m_context.getFunctionEntryLabel(it.second->getDeclaration()));
m_context << returnTag;
- appendReturnValuePacker(function);
+ appendReturnValuePacker(functionType->getReturnParameterTypes());
}
}
-unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, bool _fromMemory)
+unsigned Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory)
{
// We do not check the calldata size, everything is zero-padded.
unsigned dataOffset = CompilerUtils::dataStartOffset; // the 4 bytes of the function hash signature
//@todo this can be done more efficiently, saving some CALLDATALOAD calls
- for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
+ for (TypePointer const& type: _typeParameters)
{
- unsigned const c_numBytes = var->getType()->getCalldataEncodedSize();
+ unsigned const c_numBytes = type->getCalldataEncodedSize();
if (c_numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError()
- << errinfo_sourceLocation(var->getLocation())
- << errinfo_comment("Type " + var->getType()->toString() + " not yet supported."));
- bool const c_leftAligned = var->getType()->getCategory() == Type::Category::STRING;
+ << errinfo_comment("Type " + type->toString() + " not yet supported."));
+ bool const c_leftAligned = type->getCategory() == Type::Category::STRING;
bool const c_padToWords = true;
dataOffset += CompilerUtils(m_context).loadFromMemory(dataOffset, c_numBytes, c_leftAligned,
!_fromMemory, c_padToWords);
@@ -250,26 +189,26 @@ unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, b
return dataOffset;
}
-void Compiler::appendReturnValuePacker(FunctionDefinition const& _function)
+void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters)
{
//@todo this can be also done more efficiently
unsigned dataOffset = 0;
- vector<ASTPointer<VariableDeclaration>> const& parameters = _function.getReturnParameters();
- unsigned stackDepth = CompilerUtils(m_context).getSizeOnStack(parameters);
- for (unsigned i = 0; i < parameters.size(); ++i)
+ unsigned stackDepth = 0;
+ for (TypePointer const& type: _typeParameters)
+ stackDepth += type->getSizeOnStack();
+
+ for (TypePointer const& type: _typeParameters)
{
- Type const& paramType = *parameters[i]->getType();
- unsigned numBytes = paramType.getCalldataEncodedSize();
+ unsigned numBytes = type->getCalldataEncodedSize();
if (numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError()
- << errinfo_sourceLocation(parameters[i]->getLocation())
- << errinfo_comment("Type " + paramType.toString() + " not yet supported."));
- CompilerUtils(m_context).copyToStackTop(stackDepth, paramType);
- ExpressionCompiler::appendTypeConversion(m_context, paramType, paramType, true);
- bool const c_leftAligned = paramType.getCategory() == Type::Category::STRING;
+ << errinfo_comment("Type " + type->toString() + " not yet supported."));
+ CompilerUtils(m_context).copyToStackTop(stackDepth, *type);
+ ExpressionCompiler::appendTypeConversion(m_context, *type, *type, true);
+ bool const c_leftAligned = type->getCategory() == Type::Category::STRING;
bool const c_padToWords = true;
dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, c_leftAligned, c_padToWords);
- stackDepth -= paramType.getSizeOnStack();
+ stackDepth -= type->getSizeOnStack();
}
// note that the stack is not cleaned up here
m_context << u256(dataOffset) << u256(0) << eth::Instruction::RETURN;
@@ -282,13 +221,31 @@ void Compiler::registerStateVariables(ContractDefinition const& _contract)
m_context.addStateVariable(*variable);
}
+bool Compiler::visit(VariableDeclaration const& _variableDeclaration)
+{
+ solAssert(_variableDeclaration.isStateVariable(), "Compiler visit to non-state variable declaration.");
+
+ m_context.startFunction(_variableDeclaration);
+ m_breakTags.clear();
+ m_continueTags.clear();
+
+ m_context << m_context.getFunctionEntryLabel(_variableDeclaration);
+ ExpressionCompiler::appendStateVariableAccessor(m_context, _variableDeclaration);
+
+ unsigned sizeOnStack = _variableDeclaration.getType()->getSizeOnStack();
+ solAssert(sizeOnStack <= 15, "Stack too deep.");
+ m_context << eth::dupInstruction(sizeOnStack + 1) << eth::Instruction::JUMP;
+
+ return false;
+}
+
bool Compiler::visit(FunctionDefinition const& _function)
{
//@todo to simplify this, the calling convention could by changed such that
// caller puts: [retarg0] ... [retargm] [return address] [arg0] ... [argn]
// although note that this reduces the size of the visible stack
- m_context.startNewFunction();
+ m_context.startFunction(_function);
m_returnTag = m_context.newTag();
m_breakTags.clear();
m_continueTags.clear();
@@ -296,8 +253,6 @@ bool Compiler::visit(FunctionDefinition const& _function)
m_currentFunction = &_function;
m_modifierDepth = 0;
- m_context << m_context.getFunctionEntryLabel(_function);
-
// stack upon entry: [return address] [arg0] [arg1] ... [argn]
// reserve additional slots: [retarg0] ... [retargm] [localvar0] ... [localvarp]
diff --git a/Compiler.h b/Compiler.h
index b6547646..b3eae5b1 100644
--- a/Compiler.h
+++ b/Compiler.h
@@ -50,19 +50,15 @@ private:
void appendBaseConstructorCall(FunctionDefinition const& _constructor,
std::vector<ASTPointer<Expression>> const& _arguments);
void appendConstructorCall(FunctionDefinition const& _constructor);
- /// Recursively searches the call graph and returns all functions referenced inside _nodes.
- /// _resolveOverride is called to resolve virtual function overrides.
- std::set<FunctionDefinition const*> getFunctionsCalled(std::set<ASTNode const*> const& _nodes,
- std::function<FunctionDefinition const*(std::string const&)> const& _resolveFunctionOverride,
- std::function<ModifierDefinition const*(std::string const&)> const& _resolveModifierOverride);
void appendFunctionSelector(ContractDefinition const& _contract);
- /// Creates code that unpacks the arguments for the given function, from memory if
- /// @a _fromMemory is true, otherwise from call data. @returns the size of the data in bytes.
- unsigned appendCalldataUnpacker(FunctionDefinition const& _function, bool _fromMemory = false);
- void appendReturnValuePacker(FunctionDefinition const& _function);
+ /// Creates code that unpacks the arguments for the given function represented by a vector of TypePointers.
+ /// From memory if @a _fromMemory is true, otherwise from call data. @returns the size of the data in bytes.
+ unsigned appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory = false);
+ void appendReturnValuePacker(TypePointers const& _typeParameters);
void registerStateVariables(ContractDefinition const& _contract);
+ virtual bool visit(VariableDeclaration const& _variableDeclaration) override;
virtual bool visit(FunctionDefinition const& _function) override;
virtual bool visit(IfStatement const& _ifStatement) override;
virtual bool visit(WhileStatement const& _whileStatement) override;
diff --git a/CompilerContext.cpp b/CompilerContext.cpp
index ad1877ba..52910a55 100644
--- a/CompilerContext.cpp
+++ b/CompilerContext.cpp
@@ -43,6 +43,14 @@ void CompilerContext::addStateVariable(VariableDeclaration const& _declaration)
m_stateVariablesSize += _declaration.getType()->getStorageSize();
}
+void CompilerContext::startFunction(Declaration const& _function)
+{
+ m_functionsWithCode.insert(&_function);
+ m_localVariables.clear();
+ m_asm.setDeposit(0);
+ *this << getFunctionEntryLabel(_function);
+}
+
void CompilerContext::addVariable(VariableDeclaration const& _declaration,
unsigned _offsetToCurrent)
{
@@ -59,18 +67,6 @@ void CompilerContext::addAndInitializeVariable(VariableDeclaration const& _decla
*this << u256(0);
}
-void CompilerContext::addFunction(FunctionDefinition const& _function)
-{
- eth::AssemblyItem tag(m_asm.newTag());
- m_functionEntryLabels.insert(make_pair(&_function, tag));
- m_virtualFunctionEntryLabels.insert(make_pair(_function.getName(), tag));
-}
-
-void CompilerContext::addModifier(ModifierDefinition const& _modifier)
-{
- m_functionModifiers.insert(make_pair(_modifier.getName(), &_modifier));
-}
-
bytes const& CompilerContext::getCompiledContract(const ContractDefinition& _contract) const
{
auto ret = m_compiledContracts.find(&_contract);
@@ -83,25 +79,62 @@ bool CompilerContext::isLocalVariable(Declaration const* _declaration) const
return m_localVariables.count(_declaration);
}
-eth::AssemblyItem CompilerContext::getFunctionEntryLabel(FunctionDefinition const& _function) const
+eth::AssemblyItem CompilerContext::getFunctionEntryLabel(Declaration const& _declaration)
+{
+ auto res = m_functionEntryLabels.find(&_declaration);
+ if (res == m_functionEntryLabels.end())
+ {
+ eth::AssemblyItem tag(m_asm.newTag());
+ m_functionEntryLabels.insert(make_pair(&_declaration, tag));
+ return tag.tag();
+ }
+ else
+ return res->second.tag();
+}
+
+eth::AssemblyItem CompilerContext::getVirtualFunctionEntryLabel(FunctionDefinition const& _function)
+{
+ solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");
+ for (ContractDefinition const* contract: m_inheritanceHierarchy)
+ for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions())
+ if (!function->isConstructor() && function->getName() == _function.getName())
+ return getFunctionEntryLabel(*function);
+ solAssert(false, "Virtual function " + _function.getName() + " not found.");
+ return m_asm.newTag(); // not reached
+}
+
+eth::AssemblyItem CompilerContext::getSuperFunctionEntryLabel(string const& _name, ContractDefinition const& _base)
{
- auto res = m_functionEntryLabels.find(&_function);
- solAssert(res != m_functionEntryLabels.end(), "Function entry label not found.");
- return res->second.tag();
+ // search for first contract after _base
+ solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");
+ auto it = find(m_inheritanceHierarchy.begin(), m_inheritanceHierarchy.end(), &_base);
+ solAssert(it != m_inheritanceHierarchy.end(), "Base not found in inheritance hierarchy.");
+ for (++it; it != m_inheritanceHierarchy.end(); ++it)
+ for (ASTPointer<FunctionDefinition> const& function: (*it)->getDefinedFunctions())
+ if (!function->isConstructor() && function->getName() == _name)
+ return getFunctionEntryLabel(*function);
+ solAssert(false, "Super function " + _name + " not found.");
+ return m_asm.newTag(); // not reached
}
-eth::AssemblyItem CompilerContext::getVirtualFunctionEntryLabel(FunctionDefinition const& _function) const
+set<Declaration const*> CompilerContext::getFunctionsWithoutCode()
{
- auto res = m_virtualFunctionEntryLabels.find(_function.getName());
- solAssert(res != m_virtualFunctionEntryLabels.end(), "Function entry label not found.");
- return res->second.tag();
+ set<Declaration const*> functions;
+ for (auto const& it: m_functionEntryLabels)
+ if (m_functionsWithCode.count(it.first) == 0)
+ functions.insert(it.first);
+ return move(functions);
}
ModifierDefinition const& CompilerContext::getFunctionModifier(string const& _name) const
{
- auto res = m_functionModifiers.find(_name);
- solAssert(res != m_functionModifiers.end(), "Function modifier override not found.");
- return *res->second;
+ solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");
+ for (ContractDefinition const* contract: m_inheritanceHierarchy)
+ for (ASTPointer<ModifierDefinition> const& modifier: contract->getFunctionModifiers())
+ if (modifier->getName() == _name)
+ return *modifier.get();
+ BOOST_THROW_EXCEPTION(InternalCompilerError()
+ << errinfo_comment("Function modifier " + _name + " not found."));
}
unsigned CompilerContext::getBaseStackOffsetOfVariable(Declaration const& _declaration) const
diff --git a/CompilerContext.h b/CompilerContext.h
index d82dfe51..6d6a65b6 100644
--- a/CompilerContext.h
+++ b/CompilerContext.h
@@ -41,12 +41,8 @@ class CompilerContext
public:
void addMagicGlobal(MagicVariableDeclaration const& _declaration);
void addStateVariable(VariableDeclaration const& _declaration);
- void startNewFunction() { m_localVariables.clear(); m_asm.setDeposit(0); }
void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0);
void addAndInitializeVariable(VariableDeclaration const& _declaration);
- void addFunction(FunctionDefinition const& _function);
- /// Adds the given modifier to the list by name if the name is not present already.
- void addModifier(ModifierDefinition const& _modifier);
void setCompiledContracts(std::map<ContractDefinition const*, bytes const*> const& _contracts) { m_compiledContracts = _contracts; }
bytes const& getCompiledContract(ContractDefinition const& _contract) const;
@@ -54,13 +50,22 @@ public:
void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); }
bool isMagicGlobal(Declaration const* _declaration) const { return m_magicGlobals.count(_declaration) != 0; }
- bool isFunctionDefinition(Declaration const* _declaration) const { return m_functionEntryLabels.count(_declaration) != 0; }
bool isLocalVariable(Declaration const* _declaration) const;
bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration) != 0; }
- eth::AssemblyItem getFunctionEntryLabel(FunctionDefinition const& _function) const;
+ eth::AssemblyItem getFunctionEntryLabel(Declaration const& _declaration);
+ void setInheritanceHierarchy(std::vector<ContractDefinition const*> const& _hierarchy) { m_inheritanceHierarchy = _hierarchy; }
/// @returns the entry label of the given function and takes overrides into account.
- eth::AssemblyItem getVirtualFunctionEntryLabel(FunctionDefinition const& _function) const;
+ eth::AssemblyItem getVirtualFunctionEntryLabel(FunctionDefinition const& _function);
+ /// @returns the entry label of function with the given name from the most derived class just
+ /// above _base in the current inheritance hierarchy.
+ eth::AssemblyItem getSuperFunctionEntryLabel(std::string const& _name, ContractDefinition const& _base);
+ /// @returns the set of functions for which we still need to generate code
+ std::set<Declaration const*> getFunctionsWithoutCode();
+ /// Resets function specific members, inserts the function entry label and marks the function
+ /// as "having code".
+ void startFunction(Declaration const& _function);
+
ModifierDefinition const& getFunctionModifier(std::string const& _name) const;
/// Returns the distance of the given local variable from the bottom of the stack (of the current function).
unsigned getBaseStackOffsetOfVariable(Declaration const& _declaration) const;
@@ -115,14 +120,14 @@ private:
u256 m_stateVariablesSize = 0;
/// Storage offsets of state variables
std::map<Declaration const*, u256> m_stateVariables;
- /// Positions of local variables on the stack.
+ /// Offsets of local variables on the stack (relative to stack base).
std::map<Declaration const*, unsigned> m_localVariables;
- /// Labels pointing to the entry points of funcitons.
+ /// Labels pointing to the entry points of functions.
std::map<Declaration const*, eth::AssemblyItem> m_functionEntryLabels;
- /// Labels pointing to the entry points of function overrides.
- std::map<std::string, eth::AssemblyItem> m_virtualFunctionEntryLabels;
- /// Mapping to obtain function modifiers by name. Should be filled from derived to base.
- std::map<std::string, ModifierDefinition const*> m_functionModifiers;
+ /// Set of functions for which we did not yet generate code.
+ std::set<Declaration const*> m_functionsWithCode;
+ /// List of current inheritance hierarchy from derived to base.
+ std::vector<ContractDefinition const*> m_inheritanceHierarchy;
};
}
diff --git a/CompilerStack.cpp b/CompilerStack.cpp
index 28687e90..0ec88ebd 100644
--- a/CompilerStack.cpp
+++ b/CompilerStack.cpp
@@ -94,6 +94,7 @@ void CompilerStack::parse()
{
m_globalContext->setCurrentContract(*contract);
resolver.updateDeclaration(*m_globalContext->getCurrentThis());
+ resolver.updateDeclaration(*m_globalContext->getCurrentSuper());
resolver.resolveNamesAndTypes(*contract);
m_contracts[contract->getName()].contract = contract;
}
diff --git a/CompilerStack.h b/CompilerStack.h
index 66b05d42..a6c3df8e 100644
--- a/CompilerStack.h
+++ b/CompilerStack.h
@@ -60,7 +60,7 @@ class CompilerStack: boost::noncopyable
{
public:
/// Creates a new compiler stack. Adds standard sources if @a _addStandardSources.
- explicit CompilerStack(bool _addStandardSources = true);
+ explicit CompilerStack(bool _addStandardSources = false);
/// Adds a source object (e.g. file) to the parser. After this, parse has to be called again.
/// @returns true if a source object by the name already existed and was replaced.
diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp
index bd8c8653..5d44c86f 100644
--- a/ExpressionCompiler.cpp
+++ b/ExpressionCompiler.cpp
@@ -48,6 +48,12 @@ void ExpressionCompiler::appendTypeConversion(CompilerContext& _context, Type co
compiler.appendTypeConversion(_typeOnStack, _targetType, _cleanupNeeded);
}
+void ExpressionCompiler::appendStateVariableAccessor(CompilerContext& _context, VariableDeclaration const& _varDecl, bool _optimize)
+{
+ ExpressionCompiler compiler(_context, _optimize);
+ compiler.appendStateVariableAccessor(_varDecl);
+}
+
bool ExpressionCompiler::visit(Assignment const& _assignment)
{
_assignment.getRightHandSide().accept(*this);
@@ -60,7 +66,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
{
if (m_currentLValue.storesReferenceOnStack())
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2;
- m_currentLValue.retrieveValue(_assignment, true);
+ m_currentLValue.retrieveValue(_assignment.getType(), _assignment.getLocation(), true);
appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.getType());
if (m_currentLValue.storesReferenceOnStack())
m_context << eth::Instruction::SWAP1;
@@ -101,7 +107,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
case Token::INC: // ++ (pre- or postfix)
case Token::DEC: // -- (pre- or postfix)
solAssert(m_currentLValue.isValid(), "LValue not retrieved.");
- m_currentLValue.retrieveValue(_unaryOperation);
+ m_currentLValue.retrieveValue(_unaryOperation.getType(), _unaryOperation.getLocation());
if (!_unaryOperation.isPrefixOperation())
{
if (m_currentLValue.storesReferenceOnStack())
@@ -359,15 +365,25 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
{
case Type::Category::CONTRACT:
{
+ bool alsoSearchInteger = false;
ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.getExpression().getType());
- u256 identifier = type.getFunctionIdentifier(member);
- if (identifier != Invalid256)
+ if (type.isSuper())
+ m_context << m_context.getSuperFunctionEntryLabel(member, type.getContractDefinition()).pushTag();
+ else
{
- appendTypeConversion(type, IntegerType(0, IntegerType::Modifier::ADDRESS), true);
- m_context << identifier;
- break;
+ // ordinary contract type
+ u256 identifier = type.getFunctionIdentifier(member);
+ if (identifier != Invalid256)
+ {
+ appendTypeConversion(type, IntegerType(0, IntegerType::Modifier::ADDRESS), true);
+ m_context << identifier;
+ }
+ else
+ // not found in contract, search in members inherited from address
+ alsoSearchInteger = true;
}
- // fall-through to "integer" otherwise (address)
+ if (!alsoSearchInteger)
+ break;
}
case Type::Category::INTEGER:
if (member == "balance")
@@ -463,8 +479,10 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
Declaration const* declaration = _identifier.getReferencedDeclaration();
if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration))
{
- if (magicVar->getType()->getCategory() == Type::Category::CONTRACT) // must be "this"
- m_context << eth::Instruction::ADDRESS;
+ if (magicVar->getType()->getCategory() == Type::Category::CONTRACT)
+ // "this" or "super"
+ if (!dynamic_cast<ContractType const&>(*magicVar->getType()).isSuper())
+ m_context << eth::Instruction::ADDRESS;
}
else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
m_context << m_context.getVirtualFunctionEntryLabel(*functionDef).pushTag();
@@ -789,6 +807,13 @@ unsigned ExpressionCompiler::appendArgumentCopyToMemory(TypePointers const& _typ
return length;
}
+void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl)
+{
+ m_currentLValue.fromStateVariable(_varDecl, _varDecl.getType());
+ solAssert(m_currentLValue.isInStorage(), "");
+ m_currentLValue.retrieveValue(_varDecl.getType(), Location(), true);
+}
+
ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType,
unsigned _baseStackOffset):
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset)
@@ -801,7 +826,7 @@ ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType
m_size = unsigned(_dataType.getSizeOnStack());
}
-void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bool _remove) const
+void ExpressionCompiler::LValue::retrieveValue(TypePointer const& _type, Location const& _location, bool _remove) const
{
switch (m_type)
{
@@ -809,42 +834,47 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo
{
unsigned stackPos = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset));
if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory
- BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Stack too deep."));
for (unsigned i = 0; i < m_size; ++i)
*m_context << eth::dupInstruction(stackPos + 1);
break;
}
case STORAGE:
- if (!_expression.getType()->isValueType())
- break; // no distinction between value and reference for non-value types
- if (!_remove)
- *m_context << eth::Instruction::DUP1;
- if (m_size == 1)
- *m_context << eth::Instruction::SLOAD;
- else
- for (unsigned i = 0; i < m_size; ++i)
- {
- *m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1;
- if (i + 1 < m_size)
- *m_context << u256(1) << eth::Instruction::ADD;
- else
- *m_context << eth::Instruction::POP;
- }
+ retrieveValueFromStorage(_type, _remove);
break;
case MEMORY:
- if (!_expression.getType()->isValueType())
+ if (!_type->isValueType())
break; // no distinction between value and reference for non-value types
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Location type not yet implemented."));
break;
default:
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Unsupported location type."));
break;
}
}
+void ExpressionCompiler::LValue::retrieveValueFromStorage(TypePointer const& _type, bool _remove) const
+{
+ if (!_type->isValueType())
+ return; // no distinction between value and reference for non-value types
+ if (!_remove)
+ *m_context << eth::Instruction::DUP1;
+ if (m_size == 1)
+ *m_context << eth::Instruction::SLOAD;
+ else
+ for (unsigned i = 0; i < m_size; ++i)
+ {
+ *m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1;
+ if (i + 1 < m_size)
+ *m_context << u256(1) << eth::Instruction::ADD;
+ else
+ *m_context << eth::Instruction::POP;
+ }
+}
+
void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool _move) const
{
switch (m_type)
@@ -859,7 +889,7 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
for (unsigned i = 0; i < m_size; ++i)
*m_context << eth::swapInstruction(stackDiff) << eth::Instruction::POP;
if (!_move)
- retrieveValue(_expression);
+ retrieveValue(_expression.getType(), _expression.getLocation());
break;
}
case LValue::STORAGE:
@@ -946,11 +976,19 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression co
{
if (!_expression.lvalueRequested())
{
- retrieveValue(_expression, true);
+ retrieveValue(_expression.getType(), _expression.getLocation(), true);
reset();
}
}
+void ExpressionCompiler::LValue::fromStateVariable(Declaration const& _varDecl, TypePointer const& _type)
+{
+ m_type = STORAGE;
+ solAssert(_type->getStorageSize() <= numeric_limits<unsigned>::max(), "The storage size of " + _type->toString() + " should fit in an unsigned");
+ *m_context << m_context->getStorageLocationOfVariable(_varDecl);
+ m_size = unsigned(_type->getStorageSize());
+}
+
void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, Declaration const& _declaration)
{
if (m_context->isLocalVariable(&_declaration))
@@ -961,10 +999,7 @@ void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, D
}
else if (m_context->isStateVariable(&_declaration))
{
- m_type = STORAGE;
- solAssert(_identifier.getType()->getStorageSize() <= numeric_limits<unsigned>::max(), "The storage size of " + _identifier.getType()->toString() + " should fit in unsigned");
- m_size = unsigned(_identifier.getType()->getStorageSize());
- *m_context << m_context->getStorageLocationOfVariable(_declaration);
+ fromStateVariable(_declaration, _identifier.getType());
}
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_identifier.getLocation())
diff --git a/ExpressionCompiler.h b/ExpressionCompiler.h
index 8f784fde..748cc6c6 100644
--- a/ExpressionCompiler.h
+++ b/ExpressionCompiler.h
@@ -53,6 +53,8 @@ public:
/// Appends code to remove dirty higher order bits in case of an implicit promotion to a wider type.
static void appendTypeConversion(CompilerContext& _context, Type const& _typeOnStack,
Type const& _targetType, bool _cleanupNeeded = false);
+ /// Appends code for a State Variable accessor function
+ static void appendStateVariableAccessor(CompilerContext& _context, VariableDeclaration const& _varDecl, bool _optimize = false);
private:
explicit ExpressionCompiler(CompilerContext& _compilerContext, bool _optimize = false):
@@ -95,6 +97,9 @@ private:
unsigned appendArgumentCopyToMemory(TypePointers const& _functionType, std::vector<ASTPointer<Expression const>> const& _arguments,
unsigned _memoryOffset = 0);
+ /// Appends code for a State Variable accessor function
+ void appendStateVariableAccessor(VariableDeclaration const& _varDecl);
+
/**
* Helper class to store and retrieve lvalues to and from various locations.
* All types except STACK store a reference in a slot on the stack, STACK just
@@ -111,6 +116,8 @@ private:
/// Set type according to the declaration and retrieve the reference.
/// @a _expression is the current expression
void fromIdentifier(Identifier const& _identifier, Declaration const& _declaration);
+ /// Convenience function to set type for a state variable and retrieve the reference
+ void fromStateVariable(Declaration const& _varDecl, TypePointer const& _type);
void reset() { m_type = NONE; m_baseStackOffset = 0; m_size = 0; }
bool isValid() const { return m_type != NONE; }
@@ -123,8 +130,9 @@ private:
/// Copies the value of the current lvalue to the top of the stack and, if @a _remove is true,
/// also removes the reference from the stack (note that is does not reset the type to @a NONE).
- /// @a _expression is the current expression, used for error reporting.
- void retrieveValue(Expression const& _expression, bool _remove = false) const;
+ /// @a _type is the type of the current expression and @ _location its location, used for error reporting.
+ /// @a _location can be a nullptr for expressions that don't have an actual ASTNode equivalent
+ void retrieveValue(TypePointer const& _type, Location const& _location, bool _remove = false) const;
/// Stores a value (from the stack directly beneath the reference, which is assumed to
/// be on the top of the stack, if any) in the lvalue and removes the reference.
/// Also removes the stored value from the stack if @a _move is
@@ -138,6 +146,9 @@ private:
void retrieveValueIfLValueNotRequested(Expression const& _expression);
private:
+ /// Convenience function to retrieve Value from Storage. Specific version of @ref retrieveValue
+ void retrieveValueFromStorage(TypePointer const& _type, bool _remove = false) const;
+
CompilerContext* m_context;
LValueType m_type = NONE;
/// If m_type is STACK, this is base stack offset (@see
diff --git a/GlobalContext.cpp b/GlobalContext.cpp
index c7eea92d..687c9c9d 100644
--- a/GlobalContext.cpp
+++ b/GlobalContext.cpp
@@ -83,5 +83,13 @@ MagicVariableDeclaration const* GlobalContext::getCurrentThis() const
}
+MagicVariableDeclaration const* GlobalContext::getCurrentSuper() const
+{
+ if (!m_superPointer[m_currentContract])
+ m_superPointer[m_currentContract] = make_shared<MagicVariableDeclaration>(
+ "super", make_shared<ContractType>(*m_currentContract, true));
+ return m_superPointer[m_currentContract].get();
+}
+
}
}
diff --git a/GlobalContext.h b/GlobalContext.h
index dfdc6662..f861c67d 100644
--- a/GlobalContext.h
+++ b/GlobalContext.h
@@ -48,6 +48,7 @@ public:
GlobalContext();
void setCurrentContract(ContractDefinition const& _contract);
MagicVariableDeclaration const* getCurrentThis() const;
+ MagicVariableDeclaration const* getCurrentSuper() const;
/// @returns a vector of all implicit global declarations excluding "this".
std::vector<Declaration const*> getDeclarations() const;
@@ -56,6 +57,7 @@ private:
std::vector<std::shared_ptr<MagicVariableDeclaration const>> m_magicVariables;
ContractDefinition const* m_currentContract = nullptr;
std::map<ContractDefinition const*, std::shared_ptr<MagicVariableDeclaration const>> mutable m_thisPointer;
+ std::map<ContractDefinition const*, std::shared_ptr<MagicVariableDeclaration const>> mutable m_superPointer;
};
}
diff --git a/InterfaceHandler.cpp b/InterfaceHandler.cpp
index 1adce8cb..92cd5156 100644
--- a/InterfaceHandler.cpp
+++ b/InterfaceHandler.cpp
@@ -45,23 +45,26 @@ std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinitio
Json::Value inputs(Json::arrayValue);
Json::Value outputs(Json::arrayValue);
- auto populateParameters = [](std::vector<ASTPointer<VariableDeclaration>> const& _vars)
+ auto populateParameters = [](vector<string> const& _paramNames,
+ vector<string> const& _paramTypes)
{
Json::Value params(Json::arrayValue);
- for (ASTPointer<VariableDeclaration> const& var: _vars)
+ solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match");
+ for (unsigned i = 0; i < _paramNames.size(); ++i)
{
Json::Value input;
- input["name"] = var->getName();
- input["type"] = var->getType()->toString();
+ input["name"] = _paramNames[i];
+ input["type"] = _paramTypes[i];
params.append(input);
}
return params;
};
-
- method["name"] = it.second->getName();
- method["constant"] = it.second->isDeclaredConst();
- method["inputs"] = populateParameters(it.second->getParameters());
- method["outputs"] = populateParameters(it.second->getReturnParameters());
+ method["name"] = it.second->getDeclaration().getName();
+ method["constant"] = it.second->isConstant();
+ method["inputs"] = populateParameters(it.second->getParameterNames(),
+ it.second->getParameterTypeNames());
+ method["outputs"] = populateParameters(it.second->getReturnParameterNames(),
+ it.second->getReturnParameterTypeNames());
methods.append(method);
}
return std::unique_ptr<std::string>(new std::string(m_writer.write(methods)));
@@ -72,17 +75,20 @@ unique_ptr<string> InterfaceHandler::getABISolidityInterface(ContractDefinition
string ret = "contract " + _contractDef.getName() + "{";
for (auto const& it: _contractDef.getInterfaceFunctions())
{
- FunctionDefinition const* f = it.second;
- auto populateParameters = [](vector<ASTPointer<VariableDeclaration>> const& _vars)
+ auto populateParameters = [](vector<string> const& _paramNames,
+ vector<string> const& _paramTypes)
{
string r = "";
- for (ASTPointer<VariableDeclaration> const& var: _vars)
- r += (r.size() ? "," : "(") + var->getType()->toString() + " " + var->getName();
+ solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match");
+ for (unsigned i = 0; i < _paramNames.size(); ++i)
+ r += (r.size() ? "," : "(") + _paramTypes[i] + " " + _paramNames[i];
return r.size() ? r + ")" : "()";
};
- ret += "function " + f->getName() + populateParameters(f->getParameters()) + (f->isDeclaredConst() ? "constant " : "");
- if (f->getReturnParameters().size())
- ret += "returns" + populateParameters(f->getReturnParameters());
+ ret += "function " + it.second->getDeclaration().getName() +
+ populateParameters(it.second->getParameterNames(), it.second->getParameterTypeNames()) +
+ (it.second->isConstant() ? "constant " : "");
+ if (it.second->getReturnParameterTypes().size())
+ ret += "returns" + populateParameters(it.second->getReturnParameterNames(), it.second->getReturnParameterTypeNames());
else if (ret.back() == ' ')
ret.pop_back();
ret += "{}";
diff --git a/Parser.cpp b/Parser.cpp
index d99d33ac..5cfc8f46 100644
--- a/Parser.cpp
+++ b/Parser.cpp
@@ -150,7 +150,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
Token::isElementaryTypeName(currentToken))
{
bool const allowVar = false;
- stateVariables.push_back(parseVariableDeclaration(allowVar));
+ stateVariables.push_back(parseVariableDeclaration(allowVar, visibilityIsPublic, true));
expectToken(Token::SEMICOLON);
}
else if (currentToken == Token::MODIFIER)
@@ -245,12 +245,12 @@ ASTPointer<StructDefinition> Parser::parseStructDefinition()
return nodeFactory.createNode<StructDefinition>(name, members);
}
-ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(bool _allowVar)
+ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(bool _allowVar, bool _isPublic, bool _isStateVariable)
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<TypeName> type = parseTypeName(_allowVar);
nodeFactory.markEndPosition();
- return nodeFactory.createNode<VariableDeclaration>(type, expectIdentifierToken());
+ return nodeFactory.createNode<VariableDeclaration>(type, expectIdentifierToken(), _isPublic, _isStateVariable);
}
ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
diff --git a/Parser.h b/Parser.h
index 211e952d..d3bff67e 100644
--- a/Parser.h
+++ b/Parser.h
@@ -52,7 +52,7 @@ private:
ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier();
ASTPointer<FunctionDefinition> parseFunctionDefinition(bool _isPublic, ASTString const* _contractName);
ASTPointer<StructDefinition> parseStructDefinition();
- ASTPointer<VariableDeclaration> parseVariableDeclaration(bool _allowVar);
+ ASTPointer<VariableDeclaration> parseVariableDeclaration(bool _allowVar, bool _isPublic = false, bool _isStateVar = false);
ASTPointer<ModifierDefinition> parseModifierDefinition();
ASTPointer<ModifierInvocation> parseModifierInvocation();
ASTPointer<Identifier> parseIdentifier();
diff --git a/Types.cpp b/Types.cpp
index dad3a145..bebb4be1 100644
--- a/Types.cpp
+++ b/Types.cpp
@@ -450,7 +450,9 @@ bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
if (_convertTo.getCategory() == Category::CONTRACT)
{
auto const& bases = getContractDefinition().getLinearizedBaseContracts();
- return find(bases.begin(), bases.end(),
+ if (m_super && bases.size() <= 1)
+ return false;
+ return find(m_super ? ++bases.begin() : bases.begin(), bases.end(),
&dynamic_cast<ContractType const&>(_convertTo).getContractDefinition()) != bases.end();
}
return false;
@@ -472,12 +474,12 @@ bool ContractType::operator==(Type const& _other) const
if (_other.getCategory() != getCategory())
return false;
ContractType const& other = dynamic_cast<ContractType const&>(_other);
- return other.m_contract == m_contract;
+ return other.m_contract == m_contract && other.m_super == m_super;
}
string ContractType::toString() const
{
- return "contract " + m_contract.getName();
+ return "contract " + string(m_super ? "super " : "") + m_contract.getName();
}
MemberList const& ContractType::getMembers() const
@@ -488,8 +490,16 @@ MemberList const& ContractType::getMembers() const
// All address members and all interface functions
map<string, shared_ptr<Type const>> members(IntegerType::AddressMemberList.begin(),
IntegerType::AddressMemberList.end());
- for (auto const& it: m_contract.getInterfaceFunctions())
- members[it.second->getName()] = make_shared<FunctionType>(*it.second, false);
+ if (m_super)
+ {
+ for (ContractDefinition const* base: m_contract.getLinearizedBaseContracts())
+ for (ASTPointer<FunctionDefinition> const& function: base->getDefinedFunctions())
+ if (!function->isConstructor())
+ members.insert(make_pair(function->getName(), make_shared<FunctionType>(*function, true)));
+ }
+ else
+ for (auto const& it: m_contract.getInterfaceFunctions())
+ members[it.second->getDeclaration().getName()] = it.second;
m_members.reset(new MemberList(members));
}
return *m_members;
@@ -511,9 +521,9 @@ shared_ptr<FunctionType const> const& ContractType::getConstructorType() const
u256 ContractType::getFunctionIdentifier(string const& _functionName) const
{
auto interfaceFunctions = m_contract.getInterfaceFunctions();
- for (auto it = interfaceFunctions.cbegin(); it != interfaceFunctions.cend(); ++it)
- if (it->second->getName() == _functionName)
- return FixedHash<4>::Arith(it->first);
+ for (auto const& it: m_contract.getInterfaceFunctions())
+ if (it.second->getDeclaration().getName() == _functionName)
+ return FixedHash<4>::Arith(it.first);
return Invalid256;
}
@@ -579,18 +589,48 @@ u256 StructType::getStorageOffsetOfMember(string const& _name) const
}
FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal):
- m_location(_isInternal ? Location::INTERNAL : Location::EXTERNAL)
+ m_location(_isInternal ? Location::INTERNAL : Location::EXTERNAL),
+ m_isConstant(_function.isDeclaredConst()),
+ m_declaration(&_function)
{
TypePointers params;
+ vector<string> paramNames;
TypePointers retParams;
+ vector<string> retParamNames;
+
params.reserve(_function.getParameters().size());
+ paramNames.reserve(_function.getParameters().size());
for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
+ {
+ paramNames.push_back(var->getName());
params.push_back(var->getType());
+ }
retParams.reserve(_function.getReturnParameters().size());
+ retParamNames.reserve(_function.getReturnParameters().size());
for (ASTPointer<VariableDeclaration> const& var: _function.getReturnParameters())
+ {
+ retParamNames.push_back(var->getName());
retParams.push_back(var->getType());
+ }
+ swap(params, m_parameterTypes);
+ swap(paramNames, m_parameterNames);
+ swap(retParams, m_returnParameterTypes);
+ swap(retParamNames, m_returnParameterNames);
+}
+
+FunctionType::FunctionType(VariableDeclaration const& _varDecl):
+ m_location(Location::EXTERNAL), m_isConstant(true), m_declaration(&_varDecl)
+{
+ TypePointers params({});
+ vector<string> paramNames({});
+ TypePointers retParams({_varDecl.getType()});
+ vector<string> retParamNames({ _varDecl.getName()});
+ // for now, no input parameters LTODO: change for some things like mapping
+
swap(params, m_parameterTypes);
+ swap(paramNames, m_parameterNames);
swap(retParams, m_returnParameterTypes);
+ swap(retParamNames, m_returnParameterNames);
}
bool FunctionType::operator==(Type const& _other) const
@@ -601,6 +641,9 @@ bool FunctionType::operator==(Type const& _other) const
if (m_location != other.m_location)
return false;
+ if (m_isConstant != other.isConstant())
+ return false;
+
if (m_parameterTypes.size() != other.m_parameterTypes.size() ||
m_returnParameterTypes.size() != other.m_returnParameterTypes.size())
return false;
@@ -672,9 +715,15 @@ MemberList const& FunctionType::getMembers() const
}
}
-string FunctionType::getCanonicalSignature() const
+string FunctionType::getCanonicalSignature(std::string const& _name) const
{
- string ret = "(";
+ std::string funcName = _name;
+ if (_name == "")
+ {
+ solAssert(m_declaration != nullptr, "Function type without name needs a declaration");
+ funcName = m_declaration->getName();
+ }
+ string ret = funcName + "(";
for (auto it = m_parameterTypes.cbegin(); it != m_parameterTypes.cend(); ++it)
ret += (*it)->toString() + (it + 1 == m_parameterTypes.cend() ? "" : ",");
@@ -697,6 +746,33 @@ TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) con
m_gasSet || _setGas, m_valueSet || _setValue);
}
+vector<string> const FunctionType::getParameterTypeNames() const
+{
+ vector<string> names;
+ for (TypePointer const& t: m_parameterTypes)
+ names.push_back(t->toString());
+
+ return names;
+}
+
+vector<string> const FunctionType::getReturnParameterTypeNames() const
+{
+ vector<string> names;
+ for (TypePointer const& t: m_returnParameterTypes)
+ names.push_back(t->toString());
+
+ return names;
+}
+
+ASTPointer<ASTString> FunctionType::getDocumentation() const
+{
+ auto function = dynamic_cast<Documented const*>(m_declaration);
+ if (function)
+ return function->getDocumentation();
+
+ return ASTPointer<ASTString>();
+}
+
bool MappingType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
diff --git a/Types.h b/Types.h
index 83436efa..4b4d17d0 100644
--- a/Types.h
+++ b/Types.h
@@ -41,6 +41,7 @@ namespace solidity
class Type; // forward
class FunctionType; // forward
using TypePointer = std::shared_ptr<Type const>;
+using FunctionTypePointer = std::shared_ptr<FunctionType const>;
using TypePointers = std::vector<TypePointer>;
/**
@@ -277,7 +278,8 @@ class ContractType: public Type
{
public:
virtual Category getCategory() const override { return Category::CONTRACT; }
- ContractType(ContractDefinition const& _contract): m_contract(_contract) {}
+ explicit ContractType(ContractDefinition const& _contract, bool _super = false):
+ m_contract(_contract), m_super(_super) {}
/// Contracts can be implicitly converted to super classes and to addresses.
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
/// Contracts can be converted to themselves and to integers.
@@ -289,11 +291,12 @@ public:
virtual MemberList const& getMembers() const override;
+ bool isSuper() const { return m_super; }
ContractDefinition const& getContractDefinition() const { return m_contract; }
/// Returns the function type of the constructor. Note that the location part of the function type
/// is not used, as this type cannot be the type of a variable or expression.
- std::shared_ptr<FunctionType const> const& getConstructorType() const;
+ FunctionTypePointer const& getConstructorType() const;
/// @returns the identifier of the function with the given name or Invalid256 if such a name does
/// not exist.
@@ -301,8 +304,11 @@ public:
private:
ContractDefinition const& m_contract;
+ /// If true, it is the "super" type of the current contract, i.e. it contains only inherited
+ /// members.
+ bool m_super;
/// Type of the constructor, @see getConstructorType. Lazily initialized.
- mutable std::shared_ptr<FunctionType const> m_constructorType;
+ mutable FunctionTypePointer m_constructorType;
/// List of member types, will be lazy-initialized because of recursive references.
mutable std::unique_ptr<MemberList> m_members;
};
@@ -314,7 +320,7 @@ class StructType: public Type
{
public:
virtual Category getCategory() const override { return Category::STRUCT; }
- StructType(StructDefinition const& _struct): m_struct(_struct) {}
+ explicit StructType(StructDefinition const& _struct): m_struct(_struct) {}
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const override;
@@ -353,6 +359,7 @@ public:
virtual Category getCategory() const override { return Category::FUNCTION; }
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
+ explicit FunctionType(VariableDeclaration const& _varDecl);
FunctionType(strings const& _parameterTypes, strings const& _returnParameterTypes,
Location _location = Location::INTERNAL):
FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes),
@@ -364,7 +371,11 @@ public:
m_location(_location), m_gasSet(_gasSet), m_valueSet(_valueSet) {}
TypePointers const& getParameterTypes() const { return m_parameterTypes; }
+ std::vector<std::string> const& getParameterNames() const { return m_parameterNames; }
+ std::vector<std::string> const getParameterTypeNames() const;
TypePointers const& getReturnParameterTypes() const { return m_returnParameterTypes; }
+ std::vector<std::string> const& getReturnParameterNames() const { return m_returnParameterNames; }
+ std::vector<std::string> const getReturnParameterTypeNames() const;
virtual bool operator==(Type const& _other) const override;
virtual std::string toString() const override;
@@ -375,7 +386,20 @@ public:
virtual MemberList const& getMembers() const override;
Location const& getLocation() const { return m_location; }
- std::string getCanonicalSignature() const;
+ /// @returns the canonical signature of this function type given the function name
+ /// If @a _name is not provided (empty string) then the @c m_declaration member of the
+ /// function type is used
+ std::string getCanonicalSignature(std::string const& _name = "") const;
+ Declaration const& getDeclaration() const
+ {
+ solAssert(m_declaration, "Requested declaration from a FunctionType that has none");
+ return *m_declaration;
+ }
+ bool hasDeclaration() const { return !!m_declaration; }
+ bool isConstant() const { return m_isConstant; }
+ /// @return A shared pointer of an ASTString.
+ /// Can contain a nullptr in which case indicates absence of documentation
+ ASTPointer<ASTString> getDocumentation() const;
bool gasSet() const { return m_gasSet; }
bool valueSet() const { return m_valueSet; }
@@ -389,10 +413,14 @@ private:
TypePointers m_parameterTypes;
TypePointers m_returnParameterTypes;
+ std::vector<std::string> m_parameterNames;
+ std::vector<std::string> m_returnParameterNames;
Location const m_location;
bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack
bool const m_valueSet = false; ///< true iff the value to be sent is on the stack
+ bool m_isConstant;
mutable std::unique_ptr<MemberList> m_members;
+ Declaration const* m_declaration = nullptr;
};
/**
@@ -443,7 +471,7 @@ class TypeType: public Type
{
public:
virtual Category getCategory() const override { return Category::TYPE; }
- TypeType(TypePointer const& _actualType, ContractDefinition const* _currentContract = nullptr):
+ explicit TypeType(TypePointer const& _actualType, ContractDefinition const* _currentContract = nullptr):
m_actualType(_actualType), m_currentContract(_currentContract) {}
TypePointer const& getActualType() const { return m_actualType; }
@@ -452,6 +480,7 @@ public:
virtual bool canBeStored() const override { return false; }
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); }
virtual bool canLiveOutsideStorage() const override { return false; }
+ virtual unsigned getSizeOnStack() const override { return 0; }
virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; }
virtual MemberList const& getMembers() const override;
@@ -477,6 +506,7 @@ public:
virtual bool canBeStored() const override { return false; }
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); }
virtual bool canLiveOutsideStorage() const override { return false; }
+ virtual unsigned getSizeOnStack() const override { return 0; }
virtual bool operator==(Type const& _other) const override;
virtual std::string toString() const override;
@@ -495,7 +525,7 @@ public:
enum class Kind { BLOCK, MSG, TX };
virtual Category getCategory() const override { return Category::MAGIC; }
- MagicType(Kind _kind);
+ explicit MagicType(Kind _kind);
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override
{