From f3a84eab91a6f05cfe58e260f206f9af51811313 Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Fri, 18 Nov 2016 07:37:15 -0800 Subject: Error out when contracts collide on name The previous behaviour, courtesy of the [] operator in std::map, would uncritically store a new ContractDefinition in m_contracts even when a ContractDefinition already existed. This "resolved" collissions on contract names by clobbering the original one with the new one, and could lead to scenarios where the clobber would only be discovered when the original ContractDefinition could not be found or referred to, which was an unhelpful InternalCompilerError. This change checks the m_contracts map for a collision first and will not let the ContractDefinition be changed to a new one once it's set, throwing a CompilerError with information about the conflict. --- libsolidity/interface/CompilerStack.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'libsolidity') diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index a31df584..5d4ccd92 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -180,6 +180,15 @@ bool CompilerStack::parse() if (!resolver.updateDeclaration(*m_globalContext->currentThis())) return false; if (!resolver.updateDeclaration(*m_globalContext->currentSuper())) return false; if (!resolver.resolveNamesAndTypes(*contract)) return false; + if (m_contracts.find(contract->name()) != m_contracts.end()) + { + const ContractDefinition* existingContract = m_contracts.find(contract->name())->second.contract; + if (contract != existingContract) + BOOST_THROW_EXCEPTION(CompilerError() << + errinfo_sourceLocation(contract->location()) << + errinfo_comment(contract->name() + " conflicts with contract at " + + *(existingContract->location().sourceName))); + } m_contracts[contract->name()].contract = contract; } @@ -201,6 +210,16 @@ bool CompilerStack::parse() else noErrors = false; + if (m_contracts.find(contract->name()) != m_contracts.end()) + { + const ContractDefinition* existingContract = m_contracts.find(contract->name())->second.contract; + if (contract != existingContract) + BOOST_THROW_EXCEPTION(CompilerError() << + errinfo_sourceLocation(contract->location()) << + errinfo_comment(contract->name() + " conflicts with!!! contract at " + + *(existingContract->location().sourceName))); + } + m_contracts[contract->name()].contract = contract; } -- cgit From b24ca4fa236ccc89600d30206172baf3eee386a7 Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Fri, 18 Nov 2016 19:29:08 -0800 Subject: Fix tab, drop stupid '!!!', change error message. --- libsolidity/interface/CompilerStack.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 5d4ccd92..dded81f5 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -183,10 +183,10 @@ bool CompilerStack::parse() if (m_contracts.find(contract->name()) != m_contracts.end()) { const ContractDefinition* existingContract = m_contracts.find(contract->name())->second.contract; - if (contract != existingContract) + if (contract != existingContract) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(contract->location()) << - errinfo_comment(contract->name() + " conflicts with contract at " + errinfo_comment(contract->name() + " is already defined at " + *(existingContract->location().sourceName))); } m_contracts[contract->name()].contract = contract; @@ -216,7 +216,7 @@ bool CompilerStack::parse() if (contract != existingContract) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(contract->location()) << - errinfo_comment(contract->name() + " conflicts with!!! contract at " + errinfo_comment(contract->name() + " is already defined at " + *(existingContract->location().sourceName))); } -- cgit From ce3082dec2178273d4913a6f9df909c0aca8ef5a Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Sun, 27 Nov 2016 18:46:44 -0800 Subject: Tidy up the error message --- libsolidity/interface/CompilerStack.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index dded81f5..dd518860 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -184,10 +184,10 @@ bool CompilerStack::parse() { const ContractDefinition* existingContract = m_contracts.find(contract->name())->second.contract; if (contract != existingContract) - BOOST_THROW_EXCEPTION(CompilerError() << + BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(contract->location()) << - errinfo_comment(contract->name() + " is already defined at " - + *(existingContract->location().sourceName))); + errinfo_comment(contract->name() + " is already defined.") << + errinfo_secondarySourceLocation(SecondarySourceLocation().append(existingContract->location()), "Previous definition is here:")); } m_contracts[contract->name()].contract = contract; } -- cgit From 9e88f1eebe27c780c80be06d702eac30e2fc5fa3 Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Sun, 27 Nov 2016 18:50:42 -0800 Subject: Tab whitespace cleanup (again) --- libsolidity/interface/CompilerStack.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'libsolidity') diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index dd518860..9c4618e7 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -187,7 +187,8 @@ bool CompilerStack::parse() BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(contract->location()) << errinfo_comment(contract->name() + " is already defined.") << - errinfo_secondarySourceLocation(SecondarySourceLocation().append(existingContract->location()), "Previous definition is here:")); + errinfo_secondarySourceLocation( + SecondarySourceLocation().append(existingContract->location()), "Previous definition is here:")); } m_contracts[contract->name()].contract = contract; } -- cgit From 071b936b371cd5287717d0ac27b80b837836809a Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Sun, 11 Dec 2016 14:17:38 -0800 Subject: Only avoid collision if it's the same file @chriseth had suggested that it would be better if contracts were referenced in a file:contract notation, and that we output .bin files that prepend original path names if necessary to avoid a collision. This commit is mostly a draft; it still needs to be run through the test suite. --- libsolidity/ast/AST.cpp | 6 +++++ libsolidity/ast/AST.h | 2 ++ libsolidity/interface/CompilerStack.cpp | 48 ++++++++++++++++++++++++--------- libsolidity/interface/CompilerStack.h | 4 +++ 4 files changed, 48 insertions(+), 12 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 6f7a64dc..480fce44 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -190,6 +190,12 @@ void ContractDefinition::setUserDocumentation(Json::Value const& _userDocumentat } +std::string ContractDefinition::fullyQualifiedName() const +{ + std::string qualifiedName = *(location().sourceName) + ":" + name(); + return qualifiedName; +} + vector const& ContractDefinition::inheritableMembers() const { if (!m_inheritableMembers) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 2d092408..060cf973 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -358,6 +358,8 @@ public: Json::Value const& devDocumentation() const; void setDevDocumentation(Json::Value const& _devDocumentation); + std::string fullyQualifiedName() const; + virtual TypePointer type() const override; virtual ContractDefinitionAnnotation& annotation() const override; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 9c4618e7..13cf7d6c 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -21,6 +21,7 @@ * Full-stack compiler that converts a source code string to bytecode. */ + #include #include @@ -180,17 +181,18 @@ bool CompilerStack::parse() if (!resolver.updateDeclaration(*m_globalContext->currentThis())) return false; if (!resolver.updateDeclaration(*m_globalContext->currentSuper())) return false; if (!resolver.resolveNamesAndTypes(*contract)) return false; - if (m_contracts.find(contract->name()) != m_contracts.end()) + + if (m_contracts.find(contract->fullyQualifiedName()) != m_contracts.end()) { - const ContractDefinition* existingContract = m_contracts.find(contract->name())->second.contract; + const ContractDefinition* existingContract = m_contracts.find(contract->fullyQualifiedName())->second.contract; if (contract != existingContract) - BOOST_THROW_EXCEPTION(DeclarationError() << + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(contract->location()) << errinfo_comment(contract->name() + " is already defined.") << errinfo_secondarySourceLocation( - SecondarySourceLocation().append(existingContract->location()), "Previous definition is here:")); + SecondarySourceLocation().append("Previous definition is here:", existingContract->location()))); } - m_contracts[contract->name()].contract = contract; + m_contracts[contract->fullyQualifiedName()].contract = contract; } if (!checkLibraryNameClashes()) @@ -211,9 +213,9 @@ bool CompilerStack::parse() else noErrors = false; - if (m_contracts.find(contract->name()) != m_contracts.end()) + if (m_contracts.find(contract->fullyQualifiedName()) != m_contracts.end()) { - const ContractDefinition* existingContract = m_contracts.find(contract->name())->second.contract; + const ContractDefinition* existingContract = m_contracts.find(contract->fullyQualifiedName())->second.contract; if (contract != existingContract) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(contract->location()) << @@ -221,7 +223,7 @@ bool CompilerStack::parse() + *(existingContract->location().sourceName))); } - m_contracts[contract->name()].contract = contract; + m_contracts[contract->fullyQualifiedName()].contract = contract; } if (noErrors) @@ -335,6 +337,28 @@ string const* CompilerStack::runtimeSourceMapping(string const& _contractName) c return c.runtimeSourceMapping.get(); } +std::string const CompilerStack::filesystemFriendlyName(string const& _contractName) const +{ + // Look up the contract (by its fully-qualified name) + Contract const& matchContract = m_contracts.at(_contractName); + // Check to see if it could collide on name + for (auto const& contract: m_contracts) + { + if (contract.second.contract->name() == matchContract.contract->name() && + contract.second.contract != matchContract.contract) + { + // If it does, then return its fully-qualified name, made fs-friendly + std::string friendlyName = boost::algorithm::replace_all_copy(_contractName, "/", "_"); + boost::algorithm::replace_all(friendlyName, ":", "_"); + boost::algorithm::replace_all(friendlyName, ".", "_"); + return friendlyName; + } + } + // If no collision, return the contract's name + // String is copied to ensure that the contract's name can't be messed with + return std::string(matchContract.contract->name()); +} + eth::LinkerObject const& CompilerStack::object(string const& _contractName) const { return contract(_contractName).object; @@ -598,7 +622,7 @@ bool CompilerStack::checkLibraryNameClashes() if (ContractDefinition* contract = dynamic_cast(node.get())) if (contract->isLibrary()) { - if (libraries.count(contract->name())) + if (libraries.count(contract->fullyQualifiedName())) { auto err = make_shared(Error::Type::DeclarationError); *err << @@ -615,7 +639,7 @@ bool CompilerStack::checkLibraryNameClashes() clashFound = true; } else - libraries[contract->name()] = contract->location(); + libraries[contract->fullyQualifiedName()] = contract->location(); } return !clashFound; } @@ -648,7 +672,7 @@ void CompilerStack::compileContract( compileContract(*dependency, _compiledContracts); shared_ptr compiler = make_shared(m_optimize, m_optimizeRuns); - Contract& compiledContract = m_contracts.at(_contract.name()); + Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName()); string onChainMetadata = createOnChainMetadata(compiledContract); bytes cborEncodedMetadata = // CBOR-encoding of {"bzzr0": dev::swarmHash(onChainMetadata)} @@ -694,7 +718,7 @@ CompilerStack::Contract const& CompilerStack::contract(string const& _contractNa for (auto const& it: m_sources) for (ASTPointer const& node: it.second.ast->nodes()) if (auto contract = dynamic_cast(node.get())) - contractName = contract->name(); + contractName = contract->fullyQualifiedName(); auto it = m_contracts.find(contractName); if (it == m_contracts.end()) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Contract " + _contractName + " not found.")); diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index d49a8df1..9436bd83 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -148,6 +148,10 @@ public: /// @returns the string that provides a mapping between runtime bytecode and sourcecode. /// if the contract does not (yet) have bytecode. std::string const* runtimeSourceMapping(std::string const& _contractName = "") const; + + /// @returns either the contract's name or a mixture of its name and source file, sanitized for filesystem use + std::string const filesystemFriendlyName(std::string const& _contractName) const; + /// @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. -- cgit From 8f25bd54e35dba3fb632c2d209a86acaba63d33d Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Sun, 11 Dec 2016 23:06:57 -0800 Subject: Drop ':' if the source file name is empty A large number of tests compile contracts while passing in an empty string for the source name. This leads to it being keyed by the name ":", while the tests try to look it up under the name "". This change resolves that issue by dropping the ':' in cases where there is, effectively, no source file to prepend anyway. --- libsolidity/ast/AST.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'libsolidity') diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 480fce44..562ac828 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -192,7 +192,8 @@ void ContractDefinition::setUserDocumentation(Json::Value const& _userDocumentat std::string ContractDefinition::fullyQualifiedName() const { - std::string qualifiedName = *(location().sourceName) + ":" + name(); + std::string sourceString = *(location().sourceName); + std::string qualifiedName = (sourceString.empty() ? ("") : (sourceString + ":")) + name(); return qualifiedName; } -- cgit From e3b0827721f114a371df57b7079205034683ed25 Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Sun, 11 Dec 2016 23:58:01 -0800 Subject: Push the error instead of throwing it Throwing a CompilerError on multiple contract definition violates the expectations of the test suite, which thinks that compile() will return false if the code can't compile. This brings contract collision reporting in line with most of the other errors. --- libsolidity/interface/CompilerStack.cpp | 44 +++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 10 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 13cf7d6c..7626406c 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -186,13 +186,24 @@ bool CompilerStack::parse() { const ContractDefinition* existingContract = m_contracts.find(contract->fullyQualifiedName())->second.contract; if (contract != existingContract) - BOOST_THROW_EXCEPTION(CompilerError() << + { + auto err = make_shared(Error::Type::DeclarationError); + *err << errinfo_sourceLocation(contract->location()) << - errinfo_comment(contract->name() + " is already defined.") << - errinfo_secondarySourceLocation( - SecondarySourceLocation().append("Previous definition is here:", existingContract->location()))); + errinfo_comment( + "Contract/Library \"" + contract->name() + "\" declared twice " + ) << + errinfo_secondarySourceLocation(SecondarySourceLocation().append( + "The other declaration is here:", existingContract->location())); + + m_errors.push_back(err); + noErrors = false; + } + } + else + { + m_contracts[contract->fullyQualifiedName()].contract = contract; } - m_contracts[contract->fullyQualifiedName()].contract = contract; } if (!checkLibraryNameClashes()) @@ -216,14 +227,27 @@ bool CompilerStack::parse() if (m_contracts.find(contract->fullyQualifiedName()) != m_contracts.end()) { const ContractDefinition* existingContract = m_contracts.find(contract->fullyQualifiedName())->second.contract; + if (contract != existingContract) - BOOST_THROW_EXCEPTION(CompilerError() << + { + auto err = make_shared(Error::Type::DeclarationError); + *err << errinfo_sourceLocation(contract->location()) << - errinfo_comment(contract->name() + " is already defined at " - + *(existingContract->location().sourceName))); - } + errinfo_comment( + "Contract/Library \"" + contract->name() + "\" declared twice " + ) << + errinfo_secondarySourceLocation(SecondarySourceLocation().append( + "The other declaration is here:", existingContract->location())); - m_contracts[contract->fullyQualifiedName()].contract = contract; + m_errors.push_back(err); + noErrors = false; + } + + } + else + { + m_contracts[contract->fullyQualifiedName()].contract = contract; + } } if (noErrors) -- cgit From 0c98e4b2da6b5de885114a744339a1f0f96c79be Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Fri, 16 Dec 2016 04:22:42 -0800 Subject: Stylistic corrections --- libsolidity/ast/AST.cpp | 2 +- libsolidity/interface/CompilerStack.cpp | 12 ++++-------- 2 files changed, 5 insertions(+), 9 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 562ac828..45ff69c5 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -190,7 +190,7 @@ void ContractDefinition::setUserDocumentation(Json::Value const& _userDocumentat } -std::string ContractDefinition::fullyQualifiedName() const +string ContractDefinition::fullyQualifiedName() const { std::string sourceString = *(location().sourceName); std::string qualifiedName = (sourceString.empty() ? ("") : (sourceString + ":")) + name(); diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 7626406c..fbaf1bcc 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -184,7 +184,7 @@ bool CompilerStack::parse() if (m_contracts.find(contract->fullyQualifiedName()) != m_contracts.end()) { - const ContractDefinition* existingContract = m_contracts.find(contract->fullyQualifiedName())->second.contract; + ContractDefinition const* existingContract = m_contracts[contract->fullyQualifiedName()].contract; if (contract != existingContract) { auto err = make_shared(Error::Type::DeclarationError); @@ -201,9 +201,7 @@ bool CompilerStack::parse() } } else - { m_contracts[contract->fullyQualifiedName()].contract = contract; - } } if (!checkLibraryNameClashes()) @@ -224,9 +222,10 @@ bool CompilerStack::parse() else noErrors = false; + // Note that find() must be used here to prevent an automatic insert into the map if (m_contracts.find(contract->fullyQualifiedName()) != m_contracts.end()) { - const ContractDefinition* existingContract = m_contracts.find(contract->fullyQualifiedName())->second.contract; + ContractDefinition const* existingContract = m_contracts[contract->fullyQualifiedName()].contract; if (contract != existingContract) { @@ -245,9 +244,7 @@ bool CompilerStack::parse() } else - { m_contracts[contract->fullyQualifiedName()].contract = contract; - } } if (noErrors) @@ -379,8 +376,7 @@ std::string const CompilerStack::filesystemFriendlyName(string const& _contractN } } // If no collision, return the contract's name - // String is copied to ensure that the contract's name can't be messed with - return std::string(matchContract.contract->name()); + return matchContract.contract->name(); } eth::LinkerObject const& CompilerStack::object(string const& _contractName) const -- cgit From f10bf36ae3681169be7c75dac32567f1746c529d Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Fri, 16 Dec 2016 04:52:19 -0800 Subject: Move fullyQualified() name to Declaration --- libsolidity/ast/AST.cpp | 8 -------- libsolidity/ast/AST.h | 3 +-- 2 files changed, 1 insertion(+), 10 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 45ff69c5..dd582e54 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -189,14 +189,6 @@ void ContractDefinition::setUserDocumentation(Json::Value const& _userDocumentat m_userDocumentation = _userDocumentation; } - -string ContractDefinition::fullyQualifiedName() const -{ - std::string sourceString = *(location().sourceName); - std::string qualifiedName = (sourceString.empty() ? ("") : (sourceString + ":")) + name(); - return qualifiedName; -} - vector const& ContractDefinition::inheritableMembers() const { if (!m_inheritableMembers) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 060cf973..f72b272c 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -161,6 +161,7 @@ public: /// @returns the source name this declaration is present in. /// Can be combined with annotation().canonicalName to form a globally unique name. std::string sourceUnitName() const; + std::string fullyQualifiedName() const { return sourceUnitName() + ":" + name(); } virtual bool isLValue() const { return false; } virtual bool isPartOfExternalInterface() const { return false; } @@ -358,8 +359,6 @@ public: Json::Value const& devDocumentation() const; void setDevDocumentation(Json::Value const& _devDocumentation); - std::string fullyQualifiedName() const; - virtual TypePointer type() const override; virtual ContractDefinitionAnnotation& annotation() const override; -- cgit From 85c55c796adf4d93c3d11ccb8d048a51dc46cf8d Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Tue, 20 Dec 2016 04:57:46 -0800 Subject: Remove unique error for contract collision Because contracts are uniquely identified by their source unit, there is no need for a unique error for this; it's actually covered by the checker for double-declaration of identifiers. --- libsolidity/interface/CompilerStack.cpp | 49 +++++++-------------------------- 1 file changed, 10 insertions(+), 39 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index fbaf1bcc..d4675a23 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -182,25 +182,12 @@ bool CompilerStack::parse() if (!resolver.updateDeclaration(*m_globalContext->currentSuper())) return false; if (!resolver.resolveNamesAndTypes(*contract)) return false; - if (m_contracts.find(contract->fullyQualifiedName()) != m_contracts.end()) - { - ContractDefinition const* existingContract = m_contracts[contract->fullyQualifiedName()].contract; - if (contract != existingContract) - { - auto err = make_shared(Error::Type::DeclarationError); - *err << - errinfo_sourceLocation(contract->location()) << - errinfo_comment( - "Contract/Library \"" + contract->name() + "\" declared twice " - ) << - errinfo_secondarySourceLocation(SecondarySourceLocation().append( - "The other declaration is here:", existingContract->location())); + // Note that we now reference contracts by their fully qualified names, and + // thus contracts can only conflict if declared in the same source file. This + // already causes a double-declaration error elsewhere, so we do not report + // an error here and instead silently drop any additional contracts we find. - m_errors.push_back(err); - noErrors = false; - } - } - else + if (m_contracts.find(contract->fullyQualifiedName()) == m_contracts.end()) m_contracts[contract->fullyQualifiedName()].contract = contract; } @@ -222,28 +209,12 @@ bool CompilerStack::parse() else noErrors = false; - // Note that find() must be used here to prevent an automatic insert into the map - if (m_contracts.find(contract->fullyQualifiedName()) != m_contracts.end()) - { - ContractDefinition const* existingContract = m_contracts[contract->fullyQualifiedName()].contract; - - if (contract != existingContract) - { - auto err = make_shared(Error::Type::DeclarationError); - *err << - errinfo_sourceLocation(contract->location()) << - errinfo_comment( - "Contract/Library \"" + contract->name() + "\" declared twice " - ) << - errinfo_secondarySourceLocation(SecondarySourceLocation().append( - "The other declaration is here:", existingContract->location())); - - m_errors.push_back(err); - noErrors = false; - } + // Note that we now reference contracts by their fully qualified names, and + // thus contracts can only conflict if declared in the same source file. This + // already causes a double-declaration error elsewhere, so we do not report + // an error here and instead silently drop any additional contracts we find. - } - else + if (m_contracts.find(contract->fullyQualifiedName()) == m_contracts.end()) m_contracts[contract->fullyQualifiedName()].contract = contract; } -- cgit From 1f30982ab532fdf719f3924e24e80057fe85e031 Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Wed, 21 Dec 2016 11:26:22 -0800 Subject: Use fully-qualified names for linking, too Using libraries leaves behind a library link reference in the binary which the linker must later resolve. These link references were still being generated by name and not by fully-qualified name. This would lead to a link-time collision between two libraries having the same name but in different source units. This change changes linker symbols over to fully-qualified names, which resolves that issue. This does potentially introduce a new problem, which is that linker symbols appear to be limited to 36 characters and are truncated. Storing paths extends the average symbol size, and it would be great if truncation was from the tail rather than the head. --- libsolidity/codegen/ContractCompiler.cpp | 2 +- libsolidity/codegen/ExpressionCompiler.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index a0f196bc..fa77c1aa 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -575,7 +575,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) else if (auto contract = dynamic_cast(decl)) { solAssert(contract->isLibrary(), ""); - _assembly.appendLibraryAddress(contract->name()); + _assembly.appendLibraryAddress(contract->fullyQualifiedName()); } else solAssert(false, "Invalid declaration type."); diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 3922da88..37bd1458 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -892,7 +892,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) solAssert(funType->location() == FunctionType::Location::DelegateCall, ""); auto contract = dynamic_cast(funType->declaration().scope()); solAssert(contract && contract->isLibrary(), ""); - m_context.appendLibraryAddress(contract->name()); + m_context.appendLibraryAddress(contract->fullyQualifiedName()); m_context << funType->externalIdentifier(); utils().moveIntoStack(funType->selfType()->sizeOnStack(), 2); } @@ -1270,7 +1270,7 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) else if (auto contract = dynamic_cast(declaration)) { if (contract->isLibrary()) - m_context.appendLibraryAddress(contract->name()); + m_context.appendLibraryAddress(contract->fullyQualifiedName()); } else if (dynamic_cast(declaration)) { -- cgit From 5a2331a9f6615e58f4d10d266870daaaf05deb6b Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Wed, 21 Dec 2016 11:40:13 -0800 Subject: Remove checkLibraryNameClashes() The library name clash checker throws errors when two libraries of the same name are spotted. In a previous commit, this function was rewritten to use fully-qualified names instead, which makes it redundant to the checker for multiply-declared identifiers. Since it no longer serves a clear purpose, the function is being dropped. --- libsolidity/interface/CompilerStack.cpp | 34 --------------------------------- libsolidity/interface/CompilerStack.h | 3 --- 2 files changed, 37 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index d4675a23..d8bb20d7 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -191,9 +191,6 @@ bool CompilerStack::parse() m_contracts[contract->fullyQualifiedName()].contract = contract; } - if (!checkLibraryNameClashes()) - noErrors = false; - for (Source const* source: m_sourceOrder) for (ASTPointer const& node: source->ast->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) @@ -604,37 +601,6 @@ void CompilerStack::resolveImports() swap(m_sourceOrder, sourceOrder); } -bool CompilerStack::checkLibraryNameClashes() -{ - bool clashFound = false; - map libraries; - for (Source const* source: m_sourceOrder) - for (ASTPointer const& node: source->ast->nodes()) - if (ContractDefinition* contract = dynamic_cast(node.get())) - if (contract->isLibrary()) - { - if (libraries.count(contract->fullyQualifiedName())) - { - auto err = make_shared(Error::Type::DeclarationError); - *err << - errinfo_sourceLocation(contract->location()) << - errinfo_comment( - "Library \"" + contract->name() + "\" declared twice " - "(will create ambiguities during linking)." - ) << - errinfo_secondarySourceLocation(SecondarySourceLocation().append( - "The other declaration is here:", libraries[contract->name()] - )); - - m_errors.push_back(err); - clashFound = true; - } - else - libraries[contract->fullyQualifiedName()] = contract->location(); - } - return !clashFound; -} - string CompilerStack::absolutePath(string const& _path, string const& _reference) const { using path = boost::filesystem::path; diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 9436bd83..61edc284 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -234,9 +234,6 @@ private: StringMap loadMissingSources(SourceUnit const& _ast, std::string const& _path); std::string applyRemapping(std::string const& _path, std::string const& _context); void resolveImports(); - /// Checks whether there are libraries with the same name, reports that as an error and - /// @returns false in this case. - bool checkLibraryNameClashes(); /// @returns the absolute path corresponding to @a _path relative to @a _reference. std::string absolutePath(std::string const& _path, std::string const& _reference) const; /// Helper function to return path converted strings. -- cgit From 94b092d87c051e8846f5d61eaa1a4581b6588c71 Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Mon, 16 Jan 2017 23:47:04 -0500 Subject: Provide fall-back method for contract lookup Properly, contracts are now looked up via : identifiers called "fully qualified names." As a modicum of backward-compatibility, failure on a lookup is now backed up by seeing if the ":" exists at all, and if it doesn't, then the known contracts are scanned for any matching contract name. --- libsolidity/interface/CompilerStack.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'libsolidity') diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index d8bb20d7..262b91ff 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -677,8 +677,25 @@ CompilerStack::Contract const& CompilerStack::contract(string const& _contractNa if (auto contract = dynamic_cast(node.get())) contractName = contract->fullyQualifiedName(); auto it = m_contracts.find(contractName); - if (it == m_contracts.end()) + // To provide a measure of backward-compatibility, if a contract is not located by its + // fully-qualified name, a lookup will be attempted purely on the contract's name to see + // if anything will satisfy. + if (it == m_contracts.end() && contractName.find(":") == string::npos) + { + for (auto const& contractEntry: m_contracts) + { + stringstream ss; + ss.str(contractEntry.first); + // All entries are : + string source; + string foundName; + getline(ss, source, ':'); + getline(ss, foundName, ':'); + if (foundName == contractName) return contractEntry.second; + } + // If we get here, both lookup methods failed. BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Contract " + _contractName + " not found.")); + } return it->second; } -- cgit From 99eaadd2cd4ece9c3ceba16fa5559c45c2e66b08 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 17 Jan 2017 10:31:09 +0100 Subject: Deterministic AST node identifiers. --- libsolidity/ast/AST.cpp | 2 ++ libsolidity/ast/AST.h | 4 ++++ libsolidity/ast/ASTJsonConverter.cpp | 4 ++-- 3 files changed, 8 insertions(+), 2 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 6f7a64dc..3db4627a 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -37,6 +37,8 @@ using namespace dev::solidity; ASTNode::ASTNode(SourceLocation const& _location): m_location(_location) { + static size_t id = 0; + m_id = ++id; } ASTNode::~ASTNode() diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 2d092408..e9df2e7d 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -57,6 +57,9 @@ public: explicit ASTNode(SourceLocation const& _location); virtual ~ASTNode(); + /// @returns an identifier of this AST node that is unique for a single compilation run. + size_t id() const { return m_id; } + virtual void accept(ASTVisitor& _visitor) = 0; virtual void accept(ASTConstVisitor& _visitor) const = 0; template @@ -94,6 +97,7 @@ public: ///@} protected: + size_t m_id = 0; /// Annotation - is specialised in derived classes, is created upon request (because of polymorphism). mutable ASTAnnotation* m_annotation = nullptr; diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index abaad0fd..de8fde92 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -42,7 +42,7 @@ void ASTJsonConverter::addJsonNode( { Json::Value node; - node["id"] = reinterpret_cast(&_node); + node["id"] = _node.id(); node["src"] = sourceLocationToString(_node.location()); node["name"] = _nodeName; if (_attributes.size() != 0) @@ -124,7 +124,7 @@ bool ASTJsonConverter::visit(ContractDefinition const& _node) { Json::Value linearizedBaseContracts(Json::arrayValue); for (auto const& baseContract: _node.annotation().linearizedBaseContracts) - linearizedBaseContracts.append(reinterpret_cast(baseContract)); + linearizedBaseContracts.append(baseContract->id()); addJsonNode(_node, "ContractDefinition", { make_pair("name", _node.name()), make_pair("isLibrary", _node.isLibrary()), -- cgit From 350c7e7e2c1bafbe8d543fc052aedad3e69ba5e1 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 17 Jan 2017 12:09:13 +0000 Subject: Store strict version number in metadata (exclude the platform) --- libsolidity/interface/CompilerStack.cpp | 2 +- libsolidity/interface/Version.cpp | 3 +++ libsolidity/interface/Version.h | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) (limited to 'libsolidity') diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index a31df584..08b21715 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -695,7 +695,7 @@ string CompilerStack::createOnChainMetadata(Contract const& _contract) const Json::Value meta; meta["version"] = 1; meta["language"] = "Solidity"; - meta["compiler"]["version"] = VersionString; + meta["compiler"]["version"] = VersionStringStrict; meta["sources"] = Json::objectValue; for (auto const& s: m_sources) diff --git a/libsolidity/interface/Version.cpp b/libsolidity/interface/Version.cpp index ff66f039..30923fc2 100644 --- a/libsolidity/interface/Version.cpp +++ b/libsolidity/interface/Version.cpp @@ -38,6 +38,9 @@ string const dev::solidity::VersionString = (string(SOL_VERSION_PRERELEASE).empty() ? "" : "-" + string(SOL_VERSION_PRERELEASE)) + (string(SOL_VERSION_BUILDINFO).empty() ? "" : "+" + string(SOL_VERSION_BUILDINFO)); +string const dev::solidity::VersionStringStrict = + string(dev::solidity::VersionNumber) + + (string(SOL_VERSION_PRERELEASE).empty() ? "" : "-" + string(SOL_VERSION_PRERELEASE)); bytes dev::solidity::binaryVersion() { diff --git a/libsolidity/interface/Version.h b/libsolidity/interface/Version.h index 5b07b3f4..24c3555d 100644 --- a/libsolidity/interface/Version.h +++ b/libsolidity/interface/Version.h @@ -32,6 +32,7 @@ namespace solidity extern char const* VersionNumber; extern std::string const VersionString; +extern std::string const VersionStringStrict; /// @returns a binary form of the version string, where A.B.C-HASH is encoded such that /// the first byte is zero, the following three bytes encode A B and C (interpreted as decimals) -- cgit From 467559917008525d6b606de264e4a86a1c5e0620 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 17 Jan 2017 12:35:13 +0000 Subject: Include SOL_VERSION_COMMIT/SOL_VERSION_PLATFORM in buildinfo.h --- libsolidity/interface/Version.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'libsolidity') diff --git a/libsolidity/interface/Version.cpp b/libsolidity/interface/Version.cpp index 30923fc2..0d23f9c3 100644 --- a/libsolidity/interface/Version.cpp +++ b/libsolidity/interface/Version.cpp @@ -40,7 +40,8 @@ string const dev::solidity::VersionString = string const dev::solidity::VersionStringStrict = string(dev::solidity::VersionNumber) + - (string(SOL_VERSION_PRERELEASE).empty() ? "" : "-" + string(SOL_VERSION_PRERELEASE)); + (string(SOL_VERSION_PRERELEASE).empty() ? "" : "-" + string(SOL_VERSION_PRERELEASE)) + + (string(SOL_VERSION_COMMIT).empty() ? "" : "+" + string(SOL_VERSION_COMMIT)); bytes dev::solidity::binaryVersion() { -- cgit From c16e141ffbca70174c0efa119dbe7c7ef14b5286 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 18 Jan 2017 14:09:40 +0100 Subject: Fix JSON output on macos. --- libsolidity/ast/ASTJsonConverter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index de8fde92..69c10c8d 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -42,7 +42,7 @@ void ASTJsonConverter::addJsonNode( { Json::Value node; - node["id"] = _node.id(); + node["id"] = Json::UInt64(_node.id()); node["src"] = sourceLocationToString(_node.location()); node["name"] = _nodeName; if (_attributes.size() != 0) @@ -124,7 +124,7 @@ bool ASTJsonConverter::visit(ContractDefinition const& _node) { Json::Value linearizedBaseContracts(Json::arrayValue); for (auto const& baseContract: _node.annotation().linearizedBaseContracts) - linearizedBaseContracts.append(baseContract->id()); + linearizedBaseContracts.append(Json::UInt64(baseContract->id())); addJsonNode(_node, "ContractDefinition", { make_pair("name", _node.name()), make_pair("isLibrary", _node.isLibrary()), -- cgit From d40ae663ecbdbccc2b71eff175bb6765291ba078 Mon Sep 17 00:00:00 2001 From: Federico Bond Date: Wed, 18 Jan 2017 12:43:23 -0300 Subject: Fix typo in comment --- libsolidity/ast/AST.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libsolidity') diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index d11a246c..4af64963 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -606,7 +606,7 @@ private: /** * Declaration of a variable. This can be used in various places, e.g. in function parameter - * lists, struct definitions and even function bodys. + * lists, struct definitions and even function bodies. */ class VariableDeclaration: public Declaration { -- cgit From 23a654ade8bb20fc2768e88b77059a14dce21635 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 19 Jan 2017 11:10:09 +0100 Subject: Fix default function type name visibility. --- libsolidity/analysis/ReferencesResolver.cpp | 1 - libsolidity/ast/AST.h | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 66bf1d0e..df579c3d 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -87,7 +87,6 @@ void ReferencesResolver::endVisit(FunctionTypeName const& _typeName) { switch (_typeName.visibility()) { - case VariableDeclaration::Visibility::Default: case VariableDeclaration::Visibility::Internal: case VariableDeclaration::Visibility::External: break; diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 4af64963..6e81892b 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -867,7 +867,10 @@ public: std::vector> const& parameterTypes() const { return m_parameterTypes->parameters(); } std::vector> const& returnParameterTypes() const { return m_returnTypes->parameters(); } - Declaration::Visibility visibility() const { return m_visibility; } + Declaration::Visibility visibility() const + { + return m_visibility == Declaration::Visibility::Default ? Declaration::Visibility::Internal : m_visibility; + } bool isDeclaredConst() const { return m_isDeclaredConst; } bool isPayable() const { return m_isPayable; } -- cgit From 3fed790a56cea7bae481d01c15949aa500823968 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 17 Jan 2017 10:47:44 +0100 Subject: Type identifiers. --- libsolidity/ast/Types.cpp | 189 +++++++++++++++++++++++++++++++++++++++++++++- libsolidity/ast/Types.h | 24 ++++++ 2 files changed, 210 insertions(+), 3 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 03ff8471..9e722474 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -272,7 +272,15 @@ IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier): solAssert( m_bits > 0 && m_bits <= 256 && m_bits % 8 == 0, "Invalid bit number for integer type: " + dev::toString(_bits) - ); + ); +} + +string IntegerType::identifier() const +{ + if (isAddress()) + return "t_address"; + else + return "t_" + string(isSigned() ? "" : "u") + "int" + std::to_string(numBits()); } bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const @@ -412,7 +420,12 @@ FixedPointType::FixedPointType(int _integerBits, int _fractionalBits, FixedPoint m_fractionalBits % 8 == 0, "Invalid bit number(s) for fixed type: " + dev::toString(_integerBits) + "x" + dev::toString(_fractionalBits) - ); + ); +} + +string FixedPointType::identifier() const +{ + return "t_" + string(isSigned() ? "" : "u") + "fixed" + std::to_string(integerBits()) + "x" + std::to_string(fractionalBits()); } bool FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const @@ -770,6 +783,11 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ } } +string RationalNumberType::identifier() const +{ + return "t_rational_" + m_value.numerator().str() + "_by_" + m_value.denominator().str(); +} + bool RationalNumberType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -909,6 +927,13 @@ bool StringLiteralType::isImplicitlyConvertibleTo(Type const& _convertTo) const return false; } +string StringLiteralType::identifier() const +{ + // Since we have to return a valid identifier and the string itself may contain + // anything, we hash it. + return "t_stringliteral_" + toHex(keccak256(m_value).asBytes()); +} + bool StringLiteralType::operator==(const Type& _other) const { if (_other.category() != category()) @@ -1002,6 +1027,11 @@ MemberList::MemberMap FixedBytesType::nativeMembers(const ContractDefinition*) c return MemberList::MemberMap{MemberList::Member{"length", make_shared(8)}}; } +string FixedBytesType::identifier() const +{ + return "t_bytes" + std::to_string(m_bytes); +} + bool FixedBytesType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -1115,6 +1145,20 @@ string ReferenceType::stringForReferencePart() const return ""; } +string ReferenceType::identifierLocationSuffix() const +{ + string id; + if (location() == DataLocation::Storage) + id += "_storage"; + else if (location() == DataLocation::Memory) + id += "_memory"; + else + id += "_calldata"; + if (isPointer()) + id += "_ptr"; + return id; +} + bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const { if (_convertTo.category() != category()) @@ -1170,6 +1214,26 @@ bool ArrayType::isExplicitlyConvertibleTo(const Type& _convertTo) const return true; } +string ArrayType::identifier() const +{ + string id; + if (isString()) + id += "t_string"; + else if (isByteArray()) + id += "t_bytes"; + else + { + id = baseType()->identifier(); + if (isDynamicallySized()) + id += "_arraydyn"; + else + id += string("_array") + length().str(); + } + id += identifierLocationSuffix(); + + return id; +} + bool ArrayType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -1184,7 +1248,7 @@ bool ArrayType::operator==(Type const& _other) const return false; if (*other.baseType() != *baseType()) return false; - return isDynamicallySized() || length() == other.length(); + return isDynamicallySized() || length() == other.length(); } unsigned ArrayType::calldataEncodedSize(bool _padded) const @@ -1356,6 +1420,11 @@ TypePointer ArrayType::copyForLocation(DataLocation _location, bool _isPointer) return copy; } +string ContractType::identifier() const +{ + return (m_super ? "t_super_" : "t_contract_") + m_contract.name() + "_" + std::to_string(m_contract.id()); +} + bool ContractType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -1465,6 +1534,11 @@ bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const return this->m_struct == convertTo.m_struct; } +string StructType::identifier() const +{ + return "t_struct_" + m_struct.name() + "_" + std::to_string(m_struct.id()) + identifierLocationSuffix(); +} + bool StructType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -1605,6 +1679,11 @@ TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const return _operator == Token::Delete ? make_shared() : TypePointer(); } +string EnumType::identifier() const +{ + return "t_enum_" + m_enum.name() + "_" + std::to_string(m_enum.id()); +} + bool EnumType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -1686,6 +1765,18 @@ bool TupleType::isImplicitlyConvertibleTo(Type const& _other) const return false; } +string TupleType::identifier() const +{ + string id = "t_tuple" + std::to_string(components().size()) + "_"; + for (auto const& c: components()) + if (c) + id += c->identifier() + "_"; + else + id += "t_empty_"; + id += "tuple_end"; + return id; +} + bool TupleType::operator==(Type const& _other) const { if (auto tupleType = dynamic_cast(&_other)) @@ -1934,6 +2025,59 @@ TypePointers FunctionType::parameterTypes() const return TypePointers(m_parameterTypes.cbegin() + 1, m_parameterTypes.cend()); } +string FunctionType::identifier() const +{ + string id = "t_function_"; + switch (location()) + { + case Location::Internal: id += "internal"; break; + case Location::External: id += "external"; break; + case Location::CallCode: id += "callcode"; break; + case Location::DelegateCall: id += "delegatecall"; break; + case Location::Bare: id += "bare"; break; + case Location::BareCallCode: id += "barecallcode"; break; + case Location::BareDelegateCall: id += "baredelegatecall"; break; + case Location::Creation: id += "creation"; break; + case Location::Send: id += "send"; break; + case Location::SHA3: id += "sha3"; break; + case Location::Selfdestruct: id += "selfdestruct"; break; + case Location::ECRecover: id += "ecrecover"; break; + case Location::SHA256: id += "sha256"; break; + case Location::RIPEMD160: id += "ripemd160"; break; + case Location::Log0: id += "log0"; break; + case Location::Log1: id += "log1"; break; + case Location::Log2: id += "log2"; break; + case Location::Log3: id += "log3"; break; + case Location::Log4: id += "log4"; break; + case Location::Event: id += "event"; break; + case Location::SetGas: id += "setgas"; break; + case Location::SetValue: id += "setvalue"; break; + case Location::BlockHash: id += "blockhash"; break; + case Location::AddMod: id += "addmod"; break; + case Location::MulMod: id += "mulmod"; break; + case Location::ArrayPush: id += "arraypush"; break; + case Location::ByteArrayPush: id += "bytearraypush"; break; + case Location::ObjectCreation: id += "objectcreation"; break; + default: solAssert(false, "Unknown function location."); break; + } + if (isConstant()) + id += "_constant"; + id += "_param" + std::to_string(m_parameterTypes.size()) + "_"; + for (auto const& p: m_parameterTypes) + id += p->identifier() + "_"; + id += "return" + std::to_string(m_returnParameterTypes.size()) + "_"; + for (auto const& r: m_returnParameterTypes) + id += r->identifier() + "_"; + if (m_gasSet) + id += "gas_set_"; + if (m_valueSet) + id += "value_set_"; + if (bound()) + id += "bound_to" + selfType()->identifier() + "_"; + id += "function_end"; + return id; +} + bool FunctionType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -2354,6 +2498,11 @@ ASTPointer FunctionType::documentation() const return ASTPointer(); } +string MappingType::identifier() const +{ + return "t_mapping_" + m_keyType->identifier() + "_to_" + m_valueType->identifier() + "_mapping_end"; +} + bool MappingType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -2372,6 +2521,11 @@ string MappingType::canonicalName(bool) const return "mapping(" + keyType()->canonicalName(false) + " => " + valueType()->canonicalName(false) + ")"; } +string TypeType::identifier() const +{ + return "t_type_" + actualType()->identifier(); +} + bool TypeType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -2456,6 +2610,14 @@ u256 ModifierType::storageSize() const << errinfo_comment("Storage size of non-storable type type requested.")); } +string ModifierType::identifier() const +{ + string id = "t_modifier_param" + std::to_string(m_parameterTypes.size()) + "_"; + for (auto const& p: m_parameterTypes) + id += p->identifier() + "_"; + return id + "end_modifier"; +} + bool ModifierType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -2480,6 +2642,11 @@ string ModifierType::toString(bool _short) const return name + ")"; } +string ModuleType::identifier() const +{ + return "t_module_" + std::to_string(m_sourceUnit.id()); +} + bool ModuleType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -2501,6 +2668,22 @@ string ModuleType::toString(bool) const return string("module \"") + m_sourceUnit.annotation().path + string("\""); } +string MagicType::identifier() const +{ + switch (m_kind) + { + case Kind::Block: + return "t_magic_block"; + case Kind::Message: + return "t_magic_message"; + case Kind::Transaction: + return "t_magic_transaction"; + default: + solAssert(false, "Unknown kind of magic"); + } + return ""; +} + bool MagicType::operator==(Type const& _other) const { if (_other.category() != category()) diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 26e2b8f2..c1f6a476 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -155,6 +155,10 @@ public: static TypePointer commonType(TypePointer const& _a, TypePointer const& _b); virtual Category category() const = 0; + /// @returns a valid solidity identifier such that two types should compare equal if and + /// only if they have the same identifier. + /// The identifier should start with "t_". + virtual std::string identifier() const = 0; virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; } virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const { @@ -288,6 +292,7 @@ public: explicit IntegerType(int _bits, Modifier _modifier = Modifier::Unsigned); + virtual std::string identifier() const override; virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; @@ -329,6 +334,7 @@ public: explicit FixedPointType(int _integerBits, int _fractionalBits, Modifier _modifier = Modifier::Unsigned); + virtual std::string identifier() const override; virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; @@ -378,6 +384,7 @@ public: virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } @@ -416,6 +423,7 @@ public: return TypePointer(); } + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } @@ -449,6 +457,7 @@ public: virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; @@ -476,6 +485,7 @@ class BoolType: public Type public: BoolType() {} virtual Category category() const override { return Category::Bool; } + virtual std::string identifier() const override { return "t_bool"; } virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; @@ -533,6 +543,8 @@ protected: TypePointer copyForLocationIfReference(TypePointer const& _type) const; /// @returns a human-readable description of the reference part of the type. std::string stringForReferencePart() const; + /// @returns the suffix computed from the reference part to be used by identifier(); + std::string identifierLocationSuffix() const; DataLocation m_location = DataLocation::Storage; bool m_isPointer = true; @@ -573,6 +585,7 @@ public: virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual std::string identifier() const override; virtual bool operator==(const Type& _other) const override; virtual unsigned calldataEncodedSize(bool _padded) const override; virtual bool isDynamicallySized() const override { return m_hasDynamicLength; } @@ -622,6 +635,7 @@ public: /// Contracts can be converted to themselves and to integers. virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual unsigned calldataEncodedSize(bool _padded ) const override { @@ -677,6 +691,7 @@ public: explicit StructType(StructDefinition const& _struct, DataLocation _location = DataLocation::Storage): ReferenceType(_location), m_struct(_struct) {} virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override; + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual unsigned calldataEncodedSize(bool _padded) const override; u256 memorySize() const; @@ -720,6 +735,7 @@ public: virtual Category category() const override { return Category::Enum; } explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {} virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual unsigned calldataEncodedSize(bool _padded) const override { @@ -760,6 +776,7 @@ public: virtual Category category() const override { return Category::Tuple; } explicit TupleType(std::vector const& _types = std::vector()): m_components(_types) {} virtual bool isImplicitlyConvertibleTo(Type const& _other) const override; + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } virtual std::string toString(bool) const override; @@ -897,6 +914,7 @@ public: /// @returns the "self" parameter type for a bound function TypePointer selfType() const; + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual std::string canonicalName(bool /*_addDataLocation*/) const override; @@ -995,6 +1013,7 @@ public: MappingType(TypePointer const& _keyType, TypePointer const& _valueType): m_keyType(_keyType), m_valueType(_valueType) {} + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual std::string toString(bool _short) const override; virtual std::string canonicalName(bool _addDataLocation) const override; @@ -1029,6 +1048,7 @@ public: TypePointer const& actualType() const { return m_actualType; } virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } virtual u256 storageSize() const override; @@ -1056,6 +1076,7 @@ public: virtual u256 storageSize() const override; virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned sizeOnStack() const override { return 0; } + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual std::string toString(bool _short) const override; @@ -1080,6 +1101,7 @@ public: return TypePointer(); } + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } virtual bool canLiveOutsideStorage() const override { return true; } @@ -1109,6 +1131,7 @@ public: return TypePointer(); } + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } virtual bool canLiveOutsideStorage() const override { return true; } @@ -1132,6 +1155,7 @@ class InaccessibleDynamicType: public Type public: virtual Category category() const override { return Category::InaccessibleDynamic; } + virtual std::string identifier() const override { return "t_inaccessible"; } virtual bool isImplicitlyConvertibleTo(Type const&) const override { return false; } virtual bool isExplicitlyConvertibleTo(Type const&) const override { return false; } virtual unsigned calldataEncodedSize(bool _padded) const override { (void)_padded; return 32; } -- cgit From da178d967fb66ca508d16bbe3feecf1962dcf6ef Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 19 Jan 2017 13:18:56 +0100 Subject: Properly escape user strings and lists. --- libsolidity/ast/Types.cpp | 108 ++++++++++++++++++++++++++++++---------------- libsolidity/ast/Types.h | 22 ++++++---- 2 files changed, 86 insertions(+), 44 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 9e722474..6e9e9d7e 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -21,15 +21,21 @@ */ #include -#include -#include -#include + +#include +#include + #include #include #include #include -#include -#include + +#include +#include +#include +#include + +#include using namespace std; using namespace dev; @@ -117,6 +123,51 @@ u256 const& MemberList::storageSize() const return m_storageOffsets->storageSize(); } +/// Helper functions for type identifier +namespace +{ + +string parenthesizeIdentifier(string const& _internal) +{ + return "$_" + _internal + "_$"; +} + +template +string identifierList(Range const&& _list) +{ + return parenthesizeIdentifier(boost::algorithm::join(_list, "_$_")); +} + +string identifier(TypePointer const& _type) +{ + return _type ? _type->identifier() : ""; +} + +string identifierList(vector const& _list) +{ + return identifierList(_list | boost::adaptors::transformed(identifier)); +} + +string identifierList(TypePointer const& _type) +{ + return parenthesizeIdentifier(identifier(_type)); +} + +string identifierList(TypePointer const& _type1, TypePointer const& _type2) +{ + TypePointers list; + list.push_back(_type1); + list.push_back(_type2); + return identifierList(list); +} + +string parenthesizeUserIdentifier(string const& _internal) +{ + return parenthesizeIdentifier(boost::replace_all_copy(_internal, "$", "$$$")); +} + +} + TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type) { solAssert(Token::isElementaryTypeName(_type.token()), @@ -1218,16 +1269,17 @@ string ArrayType::identifier() const { string id; if (isString()) - id += "t_string"; + id = "t_string"; else if (isByteArray()) - id += "t_bytes"; + id = "t_bytes"; else { - id = baseType()->identifier(); + id = "t_array"; + id += identifierList(baseType()); if (isDynamicallySized()) - id += "_arraydyn"; + id += "dyn"; else - id += string("_array") + length().str(); + id += length().str(); } id += identifierLocationSuffix(); @@ -1422,7 +1474,7 @@ TypePointer ArrayType::copyForLocation(DataLocation _location, bool _isPointer) string ContractType::identifier() const { - return (m_super ? "t_super_" : "t_contract_") + m_contract.name() + "_" + std::to_string(m_contract.id()); + return (m_super ? "t_super" : "t_contract") + parenthesizeUserIdentifier(m_contract.name()) + std::to_string(m_contract.id()); } bool ContractType::operator==(Type const& _other) const @@ -1536,7 +1588,7 @@ bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const string StructType::identifier() const { - return "t_struct_" + m_struct.name() + "_" + std::to_string(m_struct.id()) + identifierLocationSuffix(); + return "t_struct" + parenthesizeUserIdentifier(m_struct.name()) + std::to_string(m_struct.id()) + identifierLocationSuffix(); } bool StructType::operator==(Type const& _other) const @@ -1681,7 +1733,7 @@ TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const string EnumType::identifier() const { - return "t_enum_" + m_enum.name() + "_" + std::to_string(m_enum.id()); + return "t_enum" + parenthesizeUserIdentifier(m_enum.name()) + std::to_string(m_enum.id()); } bool EnumType::operator==(Type const& _other) const @@ -1767,14 +1819,7 @@ bool TupleType::isImplicitlyConvertibleTo(Type const& _other) const string TupleType::identifier() const { - string id = "t_tuple" + std::to_string(components().size()) + "_"; - for (auto const& c: components()) - if (c) - id += c->identifier() + "_"; - else - id += "t_empty_"; - id += "tuple_end"; - return id; + return "t_tuple" + identifierList(components()); } bool TupleType::operator==(Type const& _other) const @@ -2062,19 +2107,13 @@ string FunctionType::identifier() const } if (isConstant()) id += "_constant"; - id += "_param" + std::to_string(m_parameterTypes.size()) + "_"; - for (auto const& p: m_parameterTypes) - id += p->identifier() + "_"; - id += "return" + std::to_string(m_returnParameterTypes.size()) + "_"; - for (auto const& r: m_returnParameterTypes) - id += r->identifier() + "_"; + id += identifierList(m_parameterTypes) + "returns" + identifierList(m_returnParameterTypes); if (m_gasSet) id += "gas_set_"; if (m_valueSet) id += "value_set_"; if (bound()) - id += "bound_to" + selfType()->identifier() + "_"; - id += "function_end"; + id += "bound_to" + identifierList(selfType()); return id; } @@ -2482,7 +2521,7 @@ vector const FunctionType::returnParameterTypeNames(bool _addDataLocatio return names; } -TypePointer FunctionType::selfType() const +TypePointer const& FunctionType::selfType() const { solAssert(bound(), "Function is not bound."); solAssert(m_parameterTypes.size() > 0, "Function has no self type."); @@ -2500,7 +2539,7 @@ ASTPointer FunctionType::documentation() const string MappingType::identifier() const { - return "t_mapping_" + m_keyType->identifier() + "_to_" + m_valueType->identifier() + "_mapping_end"; + return "t_mapping" + identifierList(m_keyType, m_valueType); } bool MappingType::operator==(Type const& _other) const @@ -2523,7 +2562,7 @@ string MappingType::canonicalName(bool) const string TypeType::identifier() const { - return "t_type_" + actualType()->identifier(); + return "t_type" + identifierList(actualType()); } bool TypeType::operator==(Type const& _other) const @@ -2612,10 +2651,7 @@ u256 ModifierType::storageSize() const string ModifierType::identifier() const { - string id = "t_modifier_param" + std::to_string(m_parameterTypes.size()) + "_"; - for (auto const& p: m_parameterTypes) - id += p->identifier() + "_"; - return id + "end_modifier"; + return "t_modifier" + identifierList(m_parameterTypes); } bool ModifierType::operator==(Type const& _other) const diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index c1f6a476..1e94631e 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -22,18 +22,21 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include #include #include #include + +#include +#include #include +#include +#include + +#include +#include +#include + namespace dev { namespace solidity @@ -158,6 +161,9 @@ public: /// @returns a valid solidity identifier such that two types should compare equal if and /// only if they have the same identifier. /// The identifier should start with "t_". + /// More complex identifier strings use "parentheses", where $_ is interpreted as as + /// "opening parenthesis", _$ as "closing parenthesis", _$_ as "comma" and any $ that + /// appears as part of a user-supplied identifier is escaped as _$$$_. virtual std::string identifier() const = 0; virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; } virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const @@ -912,7 +918,7 @@ public: std::vector const& returnParameterNames() const { return m_returnParameterNames; } std::vector const returnParameterTypeNames(bool _addDataLocation) const; /// @returns the "self" parameter type for a bound function - TypePointer selfType() const; + TypePointer const& selfType() const; virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; -- cgit From 7159944f0fa5d9eb3205cd8a3e1d6ec4f133a4ad Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 20 Jan 2017 11:47:20 +0100 Subject: Reset AST node IDs between compilation runs. --- libsolidity/ast/AST.cpp | 22 ++++++++++++++++++++-- libsolidity/ast/AST.h | 2 ++ libsolidity/ast/Types.cpp | 7 ++++--- libsolidity/interface/CompilerStack.cpp | 1 + 4 files changed, 27 insertions(+), 5 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index fcd6e38c..8a43c3f7 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -34,11 +34,24 @@ using namespace std; using namespace dev; using namespace dev::solidity; +class IDDispenser +{ +public: + static size_t next() { return ++instance(); } + static void reset() { instance() = 0; } +private: + static size_t& instance() + { + static IDDispenser dispenser; + return dispenser.id; + } + size_t id = 0; +}; + ASTNode::ASTNode(SourceLocation const& _location): + m_id(IDDispenser::next()), m_location(_location) { - static size_t id = 0; - m_id = ++id; } ASTNode::~ASTNode() @@ -46,6 +59,11 @@ ASTNode::~ASTNode() delete m_annotation; } +void ASTNode::resetID() +{ + IDDispenser::reset(); +} + ASTAnnotation& ASTNode::annotation() const { if (!m_annotation) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 4af64963..116275c3 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -59,6 +59,8 @@ public: /// @returns an identifier of this AST node that is unique for a single compilation run. size_t id() const { return m_id; } + /// Resets the global ID counter. This invalidates all previous IDs. + static void resetID(); virtual void accept(ASTVisitor& _visitor) = 0; virtual void accept(ASTConstVisitor& _visitor) const = 0; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 6e9e9d7e..cefd0603 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -163,7 +164,7 @@ string identifierList(TypePointer const& _type1, TypePointer const& _type2) string parenthesizeUserIdentifier(string const& _internal) { - return parenthesizeIdentifier(boost::replace_all_copy(_internal, "$", "$$$")); + return parenthesizeIdentifier(boost::algorithm::replace_all_copy(_internal, "$", "$$$")); } } @@ -2109,9 +2110,9 @@ string FunctionType::identifier() const id += "_constant"; id += identifierList(m_parameterTypes) + "returns" + identifierList(m_returnParameterTypes); if (m_gasSet) - id += "gas_set_"; + id += "gas"; if (m_valueSet) - id += "value_set_"; + id += "value"; if (bound()) id += "bound_to" + identifierList(selfType()); return id; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 61fc7728..85ec0fb1 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -112,6 +112,7 @@ bool CompilerStack::parse() { //reset m_errors.clear(); + ASTNode::resetID(); m_parseSuccessful = false; if (SemVerVersion{string(VersionString)}.isPrerelease()) -- cgit From 07b0a0a56028c5ea9bf3dc4fc3b7fda6e4d97ec9 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 20 Jan 2017 15:56:56 +0100 Subject: Make m_id const. --- libsolidity/ast/AST.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libsolidity') diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 116275c3..c7c37445 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -99,7 +99,7 @@ public: ///@} protected: - size_t m_id = 0; + size_t const m_id = 0; /// Annotation - is specialised in derived classes, is created upon request (because of polymorphism). mutable ASTAnnotation* m_annotation = nullptr; -- cgit From 2536bdd6d0dfa1685967fd3106c682e0bcf17021 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 20 Jan 2017 19:01:19 +0100 Subject: Report source location on "stack too deep" errors. --- libsolidity/codegen/ContractCompiler.cpp | 9 ++++++++- libsolidity/codegen/ExpressionCompiler.cpp | 7 ++++++- 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index fa77c1aa..bdff0da4 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -486,7 +486,12 @@ bool ContractCompiler::visit(FunctionDefinition const& _function) stackLayout.push_back(i); stackLayout += vector(c_localVariablesSize, -1); - solAssert(stackLayout.size() <= 17, "Stack too deep, try removing local variables."); + if (stackLayout.size() > 17) + BOOST_THROW_EXCEPTION( + CompilerError() << + errinfo_sourceLocation(_function.location()) << + errinfo_comment("Stack too deep, try removing local variables.") + ); while (stackLayout.back() != int(stackLayout.size() - 1)) if (stackLayout.back() < 0) { @@ -551,6 +556,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) if (stackDiff < 1 || stackDiff > 16) BOOST_THROW_EXCEPTION( CompilerError() << + errinfo_sourceLocation(_inlineAssembly.location()) << errinfo_comment("Stack too deep, try removing local variables.") ); for (unsigned i = 0; i < variable->type()->sizeOnStack(); ++i) @@ -591,6 +597,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) if (stackDiff > 16 || stackDiff < 1) BOOST_THROW_EXCEPTION( CompilerError() << + errinfo_sourceLocation(_inlineAssembly.location()) << errinfo_comment("Stack too deep, try removing local variables.") ); for (unsigned i = 0; i < size; ++i) { diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 37bd1458..81d3409e 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -250,7 +250,12 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) } if (lvalueSize > 0) { - solAssert(itemSize + lvalueSize <= 16, "Stack too deep, try removing local variables."); + if (itemSize + lvalueSize > 16) + BOOST_THROW_EXCEPTION( + CompilerError() << + errinfo_sourceLocation(_assignment.location()) << + errinfo_comment("Stack too deep, try removing local variables.") + ); // value [lvalue_ref] updated_value for (unsigned i = 0; i < itemSize; ++i) m_context << swapInstruction(itemSize + lvalueSize) << Instruction::POP; -- cgit From 0ef460461ac280a9f8086d62d831dc9f7d2f5319 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 20 Jan 2017 19:21:43 +0100 Subject: Check if constructor is public or not. --- libsolidity/analysis/TypeChecker.cpp | 7 ++++++- libsolidity/ast/ASTAnnotations.h | 2 ++ libsolidity/interface/CompilerStack.cpp | 6 +++++- 3 files changed, 13 insertions(+), 2 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 67c8ac17..adb3d54f 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -75,7 +75,10 @@ bool TypeChecker::visit(ContractDefinition const& _contract) checkContractAbstractConstructors(_contract); FunctionDefinition const* function = _contract.constructor(); - if (function) { + if (function) + { + if (!function->isPublic()) + _contract.annotation().hasPublicConstructor = false; if (!function->returnParameters().empty()) typeError(function->returnParameterList()->location(), "Non-empty \"returns\" directive for constructor."); if (function->isDeclaredConst()) @@ -1280,6 +1283,8 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) fatalTypeError(_newExpression.location(), "Identifier is not a contract."); if (!contract->annotation().isFullyImplemented) typeError(_newExpression.location(), "Trying to create an instance of an abstract contract."); + if (!contract->annotation().hasPublicConstructor) + typeError(_newExpression.location(), "Contract with internal constructor cannot be created directly."); solAssert(!!m_scope, ""); m_scope->annotation().contractDependencies.insert(contract); diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 9c4c3ae8..61e97a55 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -80,6 +80,8 @@ struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, DocumentedAnnota { /// Whether all functions are implemented. bool isFullyImplemented = true; + /// Whether a public constructor (even the default one) is available. + bool hasPublicConstructor = true; /// List of all (direct and indirect) base contracts in order from derived to /// base, including the contract itself. std::vector linearizedBaseContracts; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 85ec0fb1..b26bd501 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -624,7 +624,11 @@ void CompilerStack::compileContract( map& _compiledContracts ) { - if (_compiledContracts.count(&_contract) || !_contract.annotation().isFullyImplemented) + if ( + _compiledContracts.count(&_contract) || + !_contract.annotation().isFullyImplemented || + !_contract.annotation().hasPublicConstructor + ) return; for (auto const* dependency: _contract.annotation().contractDependencies) compileContract(*dependency, _compiledContracts); -- cgit From 08015590f23957153e12f06a2bcba6e9246733bc Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Tue, 18 Oct 2016 14:52:27 +0200 Subject: analysis: Allow multiple events of the same name Fixes #1215 --- libsolidity/analysis/DeclarationContainer.cpp | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'libsolidity') diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp index f8c12c5b..ac80ab18 100644 --- a/libsolidity/analysis/DeclarationContainer.cpp +++ b/libsolidity/analysis/DeclarationContainer.cpp @@ -58,6 +58,13 @@ Declaration const* DeclarationContainer::conflictingDeclaration( return declaration; } } + else if (dynamic_cast(&_declaration)) + { + // check that all other declarations with the same name are events + for (Declaration const* declaration: declarations) + if (!dynamic_cast(declaration)) + return declaration; + } else if (declarations.size() == 1 && declarations.front() == &_declaration) return nullptr; else if (!declarations.empty()) -- cgit From 846f7dc3ea6591b34deb3e6738292ffd87c06ed3 Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Thu, 20 Oct 2016 10:47:15 +0200 Subject: analysis: Resolve event overloading --- libsolidity/analysis/DeclarationContainer.cpp | 21 +++++++++------------ libsolidity/analysis/NameAndTypeResolver.cpp | 4 ++-- 2 files changed, 11 insertions(+), 14 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp index ac80ab18..04836603 100644 --- a/libsolidity/analysis/DeclarationContainer.cpp +++ b/libsolidity/analysis/DeclarationContainer.cpp @@ -42,28 +42,25 @@ Declaration const* DeclarationContainer::conflictingDeclaration( if (m_invisibleDeclarations.count(*_name)) declarations += m_invisibleDeclarations.at(*_name); - if (dynamic_cast(&_declaration)) + if (dynamic_cast(&_declaration) || + dynamic_cast(&_declaration) + ) { - // check that all other declarations with the same name are functions or a public state variable + // check that all other declarations with the same name are functions or a public state variable or events. + // And then check that the signatures are different. for (Declaration const* declaration: declarations) { - if (dynamic_cast(declaration)) - continue; if (auto variableDeclaration = dynamic_cast(declaration)) { if (variableDeclaration->isStateVariable() && !variableDeclaration->isConstant() && variableDeclaration->isPublic()) continue; return declaration; } - return declaration; - } - } - else if (dynamic_cast(&_declaration)) - { - // check that all other declarations with the same name are events - for (Declaration const* declaration: declarations) - if (!dynamic_cast(declaration)) + if (!dynamic_cast(declaration) && + !dynamic_cast(declaration)) return declaration; + // Or, continue. + } } else if (declarations.size() == 1 && declarations.front() == &_declaration) return nullptr; diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 08323243..b0a82715 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -260,8 +260,8 @@ vector NameAndTypeResolver::cleanedDeclarations( for (auto it = _declarations.begin(); it != _declarations.end(); ++it) { solAssert(*it, ""); - // the declaration is functionDefinition or a VariableDeclaration while declarations > 1 - solAssert(dynamic_cast(*it) || dynamic_cast(*it), + // the declaration is functionDefinition, eventDefinition or a VariableDeclaration while declarations > 1 + solAssert(dynamic_cast(*it) || dynamic_cast(*it) || dynamic_cast(*it), "Found overloading involving something not a function or a variable"); shared_ptr functionType { (*it)->functionType(false) }; -- cgit From 399b7b695a2ffe5ae2b07628860712fc76dfe03c Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Mon, 23 Jan 2017 14:51:17 +0100 Subject: analysis: fix format --- libsolidity/analysis/DeclarationContainer.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp index 04836603..7e8cd2ca 100644 --- a/libsolidity/analysis/DeclarationContainer.cpp +++ b/libsolidity/analysis/DeclarationContainer.cpp @@ -42,9 +42,10 @@ Declaration const* DeclarationContainer::conflictingDeclaration( if (m_invisibleDeclarations.count(*_name)) declarations += m_invisibleDeclarations.at(*_name); - if (dynamic_cast(&_declaration) || + if ( + dynamic_cast(&_declaration) || dynamic_cast(&_declaration) - ) + ) { // check that all other declarations with the same name are functions or a public state variable or events. // And then check that the signatures are different. @@ -56,8 +57,10 @@ Declaration const* DeclarationContainer::conflictingDeclaration( continue; return declaration; } - if (!dynamic_cast(declaration) && - !dynamic_cast(declaration)) + if ( + !dynamic_cast(declaration) && + !dynamic_cast(declaration) + ) return declaration; // Or, continue. } -- cgit From 4e1fd68b38346ec3e4117dc0454b65f4b236741b Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Mon, 23 Jan 2017 15:24:47 +0100 Subject: analysis: disallow overloading functions with events --- libsolidity/analysis/DeclarationContainer.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'libsolidity') diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp index 7e8cd2ca..b33c8568 100644 --- a/libsolidity/analysis/DeclarationContainer.cpp +++ b/libsolidity/analysis/DeclarationContainer.cpp @@ -58,7 +58,12 @@ Declaration const* DeclarationContainer::conflictingDeclaration( return declaration; } if ( - !dynamic_cast(declaration) && + dynamic_cast(&_declaration) && + !dynamic_cast(declaration) + ) + return declaration; + if ( + dynamic_cast(&_declaration) && !dynamic_cast(declaration) ) return declaration; -- cgit From d0e8d340a53395de4c83e4e1d6ccf4c8eab5889a Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 17 Jan 2017 10:47:57 +0100 Subject: Low level named functions for CompilerContext. --- libsolidity/codegen/CompilerContext.cpp | 15 +++++++++++++++ libsolidity/codegen/CompilerContext.h | 8 ++++++++ 2 files changed, 23 insertions(+) (limited to 'libsolidity') diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index c14ab845..fcd39b33 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -125,6 +125,21 @@ Declaration const* CompilerContext::nextFunctionToCompile() const return m_functionCompilationQueue.nextFunctionToCompile(); } +eth::AssemblyItem const* CompilerContext::lowLevelFunctionEntryPoint(string const& _name) const +{ + auto it = m_lowLevelFunctions.find(_name); + if (it == m_lowLevelFunctions.end()) + return nullptr; + else + return *it; +} + +void CompilerContext::addLowLevelFunction(string const& _name, eth::AssemblyItem const& _label) +{ + solAssert(lowLevelFunctionEntryPoint(_name) != nullptr, "Low level function with that name already exists."); + m_lowLevelFunctions[_name] = _label.pushTag(); +} + ModifierDefinition const& CompilerContext::functionModifier(string const& _name) const { solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 80671528..34befc13 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -90,6 +90,12 @@ public: /// as "having code". void startFunction(Declaration const& _function); + /// Returns the label of the low level function with the given name or nullptr if it + /// does not exist. The lifetime of the returned pointer is short. + eth::AssemblyItem const* lowLevelFunctionEntryPoint(std::string const& _name) const; + /// Inserts a low level function entry point into the list of low level functions. + void addLowLevelFunction(std::string const& _name, eth::AssemblyItem const& _label); + ModifierDefinition const& functionModifier(std::string const& _name) const; /// Returns the distance of the given local variable from the bottom of the stack (of the current function). unsigned baseStackOffsetOfVariable(Declaration const& _declaration) const; @@ -248,6 +254,8 @@ private: CompilerContext *m_runtimeContext; /// The index of the runtime subroutine. size_t m_runtimeSub = -1; + /// An index of low-level function labels by name. + std::map m_lowLevelFunctions; }; } -- cgit From b60623521f052b8a36c61f8632d868cac552bf29 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 19 Jan 2017 17:21:55 +0100 Subject: Move some util functions to low-level functions. --- libsolidity/codegen/ArrayUtils.cpp | 392 ++++++++++++++++--------------- libsolidity/codegen/CompilerContext.cpp | 69 ++++-- libsolidity/codegen/CompilerContext.h | 35 ++- libsolidity/codegen/CompilerUtils.cpp | 67 +++--- libsolidity/codegen/ContractCompiler.cpp | 1 + 5 files changed, 320 insertions(+), 244 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 352c7177..6f270b4d 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -502,60 +502,70 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord } } -void ArrayUtils::clearArray(ArrayType const& _type) const +void ArrayUtils::clearArray(ArrayType const& _typeIn) const { - unsigned stackHeightStart = m_context.stackHeight(); - solAssert(_type.location() == DataLocation::Storage, ""); - if (_type.baseType()->storageBytes() < 32) - { - solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type."); - solAssert(_type.baseType()->storageSize() <= 1, "Invalid storage size for type."); - } - if (_type.baseType()->isValueType()) - solAssert(_type.baseType()->storageSize() <= 1, "Invalid size for value type."); - - m_context << Instruction::POP; // remove byte offset - if (_type.isDynamicallySized()) - clearDynamicArray(_type); - else if (_type.length() == 0 || _type.baseType()->category() == Type::Category::Mapping) - m_context << Instruction::POP; - else if (_type.baseType()->isValueType() && _type.storageSize() <= 5) - { - // unroll loop for small arrays @todo choose a good value - // Note that we loop over storage slots here, not elements. - for (unsigned i = 1; i < _type.storageSize(); ++i) - m_context - << u256(0) << Instruction::DUP2 << Instruction::SSTORE - << u256(1) << Instruction::ADD; - m_context << u256(0) << Instruction::SWAP1 << Instruction::SSTORE; - } - else if (!_type.baseType()->isValueType() && _type.length() <= 4) - { - // unroll loop for small arrays @todo choose a good value - solAssert(_type.baseType()->storageBytes() >= 32, "Invalid storage size."); - for (unsigned i = 1; i < _type.length(); ++i) + TypePointer type = _typeIn.shared_from_this(); + m_context.callLowLevelFunction( + "$clearArray_" + _typeIn.identifier(), + 2, + 0, + [type](CompilerContext& _context) { - m_context << u256(0); - StorageItem(m_context, *_type.baseType()).setToZero(SourceLocation(), false); - m_context - << Instruction::POP - << u256(_type.baseType()->storageSize()) << Instruction::ADD; + ArrayType const& _type = dynamic_cast(*type); + unsigned stackHeightStart = _context.stackHeight(); + solAssert(_type.location() == DataLocation::Storage, ""); + if (_type.baseType()->storageBytes() < 32) + { + solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type."); + solAssert(_type.baseType()->storageSize() <= 1, "Invalid storage size for type."); + } + if (_type.baseType()->isValueType()) + solAssert(_type.baseType()->storageSize() <= 1, "Invalid size for value type."); + + _context << Instruction::POP; // remove byte offset + if (_type.isDynamicallySized()) + ArrayUtils(_context).clearDynamicArray(_type); + else if (_type.length() == 0 || _type.baseType()->category() == Type::Category::Mapping) + _context << Instruction::POP; + else if (_type.baseType()->isValueType() && _type.storageSize() <= 5) + { + // unroll loop for small arrays @todo choose a good value + // Note that we loop over storage slots here, not elements. + for (unsigned i = 1; i < _type.storageSize(); ++i) + _context + << u256(0) << Instruction::DUP2 << Instruction::SSTORE + << u256(1) << Instruction::ADD; + _context << u256(0) << Instruction::SWAP1 << Instruction::SSTORE; + } + else if (!_type.baseType()->isValueType() && _type.length() <= 4) + { + // unroll loop for small arrays @todo choose a good value + solAssert(_type.baseType()->storageBytes() >= 32, "Invalid storage size."); + for (unsigned i = 1; i < _type.length(); ++i) + { + _context << u256(0); + StorageItem(_context, *_type.baseType()).setToZero(SourceLocation(), false); + _context + << Instruction::POP + << u256(_type.baseType()->storageSize()) << Instruction::ADD; + } + _context << u256(0); + StorageItem(_context, *_type.baseType()).setToZero(SourceLocation(), true); + } + else + { + _context << Instruction::DUP1 << _type.length(); + ArrayUtils(_context).convertLengthToSize(_type); + _context << Instruction::ADD << Instruction::SWAP1; + if (_type.baseType()->storageBytes() < 32) + ArrayUtils(_context).clearStorageLoop(IntegerType(256)); + else + ArrayUtils(_context).clearStorageLoop(*_type.baseType()); + _context << Instruction::POP; + } + solAssert(_context.stackHeight() == stackHeightStart - 2, ""); } - m_context << u256(0); - StorageItem(m_context, *_type.baseType()).setToZero(SourceLocation(), true); - } - else - { - m_context << Instruction::DUP1 << _type.length(); - convertLengthToSize(_type); - m_context << Instruction::ADD << Instruction::SWAP1; - if (_type.baseType()->storageBytes() < 32) - clearStorageLoop(IntegerType(256)); - else - clearStorageLoop(*_type.baseType()); - m_context << Instruction::POP; - } - solAssert(m_context.stackHeight() == stackHeightStart - 2, ""); + ); } void ArrayUtils::clearDynamicArray(ArrayType const& _type) const @@ -597,144 +607,154 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const m_context << Instruction::POP; } -void ArrayUtils::resizeDynamicArray(ArrayType const& _type) const +void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const { - solAssert(_type.location() == DataLocation::Storage, ""); - solAssert(_type.isDynamicallySized(), ""); - if (!_type.isByteArray() && _type.baseType()->storageBytes() < 32) - solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type."); - - unsigned stackHeightStart = m_context.stackHeight(); - eth::AssemblyItem resizeEnd = m_context.newTag(); - - // stack: ref new_length - // fetch old length - retrieveLength(_type, 1); - // stack: ref new_length old_length - solAssert(m_context.stackHeight() - stackHeightStart == 3 - 2, "2"); - - // Special case for short byte arrays, they are stored together with their length - if (_type.isByteArray()) - { - eth::AssemblyItem regularPath = m_context.newTag(); - // We start by a large case-distinction about the old and new length of the byte array. - - m_context << Instruction::DUP3 << Instruction::SLOAD; - // stack: ref new_length current_length ref_value - - solAssert(m_context.stackHeight() - stackHeightStart == 4 - 2, "3"); - m_context << Instruction::DUP2 << u256(31) << Instruction::LT; - eth::AssemblyItem currentIsLong = m_context.appendConditionalJump(); - m_context << Instruction::DUP3 << u256(31) << Instruction::LT; - eth::AssemblyItem newIsLong = m_context.appendConditionalJump(); - - // Here: short -> short - - // Compute 1 << (256 - 8 * new_size) - eth::AssemblyItem shortToShort = m_context.newTag(); - m_context << shortToShort; - m_context << Instruction::DUP3 << u256(8) << Instruction::MUL; - m_context << u256(0x100) << Instruction::SUB; - m_context << u256(2) << Instruction::EXP; - // Divide and multiply by that value, clearing bits. - m_context << Instruction::DUP1 << Instruction::SWAP2; - m_context << Instruction::DIV << Instruction::MUL; - // Insert 2*length. - m_context << Instruction::DUP3 << Instruction::DUP1 << Instruction::ADD; - m_context << Instruction::OR; - // Store. - m_context << Instruction::DUP4 << Instruction::SSTORE; - solAssert(m_context.stackHeight() - stackHeightStart == 3 - 2, "3"); - m_context.appendJumpTo(resizeEnd); - - m_context.adjustStackOffset(1); // we have to do that because of the jumps - // Here: short -> long - - m_context << newIsLong; - // stack: ref new_length current_length ref_value - solAssert(m_context.stackHeight() - stackHeightStart == 4 - 2, "3"); - // Zero out lower-order byte. - m_context << u256(0xff) << Instruction::NOT << Instruction::AND; - // Store at data location. - m_context << Instruction::DUP4; - CompilerUtils(m_context).computeHashStatic(); - m_context << Instruction::SSTORE; - // stack: ref new_length current_length - // Store new length: Compule 2*length + 1 and store it. - m_context << Instruction::DUP2 << Instruction::DUP1 << Instruction::ADD; - m_context << u256(1) << Instruction::ADD; - // stack: ref new_length current_length 2*new_length+1 - m_context << Instruction::DUP4 << Instruction::SSTORE; - solAssert(m_context.stackHeight() - stackHeightStart == 3 - 2, "3"); - m_context.appendJumpTo(resizeEnd); - - m_context.adjustStackOffset(1); // we have to do that because of the jumps - - m_context << currentIsLong; - m_context << Instruction::DUP3 << u256(31) << Instruction::LT; - m_context.appendConditionalJumpTo(regularPath); - - // Here: long -> short - // Read the first word of the data and store it on the stack. Clear the data location and - // then jump to the short -> short case. - - // stack: ref new_length current_length ref_value - solAssert(m_context.stackHeight() - stackHeightStart == 4 - 2, "3"); - m_context << Instruction::POP << Instruction::DUP3; - CompilerUtils(m_context).computeHashStatic(); - m_context << Instruction::DUP1 << Instruction::SLOAD << Instruction::SWAP1; - // stack: ref new_length current_length first_word data_location - m_context << Instruction::DUP3; - convertLengthToSize(_type); - m_context << Instruction::DUP2 << Instruction::ADD << Instruction::SWAP1; - // stack: ref new_length current_length first_word data_location_end data_location - clearStorageLoop(IntegerType(256)); - m_context << Instruction::POP; - // stack: ref new_length current_length first_word - solAssert(m_context.stackHeight() - stackHeightStart == 4 - 2, "3"); - m_context.appendJumpTo(shortToShort); - - m_context << regularPath; - // stack: ref new_length current_length ref_value - m_context << Instruction::POP; - } + TypePointer type = _typeIn.shared_from_this(); + m_context.callLowLevelFunction( + "$resizeDynamicArray_" + _typeIn.identifier(), + 2, + 0, + [type](CompilerContext& _context) + { + ArrayType const& _type = dynamic_cast(*type); + solAssert(_type.location() == DataLocation::Storage, ""); + solAssert(_type.isDynamicallySized(), ""); + if (!_type.isByteArray() && _type.baseType()->storageBytes() < 32) + solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type."); + + unsigned stackHeightStart = _context.stackHeight(); + eth::AssemblyItem resizeEnd = _context.newTag(); + + // stack: ref new_length + // fetch old length + ArrayUtils(_context).retrieveLength(_type, 1); + // stack: ref new_length old_length + solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "2"); + + // Special case for short byte arrays, they are stored together with their length + if (_type.isByteArray()) + { + eth::AssemblyItem regularPath = _context.newTag(); + // We start by a large case-distinction about the old and new length of the byte array. + + _context << Instruction::DUP3 << Instruction::SLOAD; + // stack: ref new_length current_length ref_value + + solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); + _context << Instruction::DUP2 << u256(31) << Instruction::LT; + eth::AssemblyItem currentIsLong = _context.appendConditionalJump(); + _context << Instruction::DUP3 << u256(31) << Instruction::LT; + eth::AssemblyItem newIsLong = _context.appendConditionalJump(); + + // Here: short -> short + + // Compute 1 << (256 - 8 * new_size) + eth::AssemblyItem shortToShort = _context.newTag(); + _context << shortToShort; + _context << Instruction::DUP3 << u256(8) << Instruction::MUL; + _context << u256(0x100) << Instruction::SUB; + _context << u256(2) << Instruction::EXP; + // Divide and multiply by that value, clearing bits. + _context << Instruction::DUP1 << Instruction::SWAP2; + _context << Instruction::DIV << Instruction::MUL; + // Insert 2*length. + _context << Instruction::DUP3 << Instruction::DUP1 << Instruction::ADD; + _context << Instruction::OR; + // Store. + _context << Instruction::DUP4 << Instruction::SSTORE; + solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "3"); + _context.appendJumpTo(resizeEnd); + + _context.adjustStackOffset(1); // we have to do that because of the jumps + // Here: short -> long + + _context << newIsLong; + // stack: ref new_length current_length ref_value + solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); + // Zero out lower-order byte. + _context << u256(0xff) << Instruction::NOT << Instruction::AND; + // Store at data location. + _context << Instruction::DUP4; + CompilerUtils(_context).computeHashStatic(); + _context << Instruction::SSTORE; + // stack: ref new_length current_length + // Store new length: Compule 2*length + 1 and store it. + _context << Instruction::DUP2 << Instruction::DUP1 << Instruction::ADD; + _context << u256(1) << Instruction::ADD; + // stack: ref new_length current_length 2*new_length+1 + _context << Instruction::DUP4 << Instruction::SSTORE; + solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "3"); + _context.appendJumpTo(resizeEnd); + + _context.adjustStackOffset(1); // we have to do that because of the jumps + + _context << currentIsLong; + _context << Instruction::DUP3 << u256(31) << Instruction::LT; + _context.appendConditionalJumpTo(regularPath); + + // Here: long -> short + // Read the first word of the data and store it on the stack. Clear the data location and + // then jump to the short -> short case. + + // stack: ref new_length current_length ref_value + solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); + _context << Instruction::POP << Instruction::DUP3; + CompilerUtils(_context).computeHashStatic(); + _context << Instruction::DUP1 << Instruction::SLOAD << Instruction::SWAP1; + // stack: ref new_length current_length first_word data_location + _context << Instruction::DUP3; + ArrayUtils(_context).convertLengthToSize(_type); + _context << Instruction::DUP2 << Instruction::ADD << Instruction::SWAP1; + // stack: ref new_length current_length first_word data_location_end data_location + ArrayUtils(_context).clearStorageLoop(IntegerType(256)); + _context << Instruction::POP; + // stack: ref new_length current_length first_word + solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); + _context.appendJumpTo(shortToShort); + + _context << regularPath; + // stack: ref new_length current_length ref_value + _context << Instruction::POP; + } - // Change of length for a regular array (i.e. length at location, data at sha3(location)). - // stack: ref new_length old_length - // store new length - m_context << Instruction::DUP2; - if (_type.isByteArray()) - // For a "long" byte array, store length as 2*length+1 - m_context << Instruction::DUP1 << Instruction::ADD << u256(1) << Instruction::ADD; - m_context<< Instruction::DUP4 << Instruction::SSTORE; - // skip if size is not reduced - m_context << Instruction::DUP2 << Instruction::DUP2 - << Instruction::ISZERO << Instruction::GT; - m_context.appendConditionalJumpTo(resizeEnd); - - // size reduced, clear the end of the array - // stack: ref new_length old_length - convertLengthToSize(_type); - m_context << Instruction::DUP2; - convertLengthToSize(_type); - // stack: ref new_length old_size new_size - // compute data positions - m_context << Instruction::DUP4; - CompilerUtils(m_context).computeHashStatic(); - // stack: ref new_length old_size new_size data_pos - m_context << Instruction::SWAP2 << Instruction::DUP3 << Instruction::ADD; - // stack: ref new_length data_pos new_size delete_end - m_context << Instruction::SWAP2 << Instruction::ADD; - // stack: ref new_length delete_end delete_start - if (_type.isByteArray() || _type.baseType()->storageBytes() < 32) - clearStorageLoop(IntegerType(256)); - else - clearStorageLoop(*_type.baseType()); + // Change of length for a regular array (i.e. length at location, data at sha3(location)). + // stack: ref new_length old_length + // store new length + _context << Instruction::DUP2; + if (_type.isByteArray()) + // For a "long" byte array, store length as 2*length+1 + _context << Instruction::DUP1 << Instruction::ADD << u256(1) << Instruction::ADD; + _context<< Instruction::DUP4 << Instruction::SSTORE; + // skip if size is not reduced + _context << Instruction::DUP2 << Instruction::DUP2 + << Instruction::ISZERO << Instruction::GT; + _context.appendConditionalJumpTo(resizeEnd); + + // size reduced, clear the end of the array + // stack: ref new_length old_length + ArrayUtils(_context).convertLengthToSize(_type); + _context << Instruction::DUP2; + ArrayUtils(_context).convertLengthToSize(_type); + // stack: ref new_length old_size new_size + // compute data positions + _context << Instruction::DUP4; + CompilerUtils(_context).computeHashStatic(); + // stack: ref new_length old_size new_size data_pos + _context << Instruction::SWAP2 << Instruction::DUP3 << Instruction::ADD; + // stack: ref new_length data_pos new_size delete_end + _context << Instruction::SWAP2 << Instruction::ADD; + // stack: ref new_length delete_end delete_start + if (_type.isByteArray() || _type.baseType()->storageBytes() < 32) + ArrayUtils(_context).clearStorageLoop(IntegerType(256)); + else + ArrayUtils(_context).clearStorageLoop(*_type.baseType()); - m_context << resizeEnd; - // cleanup - m_context << Instruction::POP << Instruction::POP << Instruction::POP; - solAssert(m_context.stackHeight() == stackHeightStart - 2, ""); + _context << resizeEnd; + // cleanup + _context << Instruction::POP << Instruction::POP << Instruction::POP; + solAssert(_context.stackHeight() == stackHeightStart - 2, ""); + } + ); } void ArrayUtils::clearStorageLoop(Type const& _type) const diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index fcd39b33..dad227c7 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -21,15 +21,18 @@ */ #include -#include -#include -#include +#include #include #include #include #include #include +#include + +#include +#include + using namespace std; namespace dev @@ -57,6 +60,51 @@ void CompilerContext::startFunction(Declaration const& _function) *this << functionEntryLabel(_function); } +void CompilerContext::callLowLevelFunction( + string const& _name, + unsigned _inArgs, + unsigned _outArgs, + function const& _generator +) +{ + eth::AssemblyItem retTag = pushNewTag(); + CompilerUtils(*this).moveIntoStack(_inArgs); + + auto it = m_lowLevelFunctions.find(_name); + if (it == m_lowLevelFunctions.end()) + { + eth::AssemblyItem tag = newTag().pushTag(); + m_lowLevelFunctions.insert(make_pair(_name, tag)); + m_lowLevelFunctionGenerationQueue.push(make_tuple(_name, _inArgs, _outArgs, _generator)); + *this << tag; + } + else + *this << it->second; + appendJump(eth::AssemblyItem::JumpType::IntoFunction); + adjustStackOffset(_outArgs - 1 - _inArgs); + *this << retTag.tag(); +} + +void CompilerContext::appendMissingLowLevelFunctions() +{ + while (!m_lowLevelFunctionGenerationQueue.empty()) + { + string name; + unsigned inArgs; + unsigned outArgs; + function generator; + tie(name, inArgs, outArgs, generator) = m_lowLevelFunctionGenerationQueue.front(); + m_lowLevelFunctionGenerationQueue.pop(); + + setStackOffset(inArgs + 1); + *this << m_lowLevelFunctions.at(name).tag(); + generator(*this); + CompilerUtils(*this).moveToStackTop(outArgs); + appendJump(eth::AssemblyItem::JumpType::OutOfFunction); + solAssert(stackHeight() == outArgs, "Invalid stack height in low-level function " + name + "."); + } +} + void CompilerContext::addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent) { @@ -125,21 +173,6 @@ Declaration const* CompilerContext::nextFunctionToCompile() const return m_functionCompilationQueue.nextFunctionToCompile(); } -eth::AssemblyItem const* CompilerContext::lowLevelFunctionEntryPoint(string const& _name) const -{ - auto it = m_lowLevelFunctions.find(_name); - if (it == m_lowLevelFunctions.end()) - return nullptr; - else - return *it; -} - -void CompilerContext::addLowLevelFunction(string const& _name, eth::AssemblyItem const& _label) -{ - solAssert(lowLevelFunctionEntryPoint(_name) != nullptr, "Low level function with that name already exists."); - m_lowLevelFunctions[_name] = _label.pushTag(); -} - ModifierDefinition const& CompilerContext::functionModifier(string const& _name) const { solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 34befc13..f024b010 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -22,17 +22,21 @@ #pragma once -#include -#include -#include -#include -#include -#include #include #include #include + +#include +#include + #include +#include +#include +#include +#include +#include + namespace dev { namespace solidity { @@ -90,11 +94,18 @@ public: /// as "having code". void startFunction(Declaration const& _function); - /// Returns the label of the low level function with the given name or nullptr if it - /// does not exist. The lifetime of the returned pointer is short. - eth::AssemblyItem const* lowLevelFunctionEntryPoint(std::string const& _name) const; - /// Inserts a low level function entry point into the list of low level functions. - void addLowLevelFunction(std::string const& _name, eth::AssemblyItem const& _label); + /// Appends a call to the named low-level function and inserts the generator into the + /// list of low-level-functions to be generated, unless it already exists. + /// Note that the generator should not assume that objects are still alive when it is called, + /// unless they are guaranteed to be alive for the whole run of the compiler (AST nodes, for example). + void callLowLevelFunction( + std::string const& _name, + unsigned _inArgs, + unsigned _outArgs, + std::function const& _generator + ); + /// Generates the code for missing low-level functions, i.e. calls the generators passed above. + void appendMissingLowLevelFunctions(); ModifierDefinition const& functionModifier(std::string const& _name) const; /// Returns the distance of the given local variable from the bottom of the stack (of the current function). @@ -256,6 +267,8 @@ private: size_t m_runtimeSub = -1; /// An index of low-level function labels by name. std::map m_lowLevelFunctions; + /// The queue of low-level functions to generate. + std::queue>> m_lowLevelFunctionGenerationQueue; }; } diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 7d382aba..caf3b1ac 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -820,37 +820,46 @@ void CompilerUtils::pushZeroValue(Type const& _type) } solAssert(referenceType->location() == DataLocation::Memory, ""); - m_context << u256(max(32u, _type.calldataEncodedSize())); - allocateMemory(); - m_context << Instruction::DUP1; + TypePointer type = _type.shared_from_this(); + m_context.callLowLevelFunction( + "$pushZeroValue_" + referenceType->identifier(), + 0, + 1, + [type](CompilerContext& _context) { + CompilerUtils utils(_context); + _context << u256(max(32u, type->calldataEncodedSize())); + utils.allocateMemory(); + _context << Instruction::DUP1; + + if (auto structType = dynamic_cast(type.get())) + for (auto const& member: structType->members(nullptr)) + { + utils.pushZeroValue(*member.type); + utils.storeInMemoryDynamic(*member.type); + } + else if (auto arrayType = dynamic_cast(type.get())) + { + if (arrayType->isDynamicallySized()) + { + // zero length + _context << u256(0); + utils.storeInMemoryDynamic(IntegerType(256)); + } + else if (arrayType->length() > 0) + { + _context << arrayType->length() << Instruction::SWAP1; + // stack: items_to_do memory_pos + utils.zeroInitialiseMemoryArray(*arrayType); + // stack: updated_memory_pos + } + } + else + solAssert(false, "Requested initialisation for unknown type: " + type->toString()); - if (auto structType = dynamic_cast(&_type)) - for (auto const& member: structType->members(nullptr)) - { - pushZeroValue(*member.type); - storeInMemoryDynamic(*member.type); + // remove the updated memory pointer + _context << Instruction::POP; } - else if (auto arrayType = dynamic_cast(&_type)) - { - if (arrayType->isDynamicallySized()) - { - // zero length - m_context << u256(0); - storeInMemoryDynamic(IntegerType(256)); - } - else if (arrayType->length() > 0) - { - m_context << arrayType->length() << Instruction::SWAP1; - // stack: items_to_do memory_pos - zeroInitialiseMemoryArray(*arrayType); - // stack: updated_memory_pos - } - } - else - solAssert(false, "Requested initialisation for unknown type: " + _type.toString()); - - // remove the updated memory pointer - m_context << Instruction::POP; + ); } void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable) diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index bdff0da4..9dc1fb37 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -827,6 +827,7 @@ void ContractCompiler::appendMissingFunctions() function->accept(*this); solAssert(m_context.nextFunctionToCompile() != function, "Compiled the wrong function?"); } + m_context.appendMissingLowLevelFunctions(); } void ContractCompiler::appendModifierOrFunctionCode() -- cgit From cea020b89e6f5bf20e27d7059ad27392e8526e9b Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 20 Jan 2017 14:19:25 +0100 Subject: Convert ArrayUtils routines into low-level functions. --- libsolidity/codegen/ArrayUtils.cpp | 479 +++++++++++++++++++------------------ 1 file changed, 251 insertions(+), 228 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 6f270b4d..f6cb7aac 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -40,9 +40,9 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons // stack layout: [source_ref] [source length] target_ref (top) solAssert(_targetType.location() == DataLocation::Storage, ""); - IntegerType uint256(256); - Type const* targetBaseType = _targetType.isByteArray() ? &uint256 : &(*_targetType.baseType()); - Type const* sourceBaseType = _sourceType.isByteArray() ? &uint256 : &(*_sourceType.baseType()); + TypePointer uint256 = make_shared(256); + TypePointer targetBaseType = _targetType.isByteArray() ? uint256 : _targetType.baseType(); + TypePointer sourceBaseType = _sourceType.isByteArray() ? uint256 : _sourceType.baseType(); // TODO unroll loop for small sizes @@ -70,202 +70,216 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons } // stack: target_ref source_ref source_length - m_context << Instruction::DUP3; - // stack: target_ref source_ref source_length target_ref - retrieveLength(_targetType); - // stack: target_ref source_ref source_length target_ref target_length - if (_targetType.isDynamicallySized()) - // store new target length - if (!_targetType.isByteArray()) - // Otherwise, length will be stored below. - m_context << Instruction::DUP3 << Instruction::DUP3 << Instruction::SSTORE; - if (sourceBaseType->category() == Type::Category::Mapping) - { - solAssert(targetBaseType->category() == Type::Category::Mapping, ""); - solAssert(_sourceType.location() == DataLocation::Storage, ""); - // nothing to copy - m_context - << Instruction::POP << Instruction::POP - << Instruction::POP << Instruction::POP; - return; - } - // stack: target_ref source_ref source_length target_ref target_length - // compute hashes (data positions) - m_context << Instruction::SWAP1; - if (_targetType.isDynamicallySized()) - CompilerUtils(m_context).computeHashStatic(); - // stack: target_ref source_ref source_length target_length target_data_pos - m_context << Instruction::SWAP1; - convertLengthToSize(_targetType); - m_context << Instruction::DUP2 << Instruction::ADD; - // stack: target_ref source_ref source_length target_data_pos target_data_end - m_context << Instruction::SWAP3; - // stack: target_ref target_data_end source_length target_data_pos source_ref - - eth::AssemblyItem copyLoopEndWithoutByteOffset = m_context.newTag(); - - // special case for short byte arrays: Store them together with their length. - if (_targetType.isByteArray()) - { - // stack: target_ref target_data_end source_length target_data_pos source_ref - m_context << Instruction::DUP3 << u256(31) << Instruction::LT; - eth::AssemblyItem longByteArray = m_context.appendConditionalJump(); - // store the short byte array - solAssert(_sourceType.isByteArray(), ""); - if (_sourceType.location() == DataLocation::Storage) - { - // just copy the slot, it contains length and data - m_context << Instruction::DUP1 << Instruction::SLOAD; - m_context << Instruction::DUP6 << Instruction::SSTORE; - } - else + TypePointer targetType = _targetType.shared_from_this(); + TypePointer sourceType = _sourceType.shared_from_this(); + m_context.callLowLevelFunction( + "$copyArrayToStorage_" + sourceType->identifier() + "_to_" + targetType->identifier(), + 3, + 1, + [=](CompilerContext& _context) { - m_context << Instruction::DUP1; - CompilerUtils(m_context).loadFromMemoryDynamic(*sourceBaseType, fromCalldata, true, false); - // stack: target_ref target_data_end source_length target_data_pos source_ref value - // clear the lower-order byte - which will hold the length - m_context << u256(0xff) << Instruction::NOT << Instruction::AND; - // fetch the length and shift it left by one - m_context << Instruction::DUP4 << Instruction::DUP1 << Instruction::ADD; - // combine value and length and store them - m_context << Instruction::OR << Instruction::DUP6 << Instruction::SSTORE; - } - // end of special case, jump right into cleaning target data area - m_context.appendJumpTo(copyLoopEndWithoutByteOffset); - m_context << longByteArray; - // Store length (2*length+1) - m_context << Instruction::DUP3 << Instruction::DUP1 << Instruction::ADD; - m_context << u256(1) << Instruction::ADD; - m_context << Instruction::DUP6 << Instruction::SSTORE; - } + ArrayUtils utils(_context); + ArrayType const& _sourceType = dynamic_cast(*sourceType); + ArrayType const& _targetType = dynamic_cast(*targetType); + // stack: target_ref source_ref source_length + _context << Instruction::DUP3; + // stack: target_ref source_ref source_length target_ref + utils.retrieveLength(_targetType); + // stack: target_ref source_ref source_length target_ref target_length + if (_targetType.isDynamicallySized()) + // store new target length + if (!_targetType.isByteArray()) + // Otherwise, length will be stored below. + _context << Instruction::DUP3 << Instruction::DUP3 << Instruction::SSTORE; + if (sourceBaseType->category() == Type::Category::Mapping) + { + solAssert(targetBaseType->category() == Type::Category::Mapping, ""); + solAssert(_sourceType.location() == DataLocation::Storage, ""); + // nothing to copy + _context + << Instruction::POP << Instruction::POP + << Instruction::POP << Instruction::POP; + return; + } + // stack: target_ref source_ref source_length target_ref target_length + // compute hashes (data positions) + _context << Instruction::SWAP1; + if (_targetType.isDynamicallySized()) + CompilerUtils(_context).computeHashStatic(); + // stack: target_ref source_ref source_length target_length target_data_pos + _context << Instruction::SWAP1; + utils.convertLengthToSize(_targetType); + _context << Instruction::DUP2 << Instruction::ADD; + // stack: target_ref source_ref source_length target_data_pos target_data_end + _context << Instruction::SWAP3; + // stack: target_ref target_data_end source_length target_data_pos source_ref + + eth::AssemblyItem copyLoopEndWithoutByteOffset = _context.newTag(); + + // special case for short byte arrays: Store them together with their length. + if (_targetType.isByteArray()) + { + // stack: target_ref target_data_end source_length target_data_pos source_ref + _context << Instruction::DUP3 << u256(31) << Instruction::LT; + eth::AssemblyItem longByteArray = _context.appendConditionalJump(); + // store the short byte array + solAssert(_sourceType.isByteArray(), ""); + if (_sourceType.location() == DataLocation::Storage) + { + // just copy the slot, it contains length and data + _context << Instruction::DUP1 << Instruction::SLOAD; + _context << Instruction::DUP6 << Instruction::SSTORE; + } + else + { + _context << Instruction::DUP1; + CompilerUtils(_context).loadFromMemoryDynamic(*sourceBaseType, fromCalldata, true, false); + // stack: target_ref target_data_end source_length target_data_pos source_ref value + // clear the lower-order byte - which will hold the length + _context << u256(0xff) << Instruction::NOT << Instruction::AND; + // fetch the length and shift it left by one + _context << Instruction::DUP4 << Instruction::DUP1 << Instruction::ADD; + // combine value and length and store them + _context << Instruction::OR << Instruction::DUP6 << Instruction::SSTORE; + } + // end of special case, jump right into cleaning target data area + _context.appendJumpTo(copyLoopEndWithoutByteOffset); + _context << longByteArray; + // Store length (2*length+1) + _context << Instruction::DUP3 << Instruction::DUP1 << Instruction::ADD; + _context << u256(1) << Instruction::ADD; + _context << Instruction::DUP6 << Instruction::SSTORE; + } - // skip copying if source length is zero - m_context << Instruction::DUP3 << Instruction::ISZERO; - m_context.appendConditionalJumpTo(copyLoopEndWithoutByteOffset); - - if (_sourceType.location() == DataLocation::Storage && _sourceType.isDynamicallySized()) - CompilerUtils(m_context).computeHashStatic(); - // stack: target_ref target_data_end source_length target_data_pos source_data_pos - m_context << Instruction::SWAP2; - convertLengthToSize(_sourceType); - m_context << Instruction::DUP3 << Instruction::ADD; - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end - if (haveByteOffsetTarget) - m_context << u256(0); - if (haveByteOffsetSource) - m_context << u256(0); - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] - eth::AssemblyItem copyLoopStart = m_context.newTag(); - m_context << copyLoopStart; - // check for loop condition - m_context - << dupInstruction(3 + byteOffsetSize) << dupInstruction(2 + byteOffsetSize) - << Instruction::GT << Instruction::ISZERO; - eth::AssemblyItem copyLoopEnd = m_context.appendConditionalJump(); - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] - // copy - if (sourceBaseType->category() == Type::Category::Array) - { - solAssert(byteOffsetSize == 0, "Byte offset for array as base type."); - auto const& sourceBaseArrayType = dynamic_cast(*sourceBaseType); - m_context << Instruction::DUP3; - if (sourceBaseArrayType.location() == DataLocation::Memory) - m_context << Instruction::MLOAD; - m_context << Instruction::DUP3; - copyArrayToStorage(dynamic_cast(*targetBaseType), sourceBaseArrayType); - m_context << Instruction::POP; - } - else if (directCopy) - { - solAssert(byteOffsetSize == 0, "Byte offset for direct copy."); - m_context - << Instruction::DUP3 << Instruction::SLOAD - << Instruction::DUP3 << Instruction::SSTORE; - } - else - { - // Note that we have to copy each element on its own in case conversion is involved. - // We might copy too much if there is padding at the last element, but this way end - // checking is easier. - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] - m_context << dupInstruction(3 + byteOffsetSize); - if (_sourceType.location() == DataLocation::Storage) - { + // skip copying if source length is zero + _context << Instruction::DUP3 << Instruction::ISZERO; + _context.appendConditionalJumpTo(copyLoopEndWithoutByteOffset); + + if (_sourceType.location() == DataLocation::Storage && _sourceType.isDynamicallySized()) + CompilerUtils(_context).computeHashStatic(); + // stack: target_ref target_data_end source_length target_data_pos source_data_pos + _context << Instruction::SWAP2; + utils.convertLengthToSize(_sourceType); + _context << Instruction::DUP3 << Instruction::ADD; + // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end + if (haveByteOffsetTarget) + _context << u256(0); if (haveByteOffsetSource) - m_context << Instruction::DUP2; + _context << u256(0); + // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] + eth::AssemblyItem copyLoopStart = _context.newTag(); + _context << copyLoopStart; + // check for loop condition + _context + << dupInstruction(3 + byteOffsetSize) << dupInstruction(2 + byteOffsetSize) + << Instruction::GT << Instruction::ISZERO; + eth::AssemblyItem copyLoopEnd = _context.appendConditionalJump(); + // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] + // copy + if (sourceBaseType->category() == Type::Category::Array) + { + solAssert(byteOffsetSize == 0, "Byte offset for array as base type."); + auto const& sourceBaseArrayType = dynamic_cast(*sourceBaseType); + _context << Instruction::DUP3; + if (sourceBaseArrayType.location() == DataLocation::Memory) + _context << Instruction::MLOAD; + _context << Instruction::DUP3; + utils.copyArrayToStorage(dynamic_cast(*targetBaseType), sourceBaseArrayType); + _context << Instruction::POP; + } + else if (directCopy) + { + solAssert(byteOffsetSize == 0, "Byte offset for direct copy."); + _context + << Instruction::DUP3 << Instruction::SLOAD + << Instruction::DUP3 << Instruction::SSTORE; + } else - m_context << u256(0); - StorageItem(m_context, *sourceBaseType).retrieveValue(SourceLocation(), true); + { + // Note that we have to copy each element on its own in case conversion is involved. + // We might copy too much if there is padding at the last element, but this way end + // checking is easier. + // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] + _context << dupInstruction(3 + byteOffsetSize); + if (_sourceType.location() == DataLocation::Storage) + { + if (haveByteOffsetSource) + _context << Instruction::DUP2; + else + _context << u256(0); + StorageItem(_context, *sourceBaseType).retrieveValue(SourceLocation(), true); + } + else if (sourceBaseType->isValueType()) + CompilerUtils(_context).loadFromMemoryDynamic(*sourceBaseType, fromCalldata, true, false); + else + solUnimplemented("Copying of type " + _sourceType.toString(false) + " to storage not yet supported."); + // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] ... + solAssert( + 2 + byteOffsetSize + sourceBaseType->sizeOnStack() <= 16, + "Stack too deep, try removing local variables." + ); + // fetch target storage reference + _context << dupInstruction(2 + byteOffsetSize + sourceBaseType->sizeOnStack()); + if (haveByteOffsetTarget) + _context << dupInstruction(1 + byteOffsetSize + sourceBaseType->sizeOnStack()); + else + _context << u256(0); + StorageItem(_context, *targetBaseType).storeValue(*sourceBaseType, SourceLocation(), true); + } + // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] + // increment source + if (haveByteOffsetSource) + utils.incrementByteOffset(sourceBaseType->storageBytes(), 1, haveByteOffsetTarget ? 5 : 4); + else + { + _context << swapInstruction(2 + byteOffsetSize); + if (sourceIsStorage) + _context << sourceBaseType->storageSize(); + else if (_sourceType.location() == DataLocation::Memory) + _context << sourceBaseType->memoryHeadSize(); + else + _context << sourceBaseType->calldataEncodedSize(true); + _context + << Instruction::ADD + << swapInstruction(2 + byteOffsetSize); + } + // increment target + if (haveByteOffsetTarget) + utils.incrementByteOffset(targetBaseType->storageBytes(), byteOffsetSize, byteOffsetSize + 2); + else + _context + << swapInstruction(1 + byteOffsetSize) + << targetBaseType->storageSize() + << Instruction::ADD + << swapInstruction(1 + byteOffsetSize); + _context.appendJumpTo(copyLoopStart); + _context << copyLoopEnd; + if (haveByteOffsetTarget) + { + // clear elements that might be left over in the current slot in target + // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end target_byte_offset [source_byte_offset] + _context << dupInstruction(byteOffsetSize) << Instruction::ISZERO; + eth::AssemblyItem copyCleanupLoopEnd = _context.appendConditionalJump(); + _context << dupInstruction(2 + byteOffsetSize) << dupInstruction(1 + byteOffsetSize); + StorageItem(_context, *targetBaseType).setToZero(SourceLocation(), true); + utils.incrementByteOffset(targetBaseType->storageBytes(), byteOffsetSize, byteOffsetSize + 2); + _context.appendJumpTo(copyLoopEnd); + + _context << copyCleanupLoopEnd; + _context << Instruction::POP; // might pop the source, but then target is popped next + } + if (haveByteOffsetSource) + _context << Instruction::POP; + _context << copyLoopEndWithoutByteOffset; + + // zero-out leftovers in target + // stack: target_ref target_data_end source_data_pos target_data_pos_updated source_data_end + _context << Instruction::POP << Instruction::SWAP1 << Instruction::POP; + // stack: target_ref target_data_end target_data_pos_updated + utils.clearStorageLoop(*targetBaseType); + _context << Instruction::POP; } - else if (sourceBaseType->isValueType()) - CompilerUtils(m_context).loadFromMemoryDynamic(*sourceBaseType, fromCalldata, true, false); - else - solUnimplemented("Copying of type " + _sourceType.toString(false) + " to storage not yet supported."); - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] ... - solAssert( - 2 + byteOffsetSize + sourceBaseType->sizeOnStack() <= 16, - "Stack too deep, try removing local variables." - ); - // fetch target storage reference - m_context << dupInstruction(2 + byteOffsetSize + sourceBaseType->sizeOnStack()); - if (haveByteOffsetTarget) - m_context << dupInstruction(1 + byteOffsetSize + sourceBaseType->sizeOnStack()); - else - m_context << u256(0); - StorageItem(m_context, *targetBaseType).storeValue(*sourceBaseType, SourceLocation(), true); - } - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] - // increment source - if (haveByteOffsetSource) - incrementByteOffset(sourceBaseType->storageBytes(), 1, haveByteOffsetTarget ? 5 : 4); - else - { - m_context << swapInstruction(2 + byteOffsetSize); - if (sourceIsStorage) - m_context << sourceBaseType->storageSize(); - else if (_sourceType.location() == DataLocation::Memory) - m_context << sourceBaseType->memoryHeadSize(); - else - m_context << sourceBaseType->calldataEncodedSize(true); - m_context - << Instruction::ADD - << swapInstruction(2 + byteOffsetSize); - } - // increment target - if (haveByteOffsetTarget) - incrementByteOffset(targetBaseType->storageBytes(), byteOffsetSize, byteOffsetSize + 2); - else - m_context - << swapInstruction(1 + byteOffsetSize) - << targetBaseType->storageSize() - << Instruction::ADD - << swapInstruction(1 + byteOffsetSize); - m_context.appendJumpTo(copyLoopStart); - m_context << copyLoopEnd; - if (haveByteOffsetTarget) - { - // clear elements that might be left over in the current slot in target - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end target_byte_offset [source_byte_offset] - m_context << dupInstruction(byteOffsetSize) << Instruction::ISZERO; - eth::AssemblyItem copyCleanupLoopEnd = m_context.appendConditionalJump(); - m_context << dupInstruction(2 + byteOffsetSize) << dupInstruction(1 + byteOffsetSize); - StorageItem(m_context, *targetBaseType).setToZero(SourceLocation(), true); - incrementByteOffset(targetBaseType->storageBytes(), byteOffsetSize, byteOffsetSize + 2); - m_context.appendJumpTo(copyLoopEnd); - - m_context << copyCleanupLoopEnd; - m_context << Instruction::POP; // might pop the source, but then target is popped next - } - if (haveByteOffsetSource) - m_context << Instruction::POP; - m_context << copyLoopEndWithoutByteOffset; - - // zero-out leftovers in target - // stack: target_ref target_data_end source_data_pos target_data_pos_updated source_data_end - m_context << Instruction::POP << Instruction::SWAP1 << Instruction::POP; - // stack: target_ref target_data_end target_data_pos_updated - clearStorageLoop(*targetBaseType); - m_context << Instruction::POP; + ); } void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWordBoundaries) const @@ -759,41 +773,50 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const void ArrayUtils::clearStorageLoop(Type const& _type) const { - unsigned stackHeightStart = m_context.stackHeight(); - if (_type.category() == Type::Category::Mapping) - { - m_context << Instruction::POP; - return; - } - // stack: end_pos pos - - // jump to and return from the loop to allow for duplicate code removal - eth::AssemblyItem returnTag = m_context.pushNewTag(); - m_context << Instruction::SWAP2 << Instruction::SWAP1; - - // stack: end_pos pos - eth::AssemblyItem loopStart = m_context.appendJumpToNew(); - m_context << loopStart; - // check for loop condition - m_context << Instruction::DUP1 << Instruction::DUP3 - << Instruction::GT << Instruction::ISZERO; - eth::AssemblyItem zeroLoopEnd = m_context.newTag(); - m_context.appendConditionalJumpTo(zeroLoopEnd); - // delete - m_context << u256(0); - StorageItem(m_context, _type).setToZero(SourceLocation(), false); - m_context << Instruction::POP; - // increment - m_context << _type.storageSize() << Instruction::ADD; - m_context.appendJumpTo(loopStart); - // cleanup - m_context << zeroLoopEnd; - m_context << Instruction::POP << Instruction::SWAP1; - // "return" - m_context << Instruction::JUMP; + TypePointer type = _type.shared_from_this(); + m_context.callLowLevelFunction( + "$clearStorageLoop_" + _type.identifier(), + 2, + 1, + [type](CompilerContext& _context) + { + unsigned stackHeightStart = _context.stackHeight(); + if (type->category() == Type::Category::Mapping) + { + _context << Instruction::POP; + return; + } + // stack: end_pos pos + + // jump to and return from the loop to allow for duplicate code removal + eth::AssemblyItem returnTag = _context.pushNewTag(); + _context << Instruction::SWAP2 << Instruction::SWAP1; + + // stack: end_pos pos + eth::AssemblyItem loopStart = _context.appendJumpToNew(); + _context << loopStart; + // check for loop condition + _context << Instruction::DUP1 << Instruction::DUP3 + << Instruction::GT << Instruction::ISZERO; + eth::AssemblyItem zeroLoopEnd = _context.newTag(); + _context.appendConditionalJumpTo(zeroLoopEnd); + // delete + _context << u256(0); + StorageItem(_context, *type).setToZero(SourceLocation(), false); + _context << Instruction::POP; + // increment + _context << type->storageSize() << Instruction::ADD; + _context.appendJumpTo(loopStart); + // cleanup + _context << zeroLoopEnd; + _context << Instruction::POP << Instruction::SWAP1; + // "return" + _context << Instruction::JUMP; - m_context << returnTag; - solAssert(m_context.stackHeight() == stackHeightStart - 1, ""); + _context << returnTag; + solAssert(_context.stackHeight() == stackHeightStart - 1, ""); + } + ); } void ArrayUtils::convertLengthToSize(ArrayType const& _arrayType, bool _pad) const -- cgit From 82a00e7dc50a8d8828fa6c192f78d4147944eccf Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 20 Jan 2017 14:30:02 +0100 Subject: Use shared_ptrs to enable shared_from_this. --- libsolidity/codegen/ArrayUtils.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index f6cb7aac..0b09cc1a 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -572,7 +572,7 @@ void ArrayUtils::clearArray(ArrayType const& _typeIn) const ArrayUtils(_context).convertLengthToSize(_type); _context << Instruction::ADD << Instruction::SWAP1; if (_type.baseType()->storageBytes() < 32) - ArrayUtils(_context).clearStorageLoop(IntegerType(256)); + ArrayUtils(_context).clearStorageLoop(*make_shared(256)); else ArrayUtils(_context).clearStorageLoop(*_type.baseType()); _context << Instruction::POP; @@ -613,7 +613,7 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const << Instruction::SWAP1; // stack: data_pos_end data_pos if (_type.isByteArray() || _type.baseType()->storageBytes() < 32) - clearStorageLoop(IntegerType(256)); + clearStorageLoop(*make_shared(256)); else clearStorageLoop(*_type.baseType()); // cleanup @@ -720,7 +720,7 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const ArrayUtils(_context).convertLengthToSize(_type); _context << Instruction::DUP2 << Instruction::ADD << Instruction::SWAP1; // stack: ref new_length current_length first_word data_location_end data_location - ArrayUtils(_context).clearStorageLoop(IntegerType(256)); + ArrayUtils(_context).clearStorageLoop(*make_shared(256)); _context << Instruction::POP; // stack: ref new_length current_length first_word solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); @@ -759,7 +759,7 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const _context << Instruction::SWAP2 << Instruction::ADD; // stack: ref new_length delete_end delete_start if (_type.isByteArray() || _type.baseType()->storageBytes() < 32) - ArrayUtils(_context).clearStorageLoop(IntegerType(256)); + ArrayUtils(_context).clearStorageLoop(*make_shared(256)); else ArrayUtils(_context).clearStorageLoop(*_type.baseType()); -- cgit From 23eca813f578463383afd37b76ce49d87f79c811 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 24 Jan 2017 12:06:11 +0100 Subject: Change clearStorageLoop to TypePointer. --- libsolidity/codegen/ArrayUtils.cpp | 29 ++++++++++++++--------------- libsolidity/codegen/ArrayUtils.h | 5 ++++- 2 files changed, 18 insertions(+), 16 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 0b09cc1a..4d100d1d 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -276,7 +276,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons // stack: target_ref target_data_end source_data_pos target_data_pos_updated source_data_end _context << Instruction::POP << Instruction::SWAP1 << Instruction::POP; // stack: target_ref target_data_end target_data_pos_updated - utils.clearStorageLoop(*targetBaseType); + utils.clearStorageLoop(targetBaseType); _context << Instruction::POP; } ); @@ -572,9 +572,9 @@ void ArrayUtils::clearArray(ArrayType const& _typeIn) const ArrayUtils(_context).convertLengthToSize(_type); _context << Instruction::ADD << Instruction::SWAP1; if (_type.baseType()->storageBytes() < 32) - ArrayUtils(_context).clearStorageLoop(*make_shared(256)); + ArrayUtils(_context).clearStorageLoop(make_shared(256)); else - ArrayUtils(_context).clearStorageLoop(*_type.baseType()); + ArrayUtils(_context).clearStorageLoop(_type.baseType()); _context << Instruction::POP; } solAssert(_context.stackHeight() == stackHeightStart - 2, ""); @@ -613,9 +613,9 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const << Instruction::SWAP1; // stack: data_pos_end data_pos if (_type.isByteArray() || _type.baseType()->storageBytes() < 32) - clearStorageLoop(*make_shared(256)); + clearStorageLoop(make_shared(256)); else - clearStorageLoop(*_type.baseType()); + clearStorageLoop(_type.baseType()); // cleanup m_context << endTag; m_context << Instruction::POP; @@ -720,7 +720,7 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const ArrayUtils(_context).convertLengthToSize(_type); _context << Instruction::DUP2 << Instruction::ADD << Instruction::SWAP1; // stack: ref new_length current_length first_word data_location_end data_location - ArrayUtils(_context).clearStorageLoop(*make_shared(256)); + ArrayUtils(_context).clearStorageLoop(make_shared(256)); _context << Instruction::POP; // stack: ref new_length current_length first_word solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); @@ -759,9 +759,9 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const _context << Instruction::SWAP2 << Instruction::ADD; // stack: ref new_length delete_end delete_start if (_type.isByteArray() || _type.baseType()->storageBytes() < 32) - ArrayUtils(_context).clearStorageLoop(*make_shared(256)); + ArrayUtils(_context).clearStorageLoop(make_shared(256)); else - ArrayUtils(_context).clearStorageLoop(*_type.baseType()); + ArrayUtils(_context).clearStorageLoop(_type.baseType()); _context << resizeEnd; // cleanup @@ -771,17 +771,16 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const ); } -void ArrayUtils::clearStorageLoop(Type const& _type) const +void ArrayUtils::clearStorageLoop(TypePointer const& _type) const { - TypePointer type = _type.shared_from_this(); m_context.callLowLevelFunction( - "$clearStorageLoop_" + _type.identifier(), + "$clearStorageLoop_" + _type->identifier(), 2, 1, - [type](CompilerContext& _context) + [_type](CompilerContext& _context) { unsigned stackHeightStart = _context.stackHeight(); - if (type->category() == Type::Category::Mapping) + if (_type->category() == Type::Category::Mapping) { _context << Instruction::POP; return; @@ -802,10 +801,10 @@ void ArrayUtils::clearStorageLoop(Type const& _type) const _context.appendConditionalJumpTo(zeroLoopEnd); // delete _context << u256(0); - StorageItem(_context, *type).setToZero(SourceLocation(), false); + StorageItem(_context, *_type).setToZero(SourceLocation(), false); _context << Instruction::POP; // increment - _context << type->storageSize() << Instruction::ADD; + _context << _type->storageSize() << Instruction::ADD; _context.appendJumpTo(loopStart); // cleanup _context << zeroLoopEnd; diff --git a/libsolidity/codegen/ArrayUtils.h b/libsolidity/codegen/ArrayUtils.h index d0ba2892..806fbea7 100644 --- a/libsolidity/codegen/ArrayUtils.h +++ b/libsolidity/codegen/ArrayUtils.h @@ -22,6 +22,8 @@ #pragma once +#include + namespace dev { namespace solidity @@ -30,6 +32,7 @@ namespace solidity class CompilerContext; class Type; class ArrayType; +using TypePointer = std::shared_ptr; /** * Class that provides code generation for handling arrays. @@ -67,7 +70,7 @@ public: /// Appends a loop that clears a sequence of storage slots of the given type (excluding end). /// Stack pre: end_ref start_ref /// Stack post: end_ref - void clearStorageLoop(Type const& _type) const; + void clearStorageLoop(TypePointer const& _type) const; /// Converts length to size (number of storage slots or calldata/memory bytes). /// if @a _pad then add padding to multiples of 32 bytes for calldata/memory. /// Stack pre: length -- cgit From 7e6f1b3f0008d03e6cdfa186b8f9976570865d4e Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 24 Jan 2017 12:06:21 +0100 Subject: Use int arithmetics for stack adjustment. --- libsolidity/codegen/CompilerContext.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libsolidity') diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index dad227c7..e26f96e8 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -81,7 +81,7 @@ void CompilerContext::callLowLevelFunction( else *this << it->second; appendJump(eth::AssemblyItem::JumpType::IntoFunction); - adjustStackOffset(_outArgs - 1 - _inArgs); + adjustStackOffset(int(_outArgs) - 1 - _inArgs); *this << retTag.tag(); } -- cgit From ead1a3b33fae83ad65210d6f82b2ca12150bf2bb Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 20 Jan 2017 20:16:18 +0100 Subject: Include creation code only once. --- libsolidity/codegen/ExpressionCompiler.cpp | 32 +++++++++++++++++------------- 1 file changed, 18 insertions(+), 14 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 81d3409e..fe0eeb1c 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -556,20 +556,24 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) arg->accept(*this); argumentTypes.push_back(arg->annotation().type); } - ContractDefinition const& contract = - dynamic_cast(*function.returnParameterTypes().front()).contractDefinition(); - // copy the contract's code into memory - eth::Assembly const& assembly = m_context.compiledContract(contract); - utils().fetchFreeMemoryPointer(); - // TODO we create a copy here, which is actually what we want. - // This should be revisited at the point where we fix - // https://github.com/ethereum/solidity/issues/1092 - // pushes size - auto subroutine = m_context.addSubroutine(make_shared(assembly)); - m_context << Instruction::DUP1 << subroutine; - m_context << Instruction::DUP4 << Instruction::CODECOPY; - - m_context << Instruction::ADD; + ContractDefinition const* contract = + &dynamic_cast(*function.returnParameterTypes().front()).contractDefinition(); + m_context.callLowLevelFunction( + "$copyContractCreationCodeToMemory_" + contract->type()->identifier(), + 0, + 1, + [contract](CompilerContext& _context) + { + // copy the contract's code into memory + eth::Assembly const& assembly = _context.compiledContract(*contract); + CompilerUtils(_context).fetchFreeMemoryPointer(); + // pushes size + auto subroutine = _context.addSubroutine(make_shared(assembly)); + _context << Instruction::DUP1 << subroutine; + _context << Instruction::DUP4 << Instruction::CODECOPY; + _context << Instruction::ADD; + } + ); utils().encodeToMemory(argumentTypes, function.parameterTypes()); // now on stack: memory_end_ptr // need: size, offset, endowment -- cgit From 1316bb75651ea365c5246277d2dfd3d366be9070 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 24 Jan 2017 17:38:06 +0100 Subject: Warn about invalid checksums of addresses. --- libsolidity/analysis/TypeChecker.cpp | 10 +++++++ libsolidity/ast/AST.cpp | 43 ++++++++++++++++++++++++++++-- libsolidity/ast/AST.h | 5 ++++ libsolidity/ast/Types.cpp | 8 ++++++ libsolidity/ast/Types.h | 2 ++ libsolidity/codegen/ExpressionCompiler.cpp | 1 + 6 files changed, 67 insertions(+), 2 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index adb3d54f..533c787b 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1565,6 +1565,16 @@ void TypeChecker::endVisit(ElementaryTypeNameExpression const& _expr) void TypeChecker::endVisit(Literal const& _literal) { + if (_literal.looksLikeAddress()) + { + if (_literal.passesAddressChecksum()) + { + _literal.annotation().type = make_shared(0, IntegerType::Modifier::Address); + return; + } + else + warning(_literal.location(), "This looks like an address but has an invalid checksum."); + } _literal.annotation().type = Type::forLiteral(_literal); if (!_literal.annotation().type) fatalTypeError(_literal.location(), "Invalid literal value."); diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 8a43c3f7..e2b50dd7 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -20,8 +20,6 @@ * Solidity abstract syntax tree. */ -#include -#include #include #include #include @@ -30,6 +28,11 @@ #include +#include + +#include +#include + using namespace std; using namespace dev; using namespace dev::solidity; @@ -522,3 +525,39 @@ IdentifierAnnotation& Identifier::annotation() const m_annotation = new IdentifierAnnotation(); return static_cast(*m_annotation); } + +bool Literal::looksLikeAddress() const +{ + if (subDenomination() != SubDenomination::None) + return false; + + string lit = value(); + return lit.substr(0, 2) == "0x" && abs(int(lit.length()) - 42) <= 1; +} + +bool Literal::passesAddressChecksum() const +{ + string lit = value(); + solAssert(lit.substr(0, 2) == "0x", "Expected hex prefix"); + lit = lit.substr(2); + + if (lit.length() != 40) + return false; + + h256 hash = keccak256(boost::algorithm::to_lower_copy(lit, std::locale::classic())); + for (size_t i = 0; i < 40; ++i) + { + char addressCharacter = lit[i]; + bool lowerCase; + if ('a' <= addressCharacter && addressCharacter <= 'f') + lowerCase = true; + else if ('A' <= addressCharacter && addressCharacter <= 'F') + lowerCase = false; + else + continue; + unsigned nibble = (unsigned(hash[i / 2]) >> (4 * (1 - (i % 2)))) & 0xf; + if ((nibble >= 8) == lowerCase) + return false; + } + return true; +} diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 4c75f7ea..743fdaa1 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -1584,6 +1584,11 @@ public: SubDenomination subDenomination() const { return m_subDenomination; } + /// @returns true if this looks like a checksummed address. + bool looksLikeAddress() const; + /// @returns true if it passes the address checksum test. + bool passesAddressChecksum() const; + private: Token::Value m_token; ASTPointer m_value; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index cefd0603..971e1f18 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -405,6 +405,14 @@ string IntegerType::toString(bool) const return prefix + dev::toString(m_bits); } +u256 IntegerType::literalValue(Literal const* _literal) const +{ + solAssert(m_modifier == Modifier::Address, ""); + solAssert(_literal, ""); + solAssert(_literal->value().substr(0, 2) == "0x", ""); + return u256(_literal->value()); +} + TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const { if ( diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 1e94631e..770cbb30 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -314,6 +314,8 @@ public: virtual std::string toString(bool _short) const override; + virtual u256 literalValue(Literal const* _literal) const override; + virtual TypePointer encodingType() const override { return shared_from_this(); } virtual TypePointer interfaceType(bool) const override { return shared_from_this(); } diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index fe0eeb1c..bda4e04d 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1308,6 +1308,7 @@ void ExpressionCompiler::endVisit(Literal const& _literal) { case Type::Category::RationalNumber: case Type::Category::Bool: + case Type::Category::Integer: m_context << type->literalValue(&_literal); break; case Type::Category::StringLiteral: -- cgit From 3949624a61b1dd0c32e67d30fe7d46b2511c583f Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 24 Jan 2017 23:36:07 +0100 Subject: Also check library addresses. --- libsolidity/ast/AST.cpp | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index e2b50dd7..616de54e 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -539,25 +539,5 @@ bool Literal::passesAddressChecksum() const { string lit = value(); solAssert(lit.substr(0, 2) == "0x", "Expected hex prefix"); - lit = lit.substr(2); - - if (lit.length() != 40) - return false; - - h256 hash = keccak256(boost::algorithm::to_lower_copy(lit, std::locale::classic())); - for (size_t i = 0; i < 40; ++i) - { - char addressCharacter = lit[i]; - bool lowerCase; - if ('a' <= addressCharacter && addressCharacter <= 'f') - lowerCase = true; - else if ('A' <= addressCharacter && addressCharacter <= 'F') - lowerCase = false; - else - continue; - unsigned nibble = (unsigned(hash[i / 2]) >> (4 * (1 - (i % 2)))) & 0xf; - if ((nibble >= 8) == lowerCase) - return false; - } - return true; + return dev::passesAddressChecksum(lit, true); } -- cgit From 5738e865d5375751332d8f884f2b00b006d76945 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 30 Nov 2016 15:04:07 +0100 Subject: Accept any kind of whitespace after natspec tags --- libsolidity/parsing/DocStringParser.cpp | 60 ++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 13 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/parsing/DocStringParser.cpp b/libsolidity/parsing/DocStringParser.cpp index bbee35f5..4c59c47c 100644 --- a/libsolidity/parsing/DocStringParser.cpp +++ b/libsolidity/parsing/DocStringParser.cpp @@ -16,16 +16,39 @@ static inline string::const_iterator skipLineOrEOS( return (_nlPos == _end) ? _end : ++_nlPos; } -static inline string::const_iterator firstSpaceOrNl( +static inline string::const_iterator firstSpaceOrTab( string::const_iterator _pos, string::const_iterator _end ) { auto spacePos = find(_pos, _end, ' '); - auto nlPos = find(_pos, _end, '\n'); - return (spacePos < nlPos) ? spacePos : nlPos; + auto tabPos = find(_pos, _end, '\t'); + return (spacePos < tabPos) ? spacePos : tabPos; } +static inline string::const_iterator firstWsOrNl( + string::const_iterator _pos, + string::const_iterator _end +) +{ + auto wsPos = firstSpaceOrTab(_pos, _end); + auto nlPos = find(wsPos, _end, '\n'); + return (wsPos < nlPos) ? wsPos : nlPos; +} + + +static inline string::const_iterator skipWhitespace( + string::const_iterator _pos, + string::const_iterator _end +) +{ + auto currPos = _pos; + while ((*currPos == ' ' || *currPos == '\t') && currPos != _end) + currPos += 1; + return currPos; +} + + bool DocStringParser::parse(string const& _docString, ErrorList& _errors) { m_errors = &_errors; @@ -43,7 +66,7 @@ bool DocStringParser::parse(string const& _docString, ErrorList& _errors) if (tagPos != end && tagPos < nlPos) { // we found a tag - auto tagNameEndPos = firstSpaceOrNl(tagPos, end); + auto tagNameEndPos = firstWsOrNl(tagPos, end); if (tagNameEndPos == end) { appendError("End of tag " + string(tagPos, tagNameEndPos) + "not found"); @@ -75,7 +98,7 @@ DocStringParser::iter DocStringParser::parseDocTagLine(iter _pos, iter _end, boo { solAssert(!!m_lastTag, ""); auto nlPos = find(_pos, _end, '\n'); - if (_appending && _pos < _end && *_pos != ' ') + if (_appending && _pos < _end && *_pos != ' ' && *_pos != '\t') m_lastTag->content += " "; copy(_pos, nlPos, back_inserter(m_lastTag->content)); return skipLineOrEOS(nlPos, _end); @@ -83,19 +106,30 @@ DocStringParser::iter DocStringParser::parseDocTagLine(iter _pos, iter _end, boo DocStringParser::iter DocStringParser::parseDocTagParam(iter _pos, iter _end) { - // find param name - auto currPos = find(_pos, _end, ' '); - if (currPos == _end) + // find param name start + auto nameStartPos = skipWhitespace(_pos, _end); + if (nameStartPos == _end) + { + appendError("No param name given" + string(nameStartPos, _end)); + return _end; + } + auto nameEndPos = firstSpaceOrTab(nameStartPos, _end); + if (nameEndPos == _end) { - appendError("End of param name not found" + string(_pos, _end)); + appendError("End of param name not found" + string(nameStartPos, _end)); return _end; } + auto paramName = string(nameStartPos, nameEndPos); - auto paramName = string(_pos, currPos); + auto descStartPos = skipWhitespace(nameEndPos, _end); + if (descStartPos == _end) + { + appendError("No description given for param" + paramName); + return _end; + } - currPos += 1; - auto nlPos = find(currPos, _end, '\n'); - auto paramDesc = string(currPos, nlPos); + auto nlPos = find(descStartPos, _end, '\n'); + auto paramDesc = string(descStartPos, nlPos); newTag("param"); m_lastTag->paramName = paramName; m_lastTag->content = paramDesc; -- cgit From 9ca0fde853735d183c3db5c77c83de7c70c4ec98 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 1 Dec 2016 10:14:19 +0100 Subject: Fix and better output for tests --- libsolidity/parsing/DocStringParser.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'libsolidity') diff --git a/libsolidity/parsing/DocStringParser.cpp b/libsolidity/parsing/DocStringParser.cpp index 4c59c47c..5d9a75ef 100644 --- a/libsolidity/parsing/DocStringParser.cpp +++ b/libsolidity/parsing/DocStringParser.cpp @@ -99,7 +99,13 @@ DocStringParser::iter DocStringParser::parseDocTagLine(iter _pos, iter _end, boo solAssert(!!m_lastTag, ""); auto nlPos = find(_pos, _end, '\n'); if (_appending && _pos < _end && *_pos != ' ' && *_pos != '\t') + { m_lastTag->content += " "; + } + else if (!_appending) + { + _pos = skipWhitespace(_pos, _end); + } copy(_pos, nlPos, back_inserter(m_lastTag->content)); return skipLineOrEOS(nlPos, _end); } -- cgit From 900c56d996472cb2053b69c7104ef007c13b1e80 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 25 Jan 2017 10:33:09 +0000 Subject: Do not allow shadowing inline assembly instructions with variables --- libsolidity/inlineasm/AsmParser.cpp | 11 ++++++++++- libsolidity/inlineasm/AsmParser.h | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'libsolidity') diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index ef3da255..c0efb651 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -130,7 +130,7 @@ assembly::Statement Parser::parseExpression() return operation; } -assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) +std::map Parser::getInstructions() { // Allowed instructions, lowercase names. static map s_instructions; @@ -151,6 +151,12 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) // add alias for selfdestruct s_instructions["selfdestruct"] = solidity::Instruction::SUICIDE; } + return s_instructions; +} + +assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) +{ + map s_instructions = getInstructions(); Statement ret; switch (m_scanner->currentToken()) @@ -204,9 +210,12 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) assembly::VariableDeclaration Parser::parseVariableDeclaration() { + map s_instructions = getInstructions(); VariableDeclaration varDecl = createWithLocation(); expectToken(Token::Let); varDecl.name = m_scanner->currentLiteral(); + if (s_instructions.count(varDecl.name)) + fatalParserError("Cannot shadow instructions with variable declaration."); expectToken(Token::Identifier); expectToken(Token::Colon); expectToken(Token::Assign); diff --git a/libsolidity/inlineasm/AsmParser.h b/libsolidity/inlineasm/AsmParser.h index 8b56ab90..764c53f6 100644 --- a/libsolidity/inlineasm/AsmParser.h +++ b/libsolidity/inlineasm/AsmParser.h @@ -64,6 +64,7 @@ protected: Statement parseStatement(); /// Parses a functional expression that has to push exactly one stack element Statement parseExpression(); + std::map getInstructions(); Statement parseElementaryOperation(bool _onlySinglePusher = false); VariableDeclaration parseVariableDeclaration(); FunctionalInstruction parseFunctionalInstruction(Statement&& _instruction); -- cgit From 27ba665694c4a961e098559cb36176aeafd5ec44 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 25 Jan 2017 17:24:43 +0100 Subject: Moved test. --- libsolidity/inlineasm/AsmParser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libsolidity') diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index c0efb651..cd3ea0da 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -215,7 +215,7 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration() expectToken(Token::Let); varDecl.name = m_scanner->currentLiteral(); if (s_instructions.count(varDecl.name)) - fatalParserError("Cannot shadow instructions with variable declaration."); + fatalParserError("Cannot use instruction names for identifier names."); expectToken(Token::Identifier); expectToken(Token::Colon); expectToken(Token::Assign); -- cgit From a5696e1f0a87a2912cc64d2538751836658e7c63 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 25 Jan 2017 17:24:50 +0100 Subject: Renamed function. --- libsolidity/inlineasm/AsmParser.cpp | 8 ++++---- libsolidity/inlineasm/AsmParser.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index cd3ea0da..048d916d 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -130,7 +130,7 @@ assembly::Statement Parser::parseExpression() return operation; } -std::map Parser::getInstructions() +std::map const& Parser::instructions() { // Allowed instructions, lowercase names. static map s_instructions; @@ -156,7 +156,7 @@ std::map Parser::getInstructions() assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) { - map s_instructions = getInstructions(); + map const& s_instructions = instructions(); Statement ret; switch (m_scanner->currentToken()) @@ -178,7 +178,7 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) // first search the set of instructions. if (s_instructions.count(literal)) { - dev::solidity::Instruction const& instr = s_instructions[literal]; + dev::solidity::Instruction const& instr = s_instructions.at(literal); if (_onlySinglePusher) { InstructionInfo info = dev::solidity::instructionInfo(instr); @@ -210,7 +210,7 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) assembly::VariableDeclaration Parser::parseVariableDeclaration() { - map s_instructions = getInstructions(); + map const& s_instructions = instructions(); VariableDeclaration varDecl = createWithLocation(); expectToken(Token::Let); varDecl.name = m_scanner->currentLiteral(); diff --git a/libsolidity/inlineasm/AsmParser.h b/libsolidity/inlineasm/AsmParser.h index 764c53f6..643548dd 100644 --- a/libsolidity/inlineasm/AsmParser.h +++ b/libsolidity/inlineasm/AsmParser.h @@ -64,7 +64,7 @@ protected: Statement parseStatement(); /// Parses a functional expression that has to push exactly one stack element Statement parseExpression(); - std::map getInstructions(); + std::map const& instructions(); Statement parseElementaryOperation(bool _onlySinglePusher = false); VariableDeclaration parseVariableDeclaration(); FunctionalInstruction parseFunctionalInstruction(Statement&& _instruction); -- cgit From f62e269115822f791a0ad2e3699815f0725a0ef7 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 25 Jan 2017 17:29:06 +0100 Subject: Disallow instructions in assignment. --- libsolidity/inlineasm/AsmParser.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 048d916d..bed90139 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -101,6 +101,8 @@ assembly::Statement Parser::parseStatement() { // functional assignment FunctionalAssignment funAss = createWithLocation(identifier.location); + if (instructions().count(identifier.name)) + fatalParserError("Cannot use instruction names for identifier names."); m_scanner->next(); funAss.variableName = identifier; funAss.value.reset(new Statement(parseExpression())); @@ -156,8 +158,6 @@ std::map const& Parser::instructions() assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) { - map const& s_instructions = instructions(); - Statement ret; switch (m_scanner->currentToken()) { @@ -176,9 +176,9 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) else literal = m_scanner->currentLiteral(); // first search the set of instructions. - if (s_instructions.count(literal)) + if (instructions().count(literal)) { - dev::solidity::Instruction const& instr = s_instructions.at(literal); + dev::solidity::Instruction const& instr = instructions().at(literal); if (_onlySinglePusher) { InstructionInfo info = dev::solidity::instructionInfo(instr); @@ -210,11 +210,10 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) assembly::VariableDeclaration Parser::parseVariableDeclaration() { - map const& s_instructions = instructions(); VariableDeclaration varDecl = createWithLocation(); expectToken(Token::Let); varDecl.name = m_scanner->currentLiteral(); - if (s_instructions.count(varDecl.name)) + if (instructions().count(varDecl.name)) fatalParserError("Cannot use instruction names for identifier names."); expectToken(Token::Identifier); expectToken(Token::Colon); -- cgit From 525758a130941c6f1dfde8a0f884a550e7a7bb50 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 26 Jan 2017 13:40:40 +0100 Subject: Disallow assignment to non-identifiers. --- libsolidity/inlineasm/AsmParser.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'libsolidity') diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index bed90139..7ddc6d04 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -71,6 +71,8 @@ assembly::Statement Parser::parseStatement() expectToken(Token::Colon); assignment.variableName.location = location(); assignment.variableName.name = m_scanner->currentLiteral(); + if (instructions().count(assignment.variableName.name)) + fatalParserError("Identifier expected."); assignment.location.end = endPosition(); expectToken(Token::Identifier); return assignment; -- cgit From 873f2dddd635cb246af4368b9950123fb36d9b28 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 26 Jan 2017 13:52:02 +0100 Subject: Update error message. --- libsolidity/inlineasm/AsmParser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libsolidity') diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 7ddc6d04..fcc92dbb 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -72,7 +72,7 @@ assembly::Statement Parser::parseStatement() assignment.variableName.location = location(); assignment.variableName.name = m_scanner->currentLiteral(); if (instructions().count(assignment.variableName.name)) - fatalParserError("Identifier expected."); + fatalParserError("Identifier expected, got instruction name."); assignment.location.end = endPosition(); expectToken(Token::Identifier); return assignment; -- cgit From fcf483ee6b284f7e6d6f3c7f593cd95fb298da0d Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 17 Jan 2017 12:02:01 +0000 Subject: Add option to store literal sources in metadata --- libsolidity/interface/CompilerStack.cpp | 13 +++++++++---- libsolidity/interface/CompilerStack.h | 2 ++ 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index b26bd501..3335c40e 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -726,10 +726,15 @@ string CompilerStack::createOnChainMetadata(Contract const& _contract) const solAssert(s.second.scanner, "Scanner not available"); meta["sources"][s.first]["keccak256"] = "0x" + toHex(dev::keccak256(s.second.scanner->source()).asBytes()); - meta["sources"][s.first]["urls"] = Json::arrayValue; - meta["sources"][s.first]["urls"].append( - "bzzr://" + toHex(dev::swarmHash(s.second.scanner->source()).asBytes()) - ); + if (m_metadataLiteralSources) + meta["sources"][s.first]["content"] = s.second.scanner->source(); + else + { + meta["sources"][s.first]["urls"] = Json::arrayValue; + meta["sources"][s.first]["urls"].append( + "bzzr://" + toHex(dev::swarmHash(s.second.scanner->source()).asBytes()) + ); + } } meta["settings"]["optimizer"]["enabled"] = m_optimize; meta["settings"]["optimizer"]["runs"] = m_optimizeRuns; diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 61edc284..9ee70215 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -177,6 +177,7 @@ public: /// Can be one of 4 types defined at @c DocumentationType Json::Value const& metadata(std::string const& _contractName, DocumentationType _type) const; std::string const& onChainMetadata(std::string const& _contractName) const; + void useMetadataLiteralSources(bool _metadataLiteralSources) { m_metadataLiteralSources = _metadataLiteralSources; } /// @returns the previously used scanner, useful for counting lines during error reporting. Scanner const& scanner(std::string const& _sourceName = "") const; @@ -274,6 +275,7 @@ private: std::map m_contracts; std::string m_formalTranslation; ErrorList m_errors; + bool m_metadataLiteralSources = false; }; } -- cgit From 9bcbd93ac59a19320fd56e27c58a6283f2450666 Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Sun, 22 Jan 2017 20:49:12 +0100 Subject: Change translation of implicit throws (issue #1589). This adds a new invalid instruction that is used for encoding implicit throws that are emitted by the compiler. This makes it possible to distinguish such runtime errors from user-provided, explicit throws. --- libsolidity/codegen/ArrayUtils.cpp | 2 +- libsolidity/codegen/CompilerContext.cpp | 12 ++++++++++++ libsolidity/codegen/CompilerContext.h | 4 ++++ libsolidity/codegen/CompilerUtils.cpp | 6 +++--- libsolidity/codegen/ContractCompiler.cpp | 8 +++++--- libsolidity/codegen/ExpressionCompiler.cpp | 12 ++++++------ 6 files changed, 31 insertions(+), 13 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 4d100d1d..bdd29abd 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -901,7 +901,7 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType, bool _doBoundsCheck) c // check out-of-bounds access m_context << Instruction::DUP2 << Instruction::LT << Instruction::ISZERO; // out-of-bounds access throws exception - m_context.appendConditionalJumpTo(m_context.errorTag()); + m_context.appendConditionalInvalid(); } if (location == DataLocation::CallData && _arrayType.isDynamicallySized()) // remove length if present diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index e26f96e8..45450350 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -215,6 +215,18 @@ CompilerContext& CompilerContext::appendJump(eth::AssemblyItem::JumpType _jumpTy return *this << item; } +CompilerContext& CompilerContext::appendInvalid() +{ + return *this << Instruction::INVALID; +} + +CompilerContext& CompilerContext::appendConditionalInvalid() +{ + eth::AssemblyItem falseTag = appendConditionalJump(); + eth::AssemblyItem endTag = appendJumpToNew(); + return *this << falseTag << Instruction::INVALID << endTag; +} + void CompilerContext::resetVisitedNodes(ASTNode const* _node) { stack newStack; diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index f024b010..58d6cb2a 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -127,6 +127,10 @@ public: eth::AssemblyItem appendJumpToNew() { return m_asm->appendJump().tag(); } /// Appends a JUMP to a tag already on the stack CompilerContext& appendJump(eth::AssemblyItem::JumpType _jumpType = eth::AssemblyItem::JumpType::Ordinary); + /// Appends an INVALID instruction + CompilerContext& appendInvalid(); + /// Appends a conditional INVALID instruction + CompilerContext& appendConditionalInvalid(); /// Returns an "ErrorTag" eth::AssemblyItem errorTag() { return m_asm->errorTag(); } /// Appends a JUMP to a specific tag diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index caf3b1ac..67877bbf 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -468,7 +468,7 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp EnumType const& enumType = dynamic_cast(_typeOnStack); solAssert(enumType.numberOfMembers() > 0, "empty enum should have caused a parser error."); m_context << u256(enumType.numberOfMembers() - 1) << Instruction::DUP2 << Instruction::GT; - m_context.appendConditionalJumpTo(m_context.errorTag()); + m_context.appendConditionalInvalid(); enumOverflowCheckPending = false; } break; @@ -497,7 +497,7 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp EnumType const& enumType = dynamic_cast(_targetType); solAssert(enumType.numberOfMembers() > 0, "empty enum should have caused a parser error."); m_context << u256(enumType.numberOfMembers() - 1) << Instruction::DUP2 << Instruction::GT; - m_context.appendConditionalJumpTo(m_context.errorTag()); + m_context.appendConditionalInvalid(); enumOverflowCheckPending = false; } else if (targetTypeCategory == Type::Category::FixedPoint) @@ -807,7 +807,7 @@ void CompilerUtils::pushZeroValue(Type const& _type) { if (funType->location() == FunctionType::Location::Internal) { - m_context << m_context.errorTag(); + m_context.appendInvalid(); return; } } diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 9dc1fb37..56d03a05 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -106,7 +106,7 @@ void ContractCompiler::appendCallValueCheck() { // Throw if function is not payable but call contained ether. m_context << Instruction::CALLVALUE; - m_context.appendConditionalJumpTo(m_context.errorTag()); + m_context.appendConditionalInvalid(); } void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _contract) @@ -271,7 +271,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac appendReturnValuePacker(FunctionType(*fallback).returnParameterTypes(), _contract.isLibrary()); } else - m_context.appendJumpTo(m_context.errorTag()); + m_context.appendInvalid(); for (auto const& it: interfaceFunctions) { @@ -918,7 +918,9 @@ eth::AssemblyPointer ContractCompiler::cloneRuntime() a << Instruction::DELEGATECALL; //Propagate error condition (if DELEGATECALL pushes 0 on stack). a << Instruction::ISZERO; - a.appendJumpI(a.errorTag()); + eth::AssemblyItem falseTag = a.appendJumpI(); + eth::AssemblyItem endTag = a.appendJump().tag(); + a << falseTag << Instruction::INVALID << endTag; //@todo adjust for larger return values, make this dynamic. a << u256(0x20) << u256(0) << Instruction::RETURN; return make_shared(a); diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index bda4e04d..b66a3e12 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -585,7 +585,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << Instruction::CREATE; // Check if zero (out of stack or not enough balance). m_context << Instruction::DUP1 << Instruction::ISZERO; - m_context.appendConditionalJumpTo(m_context.errorTag()); + m_context.appendConditionalInvalid(); if (function.valueSet()) m_context << swapInstruction(1) << Instruction::POP; break; @@ -1234,7 +1234,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) m_context << u256(fixedBytesType.numBytes()); m_context << Instruction::DUP2 << Instruction::LT << Instruction::ISZERO; // out-of-bounds access throws exception - m_context.appendConditionalJumpTo(m_context.errorTag()); + m_context.appendConditionalInvalid(); m_context << Instruction::BYTE; m_context << (u256(1) << (256 - 8)) << Instruction::MUL; @@ -1416,7 +1416,7 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Ty { // Test for division by zero m_context << Instruction::DUP2 << Instruction::ISZERO; - m_context.appendConditionalJumpTo(m_context.errorTag()); + m_context.appendConditionalInvalid(); if (_operator == Token::Div) m_context << (c_isSigned ? Instruction::SDIV : Instruction::DIV); @@ -1477,7 +1477,7 @@ void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator, Type co if (c_amountSigned) { m_context << u256(0) << Instruction::DUP3 << Instruction::SLT; - m_context.appendConditionalJumpTo(m_context.errorTag()); + m_context.appendConditionalInvalid(); } switch (_operator) @@ -1663,7 +1663,7 @@ void ExpressionCompiler::appendExternalFunctionCall( if (funKind == FunctionKind::External || funKind == FunctionKind::CallCode || funKind == FunctionKind::DelegateCall) { m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO; - m_context.appendConditionalJumpTo(m_context.errorTag()); + m_context.appendConditionalInvalid(); existenceChecked = true; } @@ -1699,7 +1699,7 @@ void ExpressionCompiler::appendExternalFunctionCall( { //Propagate error condition (if CALL pushes 0 on stack). m_context << Instruction::ISZERO; - m_context.appendConditionalJumpTo(m_context.errorTag()); + m_context.appendConditionalInvalid(); } utils().popStackSlots(remainsSize); -- cgit From 5b7cc018f0b256fb42f7bee38ad8d1ec4e2ec634 Mon Sep 17 00:00:00 2001 From: Valentin Wüstholz Date: Mon, 23 Jan 2017 10:46:50 +0100 Subject: Address feedback from code review. --- libsolidity/codegen/CompilerContext.cpp | 6 +++--- libsolidity/codegen/ContractCompiler.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 45450350..3bb6c953 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -222,9 +222,9 @@ CompilerContext& CompilerContext::appendInvalid() CompilerContext& CompilerContext::appendConditionalInvalid() { - eth::AssemblyItem falseTag = appendConditionalJump(); - eth::AssemblyItem endTag = appendJumpToNew(); - return *this << falseTag << Instruction::INVALID << endTag; + *this << Instruction::ISZERO; + eth::AssemblyItem afterTag = appendConditionalJump(); + return *this << Instruction::INVALID << afterTag; } void CompilerContext::resetVisitedNodes(ASTNode const* _node) diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 56d03a05..3ca2f375 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -918,9 +918,9 @@ eth::AssemblyPointer ContractCompiler::cloneRuntime() a << Instruction::DELEGATECALL; //Propagate error condition (if DELEGATECALL pushes 0 on stack). a << Instruction::ISZERO; - eth::AssemblyItem falseTag = a.appendJumpI(); - eth::AssemblyItem endTag = a.appendJump().tag(); - a << falseTag << Instruction::INVALID << endTag; + a << Instruction::ISZERO; + eth::AssemblyItem afterTag = a.appendJumpI(); + a << Instruction::INVALID << afterTag; //@todo adjust for larger return values, make this dynamic. a << u256(0x20) << u256(0) << Instruction::RETURN; return make_shared(a); -- cgit From 390bebaaf9e2af51c7e2f72337d1e7b23f51486a Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 26 Jan 2017 15:59:48 +0100 Subject: Split line. --- libsolidity/codegen/CompilerContext.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'libsolidity') diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 3bb6c953..7577a606 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -224,7 +224,9 @@ CompilerContext& CompilerContext::appendConditionalInvalid() { *this << Instruction::ISZERO; eth::AssemblyItem afterTag = appendConditionalJump(); - return *this << Instruction::INVALID << afterTag; + *this << Instruction::INVALID; + *this << afterTag; + return *this; } void CompilerContext::resetVisitedNodes(ASTNode const* _node) -- cgit From d9fbb83861153499b4aec5525db85ec59445abd1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 26 Jan 2017 16:35:51 +0100 Subject: Allow inserting low-level functions without calling them. --- libsolidity/codegen/CompilerContext.cpp | 21 ++++++++++++++++----- libsolidity/codegen/CompilerContext.h | 10 ++++++++++ 2 files changed, 26 insertions(+), 5 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 7577a606..a8316109 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -70,19 +70,30 @@ void CompilerContext::callLowLevelFunction( eth::AssemblyItem retTag = pushNewTag(); CompilerUtils(*this).moveIntoStack(_inArgs); + *this << lowLevelFunctionTag(_name, _inArgs, _outArgs, _generator); + + appendJump(eth::AssemblyItem::JumpType::IntoFunction); + adjustStackOffset(int(_outArgs) - 1 - _inArgs); + *this << retTag.tag(); +} + +eth::AssemblyItem CompilerContext::lowLevelFunctionTag( + string const& _name, + unsigned _inArgs, + unsigned _outArgs, + function const& _generator +) +{ auto it = m_lowLevelFunctions.find(_name); if (it == m_lowLevelFunctions.end()) { eth::AssemblyItem tag = newTag().pushTag(); m_lowLevelFunctions.insert(make_pair(_name, tag)); m_lowLevelFunctionGenerationQueue.push(make_tuple(_name, _inArgs, _outArgs, _generator)); - *this << tag; + return tag; } else - *this << it->second; - appendJump(eth::AssemblyItem::JumpType::IntoFunction); - adjustStackOffset(int(_outArgs) - 1 - _inArgs); - *this << retTag.tag(); + return it->second; } void CompilerContext::appendMissingLowLevelFunctions() diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 58d6cb2a..c37142c9 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -104,6 +104,16 @@ public: unsigned _outArgs, std::function const& _generator ); + /// Returns the tag of the named low-level function and inserts the generator into the + /// list of low-level-functions to be generated, unless it already exists. + /// Note that the generator should not assume that objects are still alive when it is called, + /// unless they are guaranteed to be alive for the whole run of the compiler (AST nodes, for example). + eth::AssemblyItem lowLevelFunctionTag( + std::string const& _name, + unsigned _inArgs, + unsigned _outArgs, + std::function const& _generator + ); /// Generates the code for missing low-level functions, i.e. calls the generators passed above. void appendMissingLowLevelFunctions(); -- cgit From a98fa41897950b84b0217c9ce3c79c20009d0c8d Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 26 Jan 2017 16:24:03 +0100 Subject: Uninitialized internal function should call INVALID. --- libsolidity/codegen/CompilerUtils.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'libsolidity') diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 67877bbf..477f021a 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -807,7 +807,9 @@ void CompilerUtils::pushZeroValue(Type const& _type) { if (funType->location() == FunctionType::Location::Internal) { - m_context.appendInvalid(); + m_context << m_context.lowLevelFunctionTag("$invalidFunction", 0, 0, [](CompilerContext& _context) { + _context.appendInvalid(); + }); return; } } -- cgit From a9c6ff4ac8c271aaada1965894b34933b662c044 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 26 Jan 2017 18:20:54 +0100 Subject: Refactor json return type generation. --- libsolidity/ast/Types.cpp | 18 ----------- libsolidity/ast/Types.h | 2 -- libsolidity/interface/InterfaceHandler.cpp | 48 +++++++++++++++++------------- libsolidity/interface/InterfaceHandler.h | 10 +++++++ 4 files changed, 38 insertions(+), 40 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 971e1f18..dbabc8db 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2512,24 +2512,6 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound) ); } -vector const FunctionType::parameterTypeNames(bool _addDataLocation) const -{ - vector names; - for (TypePointer const& t: parameterTypes()) - names.push_back(t->canonicalName(_addDataLocation)); - - return names; -} - -vector const FunctionType::returnParameterTypeNames(bool _addDataLocation) const -{ - vector names; - for (TypePointer const& t: m_returnParameterTypes) - names.push_back(t->canonicalName(_addDataLocation)); - - return names; -} - TypePointer const& FunctionType::selfType() const { solAssert(bound(), "Function is not bound."); diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 770cbb30..a5147f17 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -915,10 +915,8 @@ public: TypePointers parameterTypes() const; std::vector parameterNames() const; - std::vector const parameterTypeNames(bool _addDataLocation) const; TypePointers const& returnParameterTypes() const { return m_returnParameterTypes; } std::vector const& returnParameterNames() const { return m_returnParameterNames; } - std::vector const returnParameterTypeNames(bool _addDataLocation) const; /// @returns the "self" parameter type for a bound function TypePointer const& selfType() const; diff --git a/libsolidity/interface/InterfaceHandler.cpp b/libsolidity/interface/InterfaceHandler.cpp index 9944bb22..6c1bb0c4 100644 --- a/libsolidity/interface/InterfaceHandler.cpp +++ b/libsolidity/interface/InterfaceHandler.cpp @@ -30,20 +30,6 @@ Json::Value InterfaceHandler::abiInterface(ContractDefinition const& _contractDe { Json::Value abi(Json::arrayValue); - auto populateParameters = [](vector const& _paramNames, vector const& _paramTypes) - { - Json::Value params(Json::arrayValue); - solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match"); - for (unsigned i = 0; i < _paramNames.size(); ++i) - { - Json::Value param; - param["name"] = _paramNames[i]; - param["type"] = _paramTypes[i]; - params.append(param); - } - return params; - }; - for (auto it: _contractDef.interfaceFunctions()) { auto externalFunctionType = it.second->interfaceFunctionType(); @@ -52,13 +38,15 @@ Json::Value InterfaceHandler::abiInterface(ContractDefinition const& _contractDe method["name"] = it.second->declaration().name(); method["constant"] = it.second->isConstant(); method["payable"] = it.second->isPayable(); - method["inputs"] = populateParameters( + method["inputs"] = formatTypeList( externalFunctionType->parameterNames(), - externalFunctionType->parameterTypeNames(_contractDef.isLibrary()) + externalFunctionType->parameterTypes(), + _contractDef.isLibrary() ); - method["outputs"] = populateParameters( + method["outputs"] = formatTypeList( externalFunctionType->returnParameterNames(), - externalFunctionType->returnParameterTypeNames(_contractDef.isLibrary()) + externalFunctionType->returnParameterTypes(), + _contractDef.isLibrary() ); abi.append(method); } @@ -69,9 +57,10 @@ Json::Value InterfaceHandler::abiInterface(ContractDefinition const& _contractDe auto externalFunction = FunctionType(*_contractDef.constructor(), false).interfaceFunctionType(); solAssert(!!externalFunction, ""); method["payable"] = externalFunction->isPayable(); - method["inputs"] = populateParameters( + method["inputs"] = formatTypeList( externalFunction->parameterNames(), - externalFunction->parameterTypeNames(_contractDef.isLibrary()) + externalFunction->parameterTypes(), + _contractDef.isLibrary() ); abi.append(method); } @@ -179,6 +168,25 @@ Json::Value InterfaceHandler::devDocumentation(ContractDefinition const& _contra return doc; } +Json::Value InterfaceHandler::formatTypeList( + vector const& _names, + vector const& _types, + bool _forLibrary +) +{ + Json::Value params(Json::arrayValue); + solAssert(_names.size() == _types.size(), "Names and types vector size does not match"); + for (unsigned i = 0; i < _names.size(); ++i) + { + solAssert(_types[i], ""); + Json::Value param; + param["name"] = _names[i]; + param["type"] = _types[i]->canonicalName(_forLibrary); + params.append(param); + } + return params; +} + string InterfaceHandler::extractDoc(multimap const& _tags, string const& _name) { string value; diff --git a/libsolidity/interface/InterfaceHandler.h b/libsolidity/interface/InterfaceHandler.h index b7e1bb00..56927d44 100644 --- a/libsolidity/interface/InterfaceHandler.h +++ b/libsolidity/interface/InterfaceHandler.h @@ -37,6 +37,8 @@ namespace solidity // Forward declarations class ContractDefinition; +class Type; +using TypePointer = std::shared_ptr; struct DocTag; enum class DocumentationType: uint8_t; @@ -84,6 +86,14 @@ public: static Json::Value devDocumentation(ContractDefinition const& _contractDef); private: + /// @returns a json value suitable for a list of types in function input or output + /// parameters or other places. If @a _forLibrary is true, complex types are referenced + /// by name, otherwise they are anonymously expanded. + static Json::Value formatTypeList( + std::vector const& _names, + std::vector const& _types, + bool _forLibrary + ); /// @returns concatenation of all content under the given tag name. static std::string extractDoc(std::multimap const& _tags, std::string const& _name); }; -- cgit From cc7834f2a96e305bba3c7470271560dbf99d17f9 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 27 Jan 2017 00:00:05 +0100 Subject: Doc tags followed by newline are now parsed properly --- libsolidity/parsing/DocStringParser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libsolidity') diff --git a/libsolidity/parsing/DocStringParser.cpp b/libsolidity/parsing/DocStringParser.cpp index 5d9a75ef..481a3349 100644 --- a/libsolidity/parsing/DocStringParser.cpp +++ b/libsolidity/parsing/DocStringParser.cpp @@ -31,8 +31,8 @@ static inline string::const_iterator firstWsOrNl( string::const_iterator _end ) { + auto nlPos = find(_pos, _end, '\n'); auto wsPos = firstSpaceOrTab(_pos, _end); - auto nlPos = find(wsPos, _end, '\n'); return (wsPos < nlPos) ? wsPos : nlPos; } -- cgit From 98b51b378e42ba67d727c0d2245314cdaab2fd53 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 27 Jan 2017 00:09:00 +0100 Subject: More verbose function naming --- libsolidity/parsing/DocStringParser.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/parsing/DocStringParser.cpp b/libsolidity/parsing/DocStringParser.cpp index 481a3349..c2af82de 100644 --- a/libsolidity/parsing/DocStringParser.cpp +++ b/libsolidity/parsing/DocStringParser.cpp @@ -26,7 +26,7 @@ static inline string::const_iterator firstSpaceOrTab( return (spacePos < tabPos) ? spacePos : tabPos; } -static inline string::const_iterator firstWsOrNl( +static inline string::const_iterator firstWhitespaceOrNewline( string::const_iterator _pos, string::const_iterator _end ) @@ -66,7 +66,7 @@ bool DocStringParser::parse(string const& _docString, ErrorList& _errors) if (tagPos != end && tagPos < nlPos) { // we found a tag - auto tagNameEndPos = firstWsOrNl(tagPos, end); + auto tagNameEndPos = firstWhitespaceOrNewline(tagPos, end); if (tagNameEndPos == end) { appendError("End of tag " + string(tagPos, tagNameEndPos) + "not found"); -- cgit From bff8fc23e6cc602511b52aaa665e63b948eba068 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 27 Jan 2017 10:18:53 +0100 Subject: Changelog and review suggestions. --- libsolidity/codegen/ContractCompiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libsolidity') diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 3ca2f375..4d33927d 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -919,7 +919,7 @@ eth::AssemblyPointer ContractCompiler::cloneRuntime() //Propagate error condition (if DELEGATECALL pushes 0 on stack). a << Instruction::ISZERO; a << Instruction::ISZERO; - eth::AssemblyItem afterTag = a.appendJumpI(); + eth::AssemblyItem afterTag = a.appendJumpI().tag(); a << Instruction::INVALID << afterTag; //@todo adjust for larger return values, make this dynamic. a << u256(0x20) << u256(0) << Instruction::RETURN; -- cgit From 0e021e76a58d0ae7a7fe1647f079e7e5087b4641 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 27 Jan 2017 11:19:48 +0100 Subject: Minor changes. --- libsolidity/parsing/DocStringParser.cpp | 36 ++++++++++++++++----------------- 1 file changed, 17 insertions(+), 19 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/parsing/DocStringParser.cpp b/libsolidity/parsing/DocStringParser.cpp index c2af82de..8e912126 100644 --- a/libsolidity/parsing/DocStringParser.cpp +++ b/libsolidity/parsing/DocStringParser.cpp @@ -1,14 +1,19 @@ #include -#include #include +#include +#include + using namespace std; using namespace dev; using namespace dev::solidity; -static inline string::const_iterator skipLineOrEOS( +namespace +{ + +string::const_iterator skipLineOrEOS( string::const_iterator _nlPos, string::const_iterator _end ) @@ -16,38 +21,35 @@ static inline string::const_iterator skipLineOrEOS( return (_nlPos == _end) ? _end : ++_nlPos; } -static inline string::const_iterator firstSpaceOrTab( +string::const_iterator firstSpaceOrTab( string::const_iterator _pos, string::const_iterator _end ) { - auto spacePos = find(_pos, _end, ' '); - auto tabPos = find(_pos, _end, '\t'); - return (spacePos < tabPos) ? spacePos : tabPos; + return boost::range::find_first_of(make_pair(_pos, _end), " \t"); } -static inline string::const_iterator firstWhitespaceOrNewline( +string::const_iterator firstWhitespaceOrNewline( string::const_iterator _pos, string::const_iterator _end ) { - auto nlPos = find(_pos, _end, '\n'); - auto wsPos = firstSpaceOrTab(_pos, _end); - return (wsPos < nlPos) ? wsPos : nlPos; + return boost::range::find_first_of(make_pair(_pos, _end), " \t\n"); } -static inline string::const_iterator skipWhitespace( +string::const_iterator skipWhitespace( string::const_iterator _pos, string::const_iterator _end ) { auto currPos = _pos; - while ((*currPos == ' ' || *currPos == '\t') && currPos != _end) + while (currPos != _end && (*currPos == ' ' || *currPos == '\t')) currPos += 1; return currPos; } +} bool DocStringParser::parse(string const& _docString, ErrorList& _errors) { @@ -99,13 +101,9 @@ DocStringParser::iter DocStringParser::parseDocTagLine(iter _pos, iter _end, boo solAssert(!!m_lastTag, ""); auto nlPos = find(_pos, _end, '\n'); if (_appending && _pos < _end && *_pos != ' ' && *_pos != '\t') - { m_lastTag->content += " "; - } else if (!_appending) - { _pos = skipWhitespace(_pos, _end); - } copy(_pos, nlPos, back_inserter(m_lastTag->content)); return skipLineOrEOS(nlPos, _end); } @@ -116,13 +114,13 @@ DocStringParser::iter DocStringParser::parseDocTagParam(iter _pos, iter _end) auto nameStartPos = skipWhitespace(_pos, _end); if (nameStartPos == _end) { - appendError("No param name given" + string(nameStartPos, _end)); + appendError("No param name given"); return _end; } auto nameEndPos = firstSpaceOrTab(nameStartPos, _end); if (nameEndPos == _end) { - appendError("End of param name not found" + string(nameStartPos, _end)); + appendError("End of param name not found: " + string(nameStartPos, _end)); return _end; } auto paramName = string(nameStartPos, nameEndPos); @@ -130,7 +128,7 @@ DocStringParser::iter DocStringParser::parseDocTagParam(iter _pos, iter _end) auto descStartPos = skipWhitespace(nameEndPos, _end); if (descStartPos == _end) { - appendError("No description given for param" + paramName); + appendError("No description given for param " + paramName); return _end; } -- cgit