diff options
author | chriseth <chris@ethereum.org> | 2017-07-27 17:07:15 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-07-27 17:07:15 +0800 |
commit | 1298a8df14bd3dd6495aa38413208fa77781d4fd (patch) | |
tree | 90fe3173fc7716789b47474cfb4053e4b90dd752 | |
parent | 16ca1eea78d476de3fd52ebdd9dcfb6fa5610aa6 (diff) | |
parent | 35feb6d47ce143c625187a95f54563fa456aa3f5 (diff) | |
download | dexon-solidity-1298a8df14bd3dd6495aa38413208fa77781d4fd.tar.gz dexon-solidity-1298a8df14bd3dd6495aa38413208fa77781d4fd.tar.zst dexon-solidity-1298a8df14bd3dd6495aa38413208fa77781d4fd.zip |
Merge pull request #2566 from ethereum/metadata-only-relevant
Metadata: only include relevant files in the source list
-rw-r--r-- | Changelog.md | 1 | ||||
-rw-r--r-- | libdevcore/CommonData.h | 6 | ||||
-rw-r--r-- | libsolidity/ast/AST.cpp | 26 | ||||
-rw-r--r-- | libsolidity/ast/AST.h | 6 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.cpp | 9 | ||||
-rw-r--r-- | test/libsolidity/Metadata.cpp | 67 |
6 files changed, 113 insertions, 2 deletions
diff --git a/Changelog.md b/Changelog.md index 8a475e4d..e765b583 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Features: * C API (``jsonCompiler``): Export the ``license`` method. * Inline Assembly: Show useful error message if trying to access calldata variables. * Inline Assembly: Support variable declaration without initial value (defaults to 0). + * Metadata: Only include files which were used to compile the given contract. * Type Checker: Disallow value transfers to contracts without a payable fallback function. * Type Checker: Include types in explicit conversion error message. * Type Checker: Raise proper error for arrays too large for ABI encoding. diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index 4297f606..ab4bfe68 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -166,6 +166,12 @@ template <class T, class U> std::vector<T>& operator+=(std::vector<T>& _a, U con _a.push_back(i); return _a; } +/// Concatenate the contents of a container onto a set +template <class T, class U> std::set<T>& operator+=(std::set<T>& _a, U const& _b) +{ + _a.insert(_b.begin(), _b.end()); + return _a; +} /// Concatenate two vectors of elements. template <class T> inline std::vector<T> operator+(std::vector<T> const& _a, std::vector<T> const& _b) diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 724a908f..ebc8bd48 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -84,13 +84,35 @@ SourceUnitAnnotation& SourceUnit::annotation() const return dynamic_cast<SourceUnitAnnotation&>(*m_annotation); } -string Declaration::sourceUnitName() const +set<SourceUnit const*> SourceUnit::referencedSourceUnits(bool _recurse, set<SourceUnit const*> _skipList) const +{ + set<SourceUnit const*> sourceUnits; + for (ImportDirective const* importDirective: filteredNodes<ImportDirective>(nodes())) + { + auto const& sourceUnit = importDirective->annotation().sourceUnit; + if (!_skipList.count(sourceUnit)) + { + _skipList.insert(sourceUnit); + sourceUnits.insert(sourceUnit); + if (_recurse) + sourceUnits += sourceUnit->referencedSourceUnits(true, _skipList); + } + } + return sourceUnits; +} + +SourceUnit const& Declaration::sourceUnit() const { solAssert(!!m_scope, ""); ASTNode const* scope = m_scope; while (dynamic_cast<Declaration const*>(scope) && dynamic_cast<Declaration const*>(scope)->m_scope) scope = dynamic_cast<Declaration const*>(scope)->m_scope; - return dynamic_cast<SourceUnit const&>(*scope).annotation().path; + return dynamic_cast<SourceUnit const&>(*scope); +} + +string Declaration::sourceUnitName() const +{ + return sourceUnit().annotation().path; } ImportAnnotation& ImportDirective::annotation() const diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 81ddc754..8012bcb4 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -136,6 +136,9 @@ public: std::vector<ASTPointer<ASTNode>> nodes() const { return m_nodes; } + /// @returns a set of referenced SourceUnits. Recursively if @a _recurse is true. + std::set<SourceUnit const*> referencedSourceUnits(bool _recurse = false, std::set<SourceUnit const*> _skipList = std::set<SourceUnit const*>()) const; + private: std::vector<ASTPointer<ASTNode>> m_nodes; }; @@ -168,6 +171,9 @@ public: ASTNode const* scope() const { return m_scope; } void setScope(ASTNode const* _scope) { m_scope = _scope; } + /// @returns the source unit this declaration is present in. + SourceUnit const& sourceUnit() const; + /// @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; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index d5a4e554..f06dd4d7 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -753,9 +753,18 @@ string CompilerStack::createMetadata(Contract const& _contract) const meta["language"] = "Solidity"; meta["compiler"]["version"] = VersionStringStrict; + /// All the source files (including self), which should be included in the metadata. + set<string> referencedSources; + referencedSources.insert(_contract.contract->sourceUnit().annotation().path); + for (auto const sourceUnit: _contract.contract->sourceUnit().referencedSourceUnits(true)) + referencedSources.insert(sourceUnit->annotation().path); + meta["sources"] = Json::objectValue; for (auto const& s: m_sources) { + if (!referencedSources.count(s.first)) + continue; + solAssert(s.second.scanner, "Scanner not available"); meta["sources"][s.first]["keccak256"] = "0x" + toHex(dev::keccak256(s.second.scanner->source()).asBytes()); diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp index e4820ad2..30de7908 100644 --- a/test/libsolidity/Metadata.cpp +++ b/test/libsolidity/Metadata.cpp @@ -58,6 +58,73 @@ BOOST_AUTO_TEST_CASE(metadata_stamp) BOOST_CHECK(std::equal(expectation.begin(), expectation.end(), bytecode.end() - metadataCBORSize - 2)); } +BOOST_AUTO_TEST_CASE(metadata_relevant_sources) +{ + CompilerStack compilerStack; + char const* sourceCode = R"( + pragma solidity >=0.0; + contract A { + function g(function(uint) external returns (uint) x) {} + } + )"; + compilerStack.addSource("A", std::string(sourceCode)); + sourceCode = R"( + pragma solidity >=0.0; + contract B { + function g(function(uint) external returns (uint) x) {} + } + )"; + compilerStack.addSource("B", std::string(sourceCode)); + ETH_TEST_REQUIRE_NO_THROW(compilerStack.compile(dev::test::Options::get().optimize), "Compiling contract failed"); + + std::string const& serialisedMetadata = compilerStack.metadata("A"); + BOOST_CHECK(dev::test::isValidMetadata(serialisedMetadata)); + Json::Value metadata; + BOOST_REQUIRE(Json::Reader().parse(serialisedMetadata, metadata, false)); + + BOOST_CHECK_EQUAL(metadata["sources"].size(), 1); + BOOST_CHECK(metadata["sources"].isMember("A")); +} + +BOOST_AUTO_TEST_CASE(metadata_relevant_sources_imports) +{ + CompilerStack compilerStack; + char const* sourceCode = R"( + pragma solidity >=0.0; + contract A { + function g(function(uint) external returns (uint) x) {} + } + )"; + compilerStack.addSource("A", std::string(sourceCode)); + sourceCode = R"( + pragma solidity >=0.0; + import "./A"; + contract B is A { + function g(function(uint) external returns (uint) x) {} + } + )"; + compilerStack.addSource("B", std::string(sourceCode)); + sourceCode = R"( + pragma solidity >=0.0; + import "./B"; + contract C is B { + function g(function(uint) external returns (uint) x) {} + } + )"; + compilerStack.addSource("C", std::string(sourceCode)); + ETH_TEST_REQUIRE_NO_THROW(compilerStack.compile(dev::test::Options::get().optimize), "Compiling contract failed"); + + std::string const& serialisedMetadata = compilerStack.metadata("C"); + BOOST_CHECK(dev::test::isValidMetadata(serialisedMetadata)); + Json::Value metadata; + BOOST_REQUIRE(Json::Reader().parse(serialisedMetadata, metadata, false)); + + BOOST_CHECK_EQUAL(metadata["sources"].size(), 3); + BOOST_CHECK(metadata["sources"].isMember("A")); + BOOST_CHECK(metadata["sources"].isMember("B")); + BOOST_CHECK(metadata["sources"].isMember("C")); +} + BOOST_AUTO_TEST_SUITE_END() } |