diff options
30 files changed, 873 insertions, 583 deletions
diff --git a/Changelog.md b/Changelog.md index 9673a057..2073c0a4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,4 +1,5 @@ ### 0.4.12 (unreleased) + * AST: export all attributes to Json format Bugfixes: * Unused variable warnings no longer issued for variables used inside inline assembly diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index d6c30db8..dc7c6cc9 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -35,7 +35,7 @@ Solidity version 0.4.0 or anything newer that does not break functionality (up to, but not including, version 0.5.0). This is to ensure that the contract does not suddenly behave differently with a new compiler version. The keyword ``pragma`` is called that way because, in general, pragmas are instructions for the compiler about how to treat the -source code (e.g. [pragma once](https://en.wikipedia.org/wiki/Pragma_once)). . +source code (e.g. `pragma once <https://en.wikipedia.org/wiki/Pragma_once>`_). . A contract in the sense of Solidity is a collection of code (its *functions*) and data (its *state*) that resides at a specific address on the Ethereum @@ -126,7 +126,7 @@ get the idea - the compiler figures that out for you. The next line, ``mapping (address => uint) public balances;`` also creates a public state variable, but it is a more complex datatype. The type maps addresses to unsigned integers. -Mappings can be seen as [hash tables](https://en.wikipedia.org/wiki/Hash_table) which are +Mappings can be seen as `hash tables <https://en.wikipedia.org/wiki/Hash_table>`_ which are virtually initialized such that every possible key exists and is mapped to a value whose byte-representation is all zeros. This analogy does not go too far, though, as it is neither possible to obtain a list of all keys of @@ -195,7 +195,7 @@ Blockchain Basics ***************** Blockchains as a concept are not too hard to understand for programmers. The reason is that -most of the complications (mining, [hashing](https://en.wikipedia.org/wiki/Cryptographic_hash_function), [elliptic-curve cryptography](https://en.wikipedia.org/wiki/Elliptic_curve_cryptography), [peer-to-peer networks](https://en.wikipedia.org/wiki/Peer-to-peer), etc.) +most of the complications (mining, `hashing <https://en.wikipedia.org/wiki/Cryptographic_hash_function>`_, `elliptic-curve cryptography <https://en.wikipedia.org/wiki/Elliptic_curve_cryptography>`_, `peer-to-peer networks <https://en.wikipedia.org/wiki/Peer-to-peer>`_, etc.) are just there to provide a certain set of features and promises. Once you accept these features as given, you do not have to worry about the underlying technology - or do you have to know how Amazon's AWS works internally in order to use it? @@ -438,7 +438,7 @@ that maps all the way up to the block level. This feature called **logs** is used by Solidity in order to implement **events**. Contracts cannot access log data after it has been created, but they can be efficiently accessed from outside the blockchain. -Since some part of the log data is stored in [bloom filters](https://en.wikipedia.org/wiki/Bloom_filter), it is +Since some part of the log data is stored in `bloom filters <https://en.wikipedia.org/wiki/Bloom_filter>`_, it is possible to search for this data in an efficient and cryptographically secure way, so network peers that do not download the whole blockchain ("light clients") can still find these logs. diff --git a/libjulia/backends/AbstractAssembly.h b/libjulia/backends/AbstractAssembly.h new file mode 100644 index 00000000..e3afa2b6 --- /dev/null +++ b/libjulia/backends/AbstractAssembly.h @@ -0,0 +1,89 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ +/** + * @date 2017 + * Abstract assembly interface, subclasses of which are to be used with the generic + * bytecode generator. + */ + +#pragma once + +#include <libdevcore/CommonData.h> + +#include <functional> + +namespace dev +{ +struct SourceLocation; +namespace solidity +{ +enum class Instruction: uint8_t; +namespace assembly +{ +struct Instruction; +struct Identifier; +} +} +namespace julia +{ + +class AbstractAssembly +{ +public: + virtual ~AbstractAssembly() {} + + /// Set a new source location valid starting from the next instruction. + virtual void setSourceLocation(SourceLocation const& _location) = 0; + /// Retrieve the current height of the stack. This does not have to be zero + /// at the beginning. + virtual int stackHeight() const = 0; + /// Append an EVM instruction. + virtual void appendInstruction(solidity::Instruction _instruction) = 0; + /// Append a constant. + virtual void appendConstant(u256 const& _constant) = 0; + /// Append a label. + virtual void appendLabel(size_t _labelId) = 0; + /// Append a label reference. + virtual void appendLabelReference(size_t _labelId) = 0; + /// Generate a new unique label. + virtual size_t newLabelId() = 0; + /// Append a reference to a to-be-linked symobl. + /// Currently, we assume that the value is always a 20 byte number. + virtual void appendLinkerSymbol(std::string const& _name) = 0; +}; + +enum class IdentifierContext { LValue, RValue }; + +/// Object that is used to resolve references and generate code for access to identifiers external +/// to inline assembly (not used in standalone assembly mode). +struct ExternalIdentifierAccess +{ + using Resolver = std::function<size_t(solidity::assembly::Identifier const&, IdentifierContext)>; + /// Resolve a an external reference given by the identifier in the given context. + /// @returns the size of the value (number of stack slots) or size_t(-1) if not found. + Resolver resolve; + using CodeGenerator = std::function<void(solidity::assembly::Identifier const&, IdentifierContext, julia::AbstractAssembly&)>; + /// Generate code for retrieving the value (rvalue context) or storing the value (lvalue context) + /// of an identifier. The code should be appended to the assembly. In rvalue context, the value is supposed + /// to be put onto the stack, in lvalue context, the value is assumed to be at the top of the stack. + CodeGenerator generateCode; +}; + + + +} +} diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 38cdc1f8..8161a3a1 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1238,13 +1238,17 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) if (auto const* typeType = dynamic_cast<TypeType const*>(expressionType.get())) { - _functionCall.annotation().isStructConstructorCall = (typeType->actualType()->category() == Type::Category::Struct); - _functionCall.annotation().isTypeConversion = !_functionCall.annotation().isStructConstructorCall; + if (typeType->actualType()->category() == Type::Category::Struct) + _functionCall.annotation().kind = FunctionCallKind::StructConstructorCall; + else + _functionCall.annotation().kind = FunctionCallKind::TypeConversion; + } else - _functionCall.annotation().isStructConstructorCall = _functionCall.annotation().isTypeConversion = false; + _functionCall.annotation().kind = FunctionCallKind::FunctionCall; + solAssert(_functionCall.annotation().kind != FunctionCallKind::Unset, ""); - if (_functionCall.annotation().isTypeConversion) + if (_functionCall.annotation().kind == FunctionCallKind::TypeConversion) { TypeType const& t = dynamic_cast<TypeType const&>(*expressionType); TypePointer resultType = t.actualType(); @@ -1274,7 +1278,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) /// For error message: Struct members that were removed during conversion to memory. set<string> membersRemovedForStructConstructor; - if (_functionCall.annotation().isStructConstructorCall) + if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall) { TypeType const& t = dynamic_cast<TypeType const&>(*expressionType); auto const& structType = dynamic_cast<StructType const&>(*t.actualType()); @@ -1312,7 +1316,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) toString(parameterTypes.size()) + "."; // Extend error message in case we try to construct a struct with mapping member. - if (_functionCall.annotation().isStructConstructorCall && !membersRemovedForStructConstructor.empty()) + if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall && !membersRemovedForStructConstructor.empty()) { msg += " Members that have to be skipped in memory:"; for (auto const& member: membersRemovedForStructConstructor) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 02234ffc..71b81a69 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -583,8 +583,7 @@ public: bool isPayable() const { return m_isPayable; } std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; } std::vector<ASTPointer<VariableDeclaration>> const& returnParameters() const { return m_returnParameters->parameters(); } - Block const& body() const { return *m_body; } - + Block const& body() const { solAssert(m_body, ""); return *m_body; } virtual bool isVisibleInContract() const override { return Declaration::isVisibleInContract() && !isConstructor() && !name().empty(); @@ -1314,7 +1313,7 @@ private: /** * Tuple, parenthesized expression, or bracketed expression. - * Examples: (1, 2), (x,), (x), (), [1, 2], + * Examples: (1, 2), (x,), (x), (), [1, 2], * Individual components might be empty shared pointers (as in the second example). * The respective types in lvalue context are: 2-tuple, 2-tuple (with wildcard), type of x, 0-tuple * Not in lvalue context: 2-tuple, _1_-tuple, type of x, 0-tuple. @@ -1327,8 +1326,8 @@ public: std::vector<ASTPointer<Expression>> const& _components, bool _isArray ): - Expression(_location), - m_components(_components), + Expression(_location), + m_components(_components), m_isArray(_isArray) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index a7d89248..45a6dd1a 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -200,12 +200,17 @@ struct BinaryOperationAnnotation: ExpressionAnnotation TypePointer commonType; }; +enum class FunctionCallKind +{ + Unset, + FunctionCall, + TypeConversion, + StructConstructorCall +}; + struct FunctionCallAnnotation: ExpressionAnnotation { - /// Whether this is an explicit type conversion. - bool isTypeConversion = false; - /// Whether this is a struct constructor call. - bool isStructConstructorCall = false; + FunctionCallKind kind = FunctionCallKind::Unset; }; } diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 9ea23687..40c552a3 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -25,6 +25,8 @@ #include <libdevcore/UTF8.h> #include <libsolidity/ast/AST.h> #include <libsolidity/interface/Exceptions.h> +#include <libsolidity/inlineasm/AsmData.h> +#include <libsolidity/inlineasm/AsmPrinter.h> using namespace std; @@ -33,49 +35,86 @@ namespace dev namespace solidity { -void ASTJsonConverter::addJsonNode( +ASTJsonConverter::ASTJsonConverter(bool _legacy, map<string, unsigned> _sourceIndices): + m_legacy(_legacy), + m_sourceIndices(_sourceIndices) +{ +} + + +void ASTJsonConverter::setJsonNode( ASTNode const& _node, string const& _nodeName, - initializer_list<pair<string const, Json::Value const>> _attributes, - bool _hasChildren = false + initializer_list<pair<string, Json::Value>>&& _attributes ) { - ASTJsonConverter::addJsonNode( + ASTJsonConverter::setJsonNode( _node, _nodeName, - std::vector<pair<string const, Json::Value const>>(_attributes), - _hasChildren + std::vector<pair<string, Json::Value>>(std::move(_attributes)) ); } - -void ASTJsonConverter::addJsonNode( + +void ASTJsonConverter::setJsonNode( ASTNode const& _node, - string const& _nodeName, - std::vector<pair<string const, Json::Value const>> const& _attributes, - bool _hasChildren = false + string const& _nodeType, + std::vector<pair<string, Json::Value>>&& _attributes ) { - Json::Value node; - - node["id"] = Json::UInt64(_node.id()); - node["src"] = sourceLocationToString(_node.location()); - node["name"] = _nodeName; - if (_attributes.size() != 0) + m_currentValue = Json::objectValue; + m_currentValue["id"] = nodeId(_node); + m_currentValue["src"] = sourceLocationToString(_node.location()); + if (!m_legacy) { - Json::Value attrs; + m_currentValue["nodeType"] = _nodeType; for (auto& e: _attributes) - attrs[e.first] = e.second; - node["attributes"] = attrs; + m_currentValue[e.first] = std::move(e.second); } - - m_jsonNodePtrs.top()->append(node); - - if (_hasChildren) + else { - Json::Value& addedNode = (*m_jsonNodePtrs.top())[m_jsonNodePtrs.top()->size() - 1]; - Json::Value children(Json::arrayValue); - addedNode["children"] = children; - m_jsonNodePtrs.push(&addedNode["children"]); + m_currentValue["name"] = _nodeType; + Json::Value attrs(Json::objectValue); + if ( + //these nodeTypes need to have a children-node even if it is empty + (_nodeType == "VariableDeclaration") || + (_nodeType == "ParameterList") || + (_nodeType == "Block") || + (_nodeType == "InlineAssembly") || + (_nodeType == "Throw") + ) + { + Json::Value children(Json::arrayValue); + m_currentValue["children"] = children; + } + + for (auto& e: _attributes) + { + if ( + (!e.second.isNull()) && + ( + (e.second.isObject() && e.second.isMember("name")) || + (e.second.isArray() && e.second[0].isObject() && e.second[0].isMember("name")) || + (e.first == "declarations") // (in the case (_,x)= ... there's a nullpointer at [0] + ) + ) + { + if (e.second.isObject()) + m_currentValue["children"].append(std::move(e.second)); + if (e.second.isArray()) + for (auto& child: e.second) + if (!child.isNull()) + m_currentValue["children"].append(std::move(child)); + } + else + { + if (e.first == "typeDescriptions") + attrs["type"] = Json::Value(e.second["typeString"]); + else + attrs[e.first] = std::move(e.second); + } + } + if (!attrs.empty()) + m_currentValue["attributes"] = std::move(attrs); } } @@ -90,34 +129,89 @@ string ASTJsonConverter::sourceLocationToString(SourceLocation const& _location) return std::to_string(_location.start) + ":" + std::to_string(length) + ":" + std::to_string(sourceIndex); } -ASTJsonConverter::ASTJsonConverter( - ASTNode const& _ast, - map<string, unsigned> _sourceIndices -): m_ast(&_ast), m_sourceIndices(_sourceIndices) +string ASTJsonConverter::namePathToString(std::vector<ASTString> const& _namePath) const { + return boost::algorithm::join(_namePath, "."); } -void ASTJsonConverter::print(ostream& _stream) +Json::Value ASTJsonConverter::typePointerToJson(TypePointer _tp) { - process(); - _stream << m_astJson; + Json::Value typeDescriptions(Json::objectValue); + typeDescriptions["typeString"] = _tp ? Json::Value(_tp->toString()) : Json::nullValue; + typeDescriptions["typeIdentifier"] = _tp ? Json::Value(_tp->identifier()) : Json::nullValue; + return typeDescriptions; + +} +Json::Value ASTJsonConverter::typePointerToJson(std::shared_ptr<std::vector<TypePointer>> _tps) +{ + if (_tps) + { + Json::Value arguments(Json::arrayValue); + for (auto const& tp: *_tps) + arguments.append(typePointerToJson(tp)); + return arguments; + } + else + return Json::nullValue; } -Json::Value const& ASTJsonConverter::json() +void ASTJsonConverter::appendExpressionAttributes( + std::vector<pair<string, Json::Value>>& _attributes, + ExpressionAnnotation const& _annotation +) { - process(); - return m_astJson; + std::vector<pair<string, Json::Value>> exprAttributes = { + make_pair("typeDescriptions", typePointerToJson(_annotation.type)), + make_pair("isConstant", _annotation.isConstant), + make_pair("isPure", _annotation.isPure), + make_pair("isLValue", _annotation.isLValue), + make_pair("lValueRequested", _annotation.lValueRequested), + make_pair("argumentTypes", typePointerToJson(_annotation.argumentTypes)) + }; + _attributes += exprAttributes; } -bool ASTJsonConverter::visit(SourceUnit const&) +Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pair<assembly::Identifier const* ,InlineAssemblyAnnotation::ExternalIdentifierInfo> _info) { - Json::Value children(Json::arrayValue); + Json::Value tuple(Json::objectValue); + tuple["src"] = sourceLocationToString(_info.first->location); + tuple["declaration"] = idOrNull(_info.second.declaration); + tuple["isSlot"] = Json::Value(_info.second.isSlot); + tuple["isOffset"] = Json::Value(_info.second.isOffset); + tuple["valueSize"] = _info.second.valueSize; + return tuple; +} - m_astJson["name"] = "SourceUnit"; - m_astJson["children"] = children; - m_jsonNodePtrs.push(&m_astJson["children"]); +void ASTJsonConverter::print(ostream& _stream, ASTNode const& _node) +{ + _stream << toJson(_node); +} - return true; +Json::Value ASTJsonConverter::toJson(ASTNode const& _node) +{ + _node.accept(*this); + return std::move(m_currentValue); +} + +bool ASTJsonConverter::visit(SourceUnit const& _node) +{ + Json::Value exportedSymbols = Json::objectValue; + for (auto const& sym: _node.annotation().exportedSymbols) + { + exportedSymbols[sym.first] = Json::arrayValue; + for (Declaration const* overload: sym.second) + exportedSymbols[sym.first].append(nodeId(*overload)); + } + setJsonNode( + _node, + "SourceUnit", + { + make_pair("absolutePath", _node.annotation().path), + make_pair("exportedSymbols", move(exportedSymbols)), + make_pair("nodes", toJson(_node.nodes())) + } + ); + return false; } bool ASTJsonConverter::visit(PragmaDirective const& _node) @@ -125,313 +219,487 @@ bool ASTJsonConverter::visit(PragmaDirective const& _node) Json::Value literals(Json::arrayValue); for (auto const& literal: _node.literals()) literals.append(literal); - addJsonNode(_node, "PragmaDirective", { make_pair("literals", literals) }); - return true; + setJsonNode( _node, "PragmaDirective", { + make_pair("literals", std::move(literals)) + }); + return false; } bool ASTJsonConverter::visit(ImportDirective const& _node) { - addJsonNode(_node, "ImportDirective", { make_pair("file", _node.path())}); - return true; + std::vector<pair<string, Json::Value>> attributes = { + make_pair("file", _node.path()), + make_pair("absolutePath", _node.annotation().absolutePath), + make_pair(m_legacy ? "SourceUnit" : "sourceUnit", nodeId(*_node.annotation().sourceUnit)), + make_pair("scope", idOrNull(_node.scope())) + }; + attributes.push_back(make_pair("unitAlias", _node.name())); + Json::Value symbolAliases(Json::arrayValue); + for (auto const& symbolAlias: _node.symbolAliases()) + { + Json::Value tuple(Json::objectValue); + solAssert(symbolAlias.first, ""); + tuple["foreign"] = nodeId(*symbolAlias.first); + tuple["local"] = symbolAlias.second ? Json::Value(*symbolAlias.second) : Json::nullValue; + symbolAliases.append(tuple); + } + attributes.push_back(make_pair("symbolAliases", std::move(symbolAliases))); + setJsonNode(_node, "ImportDirective", std::move(attributes)); + return false; } bool ASTJsonConverter::visit(ContractDefinition const& _node) { - Json::Value linearizedBaseContracts(Json::arrayValue); - for (auto const& baseContract: _node.annotation().linearizedBaseContracts) - linearizedBaseContracts.append(Json::UInt64(baseContract->id())); - addJsonNode(_node, "ContractDefinition", { + setJsonNode(_node, "ContractDefinition", { make_pair("name", _node.name()), - make_pair("isLibrary", _node.isLibrary()), + make_pair("contractKind", contractKind(_node.contractKind())), make_pair("fullyImplemented", _node.annotation().isFullyImplemented), - make_pair("linearizedBaseContracts", linearizedBaseContracts), - }, true); - return true; + make_pair("linearizedBaseContracts", getContainerIds(_node.annotation().linearizedBaseContracts)), + make_pair("baseContracts", toJson(_node.baseContracts())), + make_pair("contractDependencies", getContainerIds(_node.annotation().contractDependencies)), + make_pair("nodes", toJson(_node.subNodes())), + make_pair("scope", idOrNull(_node.scope())) + }); + return false; } bool ASTJsonConverter::visit(InheritanceSpecifier const& _node) { - addJsonNode(_node, "InheritanceSpecifier", {}, true); - return true; + setJsonNode(_node, "InheritanceSpecifier", { + make_pair("baseName", toJson(_node.name())), + make_pair("arguments", toJson(_node.arguments())) + }); + return false; } bool ASTJsonConverter::visit(UsingForDirective const& _node) { - addJsonNode(_node, "UsingForDirective", {}, true); - return true; + setJsonNode(_node, "UsingForDirective", { + make_pair("libraryName", toJson(_node.libraryName())), + make_pair("typeName", _node.typeName() ? toJson(*_node.typeName()) : Json::nullValue) + }); + return false; } bool ASTJsonConverter::visit(StructDefinition const& _node) { - addJsonNode(_node, "StructDefinition", { make_pair("name", _node.name()) }, true); - return true; + setJsonNode(_node, "StructDefinition", { + make_pair("name", _node.name()), + make_pair("visibility", visibility(_node.visibility())), + make_pair("canonicalName", _node.annotation().canonicalName), + make_pair("members", toJson(_node.members())), + make_pair("scope", idOrNull(_node.scope())) + }); + return false; } bool ASTJsonConverter::visit(EnumDefinition const& _node) { - addJsonNode(_node, "EnumDefinition", { make_pair("name", _node.name()) }, true); - return true; + setJsonNode(_node, "EnumDefinition", { + make_pair("name", _node.name()), + make_pair("canonicalName", _node.annotation().canonicalName), + make_pair("members", toJson(_node.members())) + }); + return false; } bool ASTJsonConverter::visit(EnumValue const& _node) { - addJsonNode(_node, "EnumValue", { make_pair("name", _node.name()) }); - return true; + setJsonNode(_node, "EnumValue", { + make_pair("name", _node.name()) + }); + return false; } bool ASTJsonConverter::visit(ParameterList const& _node) { - addJsonNode(_node, "ParameterList", {}, true); - return true; + setJsonNode(_node, "ParameterList", { + make_pair("parameters", toJson(_node.parameters())) + }); + return false; } bool ASTJsonConverter::visit(FunctionDefinition const& _node) { - addJsonNode(_node, "FunctionDefinition", { + std::vector<pair<string, Json::Value>> attributes = { make_pair("name", _node.name()), - make_pair("constant", _node.isDeclaredConst()), + make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.isDeclaredConst()), make_pair("payable", _node.isPayable()), - make_pair("visibility", visibility(_node.visibility())) - }, true); - return true; + make_pair("visibility", visibility(_node.visibility())), + make_pair("parameters", toJson(_node.parameterList())), + make_pair("isConstructor", _node.isConstructor()), + make_pair("returnParameters", toJson(*_node.returnParameterList())), + make_pair("modifiers", toJson(_node.modifiers())), + make_pair("body", _node.isImplemented() ? toJson(_node.body()) : Json::nullValue), + make_pair("implemented", _node.isImplemented()), + make_pair("scope", idOrNull(_node.scope())) + }; + setJsonNode(_node, "FunctionDefinition", std::move(attributes)); + return false; } bool ASTJsonConverter::visit(VariableDeclaration const& _node) { - std::vector<pair<string const, Json::Value const>> attributes = { + std::vector<pair<string, Json::Value>> attributes = { make_pair("name", _node.name()), - make_pair("type", type(_node)), + make_pair("typeName", toJsonOrNull(_node.typeName())), make_pair("constant", _node.isConstant()), + make_pair("stateVariable", _node.isStateVariable()), make_pair("storageLocation", location(_node.referenceLocation())), - make_pair("visibility", visibility(_node.visibility())) - }; + make_pair("visibility", visibility(_node.visibility())), + make_pair("value", _node.value() ? toJson(*_node.value()) : Json::nullValue), + make_pair("scope", idOrNull(_node.scope())), + make_pair("typeDescriptions", typePointerToJson(_node.annotation().type)) + }; if (m_inEvent) attributes.push_back(make_pair("indexed", _node.isIndexed())); - addJsonNode(_node, "VariableDeclaration", attributes, true); - return true; - + setJsonNode(_node, "VariableDeclaration", std::move(attributes)); + return false; } bool ASTJsonConverter::visit(ModifierDefinition const& _node) { - addJsonNode(_node, "ModifierDefinition", { make_pair("name", _node.name()) }, true); - return true; + setJsonNode(_node, "ModifierDefinition", { + make_pair("name", _node.name()), + make_pair("visibility", visibility(_node.visibility())), + make_pair("parameters", toJson(_node.parameterList())), + make_pair("body", toJson(_node.body())) + }); + return false; } bool ASTJsonConverter::visit(ModifierInvocation const& _node) { - addJsonNode(_node, "ModifierInvocation", {}, true); - return true; + setJsonNode(_node, "ModifierInvocation", { + make_pair("modifierName", toJson(*_node.name())), + make_pair("arguments", toJson(_node.arguments())) + }); + return false; } bool ASTJsonConverter::visit(TypeName const&) { - return true; + solAssert(false, "AST node of abstract type used."); + return false; } bool ASTJsonConverter::visit(EventDefinition const& _node) { m_inEvent = true; - addJsonNode(_node, "EventDefinition", { make_pair("name", _node.name()) }, true); - return true; + setJsonNode(_node, "EventDefinition", { + make_pair("name", _node.name()), + make_pair("parameters", toJson(_node.parameterList())), + make_pair("anonymous", _node.isAnonymous()) + }); + return false; } bool ASTJsonConverter::visit(ElementaryTypeName const& _node) { - addJsonNode(_node, "ElementaryTypeName", { make_pair("name", _node.typeName().toString()) }); - return true; + setJsonNode(_node, "ElementaryTypeName", { + make_pair("name", _node.typeName().toString()), + make_pair("typeDescriptions", typePointerToJson(_node.annotation().type)) + }); + return false; } bool ASTJsonConverter::visit(UserDefinedTypeName const& _node) { - addJsonNode(_node, "UserDefinedTypeName", { - make_pair("name", boost::algorithm::join(_node.namePath(), ".")) + setJsonNode(_node, "UserDefinedTypeName", { + make_pair("name", namePathToString(_node.namePath())), + make_pair("referencedDeclaration", idOrNull(_node.annotation().referencedDeclaration)), + make_pair("contractScope", idOrNull(_node.annotation().contractScope)), + make_pair("typeDescriptions", typePointerToJson(_node.annotation().type)) }); - return true; + return false; } bool ASTJsonConverter::visit(FunctionTypeName const& _node) { - addJsonNode(_node, "FunctionTypeName", { + setJsonNode(_node, "FunctionTypeName", { make_pair("payable", _node.isPayable()), make_pair("visibility", visibility(_node.visibility())), - make_pair("constant", _node.isDeclaredConst()) - }, true); - return true; + make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.isDeclaredConst()), + make_pair("parameterTypes", toJson(_node.parameterTypes())), + make_pair("returnParameterTypes", toJson(_node.returnParameterTypes())), + make_pair("typeDescriptions", typePointerToJson(_node.annotation().type)) + }); + return false; } bool ASTJsonConverter::visit(Mapping const& _node) { - addJsonNode(_node, "Mapping", {}, true); - return true; + setJsonNode(_node, "Mapping", { + make_pair("keyType", toJson(_node.keyType())), + make_pair("valueType", toJson(_node.valueType())), + make_pair("typeDescriptions", typePointerToJson(_node.annotation().type)) + }); + return false; } bool ASTJsonConverter::visit(ArrayTypeName const& _node) { - addJsonNode(_node, "ArrayTypeName", {}, true); - return true; + setJsonNode(_node, "ArrayTypeName", { + make_pair("baseType", toJson(_node.baseType())), + make_pair("length", toJsonOrNull(_node.length())), + make_pair("typeDescriptions", typePointerToJson(_node.annotation().type)) + }); + return false; } bool ASTJsonConverter::visit(InlineAssembly const& _node) { - addJsonNode(_node, "InlineAssembly", {}, true); - return true; + Json::Value externalReferences(Json::arrayValue); + for (auto const& it : _node.annotation().externalReferences) + { + if (it.first) + { + Json::Value tuple(Json::objectValue); + tuple[it.first->name] = inlineAssemblyIdentifierToJson(it); + externalReferences.append(tuple); + } + } + setJsonNode(_node, "InlineAssembly", { + make_pair("operations", Json::Value(assembly::AsmPrinter()(_node.operations()))), + make_pair("externalReferences", std::move(externalReferences)) + }); + return false; } bool ASTJsonConverter::visit(Block const& _node) { - addJsonNode(_node, "Block", {}, true); - return true; + setJsonNode(_node, "Block", { + make_pair("statements", toJson(_node.statements())) + }); + return false; } bool ASTJsonConverter::visit(PlaceholderStatement const& _node) { - addJsonNode(_node, "PlaceholderStatement", {}); - return true; + setJsonNode(_node, "PlaceholderStatement", {}); + return false; } bool ASTJsonConverter::visit(IfStatement const& _node) { - addJsonNode(_node, "IfStatement", {}, true); - return true; + setJsonNode(_node, "IfStatement", { + make_pair("condition", toJson(_node.condition())), + make_pair("trueBody", toJson(_node.trueStatement())), + make_pair("falseBody", toJsonOrNull(_node.falseStatement())) + }); + return false; } bool ASTJsonConverter::visit(WhileStatement const& _node) { - addJsonNode( + setJsonNode( _node, _node.isDoWhile() ? "DoWhileStatement" : "WhileStatement", - {}, - true); - return true; + { + make_pair("condition", toJson(_node.condition())), + make_pair("body", toJson(_node.body())) + } + ); + return false; } bool ASTJsonConverter::visit(ForStatement const& _node) { - addJsonNode(_node, "ForStatement", {}, true); - return true; + setJsonNode(_node, "ForStatement", { + make_pair("initializationExpression", toJsonOrNull(_node.initializationExpression())), + make_pair("condition", toJsonOrNull(_node.condition())), + make_pair("loopExpression", toJsonOrNull(_node.loopExpression())), + make_pair("body", toJson(_node.body())) + }); + return false; } bool ASTJsonConverter::visit(Continue const& _node) { - addJsonNode(_node, "Continue", {}); - return true; + setJsonNode(_node, "Continue", {}); + return false; } bool ASTJsonConverter::visit(Break const& _node) { - addJsonNode(_node, "Break", {}); - return true; + setJsonNode(_node, "Break", {}); + return false; } bool ASTJsonConverter::visit(Return const& _node) { - addJsonNode(_node, "Return", {}, true);; - return true; + setJsonNode(_node, "Return", { + make_pair("expression", toJsonOrNull(_node.expression())), + make_pair("functionReturnParameters", idOrNull(_node.annotation().functionReturnParameters)) + }); + return false; } bool ASTJsonConverter::visit(Throw const& _node) { - addJsonNode(_node, "Throw", {}, true);; - return true; + setJsonNode(_node, "Throw", {});; + return false; } bool ASTJsonConverter::visit(VariableDeclarationStatement const& _node) { - addJsonNode(_node, "VariableDeclarationStatement", {}, true); - return true; + Json::Value varDecs(Json::arrayValue); + for (auto const& v: _node.annotation().assignments) + varDecs.append(idOrNull(v)); + setJsonNode(_node, "VariableDeclarationStatement", { + make_pair("assignments", std::move(varDecs)), + make_pair("declarations", toJson(_node.declarations())), + make_pair("initialValue", toJsonOrNull(_node.initialValue())) + }); + return false; } bool ASTJsonConverter::visit(ExpressionStatement const& _node) { - addJsonNode(_node, "ExpressionStatement", {}, true); - return true; + setJsonNode(_node, "ExpressionStatement", { + make_pair("expression", toJson(_node.expression())) + }); + return false; } bool ASTJsonConverter::visit(Conditional const& _node) { - addJsonNode(_node, "Conditional", {}, true); - return true; + std::vector<pair<string, Json::Value>> attributes = { + make_pair("condition", toJson(_node.condition())), + make_pair("trueExpression", toJson(_node.trueExpression())), + make_pair("falseExpression", toJson(_node.falseExpression())) + }; + appendExpressionAttributes(attributes, _node.annotation()); + setJsonNode(_node, "Conditional", std::move(attributes)); + return false; } bool ASTJsonConverter::visit(Assignment const& _node) { - addJsonNode(_node, "Assignment", - { make_pair("operator", Token::toString(_node.assignmentOperator())), - make_pair("type", type(_node)) }, - true); - return true; + std::vector<pair<string, Json::Value>> attributes = { + make_pair("operator", Token::toString(_node.assignmentOperator())), + make_pair("leftHandSide", toJson(_node.leftHandSide())), + make_pair("rightHandSide", toJson(_node.rightHandSide())) + }; + appendExpressionAttributes(attributes, _node.annotation()); + setJsonNode( _node, "Assignment", std::move(attributes)); + return false; } bool ASTJsonConverter::visit(TupleExpression const& _node) { - addJsonNode(_node, "TupleExpression",{}, true); - return true; + std::vector<pair<string, Json::Value>> attributes = { + make_pair("isInlineArray", Json::Value(_node.isInlineArray())), + make_pair("components", toJson(_node.components())), + }; + appendExpressionAttributes(attributes, _node.annotation()); + setJsonNode(_node, "TupleExpression", std::move(attributes)); + return false; } bool ASTJsonConverter::visit(UnaryOperation const& _node) { - addJsonNode(_node, "UnaryOperation", - { make_pair("prefix", _node.isPrefixOperation()), - make_pair("operator", Token::toString(_node.getOperator())), - make_pair("type", type(_node)) }, - true); - return true; + std::vector<pair<string, Json::Value>> attributes = { + make_pair("prefix", _node.isPrefixOperation()), + make_pair("operator", Token::toString(_node.getOperator())), + make_pair("subExpression", toJson(_node.subExpression())) + }; + appendExpressionAttributes(attributes, _node.annotation()); + setJsonNode(_node, "UnaryOperation", std::move(attributes)); + return false; } bool ASTJsonConverter::visit(BinaryOperation const& _node) { - addJsonNode(_node, "BinaryOperation", { + std::vector<pair<string, Json::Value>> attributes = { make_pair("operator", Token::toString(_node.getOperator())), - make_pair("type", type(_node)) - }, true); - return true; + make_pair("leftExpression", toJson(_node.leftExpression())), + make_pair("rightExpression", toJson(_node.rightExpression())), + make_pair("commonType", typePointerToJson(_node.annotation().commonType)), + }; + appendExpressionAttributes(attributes, _node.annotation()); + setJsonNode(_node, "BinaryOperation", std::move(attributes)); + return false; } bool ASTJsonConverter::visit(FunctionCall const& _node) { - addJsonNode(_node, "FunctionCall", { - make_pair("type_conversion", _node.annotation().isTypeConversion), - make_pair("type", type(_node)) - }, true); - return true; + Json::Value names(Json::arrayValue); + for (auto const& name: _node.names()) + names.append(Json::Value(*name)); + std::vector<pair<string, Json::Value>> attributes = { + make_pair("expression", toJson(_node.expression())), + make_pair("names", std::move(names)), + make_pair("arguments", toJson(_node.arguments())) + }; + if (m_legacy) + { + attributes.push_back(make_pair("isStructConstructorCall", _node.annotation().kind == FunctionCallKind::StructConstructorCall)); + attributes.push_back(make_pair("type_conversion", _node.annotation().kind == FunctionCallKind::TypeConversion)); + } + else + attributes.push_back(make_pair("kind", functionCallKind(_node.annotation().kind))); + appendExpressionAttributes(attributes, _node.annotation()); + setJsonNode(_node, "FunctionCall", std::move(attributes)); + return false; } bool ASTJsonConverter::visit(NewExpression const& _node) { - addJsonNode(_node, "NewExpression", { make_pair("type", type(_node)) }, true); - return true; + std::vector<pair<string, Json::Value>> attributes = { + make_pair("typeName", toJson(_node.typeName())) + }; + appendExpressionAttributes(attributes, _node.annotation()); + setJsonNode(_node, "NewExpression", std::move(attributes)); + return false; } bool ASTJsonConverter::visit(MemberAccess const& _node) { - addJsonNode(_node, "MemberAccess", { - make_pair("member_name", _node.memberName()), - make_pair("type", type(_node)) - }, true); - return true; + std::vector<pair<string, Json::Value>> attributes = { + make_pair(m_legacy ? "member_name" : "memberName", _node.memberName()), + make_pair("expression", toJson(_node.expression())), + make_pair("referencedDeclaration", idOrNull(_node.annotation().referencedDeclaration)), + }; + appendExpressionAttributes(attributes, _node.annotation()); + setJsonNode(_node, "MemberAccess", std::move(attributes)); + return false; } bool ASTJsonConverter::visit(IndexAccess const& _node) { - addJsonNode(_node, "IndexAccess", { make_pair("type", type(_node)) }, true); - return true; + std::vector<pair<string, Json::Value>> attributes = { + make_pair("baseExpression", toJson(_node.baseExpression())), + make_pair("indexExpression", toJsonOrNull(_node.indexExpression())), + }; + appendExpressionAttributes(attributes, _node.annotation()); + setJsonNode(_node, "IndexAccess", std::move(attributes)); + return false; } bool ASTJsonConverter::visit(Identifier const& _node) { - addJsonNode(_node, "Identifier", - { make_pair("value", _node.name()), make_pair("type", type(_node)) }); - return true; + Json::Value overloads(Json::arrayValue); + for (auto const& dec: _node.annotation().overloadedDeclarations) + overloads.append(nodeId(*dec)); + setJsonNode(_node, "Identifier", { + make_pair(m_legacy ? "value" : "name", _node.name()), + make_pair("referencedDeclaration", idOrNull(_node.annotation().referencedDeclaration)), + make_pair("overloadedDeclarations", overloads), + make_pair("typeDescriptions", typePointerToJson(_node.annotation().type)), + make_pair("argumentTypes", typePointerToJson(_node.annotation().argumentTypes)) + }); + return false; } bool ASTJsonConverter::visit(ElementaryTypeNameExpression const& _node) { - addJsonNode(_node, "ElementaryTypeNameExpression", { - make_pair("value", _node.typeName().toString()), - make_pair("type", type(_node)) - }); - return true; + std::vector<pair<string, Json::Value>> attributes = { + make_pair(m_legacy ? "value" : "typeName", _node.typeName().toString()) + }; + appendExpressionAttributes(attributes, _node.annotation()); + setJsonNode(_node, "ElementaryTypeNameExpression", std::move(attributes)); + return false; } bool ASTJsonConverter::visit(Literal const& _node) @@ -441,240 +709,26 @@ bool ASTJsonConverter::visit(Literal const& _node) if (!dev::validateUTF8(_node.value())) value = Json::nullValue; Token::Value subdenomination = Token::Value(_node.subDenomination()); - addJsonNode(_node, "Literal", { + std::vector<pair<string, Json::Value>> attributes = { make_pair("token", tokenString ? tokenString : Json::Value()), make_pair("value", value), - make_pair("hexvalue", toHex(_node.value())), + make_pair(m_legacy ? "hexvalue" : "hexValue", toHex(_node.value())), make_pair( "subdenomination", subdenomination == Token::Illegal ? Json::nullValue : Json::Value{Token::toString(subdenomination)} - ), - make_pair("type", type(_node)) - }); - return true; -} - -void ASTJsonConverter::endVisit(SourceUnit const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(PragmaDirective const&) -{ -} - -void ASTJsonConverter::endVisit(ImportDirective const&) -{ -} - -void ASTJsonConverter::endVisit(ContractDefinition const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(InheritanceSpecifier const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(UsingForDirective const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(StructDefinition const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(EnumDefinition const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(EnumValue const&) -{ -} - -void ASTJsonConverter::endVisit(ParameterList const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(FunctionDefinition const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(VariableDeclaration const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(ModifierDefinition const&) -{ - goUp(); + ) + }; + appendExpressionAttributes(attributes, _node.annotation()); + setJsonNode(_node, "Literal", std::move(attributes)); + return false; } -void ASTJsonConverter::endVisit(ModifierInvocation const&) -{ - goUp(); -} void ASTJsonConverter::endVisit(EventDefinition const&) { m_inEvent = false; - goUp(); -} - -void ASTJsonConverter::endVisit(TypeName const&) -{ -} - -void ASTJsonConverter::endVisit(ElementaryTypeName const&) -{ -} - -void ASTJsonConverter::endVisit(UserDefinedTypeName const&) -{ -} - -void ASTJsonConverter::endVisit(FunctionTypeName const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(Mapping const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(ArrayTypeName const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(InlineAssembly const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(Block const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(PlaceholderStatement const&) -{ -} - -void ASTJsonConverter::endVisit(IfStatement const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(WhileStatement const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(ForStatement const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(Continue const&) -{ -} - -void ASTJsonConverter::endVisit(Break const&) -{ -} - -void ASTJsonConverter::endVisit(Return const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(Throw const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(VariableDeclarationStatement const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(ExpressionStatement const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(Conditional const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(Assignment const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(TupleExpression const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(UnaryOperation const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(BinaryOperation const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(FunctionCall const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(NewExpression const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(MemberAccess const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(IndexAccess const&) -{ - goUp(); -} - -void ASTJsonConverter::endVisit(Identifier const&) -{ -} - -void ASTJsonConverter::endVisit(ElementaryTypeNameExpression const&) -{ -} - -void ASTJsonConverter::endVisit(Literal const&) -{ -} - -void ASTJsonConverter::process() -{ - if (!processed) - m_ast->accept(*this); - processed = true; } string ASTJsonConverter::visibility(Declaration::Visibility const& _visibility) @@ -709,6 +763,36 @@ string ASTJsonConverter::location(VariableDeclaration::Location _location) } } +string ASTJsonConverter::contractKind(ContractDefinition::ContractKind _kind) +{ + switch (_kind) + { + case ContractDefinition::ContractKind::Interface: + return "interface"; + case ContractDefinition::ContractKind::Contract: + return "contract"; + case ContractDefinition::ContractKind::Library: + return "library"; + default: + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of contract.")); + } +} + +string ASTJsonConverter::functionCallKind(FunctionCallKind _kind) +{ + switch (_kind) + { + case FunctionCallKind::FunctionCall: + return "functionCall"; + case FunctionCallKind::TypeConversion: + return "typeConversion"; + case FunctionCallKind::StructConstructorCall: + return "structConstructorCall"; + default: + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of function call .")); + } +} + string ASTJsonConverter::type(Expression const& _expression) { return _expression.annotation().type ? _expression.annotation().type->toString() : "Unknown"; diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h index bd5e6560..5ee40536 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -42,15 +42,23 @@ class ASTJsonConverter: public ASTConstVisitor { public: /// Create a converter to JSON for the given abstract syntax tree. + /// @a _legacy if true, use legacy format /// @a _sourceIndices is used to abbreviate source names in source locations. explicit ASTJsonConverter( - ASTNode const& _ast, + bool _legacy, std::map<std::string, unsigned> _sourceIndices = std::map<std::string, unsigned>() ); /// Output the json representation of the AST to _stream. - void print(std::ostream& _stream); - Json::Value const& json(); - + void print(std::ostream& _stream, ASTNode const& _node); + Json::Value toJson(ASTNode const& _node); + template <class T> + Json::Value toJson(std::vector<ASTPointer<T>> const& _nodes) + { + Json::Value ret(Json::arrayValue); + for (auto const& n: _nodes) + ret.append(n ? toJson(*n) : Json::nullValue); + return ret; + } bool visit(SourceUnit const& _node) override; bool visit(PragmaDirective const& _node) override; bool visit(ImportDirective const& _node) override; @@ -97,82 +105,60 @@ public: bool visit(ElementaryTypeNameExpression const& _node) override; bool visit(Literal const& _node) override; - void endVisit(SourceUnit const&) override; - void endVisit(PragmaDirective const&) override; - void endVisit(ImportDirective const&) override; - void endVisit(ContractDefinition const&) override; - void endVisit(InheritanceSpecifier const&) override; - void endVisit(UsingForDirective const&) override; - void endVisit(StructDefinition const&) override; - void endVisit(EnumDefinition const&) override; - void endVisit(EnumValue const&) override; - void endVisit(ParameterList const&) override; - void endVisit(FunctionDefinition const&) override; - void endVisit(VariableDeclaration const&) override; - void endVisit(ModifierDefinition const&) override; - void endVisit(ModifierInvocation const&) override; void endVisit(EventDefinition const&) override; - void endVisit(TypeName const&) override; - void endVisit(ElementaryTypeName const&) override; - void endVisit(UserDefinedTypeName const&) override; - void endVisit(FunctionTypeName const&) override; - void endVisit(Mapping const&) override; - void endVisit(ArrayTypeName const&) override; - void endVisit(InlineAssembly const&) override; - void endVisit(Block const&) override; - void endVisit(PlaceholderStatement const&) override; - void endVisit(IfStatement const&) override; - void endVisit(WhileStatement const&) override; - void endVisit(ForStatement const&) override; - void endVisit(Continue const&) override; - void endVisit(Break const&) override; - void endVisit(Return const&) override; - void endVisit(Throw const&) override; - void endVisit(VariableDeclarationStatement const&) override; - void endVisit(ExpressionStatement const&) override; - void endVisit(Conditional const&) override; - void endVisit(Assignment const&) override; - void endVisit(TupleExpression const&) override; - void endVisit(UnaryOperation const&) override; - void endVisit(BinaryOperation const&) override; - void endVisit(FunctionCall const&) override; - void endVisit(NewExpression const&) override; - void endVisit(MemberAccess const&) override; - void endVisit(IndexAccess const&) override; - void endVisit(Identifier const&) override; - void endVisit(ElementaryTypeNameExpression const&) override; - void endVisit(Literal const&) override; private: - void process(); - void addJsonNode( + void setJsonNode( ASTNode const& _node, std::string const& _nodeName, - std::initializer_list<std::pair<std::string const, Json::Value const>> _attributes, - bool _hasChildren + std::initializer_list<std::pair<std::string, Json::Value>>&& _attributes ); - void addJsonNode( + void setJsonNode( ASTNode const& _node, std::string const& _nodeName, - std::vector<std::pair<std::string const, Json::Value const>> const& _attributes, - bool _hasChildren + std::vector<std::pair<std::string, Json::Value>>&& _attributes ); std::string sourceLocationToString(SourceLocation const& _location) const; + std::string namePathToString(std::vector<ASTString> const& _namePath) const; + Json::Value idOrNull(ASTNode const* _pt) + { + return _pt ? Json::Value(nodeId(*_pt)) : Json::nullValue; + } + Json::Value toJsonOrNull(ASTNode const* _node) + { + return _node ? toJson(*_node) : Json::nullValue; + } + Json::Value inlineAssemblyIdentifierToJson(std::pair<assembly::Identifier const* , InlineAssemblyAnnotation::ExternalIdentifierInfo> _info); std::string visibility(Declaration::Visibility const& _visibility); std::string location(VariableDeclaration::Location _location); + std::string contractKind(ContractDefinition::ContractKind _kind); + std::string functionCallKind(FunctionCallKind _kind); std::string type(Expression const& _expression); std::string type(VariableDeclaration const& _varDecl); - inline void goUp() + int nodeId(ASTNode const& _node) { - solAssert(!m_jsonNodePtrs.empty(), "Uneven json nodes stack. Internal error."); - m_jsonNodePtrs.pop(); + return _node.id(); } - + template<class Container> + Json::Value getContainerIds(Container const& container) + { + Json::Value tmp(Json::arrayValue); + for (auto const& element: container) + { + solAssert(element, ""); + tmp.append(nodeId(*element)); + } + return tmp; + } + Json::Value typePointerToJson(TypePointer _tp); + Json::Value typePointerToJson(std::shared_ptr<std::vector<TypePointer>> _tps); + void appendExpressionAttributes( + std::vector<std::pair<std::string, Json::Value>> &_attributes, + ExpressionAnnotation const& _annotation + ); + bool m_legacy = false; ///< if true, use legacy format bool m_inEvent = false; ///< whether we are currently inside an event or not - bool processed = false; - Json::Value m_astJson; - std::stack<Json::Value*> m_jsonNodePtrs; - ASTNode const* m_ast; + Json::Value m_currentValue; std::map<std::string, unsigned> m_sourceIndices; }; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 41ee6498..cfee041e 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2183,6 +2183,8 @@ string FunctionType::identifier() const case Kind::ArrayPush: id += "arraypush"; break; case Kind::ByteArrayPush: id += "bytearraypush"; break; case Kind::ObjectCreation: id += "objectcreation"; break; + case Kind::Assert: id += "assert"; break; + case Kind::Require: id += "require";break; default: solAssert(false, "Unknown function location."); break; } if (isConstant()) diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 51dd9fd2..5c4f88c4 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -264,7 +264,7 @@ void CompilerContext::appendInlineAssembly( assembly = &replacedAssembly; } - unsigned startStackHeight = stackHeight(); + int startStackHeight = stackHeight(); assembly::ExternalIdentifierAccess identifierAccess; identifierAccess.resolve = [&]( @@ -278,26 +278,26 @@ void CompilerContext::appendInlineAssembly( identifierAccess.generateCode = [&]( assembly::Identifier const& _identifier, assembly::IdentifierContext _context, - eth::Assembly& _assembly + julia::AbstractAssembly& _assembly ) { auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name); solAssert(it != _localVariables.end(), ""); - unsigned stackDepth = _localVariables.end() - it; - int stackDiff = _assembly.deposit() - startStackHeight + stackDepth; + int stackDepth = _localVariables.end() - it; + int stackDiff = _assembly.stackHeight() - startStackHeight + stackDepth; if (_context == assembly::IdentifierContext::LValue) stackDiff -= 1; if (stackDiff < 1 || stackDiff > 16) BOOST_THROW_EXCEPTION( CompilerError() << - errinfo_comment("Stack too deep, try removing local variables.") + errinfo_comment("Stack too deep (" + to_string(stackDiff) + "), try removing local variables.") ); if (_context == assembly::IdentifierContext::RValue) - _assembly.append(dupInstruction(stackDiff)); + _assembly.appendInstruction(dupInstruction(stackDiff)); else { - _assembly.append(swapInstruction(stackDiff)); - _assembly.append(Instruction::POP); + _assembly.appendInstruction(swapInstruction(stackDiff)); + _assembly.appendInstruction(Instruction::POP); } }; diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 34ef13c0..41940e1a 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -530,7 +530,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) return size_t(-1); return ref->second.valueSize; }; - identifierAccess.generateCode = [&](assembly::Identifier const& _identifier, assembly::IdentifierContext _context, eth::Assembly& _assembly) + identifierAccess.generateCode = [&](assembly::Identifier const& _identifier, assembly::IdentifierContext _context, julia::AbstractAssembly& _assembly) { auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier); solAssert(ref != _inlineAssembly.annotation().externalReferences.end(), ""); @@ -538,21 +538,25 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) solAssert(!!decl, ""); if (_context == assembly::IdentifierContext::RValue) { - int const depositBefore = _assembly.deposit(); + int const depositBefore = _assembly.stackHeight(); solAssert(!!decl->type(), "Type of declaration required but not yet determined."); if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(decl)) { solAssert(!ref->second.isOffset && !ref->second.isSlot, ""); functionDef = &m_context.resolveVirtualFunction(*functionDef); - _assembly.append(m_context.functionEntryLabel(*functionDef).pushTag()); + auto functionEntryLabel = m_context.functionEntryLabel(*functionDef).pushTag(); + solAssert(functionEntryLabel.data() <= std::numeric_limits<size_t>::max(), ""); + _assembly.appendLabelReference(size_t(functionEntryLabel.data())); // If there is a runtime context, we have to merge both labels into the same // stack slot in case we store it in storage. if (CompilerContext* rtc = m_context.runtimeContext()) { - _assembly.append(u256(1) << 32); - _assembly.append(Instruction::MUL); - _assembly.append(rtc->functionEntryLabel(*functionDef).toSubAssemblyTag(m_context.runtimeSub())); - _assembly.append(Instruction::OR); + _assembly.appendConstant(u256(1) << 32); + _assembly.appendInstruction(Instruction::MUL); + auto runtimeEntryLabel = rtc->functionEntryLabel(*functionDef).toSubAssemblyTag(m_context.runtimeSub()); + solAssert(runtimeEntryLabel.data() <= std::numeric_limits<size_t>::max(), ""); + _assembly.appendLabelReference(size_t(runtimeEntryLabel.data())); + _assembly.appendInstruction(Instruction::OR); } } else if (auto variable = dynamic_cast<VariableDeclaration const*>(decl)) @@ -570,7 +574,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) } else if (m_context.isLocalVariable(decl)) { - int stackDiff = _assembly.deposit() - m_context.baseStackOffsetOfVariable(*variable); + int stackDiff = _assembly.stackHeight() - m_context.baseStackOffsetOfVariable(*variable); if (ref->second.isSlot || ref->second.isOffset) { solAssert(variable->type()->dataStoredIn(DataLocation::Storage), ""); @@ -587,7 +591,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) // only slot, offset is zero if (ref->second.isOffset) { - _assembly.append(u256(0)); + _assembly.appendConstant(u256(0)); return; } } @@ -601,7 +605,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) errinfo_comment("Stack too deep, try removing local variables.") ); solAssert(variable->type()->sizeOnStack() == 1, ""); - _assembly.append(dupInstruction(stackDiff)); + _assembly.appendInstruction(dupInstruction(stackDiff)); } else solAssert(false, ""); @@ -610,11 +614,11 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) { solAssert(!ref->second.isOffset && !ref->second.isSlot, ""); solAssert(contract->isLibrary(), ""); - _assembly.appendLibraryAddress(contract->fullyQualifiedName()); + _assembly.appendLinkerSymbol(contract->fullyQualifiedName()); } else solAssert(false, "Invalid declaration type."); - solAssert(_assembly.deposit() - depositBefore == int(ref->second.valueSize), ""); + solAssert(_assembly.stackHeight() - depositBefore == int(ref->second.valueSize), ""); } else { @@ -626,15 +630,15 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) "Can only assign to stack variables in inline assembly." ); solAssert(variable->type()->sizeOnStack() == 1, ""); - int stackDiff = _assembly.deposit() - m_context.baseStackOffsetOfVariable(*variable) - 1; + int stackDiff = _assembly.stackHeight() - m_context.baseStackOffsetOfVariable(*variable) - 1; if (stackDiff > 16 || stackDiff < 1) BOOST_THROW_EXCEPTION( CompilerError() << errinfo_sourceLocation(_inlineAssembly.location()) << - errinfo_comment("Stack too deep, try removing local variables.") + errinfo_comment("Stack too deep(" + to_string(stackDiff) + "), try removing local variables.") ); - _assembly.append(swapInstruction(stackDiff)); - _assembly.append(Instruction::POP); + _assembly.appendInstruction(swapInstruction(stackDiff)); + _assembly.appendInstruction(Instruction::POP); } }; solAssert(_inlineAssembly.annotation().analysisInfo, ""); diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index f018b311..0aa82ea8 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -434,7 +434,7 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) bool ExpressionCompiler::visit(FunctionCall const& _functionCall) { CompilerContext::LocationSetter locationSetter(m_context, _functionCall); - if (_functionCall.annotation().isTypeConversion) + if (_functionCall.annotation().kind == FunctionCallKind::TypeConversion) { solAssert(_functionCall.arguments().size() == 1, ""); solAssert(_functionCall.names().empty(), ""); @@ -445,7 +445,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } FunctionTypePointer functionType; - if (_functionCall.annotation().isStructConstructorCall) + if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall) { auto const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type); auto const& structType = dynamic_cast<StructType const&>(*type.actualType()); @@ -476,7 +476,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) solAssert(found, ""); } - if (_functionCall.annotation().isStructConstructorCall) + if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall) { TypeType const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type); auto const& structType = dynamic_cast<StructType const&>(*type.actualType()); diff --git a/libsolidity/formal/Why3Translator.cpp b/libsolidity/formal/Why3Translator.cpp index b6f17907..77d3c217 100644 --- a/libsolidity/formal/Why3Translator.cpp +++ b/libsolidity/formal/Why3Translator.cpp @@ -582,7 +582,7 @@ bool Why3Translator::visit(BinaryOperation const& _binaryOperation) bool Why3Translator::visit(FunctionCall const& _node) { - if (_node.annotation().isTypeConversion || _node.annotation().isStructConstructorCall) + if (_node.annotation().kind == FunctionCallKind::TypeConversion || _node.annotation().kind == FunctionCallKind::StructConstructorCall) { error(_node, "Only ordinary function calls supported."); return true; diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index e03eea2e..9e0ade2a 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -181,10 +181,21 @@ bool AsmAnalyzer::operator()(FunctionalAssignment const& _assignment) bool AsmAnalyzer::operator()(assembly::VariableDeclaration const& _varDecl) { + int const expectedItems = _varDecl.variables.size(); int const stackHeight = m_stackHeight; bool success = boost::apply_visitor(*this, *_varDecl.value); - solAssert(m_stackHeight - stackHeight == 1, "Invalid value size."); - boost::get<Scope::Variable>(m_currentScope->identifiers.at(_varDecl.variable.name)).active = true; + if ((m_stackHeight - stackHeight) != expectedItems) + { + m_errors.push_back(make_shared<Error>( + Error::Type::DeclarationError, + "Variable count mismatch.", + _varDecl.location + )); + return false; + } + + for (auto const& variable: _varDecl.variables) + boost::get<Scope::Variable>(m_currentScope->identifiers.at(variable.name)).active = true; m_info.stackHeightInfo[&_varDecl] = m_stackHeight; return success; } diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp index 44e12b3e..02d5ced0 100644 --- a/libsolidity/inlineasm/AsmCodeGen.cpp +++ b/libsolidity/inlineasm/AsmCodeGen.cpp @@ -32,6 +32,8 @@ #include <libevmasm/SourceLocation.h> #include <libevmasm/Instruction.h> +#include <libjulia/backends/AbstractAssembly.h> + #include <libdevcore/CommonIO.h> #include <boost/range/adaptor/reversed.hpp> @@ -48,14 +50,53 @@ using namespace dev::solidity::assembly; struct GeneratorState { - GeneratorState(ErrorList& _errors, AsmAnalysisInfo& _analysisInfo, eth::Assembly& _assembly): - errors(_errors), info(_analysisInfo), assembly(_assembly) {} + GeneratorState(ErrorList& _errors, AsmAnalysisInfo& _analysisInfo): + errors(_errors), info(_analysisInfo) {} - size_t newLabelId() + ErrorList& errors; + AsmAnalysisInfo info; +}; + +class EthAssemblyAdapter: public julia::AbstractAssembly +{ +public: + EthAssemblyAdapter(eth::Assembly& _assembly): + m_assembly(_assembly) { - return assemblyTagToIdentifier(assembly.newTag()); + } + virtual void setSourceLocation(SourceLocation const& _location) override + { + m_assembly.setSourceLocation(_location); + } + virtual int stackHeight() const override { return m_assembly.deposit(); } + virtual void appendInstruction(solidity::Instruction _instruction) override + { + m_assembly.append(_instruction); + } + virtual void appendConstant(u256 const& _constant) override + { + m_assembly.append(_constant); + } + /// Append a label. + virtual void appendLabel(size_t _labelId) override + { + m_assembly.append(eth::AssemblyItem(eth::Tag, _labelId)); + } + /// Append a label reference. + virtual void appendLabelReference(size_t _labelId) override + { + m_assembly.append(eth::AssemblyItem(eth::PushTag, _labelId)); + } + virtual size_t newLabelId() override + { + return assemblyTagToIdentifier(m_assembly.newTag()); + } + virtual void appendLinkerSymbol(std::string const& _linkerSymbol) override + { + m_assembly.appendLibraryAddress(_linkerSymbol); } +private: size_t assemblyTagToIdentifier(eth::AssemblyItem const& _tag) const { u256 id = _tag.data(); @@ -63,9 +104,7 @@ struct GeneratorState return size_t(id); } - ErrorList& errors; - AsmAnalysisInfo info; - eth::Assembly& assembly; + eth::Assembly& m_assembly; }; class CodeTransform: public boost::static_visitor<> @@ -76,81 +115,84 @@ public: /// @param _identifierAccess used to resolve identifiers external to the inline assembly explicit CodeTransform( GeneratorState& _state, + julia::AbstractAssembly& _assembly, assembly::Block const& _block, assembly::ExternalIdentifierAccess const& _identifierAccess = assembly::ExternalIdentifierAccess() - ): CodeTransform(_state, _block, _identifierAccess, _state.assembly.deposit()) + ): CodeTransform(_state, _assembly, _block, _identifierAccess, _assembly.stackHeight()) { } private: CodeTransform( GeneratorState& _state, + julia::AbstractAssembly& _assembly, assembly::Block const& _block, assembly::ExternalIdentifierAccess const& _identifierAccess, - int _initialDeposit + int _initialStackHeight ): m_state(_state), + m_assembly(_assembly), m_scope(*m_state.info.scopes.at(&_block)), m_identifierAccess(_identifierAccess), - m_initialDeposit(_initialDeposit) + m_initialStackHeight(_initialStackHeight) { - int blockStartDeposit = m_state.assembly.deposit(); + int blockStartStackHeight = m_assembly.stackHeight(); std::for_each(_block.statements.begin(), _block.statements.end(), boost::apply_visitor(*this)); - m_state.assembly.setSourceLocation(_block.location); + m_assembly.setSourceLocation(_block.location); // pop variables for (auto const& identifier: m_scope.identifiers) if (identifier.second.type() == typeid(Scope::Variable)) - m_state.assembly.append(solidity::Instruction::POP); + m_assembly.appendInstruction(solidity::Instruction::POP); - int deposit = m_state.assembly.deposit() - blockStartDeposit; + int deposit = m_assembly.stackHeight() - blockStartStackHeight; solAssert(deposit == 0, "Invalid stack height at end of block."); } public: void operator()(assembly::Instruction const& _instruction) { - m_state.assembly.setSourceLocation(_instruction.location); - m_state.assembly.append(_instruction.instruction); + m_assembly.setSourceLocation(_instruction.location); + m_assembly.appendInstruction(_instruction.instruction); checkStackHeight(&_instruction); } void operator()(assembly::Literal const& _literal) { - m_state.assembly.setSourceLocation(_literal.location); + m_assembly.setSourceLocation(_literal.location); if (_literal.kind == assembly::LiteralKind::Number) - m_state.assembly.append(u256(_literal.value)); + m_assembly.appendConstant(u256(_literal.value)); else if (_literal.kind == assembly::LiteralKind::Boolean) { if (_literal.value == "true") - m_state.assembly.append(u256(1)); + m_assembly.appendConstant(u256(1)); else - m_state.assembly.append(u256(0)); + m_assembly.appendConstant(u256(0)); } else { solAssert(_literal.value.size() <= 32, ""); - m_state.assembly.append(u256(h256(_literal.value, h256::FromBinary, h256::AlignLeft))); + m_assembly.appendConstant(u256(h256(_literal.value, h256::FromBinary, h256::AlignLeft))); } checkStackHeight(&_literal); } void operator()(assembly::Identifier const& _identifier) { - m_state.assembly.setSourceLocation(_identifier.location); + m_assembly.setSourceLocation(_identifier.location); // First search internals, then externals. if (m_scope.lookup(_identifier.name, Scope::NonconstVisitor( [=](Scope::Variable& _var) { if (int heightDiff = variableHeightDiff(_var, _identifier.location, false)) - m_state.assembly.append(solidity::dupInstruction(heightDiff)); + m_assembly.appendInstruction(solidity::dupInstruction(heightDiff)); else // Store something to balance the stack - m_state.assembly.append(u256(0)); + m_assembly.appendConstant(u256(0)); }, [=](Scope::Label& _label) { assignLabelIdIfUnset(_label); - m_state.assembly.append(eth::AssemblyItem(eth::PushTag, _label.id)); + m_assembly.appendLabelReference(*_label.id); }, [=](Scope::Function&) { @@ -164,14 +206,14 @@ public: m_identifierAccess.generateCode, "Identifier not found and no external access available." ); - m_identifierAccess.generateCode(_identifier, IdentifierContext::RValue, m_state.assembly); + m_identifierAccess.generateCode(_identifier, IdentifierContext::RValue, m_assembly); checkStackHeight(&_identifier); } void operator()(FunctionalInstruction const& _instr) { for (auto it = _instr.arguments.rbegin(); it != _instr.arguments.rend(); ++it) { - int height = m_state.assembly.deposit(); + int height = m_assembly.stackHeight(); boost::apply_visitor(*this, *it); expectDeposit(1, height); } @@ -184,40 +226,44 @@ public: } void operator()(Label const& _label) { - m_state.assembly.setSourceLocation(_label.location); + m_assembly.setSourceLocation(_label.location); solAssert(m_scope.identifiers.count(_label.name), ""); Scope::Label& label = boost::get<Scope::Label>(m_scope.identifiers.at(_label.name)); assignLabelIdIfUnset(label); - m_state.assembly.append(eth::AssemblyItem(eth::Tag, label.id)); + m_assembly.appendLabel(*label.id); checkStackHeight(&_label); } void operator()(assembly::Assignment const& _assignment) { - m_state.assembly.setSourceLocation(_assignment.location); + m_assembly.setSourceLocation(_assignment.location); generateAssignment(_assignment.variableName, _assignment.location); checkStackHeight(&_assignment); } void operator()(FunctionalAssignment const& _assignment) { - int height = m_state.assembly.deposit(); + int height = m_assembly.stackHeight(); boost::apply_visitor(*this, *_assignment.value); expectDeposit(1, height); - m_state.assembly.setSourceLocation(_assignment.location); + m_assembly.setSourceLocation(_assignment.location); generateAssignment(_assignment.variableName, _assignment.location); checkStackHeight(&_assignment); } void operator()(assembly::VariableDeclaration const& _varDecl) { - int height = m_state.assembly.deposit(); + int height = m_assembly.stackHeight(); + int expectedItems = _varDecl.variables.size(); boost::apply_visitor(*this, *_varDecl.value); - expectDeposit(1, height); - auto& var = boost::get<Scope::Variable>(m_scope.identifiers.at(_varDecl.variable.name)); - var.stackHeight = height; - var.active = true; + expectDeposit(expectedItems, height); + for (auto const& variable: _varDecl.variables) + { + auto& var = boost::get<Scope::Variable>(m_scope.identifiers.at(variable.name)); + var.stackHeight = height++; + var.active = true; + } } void operator()(assembly::Block const& _block) { - CodeTransform(m_state, _block, m_identifierAccess, m_initialDeposit); + CodeTransform(m_state, m_assembly, _block, m_identifierAccess, m_initialStackHeight); checkStackHeight(&_block); } void operator()(assembly::FunctionDefinition const&) @@ -233,8 +279,8 @@ private: { Scope::Variable const& _var = boost::get<Scope::Variable>(*var); if (int heightDiff = variableHeightDiff(_var, _location, true)) - m_state.assembly.append(solidity::swapInstruction(heightDiff - 1)); - m_state.assembly.append(solidity::Instruction::POP); + m_assembly.appendInstruction(solidity::swapInstruction(heightDiff - 1)); + m_assembly.appendInstruction(solidity::Instruction::POP); } else { @@ -242,7 +288,7 @@ private: m_identifierAccess.generateCode, "Identifier not found and no external access available." ); - m_identifierAccess.generateCode(_variableName, IdentifierContext::LValue, m_state.assembly); + m_identifierAccess.generateCode(_variableName, IdentifierContext::LValue, m_assembly); } } @@ -251,7 +297,7 @@ private: /// errors and the (positive) stack height difference otherwise. int variableHeightDiff(Scope::Variable const& _var, SourceLocation const& _location, bool _forSwap) { - int heightDiff = m_state.assembly.deposit() - _var.stackHeight; + int heightDiff = m_assembly.stackHeight() - _var.stackHeight; if (heightDiff <= (_forSwap ? 1 : 0) || heightDiff > (_forSwap ? 17 : 16)) { //@TODO move this to analysis phase. @@ -268,14 +314,14 @@ private: void expectDeposit(int _deposit, int _oldHeight) { - solAssert(m_state.assembly.deposit() == _oldHeight + _deposit, "Invalid stack deposit."); + solAssert(m_assembly.stackHeight() == _oldHeight + _deposit, "Invalid stack deposit."); } void checkStackHeight(void const* _astElement) { solAssert(m_state.info.stackHeightInfo.count(_astElement), "Stack height for AST element not found."); solAssert( - m_state.info.stackHeightInfo.at(_astElement) == m_state.assembly.deposit() - m_initialDeposit, + m_state.info.stackHeightInfo.at(_astElement) == m_assembly.stackHeight() - m_initialStackHeight, "Stack height mismatch between analysis and code generation phase." ); } @@ -283,15 +329,16 @@ private: /// Assigns the label's id to a value taken from eth::Assembly if it has not yet been set. void assignLabelIdIfUnset(Scope::Label& _label) { - if (_label.id == Scope::Label::unassignedLabelId) - _label.id = m_state.newLabelId(); + if (!_label.id) + _label.id.reset(m_assembly.newLabelId()); } GeneratorState& m_state; + julia::AbstractAssembly& m_assembly; Scope& m_scope; ExternalIdentifierAccess m_identifierAccess; - int const m_initialDeposit; + int const m_initialStackHeight; }; eth::Assembly assembly::CodeGenerator::assemble( @@ -301,8 +348,9 @@ eth::Assembly assembly::CodeGenerator::assemble( ) { eth::Assembly assembly; - GeneratorState state(m_errors, _analysisInfo, assembly); - CodeTransform(state, _parsedData, _identifierAccess); + GeneratorState state(m_errors, _analysisInfo); + EthAssemblyAdapter assemblyAdapter(assembly); + CodeTransform(state, assemblyAdapter, _parsedData, _identifierAccess); return assembly; } @@ -313,6 +361,7 @@ void assembly::CodeGenerator::assemble( ExternalIdentifierAccess const& _identifierAccess ) { - GeneratorState state(m_errors, _analysisInfo, _assembly); - CodeTransform(state, _parsedData, _identifierAccess); + GeneratorState state(m_errors, _analysisInfo); + EthAssemblyAdapter assemblyAdapter(_assembly); + CodeTransform(state, assemblyAdapter, _parsedData, _identifierAccess); } diff --git a/libsolidity/inlineasm/AsmData.h b/libsolidity/inlineasm/AsmData.h index 8efe1f07..65dfbadf 100644 --- a/libsolidity/inlineasm/AsmData.h +++ b/libsolidity/inlineasm/AsmData.h @@ -65,7 +65,7 @@ struct FunctionalAssignment { SourceLocation location; Identifier variableName; struct FunctionalInstruction { SourceLocation location; Instruction instruction; std::vector<Statement> arguments; }; struct FunctionCall { SourceLocation location; Identifier functionName; std::vector<Statement> arguments; }; /// Block-scope variable declaration ("let x:u256 := mload(20:u256)"), non-hoisted -struct VariableDeclaration { SourceLocation location; TypedName variable; std::shared_ptr<Statement> value; }; +struct VariableDeclaration { SourceLocation location; TypedNameList variables; std::shared_ptr<Statement> value; }; /// Block that creates a scope (frees declared stack variables) struct Block { SourceLocation location; std::vector<Statement> statements; }; /// Function definition ("function f(a, b) -> (d, e) { ... }") diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index a96984f5..73f70e3f 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -258,7 +258,14 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration() { VariableDeclaration varDecl = createWithLocation<VariableDeclaration>(); expectToken(Token::Let); - varDecl.variable = parseTypedName(); + while (true) + { + varDecl.variables.push_back(parseTypedName()); + if (m_scanner->currentToken() == Token::Comma) + expectToken(Token::Comma); + else + break; + } expectToken(Token::Colon); expectToken(Token::Assign); varDecl.value.reset(new Statement(parseExpression())); diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libsolidity/inlineasm/AsmPrinter.cpp index 636e61b8..219a57ad 100644 --- a/libsolidity/inlineasm/AsmPrinter.cpp +++ b/libsolidity/inlineasm/AsmPrinter.cpp @@ -121,7 +121,16 @@ string AsmPrinter::operator()(assembly::FunctionalAssignment const& _functionalA string AsmPrinter::operator()(assembly::VariableDeclaration const& _variableDeclaration) { - return "let " + _variableDeclaration.variable.name + appendTypeName(_variableDeclaration.variable.type) + " := " + boost::apply_visitor(*this, *_variableDeclaration.value); + string out = "let "; + out += boost::algorithm::join( + _variableDeclaration.variables | boost::adaptors::transformed( + [this](TypedName variable) { return variable.name + appendTypeName(variable.type); } + ), + ", " + ); + out += " := "; + out += boost::apply_visitor(*this, *_variableDeclaration.value); + return out; } string AsmPrinter::operator()(assembly::FunctionDefinition const& _functionDefinition) diff --git a/libsolidity/inlineasm/AsmScope.h b/libsolidity/inlineasm/AsmScope.h index dd45613d..70786dce 100644 --- a/libsolidity/inlineasm/AsmScope.h +++ b/libsolidity/inlineasm/AsmScope.h @@ -23,6 +23,7 @@ #include <libsolidity/interface/Exceptions.h> #include <boost/variant.hpp> +#include <boost/optional.hpp> #include <functional> #include <memory> @@ -75,8 +76,7 @@ struct Scope struct Label { - size_t id = unassignedLabelId; - static const size_t unassignedLabelId = 0; + boost::optional<size_t> id; }; struct Function diff --git a/libsolidity/inlineasm/AsmScopeFiller.cpp b/libsolidity/inlineasm/AsmScopeFiller.cpp index eb10dbb3..05b1b211 100644 --- a/libsolidity/inlineasm/AsmScopeFiller.cpp +++ b/libsolidity/inlineasm/AsmScopeFiller.cpp @@ -59,7 +59,10 @@ bool ScopeFiller::operator()(Label const& _item) bool ScopeFiller::operator()(assembly::VariableDeclaration const& _varDecl) { - return registerVariable(_varDecl.variable, _varDecl.location, *m_currentScope); + for (auto const& variable: _varDecl.variables) + if (!registerVariable(variable, _varDecl.location, *m_currentScope)) + return false; + return true; } bool ScopeFiller::operator()(assembly::FunctionDefinition const& _funDef) diff --git a/libsolidity/inlineasm/AsmStack.h b/libsolidity/inlineasm/AsmStack.h index 77a7e02a..e223ccc9 100644 --- a/libsolidity/inlineasm/AsmStack.h +++ b/libsolidity/inlineasm/AsmStack.h @@ -24,6 +24,8 @@ #include <libsolidity/interface/Exceptions.h> +#include <libjulia/backends/AbstractAssembly.h> + #include <string> #include <functional> @@ -51,7 +53,7 @@ struct ExternalIdentifierAccess /// Resolve a an external reference given by the identifier in the given context. /// @returns the size of the value (number of stack slots) or size_t(-1) if not found. Resolver resolve; - using CodeGenerator = std::function<void(assembly::Identifier const&, IdentifierContext, eth::Assembly&)>; + using CodeGenerator = std::function<void(assembly::Identifier const&, IdentifierContext, julia::AbstractAssembly&)>; /// Generate code for retrieving the value (rvalue context) or storing the value (lvalue context) /// of an identifier. The code should be appended to the assembly. In rvalue context, the value is supposed /// to be put onto the stack, in lvalue context, the value is assumed to be at the top of the stack. diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 503be57b..d5dbaa46 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -377,7 +377,8 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) { Json::Value sourceResult = Json::objectValue; sourceResult["id"] = sourceIndex++; - sourceResult["legacyAST"] = ASTJsonConverter(m_compilerStack.ast(source), m_compilerStack.sourceIndices()).json(); + sourceResult["ast"] = ASTJsonConverter(false, m_compilerStack.sourceIndices()).toJson(m_compilerStack.ast(source)); + sourceResult["legacyAST"] = ASTJsonConverter(true, m_compilerStack.sourceIndices()).toJson(m_compilerStack.ast(source)); output["sources"][source] = sourceResult; } diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index d5a60b50..e55e3e7e 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -838,9 +838,9 @@ void CommandLineInterface::handleCombinedJSON() output[g_strSources] = Json::Value(Json::objectValue); for (auto const& sourceCode: m_sourceCodes) { - ASTJsonConverter converter(m_compiler->ast(sourceCode.first), m_compiler->sourceIndices()); + ASTJsonConverter converter(true, m_compiler->sourceIndices()); output[g_strSources][sourceCode.first] = Json::Value(Json::objectValue); - output[g_strSources][sourceCode.first]["AST"] = converter.json(); + output[g_strSources][sourceCode.first]["AST"] = converter.toJson(m_compiler->ast(sourceCode.first)); } } cout << dev::jsonCompactPrint(output) << endl; @@ -883,8 +883,7 @@ void CommandLineInterface::handleAst(string const& _argStr) } else { - ASTJsonConverter converter(m_compiler->ast(sourceCode.first)); - converter.print(data); + ASTJsonConverter(true).print(data, m_compiler->ast(sourceCode.first)); postfix += "_json"; } boost::filesystem::path path(sourceCode.first); @@ -908,8 +907,7 @@ void CommandLineInterface::handleAst(string const& _argStr) } else { - ASTJsonConverter converter(m_compiler->ast(sourceCode.first)); - converter.print(cout); + ASTJsonConverter(true).print(cout, m_compiler->ast(sourceCode.first)); } } } diff --git a/solc/jsonCompiler.cpp b/solc/jsonCompiler.cpp index 92a102b6..7f99324e 100644 --- a/solc/jsonCompiler.cpp +++ b/solc/jsonCompiler.cpp @@ -245,7 +245,7 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback output["sourceList"].append(source); output["sources"] = Json::Value(Json::objectValue); for (auto const& source: compiler.sourceNames()) - output["sources"][source]["AST"] = ASTJsonConverter(compiler.ast(source), compiler.sourceIndices()).json(); + output["sources"][source]["AST"] = ASTJsonConverter(true, compiler.sourceIndices()).toJson(compiler.ast(source)); } catch (...) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ddec205e..8e7b8916 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -4,6 +4,7 @@ aux_source_directory(. SRC_LIST) aux_source_directory(libdevcore SRC_LIST) aux_source_directory(libevmasm SRC_LIST) aux_source_directory(libsolidity SRC_LIST) +aux_source_directory(libjulia SRC_LIST) aux_source_directory(contracts SRC_LIST) aux_source_directory(liblll SRC_LIST) aux_source_directory(libjulia SRC_LIST) diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp index d1081067..3931ceb8 100644 --- a/test/libjulia/Parser.cpp +++ b/test/libjulia/Parser.cpp @@ -161,6 +161,11 @@ BOOST_AUTO_TEST_CASE(function_calls) BOOST_CHECK(successParse("{ function f(a:u256) -> b:u256 {} function g(a:u256, b:u256, c:u256) {} function x() { g(1:u256, 2:u256, f(mul(2:u256, 3:u256))) x() } }")); } +BOOST_AUTO_TEST_CASE(tuple_assignment) +{ + BOOST_CHECK(successParse("{ function f() -> a:u256, b:u256, c:u256 {} let x:u256, y:u256, z:u256 := f() }")); +} + BOOST_AUTO_TEST_CASE(label) { CHECK_ERROR("{ label: }", ParserError, "Labels are not supported."); diff --git a/test/libsolidity/ASTJSON.cpp b/test/libsolidity/ASTJSON.cpp index d23815b4..8a790347 100644 --- a/test/libsolidity/ASTJSON.cpp +++ b/test/libsolidity/ASTJSON.cpp @@ -44,7 +44,7 @@ BOOST_AUTO_TEST_CASE(smoke_test) c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); BOOST_CHECK_EQUAL(astJson["name"], "SourceUnit"); } @@ -55,7 +55,7 @@ BOOST_AUTO_TEST_CASE(source_location) c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); BOOST_CHECK_EQUAL(astJson["name"], "SourceUnit"); BOOST_CHECK_EQUAL(astJson["children"][0]["name"], "ContractDefinition"); BOOST_CHECK_EQUAL(astJson["children"][0]["children"][0]["name"], "FunctionDefinition"); @@ -69,7 +69,7 @@ BOOST_AUTO_TEST_CASE(inheritance_specifier) c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); BOOST_CHECK_EQUAL(astJson["children"][1]["attributes"]["name"], "C2"); BOOST_CHECK_EQUAL(astJson["children"][1]["children"][0]["name"], "InheritanceSpecifier"); BOOST_CHECK_EQUAL(astJson["children"][1]["children"][0]["src"], "30:2:1"); @@ -84,7 +84,7 @@ BOOST_AUTO_TEST_CASE(using_for_directive) c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); Json::Value usingFor = astJson["children"][1]["children"][0]; BOOST_CHECK_EQUAL(usingFor["name"], "UsingForDirective"); BOOST_CHECK_EQUAL(usingFor["src"], "26:17:1"); @@ -101,7 +101,7 @@ BOOST_AUTO_TEST_CASE(enum_value) c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); Json::Value enumDefinition = astJson["children"][0]["children"][0]; BOOST_CHECK_EQUAL(enumDefinition["children"][0]["name"], "EnumValue"); BOOST_CHECK_EQUAL(enumDefinition["children"][0]["attributes"]["name"], "A"); @@ -118,7 +118,7 @@ BOOST_AUTO_TEST_CASE(modifier_definition) c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); Json::Value modifier = astJson["children"][0]["children"][0]; BOOST_CHECK_EQUAL(modifier["name"], "ModifierDefinition"); BOOST_CHECK_EQUAL(modifier["attributes"]["name"], "M"); @@ -132,7 +132,7 @@ BOOST_AUTO_TEST_CASE(modifier_invocation) c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); Json::Value modifier = astJson["children"][0]["children"][1]["children"][2]; BOOST_CHECK_EQUAL(modifier["name"], "ModifierInvocation"); BOOST_CHECK_EQUAL(modifier["src"], "52:4:1"); @@ -148,7 +148,7 @@ BOOST_AUTO_TEST_CASE(event_definition) c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); Json::Value event = astJson["children"][0]["children"][0]; BOOST_CHECK_EQUAL(event["name"], "EventDefinition"); BOOST_CHECK_EQUAL(event["attributes"]["name"], "E"); @@ -162,7 +162,7 @@ BOOST_AUTO_TEST_CASE(array_type_name) c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); Json::Value array = astJson["children"][0]["children"][0]["children"][0]; BOOST_CHECK_EQUAL(array["name"], "ArrayTypeName"); BOOST_CHECK_EQUAL(array["src"], "13:6:1"); @@ -175,7 +175,7 @@ BOOST_AUTO_TEST_CASE(placeholder_statement) c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); Json::Value placeholder = astJson["children"][0]["children"][0]["children"][1]["children"][0]; BOOST_CHECK_EQUAL(placeholder["name"], "PlaceholderStatement"); BOOST_CHECK_EQUAL(placeholder["src"], "26:1:1"); @@ -188,7 +188,7 @@ BOOST_AUTO_TEST_CASE(non_utf8) c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); Json::Value literal = astJson["children"][0]["children"][0]["children"][2]["children"][0]["children"][1]; BOOST_CHECK_EQUAL(literal["name"], "Literal"); BOOST_CHECK_EQUAL(literal["attributes"]["hexvalue"], "ff"); @@ -207,7 +207,7 @@ BOOST_AUTO_TEST_CASE(function_type) c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); Json::Value fun = astJson["children"][0]["children"][0]; BOOST_CHECK_EQUAL(fun["name"], "FunctionDefinition"); Json::Value argument = fun["children"][0]["children"][0]; diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 0729c8db..f155cba7 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -161,6 +161,21 @@ BOOST_AUTO_TEST_CASE(vardecl) BOOST_CHECK(successParse("{ let x := 7 }")); } +BOOST_AUTO_TEST_CASE(vardecl_name_clashes) +{ + CHECK_PARSE_ERROR("{ let x := 1 let x := 2 }", DeclarationError, "Variable name x already taken in this scope."); +} + +BOOST_AUTO_TEST_CASE(vardecl_multi) +{ + BOOST_CHECK(successParse("{ function f() -> x, y {} let x, y := f() }")); +} + +BOOST_AUTO_TEST_CASE(vardecl_multi_conflict) +{ + CHECK_PARSE_ERROR("{ function f() -> x, y {} let x, x := f() }", DeclarationError, "Variable name x already taken in this scope."); +} + BOOST_AUTO_TEST_CASE(vardecl_bool) { CHECK_PARSE_ERROR("{ let x := true }", ParserError, "True and false are not valid literals."); @@ -249,6 +264,12 @@ BOOST_AUTO_TEST_CASE(variable_access_cross_functions) CHECK_PARSE_ERROR("{ let x := 2 function g() { x pop } }", DeclarationError, "Identifier not found."); } +BOOST_AUTO_TEST_CASE(invalid_tuple_assignment) +{ + /// The push(42) is added here to silence the unbalanced stack error, so that there's only one error reported. + CHECK_PARSE_ERROR("{ 42 let x, y := 1 }", DeclarationError, "Variable count mismatch."); +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(Printing) @@ -283,6 +304,11 @@ BOOST_AUTO_TEST_CASE(print_assignments) parsePrintCompare("{\n let x := mul(2, 3)\n 7\n =: x\n x := add(1, 2)\n}"); } +BOOST_AUTO_TEST_CASE(print_multi_assignments) +{ + parsePrintCompare("{\n function f() -> x, y\n {\n }\n let x, y := f()\n}"); +} + BOOST_AUTO_TEST_CASE(print_string_literals) { parsePrintCompare("{\n \"\\n'\\xab\\x95\\\"\"\n pop\n}"); diff --git a/test/libsolidity/JSONCompiler.cpp b/test/libsolidity/JSONCompiler.cpp index 46c718d7..6aec59ab 100644 --- a/test/libsolidity/JSONCompiler.cpp +++ b/test/libsolidity/JSONCompiler.cpp @@ -90,10 +90,12 @@ BOOST_AUTO_TEST_CASE(basic_compilation) BOOST_CHECK(result["sources"]["fileA"].isObject()); BOOST_CHECK(result["sources"]["fileA"]["AST"].isObject()); BOOST_CHECK(dev::jsonCompactPrint(result["sources"]["fileA"]["AST"]) == - "{\"children\":[{\"attributes\":{\"fullyImplemented\":true,\"isLibrary\":false,\"linearizedBaseContracts\":[1]," - "\"name\":\"A\"},\"children\":[],\"id\":1,\"name\":\"ContractDefinition\",\"src\":\"0:14:0\"}],\"name\":\"SourceUnit\"}"); + "{\"attributes\":{\"absolutePath\":\"fileA\",\"exportedSymbols\":{\"A\":[1]}}," + "\"children\":[{\"attributes\":{\"baseContracts\":[null],\"contractDependencies\":[null]," + "\"contractKind\":\"contract\",\"fullyImplemented\":true,\"linearizedBaseContracts\":[1]," + "\"name\":\"A\",\"nodes\":[null],\"scope\":2},\"id\":1,\"name\":\"ContractDefinition\"," + "\"src\":\"0:14:0\"}],\"id\":2,\"name\":\"SourceUnit\",\"src\":\"0:14:0\"}"); } - BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index ec2f69d9..050ca500 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -215,8 +215,10 @@ BOOST_AUTO_TEST_CASE(basic_compilation) BOOST_CHECK(result["sources"]["fileA"].isObject()); BOOST_CHECK(result["sources"]["fileA"]["legacyAST"].isObject()); BOOST_CHECK(dev::jsonCompactPrint(result["sources"]["fileA"]["legacyAST"]) == - "{\"children\":[{\"attributes\":{\"fullyImplemented\":true,\"isLibrary\":false,\"linearizedBaseContracts\":[1]," - "\"name\":\"A\"},\"children\":[],\"id\":1,\"name\":\"ContractDefinition\",\"src\":\"0:14:0\"}],\"name\":\"SourceUnit\"}"); + "{\"attributes\":{\"absolutePath\":\"fileA\",\"exportedSymbols\":{\"A\":[1]}},\"children\":" + "[{\"attributes\":{\"baseContracts\":[null],\"contractDependencies\":[null],\"contractKind\":\"contract\"," + "\"fullyImplemented\":true,\"linearizedBaseContracts\":[1],\"name\":\"A\",\"nodes\":[null],\"scope\":2}," + "\"id\":1,\"name\":\"ContractDefinition\",\"src\":\"0:14:0\"}],\"id\":2,\"name\":\"SourceUnit\",\"src\":\"0:14:0\"}"); } BOOST_AUTO_TEST_SUITE_END() |