From cca832e56a115b0c9714069871f707eae88d3a28 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Fri, 5 Jun 2020 16:31:35 +0200 Subject: [PATCH] WIP-Friday --- libsolidity/CMakeLists.txt | 2 + libsolidity/ast/AST.cpp | 6 + libsolidity/ast/AST.h | 8 +- libsolidity/ast/ASTJsonImporter.h | 16 ++ libsolidity/ast/JsonInterfaceImporter.cpp | 198 ++++++++++++++++++ libsolidity/ast/JsonInterfaceImporter.h | 93 ++++++++ libsolidity/interface/CompilerStack.cpp | 21 +- libsolidity/parsing/Parser.cpp | 2 +- libsolidity/parsing/Parser.h | 2 +- .../parsing/interface_imported.sol | 7 + .../parsing/interface_imported_I.json | 142 +++++++++++++ 11 files changed, 486 insertions(+), 11 deletions(-) create mode 100644 libsolidity/ast/JsonInterfaceImporter.cpp create mode 100644 libsolidity/ast/JsonInterfaceImporter.h create mode 100644 test/libsolidity/syntaxTests/parsing/interface_imported.sol create mode 100644 test/libsolidity/syntaxTests/parsing/interface_imported_I.json diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 8e930b6ab..8926be3b4 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -53,6 +53,8 @@ set(sources ast/ASTJsonImporter.h ast/ASTVisitor.h ast/ExperimentalFeatures.h + ast/JsonInterfaceImporter.cpp + ast/JsonInterfaceImporter.h ast/Types.cpp ast/Types.h ast/TypeProvider.cpp diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 9bc5c030d..785adf268 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -231,6 +231,12 @@ vector, FunctionTypePointer>> const& ContractDefinition: }); } +TypePointer ImportedContractDefinition::type() const +{ + return {}; + // TODO: Think about whether this type needs to be specified or returning {} is sufficient. +} + TypePointer ContractDefinition::type() const { return TypeProvider::typeType(TypeProvider::contract(*this)); diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 5e88872cc..8f59e33ce 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -454,7 +454,12 @@ protected: /// @} /** - * TODO: docstring + * Definition of an imported interface declaration. + * + * This is not the actually imported interface but the reference to it. + * It will be resolved by CompilerStack to add any missing interface definitions + * by looking for any ImportedContractDefinition in the ASTs and append them - embeded into + * SourceUnit's - to its collection of sources. */ class ImportedContractDefinition: public Declaration // TODO(needed?), public StructurallyDocumented { @@ -471,6 +476,7 @@ public: void accept(ASTVisitor& _visitor) override; void accept(ASTConstVisitor& _visitor) const override; + TypePointer type() const override; ASTPointer const& path() const noexcept { return m_path; } diff --git a/libsolidity/ast/ASTJsonImporter.h b/libsolidity/ast/ASTJsonImporter.h index c5ab44380..21d7bde9c 100644 --- a/libsolidity/ast/ASTJsonImporter.h +++ b/libsolidity/ast/ASTJsonImporter.h @@ -50,7 +50,23 @@ public: ASTPointer jsonToContract(Json::Value const& _sourceList); + ASTPointer jsonToInterface( + int64_t _nodeId, + langutil::SourceLocation const& _location, + std::string const& _name, + Json::Value const& _source + ); + private: + ASTPointer createInterfaceMember( + langutil::SourceLocation const& _location, + Json::Value const& _node + ); + + ASTPointer createInterfaceParameters( + langutil::SourceLocation const& _location, + Json::Value const& _array + ); // =========== general creation functions ============== diff --git a/libsolidity/ast/JsonInterfaceImporter.cpp b/libsolidity/ast/JsonInterfaceImporter.cpp new file mode 100644 index 000000000..159ddbd1a --- /dev/null +++ b/libsolidity/ast/JsonInterfaceImporter.cpp @@ -0,0 +1,198 @@ +#include + +using namespace std; + +namespace solidity::frontend +{ + +ASTPointer JsonInterfaceImporter::importInterfaceAsSourceUnit( + langutil::SourceLocation const& _location, + std::optional const& _license, + std::string const& _name, + Json::Value const& _source +) +{ + auto interface = importInterface(_location, _name, _source); + return make_shared( + m_nextId++, + _location, + _license, + vector>{ interface } + ); +} + +ASTPointer JsonInterfaceImporter::importInterface( + langutil::SourceLocation const& _location, + string const& _name, + Json::Value const& _source +) +{ + // TODO: contractInheritanceDefinition + // TODO: "anything in ContractDefintion that returns a vector of FunctionDefinitons needs to be refactored to return a base class of FunctionDefiniton such that the imported interface can also return something that is not directly a FunctionDefinition" + + vector> members; + for (auto const& jsonMember: _source) + if (auto member = createMember(_location, jsonMember); member != nullptr) + members.push_back(member); + + return make_shared( + m_nextId++, + _location, + make_shared(_name), + ASTPointer{}, + vector>{}, // TODO: see contractInheritanceDefinition + members, + ContractKind::Interface, + false + ); +} + +ASTPointer JsonInterfaceImporter::createMember( + langutil::SourceLocation const& _location, + Json::Value const& _node +) +{ + if (_node["type"] == "function") + return createFunction(_location, _node); + else if (_node["type"] == "event") + return createEvent(_location, _node); + else + { + // TODO: report error of invalid type. Use m_errorReporter for that. + return nullptr; + } +} + +ASTPointer JsonInterfaceImporter::createFunction( + langutil::SourceLocation const& _location, + Json::Value const& _node +) +{ + auto const mutability = stateMutability(_node["stateMutability"]); + auto const name = _node["name"].asString(); + auto const inputs = createParameters(_location, false, _node["inputs"]); + auto const outputs = createParameters(_location, false, _node["outputs"]); + auto const kind = Token::Function; + + return createASTNode( + _location, + make_shared(name), + Visibility::Default, + mutability, + kind, + true, // interface functions are always virtual + nullptr, // overrides + nullptr, // documentation + inputs, + vector>{}, + outputs, + nullptr // no body + ); +} + +ASTPointer JsonInterfaceImporter::createEvent( + langutil::SourceLocation const& _location, + Json::Value const& _node +) +{ + bool const anonymous = _node["anonymous"].asBool(); + auto inputs = createParameters(_location, true, _node["inputs"]); + auto const name = _node["name"].asString(); + + return createASTNode( + _location, + make_shared(name), + ASTPointer{}, + inputs, + anonymous + ); +} + +ASTPointer JsonInterfaceImporter::createParameters( + langutil::SourceLocation const& _location, + bool _indexed, + Json::Value const& _node +) +{ + vector> parameters; + + for (auto& param: _node) + parameters.push_back(createParameter(_location, _indexed, param)); + + return createASTNode(_location, parameters); +} + +ASTPointer JsonInterfaceImporter::createParameter( + langutil::SourceLocation const& _location, + bool _indexed, + Json::Value const& _node +) +{ + // "indexed" (only when event) + auto const parameterName = _node["name"].asString(); + auto const typeName = _node["type"].asString(); + ASTPointer type = {}; // TODO (get me from typeName) + + return createASTNode( + _location, + type, + make_shared(parameterName), + ASTPointer{}, // value + Visibility::Default, + ASTPointer{}, // documentation + false, // isStateVariable + _indexed, + VariableDeclaration::Mutability::Mutable, + ASTPointer{} + ); + // TODO + // return createASTNode( + // _location, + // nullOrCast(member(_node, "typeName")), + // make_shared(member(_node, "name").asString()), + // nullOrCast(member(_node, "value")), + // visibility(_node), + // _node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")), + // memberAsBool(_node, "stateVariable"), + // _node.isMember("indexed") ? memberAsBool(_node, "indexed") : false, + // mutability, + // _node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")), + // location(_node) + // ); +} + +StateMutability JsonInterfaceImporter::stateMutability(Json::Value const& _node) const +{ + // TODO: see that we can avoid duplication with ASTJsonImporter + astAssert(member(_node, "stateMutability").isString(), "StateMutability' expected to be string."); + string const mutabilityStr = member(_node, "stateMutability").asString(); + + if (mutabilityStr == "pure") + return StateMutability::Pure; + else if (mutabilityStr == "view") + return StateMutability::View; + else if (mutabilityStr == "nonpayable") + return StateMutability::NonPayable; + else if (mutabilityStr == "payable") + return StateMutability::Payable; + else + astAssert(false, "Unknown stateMutability"); +} + +Json::Value JsonInterfaceImporter::member(Json::Value const& _node, string const& _name) const +{ + // TODO: see that we can avoid duplication with ASTJsonImporter + astAssert(_node.isMember(_name), "Node '" + _node["nodeType"].asString() + "' (id " + _node["id"].asString() + ") is missing field '" + _name + "'."); + return _node[_name]; +} + +template +ASTPointer JsonInterfaceImporter::createASTNode( + langutil::SourceLocation const& _location, + Args&&... _args +) +{ + return make_shared(m_nextId++, _location, forward(_args)...); +} + +} // end namespace diff --git a/libsolidity/ast/JsonInterfaceImporter.h b/libsolidity/ast/JsonInterfaceImporter.h new file mode 100644 index 000000000..e9183e969 --- /dev/null +++ b/libsolidity/ast/JsonInterfaceImporter.h @@ -0,0 +1,93 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +#pragma once + +#include +#include + +#include + +#include +#include + +namespace solidity::frontend +{ + +class JsonInterfaceImporter +{ +public: + JsonInterfaceImporter(int64_t _nextId): + m_nextId{ _nextId } + {} + + ASTPointer importInterfaceAsSourceUnit( + langutil::SourceLocation const& _location, + std::optional const& _licenseString, + std::string const& _name, + Json::Value const& _source + ); + + ASTPointer importInterface( + langutil::SourceLocation const& _location, + std::string const& _name, + Json::Value const& _source + ); + +private: + ASTPointer createMember( + langutil::SourceLocation const& _location, + Json::Value const& _node + ); + + ASTPointer createFunction( + langutil::SourceLocation const& _location, + Json::Value const& _node + ); + + ASTPointer createEvent( + langutil::SourceLocation const& _location, + Json::Value const& _node + ); + + ASTPointer createParameters( + langutil::SourceLocation const& _location, + bool _indexed, + Json::Value const& _node + ); + + ASTPointer createParameter( + langutil::SourceLocation const& _location, + bool _indexed, + Json::Value const& _node + ); + +private: // helper functions (TODO: that could be shared with ASTJsonImporter) + StateMutability stateMutability(Json::Value const& _node) const; + + Json::Value member(Json::Value const& _node, std::string const& _name) const; + + template + ASTPointer createASTNode( + langutil::SourceLocation const& _location, + Args&&... _args + ); + +private: + int64_t m_nextId; +}; + +} // end namespace diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index fe4a68c16..1388d442a 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -276,7 +277,9 @@ bool CompilerStack::parse() void CompilerStack::loadMissingInterfaces(int64_t _baseNodeID) { + JsonInterfaceImporter interfaceImporter{_baseNodeID}; vector> importedSources; + for (auto const& sourcePair: m_sources) { ASTPointer const& source = sourcePair.second.ast; @@ -297,15 +300,17 @@ void CompilerStack::loadMissingInterfaces(int64_t _baseNodeID) string const& jsonString = result.responseOrErrorMessage; Json::Value json; util::jsonParseStrict(jsonString, json, nullptr); - ASTPointer jsonContract = ASTJsonImporter{m_evmVersion}.jsonToContract(json); - auto importedSource = make_shared( - _baseNodeID++, - SourceLocation{}, - source->licenseString(), - vector>{ jsonContract } - ); - importedSources.push_back(importedSource); + ASTPointer importedSource = + interfaceImporter.importInterfaceAsSourceUnit( + importedContract->location(), + source->licenseString(), + importedContract->name(), + json + ); + + if (importedSource != nullptr) + importedSources.push_back(importedSource); } else { diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 8b519d2b3..ade37addf 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -292,7 +292,7 @@ std::pair Parser::parseContractKind() return std::make_pair(kind, abstract); } -ASTPointer Parser::parseInterfaceDefinition() +ASTPointer Parser::parseInterfaceDefinition() { // interfaceDefinition // : 'interface' identifier 'from' StringLiteralFragment diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index a1cab3283..e31270c75 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -91,7 +91,7 @@ private: std::pair parseContractKind(); ASTPointer parseContractDefinition(); std::vector> parseContractInheritanceList(); - ASTPointer parseInterfaceDefinition(); + ASTPointer parseInterfaceDefinition(); std::vector> parseContractBody(); ASTPointer parseInheritanceSpecifier(); Visibility parseVisibilitySpecifier(); diff --git a/test/libsolidity/syntaxTests/parsing/interface_imported.sol b/test/libsolidity/syntaxTests/parsing/interface_imported.sol new file mode 100644 index 000000000..7ab121a83 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/interface_imported.sol @@ -0,0 +1,7 @@ +interface I from "interface_imported_I.json"; + +contract C { + function f() public pure { + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/parsing/interface_imported_I.json b/test/libsolidity/syntaxTests/parsing/interface_imported_I.json new file mode 100644 index 000000000..6ea6f4daf --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/interface_imported_I.json @@ -0,0 +1,142 @@ +[ + { + "state_mutability": "view", + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "type": "function" + }, + { + "state_mutability": "view", + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "supply", + "type": "uint256" + } + ], + "type": "function" + }, + { + "state_mutability": "payable", + "inputs": [ + { + "name": "_from", + "type": "address" + }, + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "type": "function" + }, + { + "state_mutability": "view", + "inputs": [ + { + "name": "_owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "balance", + "type": "uint256" + } + ], + "type": "function" + }, + { + "state_mutability": "payable", + "inputs": [ + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_from", + "type": "address" + }, + { + "indexed": true, + "name": "_to", + "type": "address" + }, + { + "indexed": false, + "name": "_value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_owner", + "type": "address" + }, + { + "indexed": true, + "name": "_spender", + "type": "address" + }, + { + "indexed": false, + "name": "_value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + } +]