mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
commit
4360e04596
@ -106,6 +106,9 @@ void ContractDefinition::checkTypeRequirements()
|
|||||||
));
|
));
|
||||||
hashes.insert(hash);
|
hashes.insert(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isLibrary())
|
||||||
|
checkLibraryRequirements();
|
||||||
}
|
}
|
||||||
|
|
||||||
map<FixedHash<4>, FunctionTypePointer> ContractDefinition::interfaceFunctions() const
|
map<FixedHash<4>, FunctionTypePointer> ContractDefinition::interfaceFunctions() const
|
||||||
@ -335,6 +338,17 @@ void ContractDefinition::checkExternalTypeClashes() const
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ContractDefinition::checkLibraryRequirements() const
|
||||||
|
{
|
||||||
|
solAssert(m_isLibrary, "");
|
||||||
|
if (!m_baseContracts.empty())
|
||||||
|
BOOST_THROW_EXCEPTION(createTypeError("Library is not allowed to inherit."));
|
||||||
|
|
||||||
|
for (auto const& var: m_stateVariables)
|
||||||
|
if (!var->isConstant())
|
||||||
|
BOOST_THROW_EXCEPTION(var->createTypeError("Library cannot have non-constant state variables"));
|
||||||
|
}
|
||||||
|
|
||||||
vector<ASTPointer<EventDefinition>> const& ContractDefinition::interfaceEvents() const
|
vector<ASTPointer<EventDefinition>> const& ContractDefinition::interfaceEvents() const
|
||||||
{
|
{
|
||||||
if (!m_interfaceEvents)
|
if (!m_interfaceEvents)
|
||||||
@ -452,6 +466,10 @@ void InheritanceSpecifier::checkTypeRequirements()
|
|||||||
|
|
||||||
ContractDefinition const* base = dynamic_cast<ContractDefinition const*>(&m_baseName->referencedDeclaration());
|
ContractDefinition const* base = dynamic_cast<ContractDefinition const*>(&m_baseName->referencedDeclaration());
|
||||||
solAssert(base, "Base contract not available.");
|
solAssert(base, "Base contract not available.");
|
||||||
|
|
||||||
|
if (base->isLibrary())
|
||||||
|
BOOST_THROW_EXCEPTION(createTypeError("Libraries cannot be inherited from."));
|
||||||
|
|
||||||
TypePointers parameterTypes = ContractType(*base).constructorType()->parameterTypes();
|
TypePointers parameterTypes = ContractType(*base).constructorType()->parameterTypes();
|
||||||
if (!m_arguments.empty() && parameterTypes.size() != m_arguments.size())
|
if (!m_arguments.empty() && parameterTypes.size() != m_arguments.size())
|
||||||
BOOST_THROW_EXCEPTION(createTypeError(
|
BOOST_THROW_EXCEPTION(createTypeError(
|
||||||
|
@ -215,7 +215,7 @@ protected:
|
|||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Definition of a contract. This is the only AST nodes where child nodes are not visited in
|
* Definition of a contract or library. 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
|
* document order. It first visits all struct declarations, then all variable declarations and
|
||||||
* finally all function declarations.
|
* finally all function declarations.
|
||||||
*/
|
*/
|
||||||
@ -232,7 +232,8 @@ public:
|
|||||||
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables,
|
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables,
|
||||||
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions,
|
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions,
|
||||||
std::vector<ASTPointer<ModifierDefinition>> const& _functionModifiers,
|
std::vector<ASTPointer<ModifierDefinition>> const& _functionModifiers,
|
||||||
std::vector<ASTPointer<EventDefinition>> const& _events
|
std::vector<ASTPointer<EventDefinition>> const& _events,
|
||||||
|
bool _isLibrary
|
||||||
):
|
):
|
||||||
Declaration(_location, _name),
|
Declaration(_location, _name),
|
||||||
Documented(_documentation),
|
Documented(_documentation),
|
||||||
@ -243,7 +244,8 @@ public:
|
|||||||
m_stateVariables(_stateVariables),
|
m_stateVariables(_stateVariables),
|
||||||
m_definedFunctions(_definedFunctions),
|
m_definedFunctions(_definedFunctions),
|
||||||
m_functionModifiers(_functionModifiers),
|
m_functionModifiers(_functionModifiers),
|
||||||
m_events(_events)
|
m_events(_events),
|
||||||
|
m_isLibrary(_isLibrary)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
virtual void accept(ASTVisitor& _visitor) override;
|
virtual void accept(ASTVisitor& _visitor) override;
|
||||||
@ -257,6 +259,7 @@ public:
|
|||||||
std::vector<ASTPointer<FunctionDefinition>> const& definedFunctions() const { return m_definedFunctions; }
|
std::vector<ASTPointer<FunctionDefinition>> const& definedFunctions() const { return m_definedFunctions; }
|
||||||
std::vector<ASTPointer<EventDefinition>> const& events() const { return m_events; }
|
std::vector<ASTPointer<EventDefinition>> const& events() const { return m_events; }
|
||||||
std::vector<ASTPointer<EventDefinition>> const& interfaceEvents() const;
|
std::vector<ASTPointer<EventDefinition>> const& interfaceEvents() const;
|
||||||
|
bool isLibrary() const { return m_isLibrary; }
|
||||||
|
|
||||||
virtual TypePointer type(ContractDefinition const* m_currentContract) const override;
|
virtual TypePointer type(ContractDefinition const* m_currentContract) const override;
|
||||||
|
|
||||||
@ -297,6 +300,8 @@ private:
|
|||||||
/// Checks that different functions with external visibility end up having different
|
/// Checks that different functions with external visibility end up having different
|
||||||
/// external argument types (i.e. different signature).
|
/// external argument types (i.e. different signature).
|
||||||
void checkExternalTypeClashes() const;
|
void checkExternalTypeClashes() const;
|
||||||
|
/// Checks that all requirements for a library are fulfilled if this is a library.
|
||||||
|
void checkLibraryRequirements() const;
|
||||||
|
|
||||||
std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& interfaceFunctionList() const;
|
std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& interfaceFunctionList() const;
|
||||||
|
|
||||||
@ -307,6 +312,7 @@ private:
|
|||||||
std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions;
|
std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions;
|
||||||
std::vector<ASTPointer<ModifierDefinition>> m_functionModifiers;
|
std::vector<ASTPointer<ModifierDefinition>> m_functionModifiers;
|
||||||
std::vector<ASTPointer<EventDefinition>> m_events;
|
std::vector<ASTPointer<EventDefinition>> m_events;
|
||||||
|
bool m_isLibrary;
|
||||||
|
|
||||||
// parsed Natspec documentation of the contract.
|
// parsed Natspec documentation of the contract.
|
||||||
std::string m_userDocumentation;
|
std::string m_userDocumentation;
|
||||||
|
@ -48,8 +48,10 @@ private:
|
|||||||
unsigned stackHeight;
|
unsigned stackHeight;
|
||||||
};
|
};
|
||||||
|
|
||||||
void Compiler::compileContract(ContractDefinition const& _contract,
|
void Compiler::compileContract(
|
||||||
map<ContractDefinition const*, bytes const*> const& _contracts)
|
ContractDefinition const& _contract,
|
||||||
|
std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts
|
||||||
|
)
|
||||||
{
|
{
|
||||||
m_context = CompilerContext(); // clear it just in case
|
m_context = CompilerContext(); // clear it just in case
|
||||||
{
|
{
|
||||||
@ -70,7 +72,7 @@ void Compiler::compileContract(ContractDefinition const& _contract,
|
|||||||
|
|
||||||
void Compiler::compileClone(
|
void Compiler::compileClone(
|
||||||
ContractDefinition const& _contract,
|
ContractDefinition const& _contract,
|
||||||
map<ContractDefinition const*, bytes const*> const& _contracts
|
map<ContractDefinition const*, eth::Assembly const*> const& _contracts
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
m_context = CompilerContext(); // clear it just in case
|
m_context = CompilerContext(); // clear it just in case
|
||||||
@ -98,11 +100,13 @@ eth::AssemblyItem Compiler::functionEntryLabel(FunctionDefinition const& _functi
|
|||||||
return m_runtimeContext.functionEntryLabelIfExists(_function);
|
return m_runtimeContext.functionEntryLabelIfExists(_function);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::initializeContext(ContractDefinition const& _contract,
|
void Compiler::initializeContext(
|
||||||
map<ContractDefinition const*, bytes const*> const& _contracts)
|
ContractDefinition const& _contract,
|
||||||
|
map<ContractDefinition const*, eth::Assembly const*> const& _compiledContracts
|
||||||
|
)
|
||||||
{
|
{
|
||||||
CompilerUtils(m_context).initialiseFreeMemoryPointer();
|
CompilerUtils(m_context).initialiseFreeMemoryPointer();
|
||||||
m_context.setCompiledContracts(_contracts);
|
m_context.setCompiledContracts(_compiledContracts);
|
||||||
m_context.setInheritanceHierarchy(_contract.linearizedBaseContracts());
|
m_context.setInheritanceHierarchy(_contract.linearizedBaseContracts());
|
||||||
registerStateVariables(_contract);
|
registerStateVariables(_contract);
|
||||||
m_context.resetVisitedNodes(&_contract);
|
m_context.resetVisitedNodes(&_contract);
|
||||||
|
@ -42,16 +42,19 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void compileContract(ContractDefinition const& _contract,
|
void compileContract(
|
||||||
std::map<ContractDefinition const*, bytes const*> const& _contracts);
|
ContractDefinition const& _contract,
|
||||||
|
std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts
|
||||||
|
);
|
||||||
/// Compiles a contract that uses CALLCODE to call into a pre-deployed version of the given
|
/// Compiles a contract that uses CALLCODE to call into a pre-deployed version of the given
|
||||||
/// contract at runtime, but contains the full creation-time code.
|
/// contract at runtime, but contains the full creation-time code.
|
||||||
void compileClone(
|
void compileClone(
|
||||||
ContractDefinition const& _contract,
|
ContractDefinition const& _contract,
|
||||||
std::map<ContractDefinition const*, bytes const*> const& _contracts
|
std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts
|
||||||
);
|
);
|
||||||
bytes assembledBytecode() { return m_context.assembledBytecode(); }
|
eth::Assembly const& assembly() { return m_context.assembly(); }
|
||||||
bytes runtimeBytecode() { return m_context.assembledRuntimeBytecode(m_runtimeSub); }
|
eth::LinkerObject assembledObject() { return m_context.assembledObject(); }
|
||||||
|
eth::LinkerObject runtimeObject() { return m_context.assembledRuntimeObject(m_runtimeSub); }
|
||||||
/// @arg _sourceCodes is the map of input files to source code strings
|
/// @arg _sourceCodes is the map of input files to source code strings
|
||||||
/// @arg _inJsonFromat shows whether the out should be in Json format
|
/// @arg _inJsonFromat shows whether the out should be in Json format
|
||||||
Json::Value streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const
|
Json::Value streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const
|
||||||
@ -69,8 +72,10 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
/// Registers the non-function objects inside the contract with the context.
|
/// Registers the non-function objects inside the contract with the context.
|
||||||
void initializeContext(ContractDefinition const& _contract,
|
void initializeContext(
|
||||||
std::map<ContractDefinition const*, bytes const*> const& _contracts);
|
ContractDefinition const& _contract,
|
||||||
|
std::map<ContractDefinition const*, eth::Assembly const*> const& _compiledContracts
|
||||||
|
);
|
||||||
/// Adds the code that is run at creation time. Should be run after exchanging the run-time context
|
/// Adds the code that is run at creation time. Should be run after exchanging the run-time context
|
||||||
/// with a new and initialized context. Adds the constructor code.
|
/// with a new and initialized context. Adds the constructor code.
|
||||||
void packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext);
|
void packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext);
|
||||||
|
@ -65,7 +65,7 @@ void CompilerContext::removeVariable(VariableDeclaration const& _declaration)
|
|||||||
m_localVariables.erase(&_declaration);
|
m_localVariables.erase(&_declaration);
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes const& CompilerContext::compiledContract(const ContractDefinition& _contract) const
|
eth::Assembly const& CompilerContext::compiledContract(const ContractDefinition& _contract) const
|
||||||
{
|
{
|
||||||
auto ret = m_compiledContracts.find(&_contract);
|
auto ret = m_compiledContracts.find(&_contract);
|
||||||
solAssert(ret != m_compiledContracts.end(), "Compiled contract not found.");
|
solAssert(ret != m_compiledContracts.end(), "Compiled contract not found.");
|
||||||
|
@ -47,8 +47,8 @@ public:
|
|||||||
void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0);
|
void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0);
|
||||||
void removeVariable(VariableDeclaration const& _declaration);
|
void removeVariable(VariableDeclaration const& _declaration);
|
||||||
|
|
||||||
void setCompiledContracts(std::map<ContractDefinition const*, bytes const*> const& _contracts) { m_compiledContracts = _contracts; }
|
void setCompiledContracts(std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts) { m_compiledContracts = _contracts; }
|
||||||
bytes const& compiledContract(ContractDefinition const& _contract) const;
|
eth::Assembly const& compiledContract(ContractDefinition const& _contract) const;
|
||||||
|
|
||||||
void setStackOffset(int _offset) { m_asm.setDeposit(_offset); }
|
void setStackOffset(int _offset) { m_asm.setDeposit(_offset); }
|
||||||
void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); }
|
void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); }
|
||||||
@ -112,6 +112,8 @@ public:
|
|||||||
void appendProgramSize() { return m_asm.appendProgramSize(); }
|
void appendProgramSize() { return m_asm.appendProgramSize(); }
|
||||||
/// Adds data to the data section, pushes a reference to the stack
|
/// Adds data to the data section, pushes a reference to the stack
|
||||||
eth::AssemblyItem appendData(bytes const& _data) { return m_asm.append(_data); }
|
eth::AssemblyItem appendData(bytes const& _data) { return m_asm.append(_data); }
|
||||||
|
/// Appends the address (virtual, will be filled in by linker) of a library.
|
||||||
|
void appendLibraryAddress(std::string const& _identifier) { m_asm.appendLibraryAddress(_identifier); }
|
||||||
/// Resets the stack of visited nodes with a new stack having only @c _node
|
/// Resets the stack of visited nodes with a new stack having only @c _node
|
||||||
void resetVisitedNodes(ASTNode const* _node);
|
void resetVisitedNodes(ASTNode const* _node);
|
||||||
/// Pops the stack of visited nodes
|
/// Pops the stack of visited nodes
|
||||||
@ -135,8 +137,8 @@ public:
|
|||||||
return m_asm.stream(_stream, "", _sourceCodes, _inJsonFormat);
|
return m_asm.stream(_stream, "", _sourceCodes, _inJsonFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes assembledBytecode() { return m_asm.assemble(); }
|
eth::LinkerObject const& assembledObject() { return m_asm.assemble(); }
|
||||||
bytes assembledRuntimeBytecode(size_t _subIndex) { m_asm.assemble(); return m_asm.data(u256(_subIndex)); }
|
eth::LinkerObject const& assembledRuntimeObject(size_t _subIndex) { return m_asm.sub(_subIndex).assemble(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class to pop the visited nodes stack when a scope closes
|
* Helper class to pop the visited nodes stack when a scope closes
|
||||||
@ -164,7 +166,7 @@ private:
|
|||||||
/// Magic global variables like msg, tx or this, distinguished by type.
|
/// Magic global variables like msg, tx or this, distinguished by type.
|
||||||
std::set<Declaration const*> m_magicGlobals;
|
std::set<Declaration const*> m_magicGlobals;
|
||||||
/// Other already compiled contracts to be used in contract creation calls.
|
/// Other already compiled contracts to be used in contract creation calls.
|
||||||
std::map<ContractDefinition const*, bytes const*> m_compiledContracts;
|
std::map<ContractDefinition const*, eth::Assembly const*> m_compiledContracts;
|
||||||
/// Storage offsets of state variables
|
/// Storage offsets of state variables
|
||||||
std::map<Declaration const*, std::pair<u256, unsigned>> m_stateVariables;
|
std::map<Declaration const*, std::pair<u256, unsigned>> m_stateVariables;
|
||||||
/// Offsets of local variables on the stack (relative to stack base).
|
/// Offsets of local variables on the stack (relative to stack base).
|
||||||
|
@ -153,7 +153,7 @@ void CompilerStack::compile(bool _optimize, unsigned _runs)
|
|||||||
if (!m_parseSuccessful)
|
if (!m_parseSuccessful)
|
||||||
parse();
|
parse();
|
||||||
|
|
||||||
map<ContractDefinition const*, bytes const*> contractBytecode;
|
map<ContractDefinition const*, eth::Assembly const*> compiledContracts;
|
||||||
for (Source const* source: m_sourceOrder)
|
for (Source const* source: m_sourceOrder)
|
||||||
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
|
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
|
||||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
||||||
@ -161,26 +161,24 @@ void CompilerStack::compile(bool _optimize, unsigned _runs)
|
|||||||
if (!contract->isFullyImplemented())
|
if (!contract->isFullyImplemented())
|
||||||
continue;
|
continue;
|
||||||
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize, _runs);
|
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize, _runs);
|
||||||
compiler->compileContract(*contract, contractBytecode);
|
compiler->compileContract(*contract, compiledContracts);
|
||||||
Contract& compiledContract = m_contracts.at(contract->name());
|
Contract& compiledContract = m_contracts.at(contract->name());
|
||||||
compiledContract.bytecode = compiler->assembledBytecode();
|
compiledContract.compiler = compiler;
|
||||||
compiledContract.runtimeBytecode = compiler->runtimeBytecode();
|
compiledContract.object = compiler->assembledObject();
|
||||||
compiledContract.compiler = move(compiler);
|
compiledContract.runtimeObject = compiler->runtimeObject();
|
||||||
compiler = make_shared<Compiler>(_optimize, _runs);
|
compiledContracts[compiledContract.contract] = &compiler->assembly();
|
||||||
compiler->compileContract(*contract, contractBytecode);
|
|
||||||
contractBytecode[compiledContract.contract] = &compiledContract.bytecode;
|
|
||||||
|
|
||||||
Compiler cloneCompiler(_optimize, _runs);
|
Compiler cloneCompiler(_optimize, _runs);
|
||||||
cloneCompiler.compileClone(*contract, contractBytecode);
|
cloneCompiler.compileClone(*contract, compiledContracts);
|
||||||
compiledContract.cloneBytecode = cloneCompiler.assembledBytecode();
|
compiledContract.cloneObject = cloneCompiler.assembledObject();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes const& CompilerStack::compile(string const& _sourceCode, bool _optimize)
|
eth::LinkerObject const& CompilerStack::compile(string const& _sourceCode, bool _optimize)
|
||||||
{
|
{
|
||||||
parse(_sourceCode);
|
parse(_sourceCode);
|
||||||
compile(_optimize);
|
compile(_optimize);
|
||||||
return bytecode();
|
return object();
|
||||||
}
|
}
|
||||||
|
|
||||||
eth::AssemblyItems const* CompilerStack::assemblyItems(string const& _contractName) const
|
eth::AssemblyItems const* CompilerStack::assemblyItems(string const& _contractName) const
|
||||||
@ -195,24 +193,28 @@ eth::AssemblyItems const* CompilerStack::runtimeAssemblyItems(string const& _con
|
|||||||
return currentContract.compiler ? &contract(_contractName).compiler->runtimeAssemblyItems() : nullptr;
|
return currentContract.compiler ? &contract(_contractName).compiler->runtimeAssemblyItems() : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes const& CompilerStack::bytecode(string const& _contractName) const
|
eth::LinkerObject const& CompilerStack::object(string const& _contractName) const
|
||||||
{
|
{
|
||||||
return contract(_contractName).bytecode;
|
return contract(_contractName).object;
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes const& CompilerStack::runtimeBytecode(string const& _contractName) const
|
eth::LinkerObject const& CompilerStack::runtimeObject(string const& _contractName) const
|
||||||
{
|
{
|
||||||
return contract(_contractName).runtimeBytecode;
|
return contract(_contractName).runtimeObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes const& CompilerStack::cloneBytecode(string const& _contractName) const
|
eth::LinkerObject const& CompilerStack::cloneObject(string const& _contractName) const
|
||||||
{
|
{
|
||||||
return contract(_contractName).cloneBytecode;
|
return contract(_contractName).cloneObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev::h256 CompilerStack::contractCodeHash(string const& _contractName) const
|
dev::h256 CompilerStack::contractCodeHash(string const& _contractName) const
|
||||||
{
|
{
|
||||||
return dev::sha3(runtimeBytecode(_contractName));
|
auto const& obj = runtimeObject(_contractName);
|
||||||
|
if (obj.bytecode.empty() || !obj.linkReferences.empty())
|
||||||
|
return dev::h256();
|
||||||
|
else
|
||||||
|
return dev::sha3(obj.bytecode);
|
||||||
}
|
}
|
||||||
|
|
||||||
Json::Value CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName, StringMap _sourceCodes, bool _inJsonFormat) const
|
Json::Value CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName, StringMap _sourceCodes, bool _inJsonFormat) const
|
||||||
@ -305,7 +307,7 @@ size_t CompilerStack::functionEntryPoint(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize)
|
eth::LinkerObject CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize)
|
||||||
{
|
{
|
||||||
CompilerStack stack;
|
CompilerStack stack;
|
||||||
return stack.compile(_sourceCode, _optimize);
|
return stack.compile(_sourceCode, _optimize);
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
#include <libdevcore/Common.h>
|
#include <libdevcore/Common.h>
|
||||||
#include <libdevcore/FixedHash.h>
|
#include <libdevcore/FixedHash.h>
|
||||||
#include <libevmasm/SourceLocation.h>
|
#include <libevmasm/SourceLocation.h>
|
||||||
|
#include <libevmasm/LinkerObject.h>
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
@ -92,23 +93,25 @@ public:
|
|||||||
/// Compiles the source units that were previously added and parsed.
|
/// Compiles the source units that were previously added and parsed.
|
||||||
void compile(bool _optimize = false, unsigned _runs = 200);
|
void compile(bool _optimize = false, unsigned _runs = 200);
|
||||||
/// Parses and compiles the given source code.
|
/// Parses and compiles the given source code.
|
||||||
/// @returns the compiled bytecode
|
/// @returns the compiled linker object
|
||||||
bytes const& compile(std::string const& _sourceCode, bool _optimize = false);
|
eth::LinkerObject const& compile(std::string const& _sourceCode, bool _optimize = false);
|
||||||
|
|
||||||
/// @returns the assembled bytecode for a contract.
|
/// @returns the assembled bytecode for a contract (empty if it has to be linked or lacks implementation).
|
||||||
bytes const& bytecode(std::string const& _contractName = "") const;
|
eth::LinkerObject const& object(std::string const& _contractName = "") const;
|
||||||
/// @returns the runtime bytecode for the contract, i.e. the code that is returned by the constructor.
|
/// @returns the runtime bytecode for the contract (empty if it has to be linked or lacks implementation).
|
||||||
bytes const& runtimeBytecode(std::string const& _contractName = "") const;
|
eth::LinkerObject const& runtimeObject(std::string const& _contractName = "") const;
|
||||||
/// @returns the bytecode of a contract that uses an already deployed contract via CALLCODE.
|
/// @returns the bytecode of a contract that uses an already deployed contract via CALLCODE.
|
||||||
/// The returned bytes will contain a sequence of 20 bytes of the format "XXX...XXX" which have to
|
/// The returned bytes will contain a sequence of 20 bytes of the format "XXX...XXX" which have to
|
||||||
/// substituted by the actual address. Note that this sequence starts end ends in three X
|
/// substituted by the actual address. Note that this sequence starts end ends in three X
|
||||||
/// characters but can contain anything in between.
|
/// characters but can contain anything in between.
|
||||||
bytes const& cloneBytecode(std::string const& _contractName = "") const;
|
eth::LinkerObject const& cloneObject(std::string const& _contractName = "") const;
|
||||||
/// @returns normal contract assembly items
|
/// @returns normal contract assembly items
|
||||||
eth::AssemblyItems const* assemblyItems(std::string const& _contractName = "") const;
|
eth::AssemblyItems const* assemblyItems(std::string const& _contractName = "") const;
|
||||||
/// @returns runtime contract assembly items
|
/// @returns runtime contract assembly items
|
||||||
eth::AssemblyItems const* runtimeAssemblyItems(std::string const& _contractName = "") const;
|
eth::AssemblyItems const* runtimeAssemblyItems(std::string const& _contractName = "") const;
|
||||||
/// @returns hash of the runtime bytecode for the contract, i.e. the code that is returned by the constructor.
|
/// @returns hash of the runtime bytecode for the contract, i.e. the code that is
|
||||||
|
/// returned by the constructor or the zero-h256 if the contract still needs to be linked or
|
||||||
|
/// does not have runtime code.
|
||||||
dev::h256 contractCodeHash(std::string const& _contractName = "") const;
|
dev::h256 contractCodeHash(std::string const& _contractName = "") const;
|
||||||
|
|
||||||
/// Streams a verbose version of the assembly to @a _outStream.
|
/// Streams a verbose version of the assembly to @a _outStream.
|
||||||
@ -146,7 +149,7 @@ public:
|
|||||||
|
|
||||||
/// Compile the given @a _sourceCode to bytecode. If a scanner is provided, it is used for
|
/// Compile the given @a _sourceCode to bytecode. If a scanner is provided, it is used for
|
||||||
/// scanning the source code - this is useful for printing exception information.
|
/// scanning the source code - this is useful for printing exception information.
|
||||||
static bytes staticCompile(std::string const& _sourceCode, bool _optimize = false);
|
static eth::LinkerObject staticCompile(std::string const& _sourceCode, bool _optimize = false);
|
||||||
|
|
||||||
/// Helper function for logs printing. Do only use in error cases, it's quite expensive.
|
/// Helper function for logs printing. Do only use in error cases, it's quite expensive.
|
||||||
/// line and columns are numbered starting from 1 with following order:
|
/// line and columns are numbered starting from 1 with following order:
|
||||||
@ -170,9 +173,9 @@ private:
|
|||||||
{
|
{
|
||||||
ContractDefinition const* contract = nullptr;
|
ContractDefinition const* contract = nullptr;
|
||||||
std::shared_ptr<Compiler> compiler;
|
std::shared_ptr<Compiler> compiler;
|
||||||
bytes bytecode;
|
eth::LinkerObject object;
|
||||||
bytes runtimeBytecode;
|
eth::LinkerObject runtimeObject;
|
||||||
bytes cloneBytecode;
|
eth::LinkerObject cloneObject;
|
||||||
std::shared_ptr<InterfaceHandler> interfaceHandler;
|
std::shared_ptr<InterfaceHandler> interfaceHandler;
|
||||||
mutable std::unique_ptr<std::string const> interface;
|
mutable std::unique_ptr<std::string const> interface;
|
||||||
mutable std::unique_ptr<std::string const> solidityInterface;
|
mutable std::unique_ptr<std::string const> solidityInterface;
|
||||||
|
@ -454,12 +454,11 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
ContractDefinition const& contract =
|
ContractDefinition const& contract =
|
||||||
dynamic_cast<ContractType const&>(*function.returnParameterTypes().front()).contractDefinition();
|
dynamic_cast<ContractType const&>(*function.returnParameterTypes().front()).contractDefinition();
|
||||||
// copy the contract's code into memory
|
// copy the contract's code into memory
|
||||||
bytes const& bytecode = m_context.compiledContract(contract);
|
eth::Assembly const& assembly = m_context.compiledContract(contract);
|
||||||
utils().fetchFreeMemoryPointer();
|
utils().fetchFreeMemoryPointer();
|
||||||
m_context << u256(bytecode.size()) << eth::Instruction::DUP1;
|
// pushes size
|
||||||
//@todo could be done by actually appending the Assembly, but then we probably need to compile
|
eth::AssemblyItem subroutine = m_context.addSubroutine(assembly);
|
||||||
// multiple times. Will revisit once external fuctions are inlined.
|
m_context << eth::Instruction::DUP1 << subroutine;
|
||||||
m_context.appendData(bytecode);
|
|
||||||
m_context << eth::Instruction::DUP4 << eth::Instruction::CODECOPY;
|
m_context << eth::Instruction::DUP4 << eth::Instruction::CODECOPY;
|
||||||
|
|
||||||
m_context << eth::Instruction::ADD;
|
m_context << eth::Instruction::ADD;
|
||||||
@ -772,11 +771,17 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (dynamic_cast<ContractType const*>(type.actualType().get()))
|
if (dynamic_cast<ContractType const*>(type.actualType().get()))
|
||||||
|
{
|
||||||
|
auto const& funType = dynamic_cast<FunctionType const&>(*_memberAccess.type());
|
||||||
|
if (funType.location() != FunctionType::Location::Internal)
|
||||||
|
m_context << funType.externalIdentifier();
|
||||||
|
else
|
||||||
{
|
{
|
||||||
auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.referencedDeclaration());
|
auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.referencedDeclaration());
|
||||||
solAssert(!!function, "Function not found in member access");
|
solAssert(!!function, "Function not found in member access");
|
||||||
m_context << m_context.functionEntryLabel(*function).pushTag();
|
m_context << m_context.functionEntryLabel(*function).pushTag();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (auto enumType = dynamic_cast<EnumType const*>(type.actualType().get()))
|
else if (auto enumType = dynamic_cast<EnumType const*>(type.actualType().get()))
|
||||||
m_context << enumType->memberValue(_memberAccess.memberName());
|
m_context << enumType->memberValue(_memberAccess.memberName());
|
||||||
break;
|
break;
|
||||||
@ -924,9 +929,11 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
|
|||||||
utils().convertType(*variable->value()->type(), *variable->type());
|
utils().convertType(*variable->value()->type(), *variable->type());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (dynamic_cast<ContractDefinition const*>(declaration))
|
else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration))
|
||||||
{
|
{
|
||||||
// no-op
|
if (contract->isLibrary())
|
||||||
|
//@todo name should be unique, change once we have module management
|
||||||
|
m_context.appendLibraryAddress(contract->name());
|
||||||
}
|
}
|
||||||
else if (dynamic_cast<EventDefinition const*>(declaration))
|
else if (dynamic_cast<EventDefinition const*>(declaration))
|
||||||
{
|
{
|
||||||
|
@ -71,13 +71,14 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
|
|||||||
vector<ASTPointer<ASTNode>> nodes;
|
vector<ASTPointer<ASTNode>> nodes;
|
||||||
while (m_scanner->currentToken() != Token::EOS)
|
while (m_scanner->currentToken() != Token::EOS)
|
||||||
{
|
{
|
||||||
switch (m_scanner->currentToken())
|
switch (auto token = m_scanner->currentToken())
|
||||||
{
|
{
|
||||||
case Token::Import:
|
case Token::Import:
|
||||||
nodes.push_back(parseImportDirective());
|
nodes.push_back(parseImportDirective());
|
||||||
break;
|
break;
|
||||||
case Token::Contract:
|
case Token::Contract:
|
||||||
nodes.push_back(parseContractDefinition());
|
case Token::Library:
|
||||||
|
nodes.push_back(parseContractDefinition(token == Token::Library));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
BOOST_THROW_EXCEPTION(createParserError(std::string("Expected import directive or contract definition.")));
|
BOOST_THROW_EXCEPTION(createParserError(std::string("Expected import directive or contract definition.")));
|
||||||
@ -113,13 +114,13 @@ ASTPointer<ImportDirective> Parser::parseImportDirective()
|
|||||||
return nodeFactory.createNode<ImportDirective>(url);
|
return nodeFactory.createNode<ImportDirective>(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTPointer<ContractDefinition> Parser::parseContractDefinition()
|
ASTPointer<ContractDefinition> Parser::parseContractDefinition(bool _isLibrary)
|
||||||
{
|
{
|
||||||
ASTNodeFactory nodeFactory(*this);
|
ASTNodeFactory nodeFactory(*this);
|
||||||
ASTPointer<ASTString> docString;
|
ASTPointer<ASTString> docString;
|
||||||
if (m_scanner->currentCommentLiteral() != "")
|
if (m_scanner->currentCommentLiteral() != "")
|
||||||
docString = make_shared<ASTString>(m_scanner->currentCommentLiteral());
|
docString = make_shared<ASTString>(m_scanner->currentCommentLiteral());
|
||||||
expectToken(Token::Contract);
|
expectToken(_isLibrary ? Token::Library : Token::Contract);
|
||||||
ASTPointer<ASTString> name = expectIdentifierToken();
|
ASTPointer<ASTString> name = expectIdentifierToken();
|
||||||
vector<ASTPointer<InheritanceSpecifier>> baseContracts;
|
vector<ASTPointer<InheritanceSpecifier>> baseContracts;
|
||||||
vector<ASTPointer<StructDefinition>> structs;
|
vector<ASTPointer<StructDefinition>> structs;
|
||||||
@ -177,7 +178,8 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
|
|||||||
stateVariables,
|
stateVariables,
|
||||||
functions,
|
functions,
|
||||||
modifiers,
|
modifiers,
|
||||||
events
|
events,
|
||||||
|
_isLibrary
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,15 +61,17 @@ private:
|
|||||||
///@{
|
///@{
|
||||||
///@name Parsing functions for the AST nodes
|
///@name Parsing functions for the AST nodes
|
||||||
ASTPointer<ImportDirective> parseImportDirective();
|
ASTPointer<ImportDirective> parseImportDirective();
|
||||||
ASTPointer<ContractDefinition> parseContractDefinition();
|
ASTPointer<ContractDefinition> parseContractDefinition(bool _isLibrary);
|
||||||
ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier();
|
ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier();
|
||||||
Declaration::Visibility parseVisibilitySpecifier(Token::Value _token);
|
Declaration::Visibility parseVisibilitySpecifier(Token::Value _token);
|
||||||
ASTPointer<FunctionDefinition> parseFunctionDefinition(ASTString const* _contractName);
|
ASTPointer<FunctionDefinition> parseFunctionDefinition(ASTString const* _contractName);
|
||||||
ASTPointer<StructDefinition> parseStructDefinition();
|
ASTPointer<StructDefinition> parseStructDefinition();
|
||||||
ASTPointer<EnumDefinition> parseEnumDefinition();
|
ASTPointer<EnumDefinition> parseEnumDefinition();
|
||||||
ASTPointer<EnumValue> parseEnumValue();
|
ASTPointer<EnumValue> parseEnumValue();
|
||||||
ASTPointer<VariableDeclaration> parseVariableDeclaration(VarDeclParserOptions const& _options = VarDeclParserOptions(),
|
ASTPointer<VariableDeclaration> parseVariableDeclaration(
|
||||||
ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>());
|
VarDeclParserOptions const& _options = VarDeclParserOptions(),
|
||||||
|
ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>()
|
||||||
|
);
|
||||||
ASTPointer<ModifierDefinition> parseModifierDefinition();
|
ASTPointer<ModifierDefinition> parseModifierDefinition();
|
||||||
ASTPointer<EventDefinition> parseEventDefinition();
|
ASTPointer<EventDefinition> parseEventDefinition();
|
||||||
ASTPointer<ModifierInvocation> parseModifierInvocation();
|
ASTPointer<ModifierInvocation> parseModifierInvocation();
|
||||||
|
@ -160,6 +160,7 @@ namespace solidity
|
|||||||
K(Internal, "internal", 0) \
|
K(Internal, "internal", 0) \
|
||||||
K(Import, "import", 0) \
|
K(Import, "import", 0) \
|
||||||
K(Is, "is", 0) \
|
K(Is, "is", 0) \
|
||||||
|
K(Library, "library", 0) \
|
||||||
K(Mapping, "mapping", 0) \
|
K(Mapping, "mapping", 0) \
|
||||||
K(Memory, "memory", 0) \
|
K(Memory, "memory", 0) \
|
||||||
K(Modifier, "modifier", 0) \
|
K(Modifier, "modifier", 0) \
|
||||||
@ -305,7 +306,7 @@ namespace solidity
|
|||||||
/* Identifiers (not keywords or future reserved words). */ \
|
/* Identifiers (not keywords or future reserved words). */ \
|
||||||
T(Identifier, NULL, 0) \
|
T(Identifier, NULL, 0) \
|
||||||
\
|
\
|
||||||
/* Keywords reserved for future. use. */ \
|
/* Keywords reserved for future use. */ \
|
||||||
K(As, "as", 0) \
|
K(As, "as", 0) \
|
||||||
K(Case, "case", 0) \
|
K(Case, "case", 0) \
|
||||||
K(Catch, "catch", 0) \
|
K(Catch, "catch", 0) \
|
||||||
|
@ -971,7 +971,7 @@ MemberList const& ContractType::members() const
|
|||||||
for (auto const& it: m_contract.interfaceFunctions())
|
for (auto const& it: m_contract.interfaceFunctions())
|
||||||
members.push_back(MemberList::Member(
|
members.push_back(MemberList::Member(
|
||||||
it.second->declaration().name(),
|
it.second->declaration().name(),
|
||||||
it.second->asMemberFunction(),
|
it.second->asMemberFunction(false),
|
||||||
&it.second->declaration()
|
&it.second->declaration()
|
||||||
));
|
));
|
||||||
m_members.reset(new MemberList(members));
|
m_members.reset(new MemberList(members));
|
||||||
@ -1538,7 +1538,7 @@ TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) con
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionTypePointer FunctionType::asMemberFunction() const
|
FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary) const
|
||||||
{
|
{
|
||||||
TypePointers parameterTypes;
|
TypePointers parameterTypes;
|
||||||
for (auto const& t: m_parameterTypes)
|
for (auto const& t: m_parameterTypes)
|
||||||
@ -1563,7 +1563,7 @@ FunctionTypePointer FunctionType::asMemberFunction() const
|
|||||||
returnParameterTypes,
|
returnParameterTypes,
|
||||||
m_parameterNames,
|
m_parameterNames,
|
||||||
returnParameterNames,
|
returnParameterNames,
|
||||||
m_location,
|
_inLibrary ? Location::CallCode : m_location,
|
||||||
m_arbitraryParameters,
|
m_arbitraryParameters,
|
||||||
m_declaration,
|
m_declaration,
|
||||||
m_gasSet,
|
m_gasSet,
|
||||||
@ -1633,15 +1633,32 @@ u256 TypeType::storageSize() const
|
|||||||
<< errinfo_comment("Storage size of non-storable type type requested."));
|
<< errinfo_comment("Storage size of non-storable type type requested."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned TypeType::sizeOnStack() const
|
||||||
|
{
|
||||||
|
if (auto contractType = dynamic_cast<ContractType const*>(m_actualType.get()))
|
||||||
|
if (contractType->contractDefinition().isLibrary())
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
MemberList const& TypeType::members() const
|
MemberList const& TypeType::members() const
|
||||||
{
|
{
|
||||||
// We need to lazy-initialize it because of recursive references.
|
// We need to lazy-initialize it because of recursive references.
|
||||||
if (!m_members)
|
if (!m_members)
|
||||||
{
|
{
|
||||||
MemberList::MemberMap members;
|
MemberList::MemberMap members;
|
||||||
if (m_actualType->category() == Category::Contract && m_currentContract != nullptr)
|
if (m_actualType->category() == Category::Contract)
|
||||||
{
|
{
|
||||||
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_actualType).contractDefinition();
|
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_actualType).contractDefinition();
|
||||||
|
if (contract.isLibrary())
|
||||||
|
for (auto const& it: contract.interfaceFunctions())
|
||||||
|
members.push_back(MemberList::Member(
|
||||||
|
it.second->declaration().name(),
|
||||||
|
it.second->asMemberFunction(true), // use callcode
|
||||||
|
&it.second->declaration()
|
||||||
|
));
|
||||||
|
else if (m_currentContract != nullptr)
|
||||||
|
{
|
||||||
vector<ContractDefinition const*> currentBases = m_currentContract->linearizedBaseContracts();
|
vector<ContractDefinition const*> currentBases = m_currentContract->linearizedBaseContracts();
|
||||||
if (find(currentBases.begin(), currentBases.end(), &contract) != currentBases.end())
|
if (find(currentBases.begin(), currentBases.end(), &contract) != currentBases.end())
|
||||||
// We are accessing the type of a base contract, so add all public and protected
|
// We are accessing the type of a base contract, so add all public and protected
|
||||||
@ -1649,6 +1666,7 @@ MemberList const& TypeType::members() const
|
|||||||
for (Declaration const* decl: contract.inheritableMembers())
|
for (Declaration const* decl: contract.inheritableMembers())
|
||||||
members.push_back(MemberList::Member(decl->name(), decl->type(), decl));
|
members.push_back(MemberList::Member(decl->name(), decl->type(), decl));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (m_actualType->category() == Category::Enum)
|
else if (m_actualType->category() == Category::Enum)
|
||||||
{
|
{
|
||||||
EnumDefinition const& enumDef = dynamic_cast<EnumType const&>(*m_actualType).enumDefinition();
|
EnumDefinition const& enumDef = dynamic_cast<EnumType const&>(*m_actualType).enumDefinition();
|
||||||
|
@ -517,7 +517,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of a contract instance, there is one distinct type for each contract definition.
|
* The type of a contract instance or library, there is one distinct type for each contract definition.
|
||||||
*/
|
*/
|
||||||
class ContractType: public Type
|
class ContractType: public Type
|
||||||
{
|
{
|
||||||
@ -788,7 +788,8 @@ public:
|
|||||||
/// removed and the location of reference types is changed from CallData to Memory.
|
/// removed and the location of reference types is changed from CallData to Memory.
|
||||||
/// This is needed if external functions are called on other contracts, as they cannot return
|
/// This is needed if external functions are called on other contracts, as they cannot return
|
||||||
/// dynamic values.
|
/// dynamic values.
|
||||||
FunctionTypePointer asMemberFunction() const;
|
/// @param _inLibrary if true, uses CallCode as location.
|
||||||
|
FunctionTypePointer asMemberFunction(bool _inLibrary) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static TypePointers parseElementaryTypeVector(strings const& _types);
|
static TypePointers parseElementaryTypeVector(strings const& _types);
|
||||||
@ -851,6 +852,7 @@ public:
|
|||||||
/**
|
/**
|
||||||
* The type of a type reference. The type of "uint32" when used in "a = uint32(2)" is an example
|
* The type of a type reference. The type of "uint32" when used in "a = uint32(2)" is an example
|
||||||
* of a TypeType.
|
* of a TypeType.
|
||||||
|
* For super contracts or libraries, this has members directly.
|
||||||
*/
|
*/
|
||||||
class TypeType: public Type
|
class TypeType: public Type
|
||||||
{
|
{
|
||||||
@ -865,7 +867,7 @@ public:
|
|||||||
virtual bool canBeStored() const override { return false; }
|
virtual bool canBeStored() const override { return false; }
|
||||||
virtual u256 storageSize() const override;
|
virtual u256 storageSize() const override;
|
||||||
virtual bool canLiveOutsideStorage() const override { return false; }
|
virtual bool canLiveOutsideStorage() const override { return false; }
|
||||||
virtual unsigned sizeOnStack() const override { return 0; }
|
virtual unsigned sizeOnStack() const override;
|
||||||
virtual std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; }
|
virtual std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; }
|
||||||
virtual MemberList const& members() const override;
|
virtual MemberList const& members() const override;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
ContractDefinition = 'contract' Identifier
|
ContractDefinition = ( 'contract' | 'library' ) Identifier
|
||||||
( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )?
|
( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )?
|
||||||
'{' ContractPart* '}'
|
'{' ContractPart* '}'
|
||||||
ContractPart = StateVariableDeclaration | StructDefinition | ModifierDefinition | FunctionDefinition | EnumDefinition
|
ContractPart = StateVariableDeclaration | StructDefinition | ModifierDefinition | FunctionDefinition | EnumDefinition
|
||||||
|
@ -123,31 +123,31 @@ void CommandLineInterface::handleBinary(string const& _contract)
|
|||||||
if (m_args.count(g_argBinaryStr))
|
if (m_args.count(g_argBinaryStr))
|
||||||
{
|
{
|
||||||
if (m_args.count("output-dir"))
|
if (m_args.count("output-dir"))
|
||||||
createFile(_contract + ".bin", toHex(m_compiler->bytecode(_contract)));
|
createFile(_contract + ".bin", m_compiler->object(_contract).toHex());
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cout << "Binary: " << endl;
|
cout << "Binary: " << endl;
|
||||||
cout << toHex(m_compiler->bytecode(_contract)) << endl;
|
cout << m_compiler->object(_contract).toHex() << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (m_args.count(g_argCloneBinaryStr))
|
if (m_args.count(g_argCloneBinaryStr))
|
||||||
{
|
{
|
||||||
if (m_args.count("output-dir"))
|
if (m_args.count("output-dir"))
|
||||||
createFile(_contract + ".clone_bin", toHex(m_compiler->cloneBytecode(_contract)));
|
createFile(_contract + ".clone_bin", m_compiler->cloneObject(_contract).toHex());
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cout << "Clone Binary: " << endl;
|
cout << "Clone Binary: " << endl;
|
||||||
cout << toHex(m_compiler->cloneBytecode(_contract)) << endl;
|
cout << m_compiler->cloneObject(_contract).toHex() << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (m_args.count(g_argRuntimeBinaryStr))
|
if (m_args.count(g_argRuntimeBinaryStr))
|
||||||
{
|
{
|
||||||
if (m_args.count("output-dir"))
|
if (m_args.count("output-dir"))
|
||||||
createFile(_contract + ".bin", toHex(m_compiler->runtimeBytecode(_contract)));
|
createFile(_contract + ".bin", m_compiler->runtimeObject(_contract).toHex());
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cout << "Binary of the runtime part: " << endl;
|
cout << "Binary of the runtime part: " << endl;
|
||||||
cout << toHex(m_compiler->runtimeBytecode(_contract)) << endl;
|
cout << m_compiler->runtimeObject(_contract).toHex() << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -155,11 +155,11 @@ void CommandLineInterface::handleBinary(string const& _contract)
|
|||||||
void CommandLineInterface::handleOpcode(string const& _contract)
|
void CommandLineInterface::handleOpcode(string const& _contract)
|
||||||
{
|
{
|
||||||
if (m_args.count("output-dir"))
|
if (m_args.count("output-dir"))
|
||||||
createFile(_contract + ".opcode", eth::disassemble(m_compiler->bytecode(_contract)));
|
createFile(_contract + ".opcode", eth::disassemble(m_compiler->object(_contract).bytecode));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cout << "Opcodes: " << endl;
|
cout << "Opcodes: " << endl;
|
||||||
cout << eth::disassemble(m_compiler->bytecode(_contract));
|
cout << eth::disassemble(m_compiler->object(_contract).bytecode);
|
||||||
cout << endl;
|
cout << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,7 +242,7 @@ void CommandLineInterface::handleGasEstimation(string const& _contract)
|
|||||||
if (eth::AssemblyItems const* items = m_compiler->assemblyItems(_contract))
|
if (eth::AssemblyItems const* items = m_compiler->assemblyItems(_contract))
|
||||||
{
|
{
|
||||||
Gas gas = GasEstimator::functionalEstimation(*items);
|
Gas gas = GasEstimator::functionalEstimation(*items);
|
||||||
u256 bytecodeSize(m_compiler->runtimeBytecode(_contract).size());
|
u256 bytecodeSize(m_compiler->runtimeObject(_contract).bytecode.size());
|
||||||
cout << "construction:" << endl;
|
cout << "construction:" << endl;
|
||||||
cout << " " << gas << " + " << (bytecodeSize * eth::c_createDataGas) << " = ";
|
cout << " " << gas << " + " << (bytecodeSize * eth::c_createDataGas) << " = ";
|
||||||
gas += bytecodeSize * eth::c_createDataGas;
|
gas += bytecodeSize * eth::c_createDataGas;
|
||||||
@ -500,11 +500,11 @@ void CommandLineInterface::handleCombinedJSON()
|
|||||||
if (requests.count("abi"))
|
if (requests.count("abi"))
|
||||||
contractData["abi"] = m_compiler->interface(contractName);
|
contractData["abi"] = m_compiler->interface(contractName);
|
||||||
if (requests.count("bin"))
|
if (requests.count("bin"))
|
||||||
contractData["bin"] = toHex(m_compiler->bytecode(contractName));
|
contractData["bin"] = m_compiler->runtimeObject(contractName).toHex();
|
||||||
if (requests.count("clone-bin"))
|
if (requests.count("clone-bin"))
|
||||||
contractData["clone-bin"] = toHex(m_compiler->cloneBytecode(contractName));
|
contractData["clone-bin"] = m_compiler->cloneObject(contractName).toHex();
|
||||||
if (requests.count("opcodes"))
|
if (requests.count("opcodes"))
|
||||||
contractData["opcodes"] = eth::disassemble(m_compiler->bytecode(contractName));
|
contractData["opcodes"] = eth::disassemble(m_compiler->object(contractName).bytecode);
|
||||||
if (requests.count("asm"))
|
if (requests.count("asm"))
|
||||||
{
|
{
|
||||||
ostringstream unused;
|
ostringstream unused;
|
||||||
|
@ -76,7 +76,7 @@ Json::Value estimateGas(CompilerStack const& _compiler, string const& _contract)
|
|||||||
if (eth::AssemblyItems const* items = _compiler.assemblyItems(_contract))
|
if (eth::AssemblyItems const* items = _compiler.assemblyItems(_contract))
|
||||||
{
|
{
|
||||||
Gas gas = GasEstimator::functionalEstimation(*items);
|
Gas gas = GasEstimator::functionalEstimation(*items);
|
||||||
u256 bytecodeSize(_compiler.runtimeBytecode(_contract).size());
|
u256 bytecodeSize(_compiler.runtimeObject(_contract).bytecode.size());
|
||||||
Json::Value creationGas(Json::arrayValue);
|
Json::Value creationGas(Json::arrayValue);
|
||||||
creationGas[0] = gasToJson(gas);
|
creationGas[0] = gasToJson(gas);
|
||||||
creationGas[1] = gasToJson(bytecodeSize * eth::c_createDataGas);
|
creationGas[1] = gasToJson(bytecodeSize * eth::c_createDataGas);
|
||||||
@ -168,8 +168,8 @@ string compile(string _input, bool _optimize)
|
|||||||
Json::Value contractData(Json::objectValue);
|
Json::Value contractData(Json::objectValue);
|
||||||
contractData["solidity_interface"] = compiler.solidityInterface(contractName);
|
contractData["solidity_interface"] = compiler.solidityInterface(contractName);
|
||||||
contractData["interface"] = compiler.interface(contractName);
|
contractData["interface"] = compiler.interface(contractName);
|
||||||
contractData["bytecode"] = toHex(compiler.bytecode(contractName));
|
contractData["bytecode"] = compiler.object(contractName).toHex();
|
||||||
contractData["opcodes"] = eth::disassemble(compiler.bytecode(contractName));
|
contractData["opcodes"] = eth::disassemble(compiler.object(contractName).bytecode);
|
||||||
contractData["functionHashes"] = functionHashes(compiler.contractDefinition(contractName));
|
contractData["functionHashes"] = functionHashes(compiler.contractDefinition(contractName));
|
||||||
contractData["gasEstimates"] = estimateGas(compiler, contractName);
|
contractData["gasEstimates"] = estimateGas(compiler, contractName);
|
||||||
ostringstream unused;
|
ostringstream unused;
|
||||||
|
@ -233,7 +233,7 @@ protected:
|
|||||||
m_compiler.reset(false, m_addStandardSources);
|
m_compiler.reset(false, m_addStandardSources);
|
||||||
m_compiler.addSource("", registrarCode);
|
m_compiler.addSource("", registrarCode);
|
||||||
ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed");
|
ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed");
|
||||||
s_compiledRegistrar.reset(new bytes(m_compiler.bytecode("GlobalRegistrar")));
|
s_compiledRegistrar.reset(new bytes(m_compiler.object("GlobalRegistrar").bytecode));
|
||||||
}
|
}
|
||||||
sendMessage(*s_compiledRegistrar, true);
|
sendMessage(*s_compiledRegistrar, true);
|
||||||
BOOST_REQUIRE(!m_output.empty());
|
BOOST_REQUIRE(!m_output.empty());
|
||||||
|
@ -125,7 +125,7 @@ protected:
|
|||||||
m_compiler.reset(false, m_addStandardSources);
|
m_compiler.reset(false, m_addStandardSources);
|
||||||
m_compiler.addSource("", registrarCode);
|
m_compiler.addSource("", registrarCode);
|
||||||
ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed");
|
ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed");
|
||||||
s_compiledRegistrar.reset(new bytes(m_compiler.bytecode("FixedFeeRegistrar")));
|
s_compiledRegistrar.reset(new bytes(m_compiler.object("FixedFeeRegistrar").bytecode));
|
||||||
}
|
}
|
||||||
sendMessage(*s_compiledRegistrar, true);
|
sendMessage(*s_compiledRegistrar, true);
|
||||||
BOOST_REQUIRE(!m_output.empty());
|
BOOST_REQUIRE(!m_output.empty());
|
||||||
|
@ -440,7 +440,7 @@ protected:
|
|||||||
m_compiler.reset(false, m_addStandardSources);
|
m_compiler.reset(false, m_addStandardSources);
|
||||||
m_compiler.addSource("", walletCode);
|
m_compiler.addSource("", walletCode);
|
||||||
ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed");
|
ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed");
|
||||||
s_compiledWallet.reset(new bytes(m_compiler.bytecode("Wallet")));
|
s_compiledWallet.reset(new bytes(m_compiler.object("Wallet").bytecode));
|
||||||
}
|
}
|
||||||
bytes args = encodeArgs(u256(0x60), _required, _dailyLimit, u256(_owners.size()), _owners);
|
bytes args = encodeArgs(u256(0x60), _required, _dailyLimit, u256(_owners.size()), _owners);
|
||||||
sendMessage(*s_compiledWallet + args, true, _value);
|
sendMessage(*s_compiledWallet + args, true, _value);
|
||||||
|
@ -66,7 +66,7 @@ eth::AssemblyItems compileContract(const string& _sourceCode)
|
|||||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
||||||
{
|
{
|
||||||
Compiler compiler;
|
Compiler compiler;
|
||||||
compiler.compileContract(*contract, map<ContractDefinition const*, bytes const*>{});
|
compiler.compileContract(*contract, map<ContractDefinition const*, Assembly const*>{});
|
||||||
|
|
||||||
return compiler.runtimeAssemblyItems();
|
return compiler.runtimeAssemblyItems();
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ public:
|
|||||||
auto state = make_shared<KnownState>();
|
auto state = make_shared<KnownState>();
|
||||||
PathGasMeter meter(*m_compiler.assemblyItems());
|
PathGasMeter meter(*m_compiler.assemblyItems());
|
||||||
GasMeter::GasConsumption gas = meter.estimateMax(0, state);
|
GasMeter::GasConsumption gas = meter.estimateMax(0, state);
|
||||||
u256 bytecodeSize(m_compiler.runtimeBytecode().size());
|
u256 bytecodeSize(m_compiler.runtimeObject().bytecode.size());
|
||||||
gas += bytecodeSize * c_createDataGas;
|
gas += bytecodeSize * c_createDataGas;
|
||||||
BOOST_REQUIRE(!gas.isInfinite);
|
BOOST_REQUIRE(!gas.isInfinite);
|
||||||
BOOST_CHECK(gas.value == m_gasUsed);
|
BOOST_CHECK(gas.value == m_gasUsed);
|
||||||
|
@ -5230,6 +5230,38 @@ BOOST_AUTO_TEST_CASE(storage_string_as_mapping_key_without_variable)
|
|||||||
BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(2)));
|
BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(2)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(library_call)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
library Lib { function m(uint x, uint y) returns (uint) { return x * y; } }
|
||||||
|
contract Test {
|
||||||
|
function f(uint x) returns (uint) {
|
||||||
|
return Lib.m(x, 9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode, 0, "Lib");
|
||||||
|
compileAndRun(sourceCode, 0, "Test", bytes(), map<string, Address>{{"Lib", m_contractAddress}});
|
||||||
|
BOOST_CHECK(callContractFunction("f(uint256)", u256(33)) == encodeArgs(u256(33) * 9));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(library_stray_values)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
library Lib { function m(uint x, uint y) returns (uint) { return x * y; } }
|
||||||
|
contract Test {
|
||||||
|
function f(uint x) returns (uint) {
|
||||||
|
Lib;
|
||||||
|
Lib.m;
|
||||||
|
return x + 9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode, 0, "Lib");
|
||||||
|
compileAndRun(sourceCode, 0, "Test", bytes(), map<string, Address>{{"Lib", m_contractAddress}});
|
||||||
|
BOOST_CHECK(callContractFunction("f(uint256)", u256(33)) == encodeArgs(u256(42)));
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,7 @@ bytes compileFirstExpression(const string& _sourceCode, vector<vector<string>> _
|
|||||||
|
|
||||||
for (vector<string> const& function: _functions)
|
for (vector<string> const& function: _functions)
|
||||||
context << context.functionEntryLabel(dynamic_cast<FunctionDefinition const&>(resolveDeclaration(function, resolver)));
|
context << context.functionEntryLabel(dynamic_cast<FunctionDefinition const&>(resolveDeclaration(function, resolver)));
|
||||||
bytes instructions = context.assembledBytecode();
|
bytes instructions = context.assembledObject().bytecode;
|
||||||
// debug
|
// debug
|
||||||
// cout << eth::disassemble(instructions) << endl;
|
// cout << eth::disassemble(instructions) << endl;
|
||||||
return instructions;
|
return instructions;
|
||||||
|
@ -2193,6 +2193,55 @@ BOOST_AUTO_TEST_CASE(string_bytes_conversion)
|
|||||||
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
|
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(inheriting_from_library)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
library Lib {}
|
||||||
|
contract Test is Lib {}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(inheriting_library)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract Test {}
|
||||||
|
library Lib is Test {}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(library_having_variables)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
library Lib { uint x; }
|
||||||
|
)";
|
||||||
|
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(valid_library)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
library Lib { uint constant x = 9; }
|
||||||
|
)";
|
||||||
|
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(call_to_library_function)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
library Lib {
|
||||||
|
uint constant public pimil = 3141592;
|
||||||
|
function min(uint x, uint y) returns (uint);
|
||||||
|
}
|
||||||
|
contract Test {
|
||||||
|
function f() {
|
||||||
|
uint t = Lib.min(Lib.pimil(), 7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(creating_contract_within_the_contract)
|
BOOST_AUTO_TEST_CASE(creating_contract_within_the_contract)
|
||||||
{
|
{
|
||||||
|
@ -924,6 +924,16 @@ BOOST_AUTO_TEST_CASE(empty_comment)
|
|||||||
BOOST_CHECK_NO_THROW(parseText(text));
|
BOOST_CHECK_NO_THROW(parseText(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(library_simple)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
library Lib {
|
||||||
|
function f() { }
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK_NO_THROW(parseText(text));
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -53,14 +53,17 @@ public:
|
|||||||
std::string const& _sourceCode,
|
std::string const& _sourceCode,
|
||||||
u256 const& _value = 0,
|
u256 const& _value = 0,
|
||||||
std::string const& _contractName = "",
|
std::string const& _contractName = "",
|
||||||
bytes const& _arguments = bytes()
|
bytes const& _arguments = bytes(),
|
||||||
|
std::map<std::string, Address> const& _libraryAddresses = std::map<std::string, Address>()
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
m_compiler.reset(false, m_addStandardSources);
|
m_compiler.reset(false, m_addStandardSources);
|
||||||
m_compiler.addSource("", _sourceCode);
|
m_compiler.addSource("", _sourceCode);
|
||||||
ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed");
|
ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed");
|
||||||
bytes code = m_compiler.bytecode(_contractName);
|
eth::LinkerObject obj = m_compiler.object(_contractName);
|
||||||
sendMessage(code + _arguments, true, _value);
|
obj.link(_libraryAddresses);
|
||||||
|
BOOST_REQUIRE(obj.linkReferences.empty());
|
||||||
|
sendMessage(obj.bytecode + _arguments, true, _value);
|
||||||
return m_output;
|
return m_output;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,10 +79,11 @@ public:
|
|||||||
std::string const& _sourceCode,
|
std::string const& _sourceCode,
|
||||||
u256 const& _value = 0,
|
u256 const& _value = 0,
|
||||||
std::string const& _contractName = "",
|
std::string const& _contractName = "",
|
||||||
bytes const& _arguments = bytes()
|
bytes const& _arguments = bytes(),
|
||||||
|
std::map<std::string, Address> const& _libraryAddresses = std::map<std::string, Address>()
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
compileAndRunWithoutCheck(_sourceCode, _value, _contractName, _arguments);
|
compileAndRunWithoutCheck(_sourceCode, _value, _contractName, _arguments, _libraryAddresses);
|
||||||
BOOST_REQUIRE(!m_output.empty());
|
BOOST_REQUIRE(!m_output.empty());
|
||||||
return m_output;
|
return m_output;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user