diff options
author | chriseth <c@ethdev.com> | 2015-09-12 01:35:31 +0800 |
---|---|---|
committer | chriseth <c@ethdev.com> | 2015-09-12 01:35:31 +0800 |
commit | 4360e0459622e7843ca9f8d7fb5113da0b8a044e (patch) | |
tree | e8f5fe5f139517c000c57fa271617621420ccb5d | |
parent | 3c25420b8476f1516ac8cb10f5c1552609a08675 (diff) | |
parent | d89832fa898d18f1962eff14acf3f2f7b1c4e3bc (diff) | |
download | dexon-solidity-4360e0459622e7843ca9f8d7fb5113da0b8a044e.tar.gz dexon-solidity-4360e0459622e7843ca9f8d7fb5113da0b8a044e.tar.zst dexon-solidity-4360e0459622e7843ca9f8d7fb5113da0b8a044e.zip |
Merge pull request #66 from chriseth/sol_libraries
Calling libraries.
27 files changed, 280 insertions, 113 deletions
diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 1ad34cfe..674f33b7 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -106,6 +106,9 @@ void ContractDefinition::checkTypeRequirements() )); hashes.insert(hash); } + + if (isLibrary()) + checkLibraryRequirements(); } 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 { if (!m_interfaceEvents) @@ -452,6 +466,10 @@ void InheritanceSpecifier::checkTypeRequirements() ContractDefinition const* base = dynamic_cast<ContractDefinition const*>(&m_baseName->referencedDeclaration()); solAssert(base, "Base contract not available."); + + if (base->isLibrary()) + BOOST_THROW_EXCEPTION(createTypeError("Libraries cannot be inherited from.")); + TypePointers parameterTypes = ContractType(*base).constructorType()->parameterTypes(); if (!m_arguments.empty() && parameterTypes.size() != m_arguments.size()) BOOST_THROW_EXCEPTION(createTypeError( diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 48790e6a..da6a7d58 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -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 * finally all function declarations. */ @@ -232,7 +232,8 @@ public: std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables, std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions, std::vector<ASTPointer<ModifierDefinition>> const& _functionModifiers, - std::vector<ASTPointer<EventDefinition>> const& _events + std::vector<ASTPointer<EventDefinition>> const& _events, + bool _isLibrary ): Declaration(_location, _name), Documented(_documentation), @@ -243,7 +244,8 @@ public: m_stateVariables(_stateVariables), m_definedFunctions(_definedFunctions), m_functionModifiers(_functionModifiers), - m_events(_events) + m_events(_events), + m_isLibrary(_isLibrary) {} virtual void accept(ASTVisitor& _visitor) override; @@ -257,6 +259,7 @@ public: 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& interfaceEvents() const; + bool isLibrary() const { return m_isLibrary; } 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 /// external argument types (i.e. different signature). 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; @@ -307,6 +312,7 @@ private: std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions; std::vector<ASTPointer<ModifierDefinition>> m_functionModifiers; std::vector<ASTPointer<EventDefinition>> m_events; + bool m_isLibrary; // parsed Natspec documentation of the contract. std::string m_userDocumentation; diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 8394efa3..9bdf45f8 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -48,8 +48,10 @@ private: unsigned stackHeight; }; -void Compiler::compileContract(ContractDefinition const& _contract, - map<ContractDefinition const*, bytes const*> const& _contracts) +void Compiler::compileContract( + ContractDefinition const& _contract, + std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts +) { m_context = CompilerContext(); // clear it just in case { @@ -70,7 +72,7 @@ void Compiler::compileContract(ContractDefinition const& _contract, void Compiler::compileClone( 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 @@ -98,11 +100,13 @@ eth::AssemblyItem Compiler::functionEntryLabel(FunctionDefinition const& _functi return m_runtimeContext.functionEntryLabelIfExists(_function); } -void Compiler::initializeContext(ContractDefinition const& _contract, - map<ContractDefinition const*, bytes const*> const& _contracts) +void Compiler::initializeContext( + ContractDefinition const& _contract, + map<ContractDefinition const*, eth::Assembly const*> const& _compiledContracts +) { CompilerUtils(m_context).initialiseFreeMemoryPointer(); - m_context.setCompiledContracts(_contracts); + m_context.setCompiledContracts(_compiledContracts); m_context.setInheritanceHierarchy(_contract.linearizedBaseContracts()); registerStateVariables(_contract); m_context.resetVisitedNodes(&_contract); diff --git a/libsolidity/Compiler.h b/libsolidity/Compiler.h index f283683f..7e1d3222 100644 --- a/libsolidity/Compiler.h +++ b/libsolidity/Compiler.h @@ -42,16 +42,19 @@ public: { } - void compileContract(ContractDefinition const& _contract, - std::map<ContractDefinition const*, bytes const*> const& _contracts); + void compileContract( + 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 /// contract at runtime, but contains the full creation-time code. void compileClone( 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(); } - bytes runtimeBytecode() { return m_context.assembledRuntimeBytecode(m_runtimeSub); } + eth::Assembly const& assembly() { return m_context.assembly(); } + 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 _inJsonFromat shows whether the out should be in Json format Json::Value streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const @@ -69,8 +72,10 @@ public: private: /// Registers the non-function objects inside the contract with the context. - void initializeContext(ContractDefinition const& _contract, - std::map<ContractDefinition const*, bytes const*> const& _contracts); + void initializeContext( + 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 /// with a new and initialized context. Adds the constructor code. void packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext); diff --git a/libsolidity/CompilerContext.cpp b/libsolidity/CompilerContext.cpp index af93578f..717627a5 100644 --- a/libsolidity/CompilerContext.cpp +++ b/libsolidity/CompilerContext.cpp @@ -65,7 +65,7 @@ void CompilerContext::removeVariable(VariableDeclaration const& _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); solAssert(ret != m_compiledContracts.end(), "Compiled contract not found."); diff --git a/libsolidity/CompilerContext.h b/libsolidity/CompilerContext.h index 590c8797..39f4e75f 100644 --- a/libsolidity/CompilerContext.h +++ b/libsolidity/CompilerContext.h @@ -47,8 +47,8 @@ public: void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0); void removeVariable(VariableDeclaration const& _declaration); - void setCompiledContracts(std::map<ContractDefinition const*, bytes const*> const& _contracts) { m_compiledContracts = _contracts; } - bytes const& compiledContract(ContractDefinition const& _contract) const; + void setCompiledContracts(std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts) { m_compiledContracts = _contracts; } + eth::Assembly const& compiledContract(ContractDefinition const& _contract) const; void setStackOffset(int _offset) { m_asm.setDeposit(_offset); } void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); } @@ -112,6 +112,8 @@ public: void appendProgramSize() { return m_asm.appendProgramSize(); } /// Adds data to the data section, pushes a reference to the stack 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 void resetVisitedNodes(ASTNode const* _node); /// Pops the stack of visited nodes @@ -135,8 +137,8 @@ public: return m_asm.stream(_stream, "", _sourceCodes, _inJsonFormat); } - bytes assembledBytecode() { return m_asm.assemble(); } - bytes assembledRuntimeBytecode(size_t _subIndex) { m_asm.assemble(); return m_asm.data(u256(_subIndex)); } + eth::LinkerObject const& assembledObject() { return m_asm.assemble(); } + 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 @@ -164,7 +166,7 @@ private: /// Magic global variables like msg, tx or this, distinguished by type. std::set<Declaration const*> m_magicGlobals; /// 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 std::map<Declaration const*, std::pair<u256, unsigned>> m_stateVariables; /// Offsets of local variables on the stack (relative to stack base). diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index 385533b2..3da982a4 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -153,7 +153,7 @@ void CompilerStack::compile(bool _optimize, unsigned _runs) if (!m_parseSuccessful) parse(); - map<ContractDefinition const*, bytes const*> contractBytecode; + map<ContractDefinition const*, eth::Assembly const*> compiledContracts; for (Source const* source: m_sourceOrder) for (ASTPointer<ASTNode> const& node: source->ast->nodes()) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) @@ -161,26 +161,24 @@ void CompilerStack::compile(bool _optimize, unsigned _runs) if (!contract->isFullyImplemented()) continue; shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize, _runs); - compiler->compileContract(*contract, contractBytecode); + compiler->compileContract(*contract, compiledContracts); Contract& compiledContract = m_contracts.at(contract->name()); - compiledContract.bytecode = compiler->assembledBytecode(); - compiledContract.runtimeBytecode = compiler->runtimeBytecode(); - compiledContract.compiler = move(compiler); - compiler = make_shared<Compiler>(_optimize, _runs); - compiler->compileContract(*contract, contractBytecode); - contractBytecode[compiledContract.contract] = &compiledContract.bytecode; + compiledContract.compiler = compiler; + compiledContract.object = compiler->assembledObject(); + compiledContract.runtimeObject = compiler->runtimeObject(); + compiledContracts[compiledContract.contract] = &compiler->assembly(); Compiler cloneCompiler(_optimize, _runs); - cloneCompiler.compileClone(*contract, contractBytecode); - compiledContract.cloneBytecode = cloneCompiler.assembledBytecode(); + cloneCompiler.compileClone(*contract, compiledContracts); + 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); compile(_optimize); - return bytecode(); + return object(); } 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; } -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 { - 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 @@ -305,7 +307,7 @@ size_t CompilerStack::functionEntryPoint( return 0; } -bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize) +eth::LinkerObject CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize) { CompilerStack stack; return stack.compile(_sourceCode, _optimize); diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index 0d812e02..8db8aff4 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -32,6 +32,7 @@ #include <libdevcore/Common.h> #include <libdevcore/FixedHash.h> #include <libevmasm/SourceLocation.h> +#include <libevmasm/LinkerObject.h> namespace dev { @@ -92,23 +93,25 @@ public: /// Compiles the source units that were previously added and parsed. void compile(bool _optimize = false, unsigned _runs = 200); /// Parses and compiles the given source code. - /// @returns the compiled bytecode - bytes const& compile(std::string const& _sourceCode, bool _optimize = false); + /// @returns the compiled linker object + eth::LinkerObject const& compile(std::string const& _sourceCode, bool _optimize = false); - /// @returns the assembled bytecode for a contract. - bytes const& bytecode(std::string const& _contractName = "") const; - /// @returns the runtime bytecode for the contract, i.e. the code that is returned by the constructor. - bytes const& runtimeBytecode(std::string const& _contractName = "") const; + /// @returns the assembled bytecode for a contract (empty if it has to be linked or lacks implementation). + eth::LinkerObject const& object(std::string const& _contractName = "") const; + /// @returns the runtime bytecode for the contract (empty if it has to be linked or lacks implementation). + eth::LinkerObject const& runtimeObject(std::string const& _contractName = "") const; /// @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 /// substituted by the actual address. Note that this sequence starts end ends in three X /// 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 eth::AssemblyItems const* assemblyItems(std::string const& _contractName = "") const; /// @returns runtime contract assembly items 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; /// 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 /// 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. /// line and columns are numbered starting from 1 with following order: @@ -170,9 +173,9 @@ private: { ContractDefinition const* contract = nullptr; std::shared_ptr<Compiler> compiler; - bytes bytecode; - bytes runtimeBytecode; - bytes cloneBytecode; + eth::LinkerObject object; + eth::LinkerObject runtimeObject; + eth::LinkerObject cloneObject; std::shared_ptr<InterfaceHandler> interfaceHandler; mutable std::unique_ptr<std::string const> interface; mutable std::unique_ptr<std::string const> solidityInterface; diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index d5a8362e..b22a78dc 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -454,12 +454,11 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) ContractDefinition const& contract = dynamic_cast<ContractType const&>(*function.returnParameterTypes().front()).contractDefinition(); // copy the contract's code into memory - bytes const& bytecode = m_context.compiledContract(contract); + eth::Assembly const& assembly = m_context.compiledContract(contract); utils().fetchFreeMemoryPointer(); - m_context << u256(bytecode.size()) << eth::Instruction::DUP1; - //@todo could be done by actually appending the Assembly, but then we probably need to compile - // multiple times. Will revisit once external fuctions are inlined. - m_context.appendData(bytecode); + // pushes size + eth::AssemblyItem subroutine = m_context.addSubroutine(assembly); + m_context << eth::Instruction::DUP1 << subroutine; m_context << eth::Instruction::DUP4 << eth::Instruction::CODECOPY; m_context << eth::Instruction::ADD; @@ -773,9 +772,15 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) if (dynamic_cast<ContractType const*>(type.actualType().get())) { - auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.referencedDeclaration()); - solAssert(!!function, "Function not found in member access"); - m_context << m_context.functionEntryLabel(*function).pushTag(); + 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()); + solAssert(!!function, "Function not found in member access"); + m_context << m_context.functionEntryLabel(*function).pushTag(); + } } else if (auto enumType = dynamic_cast<EnumType const*>(type.actualType().get())) m_context << enumType->memberValue(_memberAccess.memberName()); @@ -924,9 +929,11 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) 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)) { diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 7f779ed0..24f7734c 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -71,13 +71,14 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner) vector<ASTPointer<ASTNode>> nodes; while (m_scanner->currentToken() != Token::EOS) { - switch (m_scanner->currentToken()) + switch (auto token = m_scanner->currentToken()) { case Token::Import: nodes.push_back(parseImportDirective()); break; case Token::Contract: - nodes.push_back(parseContractDefinition()); + case Token::Library: + nodes.push_back(parseContractDefinition(token == Token::Library)); break; default: 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); } -ASTPointer<ContractDefinition> Parser::parseContractDefinition() +ASTPointer<ContractDefinition> Parser::parseContractDefinition(bool _isLibrary) { ASTNodeFactory nodeFactory(*this); ASTPointer<ASTString> docString; if (m_scanner->currentCommentLiteral() != "") docString = make_shared<ASTString>(m_scanner->currentCommentLiteral()); - expectToken(Token::Contract); + expectToken(_isLibrary ? Token::Library : Token::Contract); ASTPointer<ASTString> name = expectIdentifierToken(); vector<ASTPointer<InheritanceSpecifier>> baseContracts; vector<ASTPointer<StructDefinition>> structs; @@ -177,7 +178,8 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition() stateVariables, functions, modifiers, - events + events, + _isLibrary ); } diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index 36302f74..79eb73f0 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -61,15 +61,17 @@ private: ///@{ ///@name Parsing functions for the AST nodes ASTPointer<ImportDirective> parseImportDirective(); - ASTPointer<ContractDefinition> parseContractDefinition(); + ASTPointer<ContractDefinition> parseContractDefinition(bool _isLibrary); ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier(); Declaration::Visibility parseVisibilitySpecifier(Token::Value _token); ASTPointer<FunctionDefinition> parseFunctionDefinition(ASTString const* _contractName); ASTPointer<StructDefinition> parseStructDefinition(); ASTPointer<EnumDefinition> parseEnumDefinition(); ASTPointer<EnumValue> parseEnumValue(); - ASTPointer<VariableDeclaration> parseVariableDeclaration(VarDeclParserOptions const& _options = VarDeclParserOptions(), - ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>()); + ASTPointer<VariableDeclaration> parseVariableDeclaration( + VarDeclParserOptions const& _options = VarDeclParserOptions(), + ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>() + ); ASTPointer<ModifierDefinition> parseModifierDefinition(); ASTPointer<EventDefinition> parseEventDefinition(); ASTPointer<ModifierInvocation> parseModifierInvocation(); diff --git a/libsolidity/Token.h b/libsolidity/Token.h index bc242ecf..1632a693 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -160,6 +160,7 @@ namespace solidity K(Internal, "internal", 0) \ K(Import, "import", 0) \ K(Is, "is", 0) \ + K(Library, "library", 0) \ K(Mapping, "mapping", 0) \ K(Memory, "memory", 0) \ K(Modifier, "modifier", 0) \ @@ -305,7 +306,7 @@ namespace solidity /* Identifiers (not keywords or future reserved words). */ \ T(Identifier, NULL, 0) \ \ - /* Keywords reserved for future. use. */ \ + /* Keywords reserved for future use. */ \ K(As, "as", 0) \ K(Case, "case", 0) \ K(Catch, "catch", 0) \ diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index d1c59ba5..5bc7cd43 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -971,7 +971,7 @@ MemberList const& ContractType::members() const for (auto const& it: m_contract.interfaceFunctions()) members.push_back(MemberList::Member( it.second->declaration().name(), - it.second->asMemberFunction(), + it.second->asMemberFunction(false), &it.second->declaration() )); 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; for (auto const& t: m_parameterTypes) @@ -1563,7 +1563,7 @@ FunctionTypePointer FunctionType::asMemberFunction() const returnParameterTypes, m_parameterNames, returnParameterNames, - m_location, + _inLibrary ? Location::CallCode : m_location, m_arbitraryParameters, m_declaration, m_gasSet, @@ -1633,21 +1633,39 @@ u256 TypeType::storageSize() const << 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 { // We need to lazy-initialize it because of recursive references. if (!m_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(); - vector<ContractDefinition const*> currentBases = m_currentContract->linearizedBaseContracts(); - if (find(currentBases.begin(), currentBases.end(), &contract) != currentBases.end()) - // We are accessing the type of a base contract, so add all public and protected - // members. Note that this does not add inherited functions on purpose. - for (Declaration const* decl: contract.inheritableMembers()) - members.push_back(MemberList::Member(decl->name(), decl->type(), decl)); + 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(); + if (find(currentBases.begin(), currentBases.end(), &contract) != currentBases.end()) + // We are accessing the type of a base contract, so add all public and protected + // members. Note that this does not add inherited functions on purpose. + for (Declaration const* decl: contract.inheritableMembers()) + members.push_back(MemberList::Member(decl->name(), decl->type(), decl)); + } } else if (m_actualType->category() == Category::Enum) { diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 17ae0ce0..11218b7a 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -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 { @@ -788,7 +788,8 @@ public: /// 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 /// dynamic values. - FunctionTypePointer asMemberFunction() const; + /// @param _inLibrary if true, uses CallCode as location. + FunctionTypePointer asMemberFunction(bool _inLibrary) const; private: 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 * of a TypeType. + * For super contracts or libraries, this has members directly. */ class TypeType: public Type { @@ -865,7 +867,7 @@ public: virtual bool canBeStored() const override { return false; } virtual u256 storageSize() const override; 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 MemberList const& members() const override; diff --git a/libsolidity/grammar.txt b/libsolidity/grammar.txt index 6503516c..467aebee 100644 --- a/libsolidity/grammar.txt +++ b/libsolidity/grammar.txt @@ -1,4 +1,4 @@ -ContractDefinition = 'contract' Identifier +ContractDefinition = ( 'contract' | 'library' ) Identifier ( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )? '{' ContractPart* '}' ContractPart = StateVariableDeclaration | StructDefinition | ModifierDefinition | FunctionDefinition | EnumDefinition diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 6a90c69f..c34ffee0 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -123,31 +123,31 @@ void CommandLineInterface::handleBinary(string const& _contract) if (m_args.count(g_argBinaryStr)) { if (m_args.count("output-dir")) - createFile(_contract + ".bin", toHex(m_compiler->bytecode(_contract))); + createFile(_contract + ".bin", m_compiler->object(_contract).toHex()); else { 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("output-dir")) - createFile(_contract + ".clone_bin", toHex(m_compiler->cloneBytecode(_contract))); + createFile(_contract + ".clone_bin", m_compiler->cloneObject(_contract).toHex()); else { 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("output-dir")) - createFile(_contract + ".bin", toHex(m_compiler->runtimeBytecode(_contract))); + createFile(_contract + ".bin", m_compiler->runtimeObject(_contract).toHex()); else { 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) { 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 { cout << "Opcodes: " << endl; - cout << eth::disassemble(m_compiler->bytecode(_contract)); + cout << eth::disassemble(m_compiler->object(_contract).bytecode); cout << endl; } @@ -242,7 +242,7 @@ void CommandLineInterface::handleGasEstimation(string const& _contract) if (eth::AssemblyItems const* items = m_compiler->assemblyItems(_contract)) { Gas gas = GasEstimator::functionalEstimation(*items); - u256 bytecodeSize(m_compiler->runtimeBytecode(_contract).size()); + u256 bytecodeSize(m_compiler->runtimeObject(_contract).bytecode.size()); cout << "construction:" << endl; cout << " " << gas << " + " << (bytecodeSize * eth::c_createDataGas) << " = "; gas += bytecodeSize * eth::c_createDataGas; @@ -500,11 +500,11 @@ void CommandLineInterface::handleCombinedJSON() if (requests.count("abi")) contractData["abi"] = m_compiler->interface(contractName); if (requests.count("bin")) - contractData["bin"] = toHex(m_compiler->bytecode(contractName)); + contractData["bin"] = m_compiler->runtimeObject(contractName).toHex(); 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")) - contractData["opcodes"] = eth::disassemble(m_compiler->bytecode(contractName)); + contractData["opcodes"] = eth::disassemble(m_compiler->object(contractName).bytecode); if (requests.count("asm")) { ostringstream unused; diff --git a/solc/jsonCompiler.cpp b/solc/jsonCompiler.cpp index b6b9f512..b7bb9fe7 100644 --- a/solc/jsonCompiler.cpp +++ b/solc/jsonCompiler.cpp @@ -76,7 +76,7 @@ Json::Value estimateGas(CompilerStack const& _compiler, string const& _contract) if (eth::AssemblyItems const* items = _compiler.assemblyItems(_contract)) { Gas gas = GasEstimator::functionalEstimation(*items); - u256 bytecodeSize(_compiler.runtimeBytecode(_contract).size()); + u256 bytecodeSize(_compiler.runtimeObject(_contract).bytecode.size()); Json::Value creationGas(Json::arrayValue); creationGas[0] = gasToJson(gas); creationGas[1] = gasToJson(bytecodeSize * eth::c_createDataGas); @@ -168,8 +168,8 @@ string compile(string _input, bool _optimize) Json::Value contractData(Json::objectValue); contractData["solidity_interface"] = compiler.solidityInterface(contractName); contractData["interface"] = compiler.interface(contractName); - contractData["bytecode"] = toHex(compiler.bytecode(contractName)); - contractData["opcodes"] = eth::disassemble(compiler.bytecode(contractName)); + contractData["bytecode"] = compiler.object(contractName).toHex(); + contractData["opcodes"] = eth::disassemble(compiler.object(contractName).bytecode); contractData["functionHashes"] = functionHashes(compiler.contractDefinition(contractName)); contractData["gasEstimates"] = estimateGas(compiler, contractName); ostringstream unused; diff --git a/test/contracts/AuctionRegistrar.cpp b/test/contracts/AuctionRegistrar.cpp index 0935c874..a8b57f2f 100644 --- a/test/contracts/AuctionRegistrar.cpp +++ b/test/contracts/AuctionRegistrar.cpp @@ -233,7 +233,7 @@ protected: m_compiler.reset(false, m_addStandardSources); m_compiler.addSource("", registrarCode); 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); BOOST_REQUIRE(!m_output.empty()); diff --git a/test/contracts/FixedFeeRegistrar.cpp b/test/contracts/FixedFeeRegistrar.cpp index 266eb923..f42f4492 100644 --- a/test/contracts/FixedFeeRegistrar.cpp +++ b/test/contracts/FixedFeeRegistrar.cpp @@ -125,7 +125,7 @@ protected: m_compiler.reset(false, m_addStandardSources); m_compiler.addSource("", registrarCode); 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); BOOST_REQUIRE(!m_output.empty()); diff --git a/test/contracts/Wallet.cpp b/test/contracts/Wallet.cpp index 32a53199..4a4848f1 100644 --- a/test/contracts/Wallet.cpp +++ b/test/contracts/Wallet.cpp @@ -440,7 +440,7 @@ protected: m_compiler.reset(false, m_addStandardSources); m_compiler.addSource("", walletCode); 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); sendMessage(*s_compiledWallet + args, true, _value); diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index eb4999e7..5bd6fed3 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -66,7 +66,7 @@ eth::AssemblyItems compileContract(const string& _sourceCode) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) { Compiler compiler; - compiler.compileContract(*contract, map<ContractDefinition const*, bytes const*>{}); + compiler.compileContract(*contract, map<ContractDefinition const*, Assembly const*>{}); return compiler.runtimeAssemblyItems(); } diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp index 897d12fd..6d7c06a3 100644 --- a/test/libsolidity/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -63,7 +63,7 @@ public: auto state = make_shared<KnownState>(); PathGasMeter meter(*m_compiler.assemblyItems()); GasMeter::GasConsumption gas = meter.estimateMax(0, state); - u256 bytecodeSize(m_compiler.runtimeBytecode().size()); + u256 bytecodeSize(m_compiler.runtimeObject().bytecode.size()); gas += bytecodeSize * c_createDataGas; BOOST_REQUIRE(!gas.isInfinite); BOOST_CHECK(gas.value == m_gasUsed); diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index aa423330..c56845aa 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -5230,6 +5230,38 @@ BOOST_AUTO_TEST_CASE(storage_string_as_mapping_key_without_variable) 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() } diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 9b1c026f..747668b0 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -138,7 +138,7 @@ bytes compileFirstExpression(const string& _sourceCode, vector<vector<string>> _ for (vector<string> const& function: _functions) context << context.functionEntryLabel(dynamic_cast<FunctionDefinition const&>(resolveDeclaration(function, resolver))); - bytes instructions = context.assembledBytecode(); + bytes instructions = context.assembledObject().bytecode; // debug // cout << eth::disassemble(instructions) << endl; return instructions; diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index ff91d8e5..5d174367 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -2193,6 +2193,55 @@ BOOST_AUTO_TEST_CASE(string_bytes_conversion) 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) { diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 14b9e9e4..1e034863 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -924,6 +924,16 @@ BOOST_AUTO_TEST_CASE(empty_comment) 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() } diff --git a/test/libsolidity/solidityExecutionFramework.h b/test/libsolidity/solidityExecutionFramework.h index 86e7d351..3370044c 100644 --- a/test/libsolidity/solidityExecutionFramework.h +++ b/test/libsolidity/solidityExecutionFramework.h @@ -53,14 +53,17 @@ public: std::string const& _sourceCode, u256 const& _value = 0, 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.addSource("", _sourceCode); ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed"); - bytes code = m_compiler.bytecode(_contractName); - sendMessage(code + _arguments, true, _value); + eth::LinkerObject obj = m_compiler.object(_contractName); + obj.link(_libraryAddresses); + BOOST_REQUIRE(obj.linkReferences.empty()); + sendMessage(obj.bytecode + _arguments, true, _value); return m_output; } @@ -76,10 +79,11 @@ public: std::string const& _sourceCode, u256 const& _value = 0, 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()); return m_output; } |