mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge branch 'develop' of github.com:ethereum/cpp-ethereum into develop
This commit is contained in:
commit
a604202f33
39
AST.cpp
39
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
|
||||
|
95
AST.h
95
AST.h
@ -156,12 +156,43 @@ private:
|
||||
Declaration const* m_scope;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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:
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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 << ")";
|
||||
}
|
||||
|
||||
|
105
CallGraph.cpp
105
CallGraph.cpp
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
69
CallGraph.h
69
CallGraph.h
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
167
Compiler.cpp
167
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,21 +39,14 @@ void Compiler::compileContract(ContractDefinition const& _contract,
|
||||
{
|
||||
m_context = CompilerContext(); // clear it just in case
|
||||
initializeContext(_contract, _contracts);
|
||||
|
||||
for (ContractDefinition const* contract: _contract.getLinearizedBaseContracts())
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
appendFunctionSelector(_contract);
|
||||
for (ContractDefinition const* contract: _contract.getLinearizedBaseContracts())
|
||||
for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions())
|
||||
if (!function->isConstructor())
|
||||
function->accept(*this);
|
||||
set<Declaration const*> functions = m_context.getFunctionsWithoutCode();
|
||||
while (!functions.empty())
|
||||
{
|
||||
for (Declaration const* function: functions)
|
||||
function->accept(*this);
|
||||
functions = m_context.getFunctionsWithoutCode();
|
||||
}
|
||||
|
||||
// Swap the runtime context with the creation-time context
|
||||
swap(m_context, m_runtimeContext);
|
||||
@ -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]
|
||||
|
||||
|
14
Compiler.h
14
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;
|
||||
|
@ -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(&_function);
|
||||
solAssert(res != m_functionEntryLabels.end(), "Function entry label not found.");
|
||||
return res->second.tag();
|
||||
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) const
|
||||
eth::AssemblyItem CompilerContext::getVirtualFunctionEntryLabel(FunctionDefinition const& _function)
|
||||
{
|
||||
auto res = m_virtualFunctionEntryLabels.find(_function.getName());
|
||||
solAssert(res != m_virtualFunctionEntryLabels.end(), "Function entry label not found.");
|
||||
return res->second.tag();
|
||||
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)
|
||||
{
|
||||
// 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
|
||||
}
|
||||
|
||||
set<Declaration const*> CompilerContext::getFunctionsWithoutCode()
|
||||
{
|
||||
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
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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())
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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 += "{}";
|
||||
|
@ -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()
|
||||
|
2
Parser.h
2
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();
|
||||
|
98
Types.cpp
98
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())
|
||||
|
44
Types.h
44
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
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user