Merge pull request #7153 from djudjuu/newImportAST

import ast from JSON
This commit is contained in:
chriseth 2020-01-14 17:56:53 +01:00 committed by GitHub
commit b3fe84a6ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 2136 additions and 145 deletions

View File

@ -6,6 +6,7 @@ Language Features:
Compiler Features:
* General: Raise warning if runtime bytecode exceeds 24576 bytes (a limit introduced in Spurious Dragon).
* General: Support compiling starting from an imported AST. Among others, this can be used for mutation testing.
* Yul Optimizer: Apply penalty when trying to rematerialize into loops.
Bugfixes:

View File

@ -16,6 +16,7 @@ set(sources
SemVerHandler.cpp
SemVerHandler.h
SourceLocation.h
SourceLocation.cpp
SourceReferenceExtractor.cpp
SourceReferenceExtractor.h
SourceReferenceFormatter.cpp

View File

@ -40,6 +40,7 @@ struct CompilerError: virtual util::Exception {};
struct InternalCompilerError: virtual util::Exception {};
struct FatalError: virtual util::Exception {};
struct UnimplementedFeatureError: virtual util::Exception {};
struct InvalidAstError: virtual util::Exception {};
/// Assertion that throws an InternalCompilerError containing the given description if it is not met.
#define solAssert(CONDITION, DESCRIPTION) \
@ -51,6 +52,9 @@ struct UnimplementedFeatureError: virtual util::Exception {};
#define solUnimplemented(DESCRIPTION) \
solUnimplementedAssert(false, DESCRIPTION)
#define astAssert(CONDITION, DESCRIPTION) \
assertThrow(CONDITION, ::solidity::langutil::InvalidAstError, DESCRIPTION)
class Error: virtual public util::Exception
{
public:

View File

@ -0,0 +1,51 @@
/*
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/>.
*/
#include <liblangutil/Exceptions.h>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string.hpp>
using namespace solidity;
namespace solidity::langutil
{
SourceLocation const parseSourceLocation(std::string const& _input, std::string const& _sourceName, size_t _maxIndex)
{
// Expected input: "start:length:sourceindex"
enum SrcElem : size_t { Start, Length, Index };
std::vector<std::string> pos;
boost::algorithm::split(pos, _input, boost::is_any_of(":"));
astAssert(
pos.size() == 3 &&
_maxIndex >= static_cast<size_t>(stoi(pos[Index])),
"'src'-field ill-formatted or src-index too high"
);
int start = stoi(pos[Start]);
int end = start + stoi(pos[Length]);
// ASSUMPTION: only the name of source is used from here on, the m_source of the CharStream-Object can be empty
std::shared_ptr<langutil::CharStream> source = std::make_shared<langutil::CharStream>("", _sourceName);
return SourceLocation{start, end, source};
}
}

View File

@ -23,13 +23,12 @@
#pragma once
#include <libsolutil/Assertions.h>
#include <libsolutil/Common.h> // defines noexcept macro for MSVC
#include <libsolutil/Exceptions.h>
#include <liblangutil/CharStream.h>
#include <memory>
#include <string>
#include <ostream>
#include <tuple>
namespace solidity::langutil
{
@ -46,9 +45,28 @@ struct SourceLocation
return source.get() == _other.source.get() && start == _other.start && end == _other.end;
}
bool operator!=(SourceLocation const& _other) const { return !operator==(_other); }
inline bool operator<(SourceLocation const& _other) const;
inline bool contains(SourceLocation const& _other) const;
inline bool intersects(SourceLocation const& _other) const;
inline bool operator<(SourceLocation const& _other) const
{
if (!source|| !_other.source)
return std::make_tuple(int(!!source), start, end) < std::make_tuple(int(!!_other.source), _other.start, _other.end);
else
return std::make_tuple(source->name(), start, end) < std::make_tuple(_other.source->name(), _other.start, _other.end);
}
inline bool contains(SourceLocation const& _other) const
{
if (isEmpty() || _other.isEmpty() || source.get() != _other.source.get())
return false;
return start <= _other.start && _other.end <= end;
}
inline bool intersects(SourceLocation const& _other) const
{
if (isEmpty() || _other.isEmpty() || source.get() != _other.source.get())
return false;
return _other.start < end && start < _other.end;
}
bool isEmpty() const { return start == -1 && end == -1; }
@ -86,6 +104,8 @@ struct SourceLocation
std::shared_ptr<CharStream> source;
};
SourceLocation const parseSourceLocation(std::string const& _input, std::string const& _sourceName, size_t _maxIndex = -1);
/// Stream output for Location (used e.g. in boost exceptions).
inline std::ostream& operator<<(std::ostream& _out, SourceLocation const& _location)
{
@ -100,26 +120,4 @@ inline std::ostream& operator<<(std::ostream& _out, SourceLocation const& _locat
return _out;
}
bool SourceLocation::operator<(SourceLocation const& _other) const
{
if (!source|| !_other.source)
return std::make_tuple(int(!!source), start, end) < std::make_tuple(int(!!_other.source), _other.start, _other.end);
else
return std::make_tuple(source->name(), start, end) < std::make_tuple(_other.source->name(), _other.start, _other.end);
}
bool SourceLocation::contains(SourceLocation const& _other) const
{
if (isEmpty() || _other.isEmpty() || source.get() != _other.source.get())
return false;
return start <= _other.start && _other.end <= end;
}
bool SourceLocation::intersects(SourceLocation const& _other) const
{
if (isEmpty() || _other.isEmpty() || source.get() != _other.source.get())
return false;
return _other.start < end && start < _other.end;
}
}

View File

@ -39,10 +39,14 @@ set(sources
ast/ASTAnnotations.h
ast/ASTEnums.h
ast/ASTForward.h
ast/AsmJsonImporter.cpp
ast/AsmJsonImporter.h
ast/ASTJsonConverter.cpp
ast/ASTJsonConverter.h
ast/ASTUtils.cpp
ast/ASTUtils.h
ast/ASTJsonImporter.cpp
ast/ASTJsonImporter.h
ast/ASTVisitor.h
ast/ExperimentalFeatures.h
ast/Types.cpp

View File

@ -33,10 +33,44 @@ using namespace std;
namespace solidity::frontend
{
/// Magic variables get negative ids for easy differentiation
int magicVariableToID(std::string const& _name)
{
if (_name == "abi") return -1;
else if (_name == "addmod") return -2;
else if (_name == "assert") return -3;
else if (_name == "block") return -4;
else if (_name == "blockhash") return -5;
else if (_name == "ecrecover") return -6;
else if (_name == "gasleft") return -7;
else if (_name == "keccak256") return -8;
else if (_name == "log0") return -10;
else if (_name == "log1") return -11;
else if (_name == "log2") return -12;
else if (_name == "log3") return -13;
else if (_name == "log4") return -14;
else if (_name == "msg") return -15;
else if (_name == "mulmod") return -16;
else if (_name == "now") return -17;
else if (_name == "require") return -18;
else if (_name == "revert") return -19;
else if (_name == "ripemd160") return -20;
else if (_name == "selfdestruct") return -21;
else if (_name == "sha256") return -22;
else if (_name == "sha3") return -23;
else if (_name == "suicide") return -24;
else if (_name == "super") return -25;
else if (_name == "tx") return -26;
else if (_name == "type") return -27;
else if (_name == "this") return -28;
else
solAssert(false, "Unknown magic variable: \"" + _name + "\".");
}
inline vector<shared_ptr<MagicVariableDeclaration const>> constructMagicVariables()
{
static auto const magicVarDecl = [](string const& _name, Type const* _type) {
return make_shared<MagicVariableDeclaration>(_name, _type);
return make_shared<MagicVariableDeclaration>(magicVariableToID(_name), _name, _type);
};
return {
@ -97,7 +131,7 @@ vector<Declaration const*> GlobalContext::declarations() const
MagicVariableDeclaration const* GlobalContext::currentThis() const
{
if (!m_thisPointer[m_currentContract])
m_thisPointer[m_currentContract] = make_shared<MagicVariableDeclaration>("this", TypeProvider::contract(*m_currentContract));
m_thisPointer[m_currentContract] = make_shared<MagicVariableDeclaration>(magicVariableToID("this"), "this", TypeProvider::contract(*m_currentContract));
return m_thisPointer[m_currentContract].get();
}
@ -105,7 +139,7 @@ MagicVariableDeclaration const* GlobalContext::currentThis() const
MagicVariableDeclaration const* GlobalContext::currentSuper() const
{
if (!m_superPointer[m_currentContract])
m_superPointer[m_currentContract] = make_shared<MagicVariableDeclaration>("super", TypeProvider::contract(*m_currentContract, true));
m_superPointer[m_currentContract] = make_shared<MagicVariableDeclaration>(magicVariableToID("super"), "super", TypeProvider::contract(*m_currentContract, true));
return m_superPointer[m_currentContract].get();
}

View File

@ -35,31 +35,12 @@ using namespace std;
using namespace solidity;
using namespace solidity::frontend;
class IDDispenser
{
public:
static size_t next() { return ++instance(); }
static void reset() { instance() = 0; }
private:
static size_t& instance()
{
static IDDispenser dispenser;
return dispenser.id;
}
size_t id = 0;
};
ASTNode::ASTNode(SourceLocation const& _location):
m_id(IDDispenser::next()),
ASTNode::ASTNode(int64_t _id, SourceLocation const& _location):
m_id(_id),
m_location(_location)
{
}
void ASTNode::resetID()
{
IDDispenser::reset();
}
ASTAnnotation& ASTNode::annotation() const
{
if (!m_annotation)

View File

@ -64,13 +64,11 @@ class ASTNode: private boost::noncopyable
public:
using SourceLocation = langutil::SourceLocation;
explicit ASTNode(SourceLocation const& _location);
explicit ASTNode(int64_t _id, SourceLocation const& _location);
virtual ~ASTNode() {}
/// @returns an identifier of this AST node that is unique for a single compilation run.
size_t id() const { return m_id; }
/// Resets the global ID counter. This invalidates all previous IDs.
static void resetID();
int64_t id() const { return m_id; }
virtual void accept(ASTVisitor& _visitor) = 0;
virtual void accept(ASTConstVisitor& _visitor) const = 0;
@ -139,8 +137,8 @@ std::vector<_T const*> ASTNode::filteredNodes(std::vector<ASTPointer<ASTNode>> c
class SourceUnit: public ASTNode
{
public:
SourceUnit(SourceLocation const& _location, std::vector<ASTPointer<ASTNode>> const& _nodes):
ASTNode(_location), m_nodes(_nodes) {}
SourceUnit(int64_t _id, SourceLocation const& _location, std::vector<ASTPointer<ASTNode>> const& _nodes):
ASTNode(_id, _location), m_nodes(_nodes) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -206,11 +204,12 @@ public:
}
Declaration(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _name,
Visibility _visibility = Visibility::Default
):
ASTNode(_location), m_name(_name), m_visibility(_visibility) {}
ASTNode(_id, _location), m_name(_name), m_visibility(_visibility) {}
/// @returns the declared name.
ASTString const& name() const { return *m_name; }
@ -253,10 +252,11 @@ class PragmaDirective: public ASTNode
{
public:
PragmaDirective(
int64_t _id,
SourceLocation const& _location,
std::vector<Token> const& _tokens,
std::vector<ASTString> const& _literals
): ASTNode(_location), m_tokens(_tokens), m_literals(_literals)
): ASTNode(_id, _location), m_tokens(_tokens), m_literals(_literals)
{}
void accept(ASTVisitor& _visitor) override;
@ -295,12 +295,13 @@ public:
using SymbolAliasList = std::vector<SymbolAlias>;
ImportDirective(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _path,
ASTPointer<ASTString> const& _unitAlias,
SymbolAliasList _symbolAliases
):
Declaration(_location, _unitAlias),
Declaration(_id, _location, _unitAlias),
m_path(_path),
m_symbolAliases(move(_symbolAliases))
{ }
@ -385,6 +386,7 @@ class ContractDefinition: public Declaration, public Documented
{
public:
ContractDefinition(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation,
@ -393,7 +395,7 @@ public:
ContractKind _contractKind = ContractKind::Contract,
bool _abstract = false
):
Declaration(_location, _name),
Declaration(_id, _location, _name),
Documented(_documentation),
m_baseContracts(_baseContracts),
m_subNodes(_subNodes),
@ -465,11 +467,12 @@ class InheritanceSpecifier: public ASTNode
{
public:
InheritanceSpecifier(
int64_t _id,
SourceLocation const& _location,
ASTPointer<UserDefinedTypeName> const& _baseName,
std::unique_ptr<std::vector<ASTPointer<Expression>>> _arguments
):
ASTNode(_location), m_baseName(_baseName), m_arguments(std::move(_arguments)) {}
ASTNode(_id, _location), m_baseName(_baseName), m_arguments(std::move(_arguments)) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -494,11 +497,12 @@ class UsingForDirective: public ASTNode
{
public:
UsingForDirective(
int64_t _id,
SourceLocation const& _location,
ASTPointer<UserDefinedTypeName> const& _libraryName,
ASTPointer<TypeName> const& _typeName
):
ASTNode(_location), m_libraryName(_libraryName), m_typeName(_typeName) {}
ASTNode(_id, _location), m_libraryName(_libraryName), m_typeName(_typeName) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -516,11 +520,12 @@ class StructDefinition: public Declaration
{
public:
StructDefinition(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _name,
std::vector<ASTPointer<VariableDeclaration>> const& _members
):
Declaration(_location, _name), m_members(_members) {}
Declaration(_id, _location, _name), m_members(_members) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -539,11 +544,12 @@ class EnumDefinition: public Declaration
{
public:
EnumDefinition(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _name,
std::vector<ASTPointer<EnumValue>> const& _members
):
Declaration(_location, _name), m_members(_members) {}
Declaration(_id, _location, _name), m_members(_members) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -563,8 +569,8 @@ private:
class EnumValue: public Declaration
{
public:
EnumValue(SourceLocation const& _location, ASTPointer<ASTString> const& _name):
Declaration(_location, _name) {}
EnumValue(int64_t _id, SourceLocation const& _location, ASTPointer<ASTString> const& _name):
Declaration(_id, _location, _name) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -581,10 +587,11 @@ class ParameterList: public ASTNode
{
public:
ParameterList(
int64_t _id,
SourceLocation const& _location,
std::vector<ASTPointer<VariableDeclaration>> const& _parameters
):
ASTNode(_location), m_parameters(_parameters) {}
ASTNode(_id, _location), m_parameters(_parameters) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -602,6 +609,7 @@ class CallableDeclaration: public Declaration, public VariableScope
{
public:
CallableDeclaration(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _name,
Visibility _visibility,
@ -610,7 +618,7 @@ public:
ASTPointer<OverrideSpecifier> const& _overrides = nullptr,
ASTPointer<ParameterList> const& _returnParameters = ASTPointer<ParameterList>()
):
Declaration(_location, _name, _visibility),
Declaration(_id, _location, _name, _visibility),
m_parameters(_parameters),
m_overrides(_overrides),
m_returnParameters(_returnParameters),
@ -643,10 +651,11 @@ class OverrideSpecifier: public ASTNode
{
public:
OverrideSpecifier(
int64_t _id,
SourceLocation const& _location,
std::vector<ASTPointer<UserDefinedTypeName>> const& _overrides
):
ASTNode(_location),
ASTNode(_id, _location),
m_overrides(_overrides)
{
}
@ -665,6 +674,7 @@ class FunctionDefinition: public CallableDeclaration, public Documented, public
{
public:
FunctionDefinition(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _name,
Visibility _visibility,
@ -678,7 +688,7 @@ public:
ASTPointer<ParameterList> const& _returnParameters,
ASTPointer<Block> const& _body
):
CallableDeclaration(_location, _name, _visibility, _parameters, _isVirtual, _overrides, _returnParameters),
CallableDeclaration(_id, _location, _name, _visibility, _parameters, _isVirtual, _overrides, _returnParameters),
Documented(_documentation),
ImplementationOptional(_body != nullptr),
m_stateMutability(_stateMutability),
@ -748,7 +758,8 @@ public:
enum Location { Unspecified, Storage, Memory, CallData };
VariableDeclaration(
SourceLocation const& _sourceLocation,
int64_t _id,
SourceLocation const& _location,
ASTPointer<TypeName> const& _type,
ASTPointer<ASTString> const& _name,
ASTPointer<Expression> _value,
@ -759,7 +770,7 @@ public:
ASTPointer<OverrideSpecifier> const& _overrides = nullptr,
Location _referenceLocation = Location::Unspecified
):
Declaration(_sourceLocation, _name, _visibility),
Declaration(_id, _location, _name, _visibility),
m_typeName(_type),
m_value(_value),
m_isStateVariable(_isStateVar),
@ -847,6 +858,7 @@ class ModifierDefinition: public CallableDeclaration, public Documented
{
public:
ModifierDefinition(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation,
@ -855,7 +867,7 @@ public:
ASTPointer<OverrideSpecifier> const& _overrides,
ASTPointer<Block> const& _body
):
CallableDeclaration(_location, _name, Visibility::Internal, _parameters, _isVirtual, _overrides),
CallableDeclaration(_id, _location, _name, Visibility::Internal, _parameters, _isVirtual, _overrides),
Documented(_documentation),
m_body(_body)
{
@ -881,11 +893,12 @@ class ModifierInvocation: public ASTNode
{
public:
ModifierInvocation(
int64_t _id,
SourceLocation const& _location,
ASTPointer<Identifier> const& _name,
std::unique_ptr<std::vector<ASTPointer<Expression>>> _arguments
):
ASTNode(_location), m_modifierName(_name), m_arguments(std::move(_arguments)) {}
ASTNode(_id, _location), m_modifierName(_name), m_arguments(std::move(_arguments)) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -908,13 +921,14 @@ class EventDefinition: public CallableDeclaration, public Documented
{
public:
EventDefinition(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation,
ASTPointer<ParameterList> const& _parameters,
bool _anonymous = false
):
CallableDeclaration(_location, _name, Visibility::Default, _parameters),
CallableDeclaration(_id, _location, _name, Visibility::Default, _parameters),
Documented(_documentation),
m_anonymous(_anonymous)
{
@ -936,13 +950,13 @@ private:
/**
* Pseudo AST node that is used as declaration for "this", "msg", "tx", "block" and the global
* functions when such an identifier is encountered. Will never have a valid location in the source code.
* functions when such an identifier is encountered. Will never have a valid location in the source code
*/
class MagicVariableDeclaration: public Declaration
{
public:
MagicVariableDeclaration(ASTString const& _name, Type const* _type):
Declaration(SourceLocation(), std::make_shared<ASTString>(_name)), m_type(_type) {}
MagicVariableDeclaration(int _id, ASTString const& _name, Type const* _type):
Declaration(_id, SourceLocation(), std::make_shared<ASTString>(_name)), m_type(_type) { }
void accept(ASTVisitor&) override
{
@ -973,7 +987,7 @@ private:
class TypeName: public ASTNode
{
protected:
explicit TypeName(SourceLocation const& _location): ASTNode(_location) {}
explicit TypeName(int64_t _id, SourceLocation const& _location): ASTNode(_id, _location) {}
public:
TypeNameAnnotation& annotation() const override;
@ -987,10 +1001,11 @@ class ElementaryTypeName: public TypeName
{
public:
ElementaryTypeName(
int64_t _id,
SourceLocation const& _location,
ElementaryTypeNameToken const& _elem,
std::optional<StateMutability> _stateMutability = {}
): TypeName(_location), m_type(_elem), m_stateMutability(_stateMutability)
): TypeName(_id, _location), m_type(_elem), m_stateMutability(_stateMutability)
{
solAssert(!_stateMutability.has_value() || _elem.token() == Token::Address, "");
}
@ -1013,8 +1028,8 @@ private:
class UserDefinedTypeName: public TypeName
{
public:
UserDefinedTypeName(SourceLocation const& _location, std::vector<ASTString> const& _namePath):
TypeName(_location), m_namePath(_namePath) {}
UserDefinedTypeName(int64_t _id, SourceLocation const& _location, std::vector<ASTString> const& _namePath):
TypeName(_id, _location), m_namePath(_namePath) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -1033,13 +1048,14 @@ class FunctionTypeName: public TypeName
{
public:
FunctionTypeName(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ParameterList> const& _parameterTypes,
ASTPointer<ParameterList> const& _returnTypes,
Visibility _visibility,
StateMutability _stateMutability
):
TypeName(_location), m_parameterTypes(_parameterTypes), m_returnTypes(_returnTypes),
TypeName(_id, _location), m_parameterTypes(_parameterTypes), m_returnTypes(_returnTypes),
m_visibility(_visibility), m_stateMutability(_stateMutability)
{}
void accept(ASTVisitor& _visitor) override;
@ -1071,11 +1087,12 @@ class Mapping: public TypeName
{
public:
Mapping(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ElementaryTypeName> const& _keyType,
ASTPointer<TypeName> const& _valueType
):
TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {}
TypeName(_id, _location), m_keyType(_keyType), m_valueType(_valueType) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -1094,11 +1111,12 @@ class ArrayTypeName: public TypeName
{
public:
ArrayTypeName(
int64_t _id,
SourceLocation const& _location,
ASTPointer<TypeName> const& _baseType,
ASTPointer<Expression> const& _length
):
TypeName(_location), m_baseType(_baseType), m_length(_length) {}
TypeName(_id, _location), m_baseType(_baseType), m_length(_length) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -1123,9 +1141,10 @@ class Statement: public ASTNode, public Documented
{
public:
explicit Statement(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _docString
): ASTNode(_location), Documented(_docString) {}
): ASTNode(_id, _location), Documented(_docString) {}
StatementAnnotation& annotation() const override;
};
@ -1137,12 +1156,13 @@ class InlineAssembly: public Statement
{
public:
InlineAssembly(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _docString,
yul::Dialect const& _dialect,
std::shared_ptr<yul::Block> const& _operations
):
Statement(_location, _docString), m_dialect(_dialect), m_operations(_operations) {}
Statement(_id, _location, _docString), m_dialect(_dialect), m_operations(_operations) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -1163,11 +1183,12 @@ class Block: public Statement, public Scopable
{
public:
Block(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _docString,
std::vector<ASTPointer<Statement>> const& _statements
):
Statement(_location, _docString), m_statements(_statements) {}
Statement(_id, _location, _docString), m_statements(_statements) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -1187,9 +1208,10 @@ class PlaceholderStatement: public Statement
{
public:
explicit PlaceholderStatement(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _docString
): Statement(_location, _docString) {}
): Statement(_id, _location, _docString) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -1203,13 +1225,14 @@ class IfStatement: public Statement
{
public:
IfStatement(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _docString,
ASTPointer<Expression> const& _condition,
ASTPointer<Statement> const& _trueBody,
ASTPointer<Statement> const& _falseBody
):
Statement(_location, _docString),
Statement(_id, _location, _docString),
m_condition(_condition),
m_trueBody(_trueBody),
m_falseBody(_falseBody)
@ -1237,12 +1260,13 @@ class TryCatchClause: public ASTNode, public Scopable
{
public:
TryCatchClause(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _errorName,
ASTPointer<ParameterList> const& _parameters,
ASTPointer<Block> const& _block
):
ASTNode(_location),
ASTNode(_id, _location),
m_errorName(_errorName),
m_parameters(_parameters),
m_block(_block)
@ -1280,12 +1304,13 @@ class TryStatement: public Statement
{
public:
TryStatement(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _docString,
ASTPointer<Expression> const& _externalCall,
std::vector<ASTPointer<TryCatchClause>> const& _clauses
):
Statement(_location, _docString),
Statement(_id, _location, _docString),
m_externalCall(_externalCall),
m_clauses(_clauses)
{}
@ -1307,22 +1332,24 @@ class BreakableStatement: public Statement
{
public:
explicit BreakableStatement(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _docString
): Statement(_location, _docString) {}
): Statement(_id, _location, _docString) {}
};
class WhileStatement: public BreakableStatement
{
public:
WhileStatement(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _docString,
ASTPointer<Expression> const& _condition,
ASTPointer<Statement> const& _body,
bool _isDoWhile
):
BreakableStatement(_location, _docString), m_condition(_condition), m_body(_body),
BreakableStatement(_id, _location, _docString), m_condition(_condition), m_body(_body),
m_isDoWhile(_isDoWhile) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -1344,6 +1371,7 @@ class ForStatement: public BreakableStatement, public Scopable
{
public:
ForStatement(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _docString,
ASTPointer<Statement> const& _initExpression,
@ -1351,7 +1379,7 @@ public:
ASTPointer<ExpressionStatement> const& _loopExpression,
ASTPointer<Statement> const& _body
):
BreakableStatement(_location, _docString),
BreakableStatement(_id, _location, _docString),
m_initExpression(_initExpression),
m_condExpression(_conditionExpression),
m_loopExpression(_loopExpression),
@ -1381,8 +1409,8 @@ private:
class Continue: public Statement
{
public:
explicit Continue(SourceLocation const& _location, ASTPointer<ASTString> const& _docString):
Statement(_location, _docString) {}
explicit Continue(int64_t _id, SourceLocation const& _location, ASTPointer<ASTString> const& _docString):
Statement(_id, _location, _docString) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
};
@ -1390,8 +1418,8 @@ public:
class Break: public Statement
{
public:
explicit Break(SourceLocation const& _location, ASTPointer<ASTString> const& _docString):
Statement(_location, _docString) {}
explicit Break(int64_t _id, SourceLocation const& _location, ASTPointer<ASTString> const& _docString):
Statement(_id, _location, _docString) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
};
@ -1400,10 +1428,11 @@ class Return: public Statement
{
public:
Return(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _docString,
ASTPointer<Expression> _expression
): Statement(_location, _docString), m_expression(_expression) {}
): Statement(_id, _location, _docString), m_expression(_expression) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -1421,8 +1450,8 @@ private:
class Throw: public Statement
{
public:
explicit Throw(SourceLocation const& _location, ASTPointer<ASTString> const& _docString):
Statement(_location, _docString) {}
explicit Throw(int64_t _id, SourceLocation const& _location, ASTPointer<ASTString> const& _docString):
Statement(_id, _location, _docString) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
};
@ -1434,11 +1463,12 @@ class EmitStatement: public Statement
{
public:
explicit EmitStatement(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _docString,
ASTPointer<FunctionCall> const& _functionCall
):
Statement(_location, _docString), m_eventCall(_functionCall) {}
Statement(_id, _location, _docString), m_eventCall(_functionCall) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -1460,12 +1490,13 @@ class VariableDeclarationStatement: public Statement
{
public:
VariableDeclarationStatement(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _docString,
std::vector<ASTPointer<VariableDeclaration>> const& _variables,
ASTPointer<Expression> const& _initialValue
):
Statement(_location, _docString), m_variables(_variables), m_initialValue(_initialValue) {}
Statement(_id, _location, _docString), m_variables(_variables), m_initialValue(_initialValue) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -1489,11 +1520,12 @@ class ExpressionStatement: public Statement
{
public:
ExpressionStatement(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _docString,
ASTPointer<Expression> _expression
):
Statement(_location, _docString), m_expression(_expression) {}
Statement(_id, _location, _docString), m_expression(_expression) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -1516,7 +1548,7 @@ private:
class Expression: public ASTNode
{
public:
explicit Expression(SourceLocation const& _location): ASTNode(_location) {}
explicit Expression(int64_t _id, SourceLocation const& _location): ASTNode(_id, _location) {}
ExpressionAnnotation& annotation() const override;
};
@ -1525,12 +1557,13 @@ class Conditional: public Expression
{
public:
Conditional(
int64_t _id,
SourceLocation const& _location,
ASTPointer<Expression> const& _condition,
ASTPointer<Expression> const& _trueExpression,
ASTPointer<Expression> const& _falseExpression
):
Expression(_location),
Expression(_id, _location),
m_condition(_condition),
m_trueExpression(_trueExpression),
m_falseExpression(_falseExpression)
@ -1554,12 +1587,13 @@ class Assignment: public Expression
{
public:
Assignment(
int64_t _id,
SourceLocation const& _location,
ASTPointer<Expression> const& _leftHandSide,
Token _assignmentOperator,
ASTPointer<Expression> const& _rightHandSide
):
Expression(_location),
Expression(_id, _location),
m_leftHandSide(_leftHandSide),
m_assigmentOperator(_assignmentOperator),
m_rightHandSide(_rightHandSide)
@ -1591,11 +1625,12 @@ class TupleExpression: public Expression
{
public:
TupleExpression(
int64_t _id,
SourceLocation const& _location,
std::vector<ASTPointer<Expression>> const& _components,
bool _isArray
):
Expression(_location),
Expression(_id, _location),
m_components(_components),
m_isArray(_isArray) {}
void accept(ASTVisitor& _visitor) override;
@ -1617,12 +1652,13 @@ class UnaryOperation: public Expression
{
public:
UnaryOperation(
int64_t _id,
SourceLocation const& _location,
Token _operator,
ASTPointer<Expression> const& _subExpression,
bool _isPrefix
):
Expression(_location),
Expression(_id, _location),
m_operator(_operator),
m_subExpression(_subExpression),
m_isPrefix(_isPrefix)
@ -1650,12 +1686,13 @@ class BinaryOperation: public Expression
{
public:
BinaryOperation(
int64_t _id,
SourceLocation const& _location,
ASTPointer<Expression> const& _left,
Token _operator,
ASTPointer<Expression> const& _right
):
Expression(_location), m_left(_left), m_operator(_operator), m_right(_right)
Expression(_id, _location), m_left(_left), m_operator(_operator), m_right(_right)
{
solAssert(TokenTraits::isBinaryOp(_operator) || TokenTraits::isCompareOp(_operator), "");
}
@ -1681,12 +1718,13 @@ class FunctionCall: public Expression
{
public:
FunctionCall(
int64_t _id,
SourceLocation const& _location,
ASTPointer<Expression> const& _expression,
std::vector<ASTPointer<Expression>> const& _arguments,
std::vector<ASTPointer<ASTString>> const& _names
):
Expression(_location), m_expression(_expression), m_arguments(_arguments), m_names(_names) {}
Expression(_id, _location), m_expression(_expression), m_arguments(_arguments), m_names(_names) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -1710,10 +1748,11 @@ class NewExpression: public Expression
{
public:
NewExpression(
int64_t _id,
SourceLocation const& _location,
ASTPointer<TypeName> const& _typeName
):
Expression(_location), m_typeName(_typeName) {}
Expression(_id, _location), m_typeName(_typeName) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -1730,11 +1769,12 @@ class MemberAccess: public Expression
{
public:
MemberAccess(
int64_t _id,
SourceLocation const& _location,
ASTPointer<Expression> _expression,
ASTPointer<ASTString> const& _memberName
):
Expression(_location), m_expression(_expression), m_memberName(_memberName) {}
Expression(_id, _location), m_expression(_expression), m_memberName(_memberName) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
Expression const& expression() const { return *m_expression; }
@ -1754,11 +1794,12 @@ class IndexAccess: public Expression
{
public:
IndexAccess(
int64_t _id,
SourceLocation const& _location,
ASTPointer<Expression> const& _base,
ASTPointer<Expression> const& _index
):
Expression(_location), m_base(_base), m_index(_index) {}
Expression(_id, _location), m_base(_base), m_index(_index) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -1777,12 +1818,13 @@ class IndexRangeAccess: public Expression
{
public:
IndexRangeAccess(
int64_t _id,
SourceLocation const& _location,
ASTPointer<Expression> const& _base,
ASTPointer<Expression> const& _start,
ASTPointer<Expression> const& _end
):
Expression(_location), m_base(_base), m_start(_start), m_end(_end) {}
Expression(_id, _location), m_base(_base), m_start(_start), m_end(_end) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -1803,7 +1845,7 @@ private:
class PrimaryExpression: public Expression
{
public:
PrimaryExpression(SourceLocation const& _location): Expression(_location) {}
PrimaryExpression(int64_t _id, SourceLocation const& _location): Expression(_id, _location) {}
};
/**
@ -1813,10 +1855,11 @@ class Identifier: public PrimaryExpression
{
public:
Identifier(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _name
):
PrimaryExpression(_location), m_name(_name) {}
PrimaryExpression(_id, _location), m_name(_name) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -1837,10 +1880,11 @@ class ElementaryTypeNameExpression: public PrimaryExpression
{
public:
ElementaryTypeNameExpression(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ElementaryTypeName> const& _type
):
PrimaryExpression(_location),
PrimaryExpression(_id, _location),
m_type(_type)
{
}
@ -1874,12 +1918,13 @@ public:
Year = static_cast<int>(Token::SubYear)
};
Literal(
int64_t _id,
SourceLocation const& _location,
Token _token,
ASTPointer<ASTString> const& _value,
SubDenomination _sub = SubDenomination::None
):
PrimaryExpression(_location), m_token(_token), m_value(_value), m_subDenomination(_sub) {}
PrimaryExpression(_id, _location), m_token(_token), m_value(_value), m_subDenomination(_sub) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;

View File

@ -26,6 +26,8 @@
#include <libyul/AsmJsonConverter.h>
#include <libyul/AsmData.h>
#include <libyul/AsmPrinter.h>
#include <libyul/backends/evm/EVMDialect.h>
#include <libsolutil/JSON.h>
#include <libsolutil/UTF8.h>
@ -492,13 +494,16 @@ bool ASTJsonConverter::visit(ArrayTypeName const& _node)
bool ASTJsonConverter::visit(InlineAssembly const& _node)
{
vector<pair<string, Json::Value>> externalReferences;
for (auto const& it: _node.annotation().externalReferences)
if (it.first)
externalReferences.emplace_back(make_pair(
it.first->name.str(),
inlineAssemblyIdentifierToJson(it)
));
Json::Value externalReferencesJson = Json::arrayValue;
for (auto&& it: boost::range::sort(externalReferences))
externalReferencesJson.append(std::move(it.second));
@ -506,8 +511,10 @@ bool ASTJsonConverter::visit(InlineAssembly const& _node)
m_legacy ?
make_pair("operations", Json::Value(yul::AsmPrinter()(_node.operations()))) :
make_pair("AST", Json::Value(yul::AsmJsonConverter(sourceIndexFromLocation(_node.location()))(_node.operations()))),
make_pair("externalReferences", std::move(externalReferencesJson))
make_pair("externalReferences", std::move(externalReferencesJson)),
make_pair("evmVersion", dynamic_cast<solidity::yul::EVMDialect const&>(_node.dialect()).evmVersion().name())
});
return false;
}

View File

@ -0,0 +1,982 @@
/*
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/>.
*/
/**
* @author julius <djudju@protonmail.com>
* @date 2019
*Component that imports an AST from json format to the internal format
*/
#include <libsolidity/ast/ASTJsonImporter.h>
#include <libsolidity/ast/AsmJsonImporter.h>
#include <liblangutil/Scanner.h>
#include <libyul/Dialect.h>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string.hpp>
#include <liblangutil/Token.h>
#include <libyul/AsmParser.h>
#include <libyul/backends/evm/EVMDialect.h>
#include <liblangutil/SourceLocation.h>
#include <liblangutil/Exceptions.h>
#include <liblangutil/ErrorReporter.h>
using namespace std;
namespace solidity::frontend
{
using SourceLocation = langutil::SourceLocation;
template<class T>
ASTPointer<T> ASTJsonImporter::nullOrCast(Json::Value const& _json)
{
if (_json.isNull())
return nullptr;
else
return dynamic_pointer_cast<T>(convertJsonToASTNode(_json));
}
// ============ public ===========================
map<string, ASTPointer<SourceUnit>> ASTJsonImporter::jsonToSourceUnit(map<string, Json::Value> const& _sourceList)
{
m_sourceList = _sourceList;
for (auto const& src: _sourceList)
m_sourceLocations.emplace_back(make_shared<string const>(src.first));
for (auto const& srcPair: m_sourceList)
{
astAssert(!srcPair.second.isNull(), "");
astAssert(member(srcPair.second,"nodeType") == "SourceUnit", "The 'nodeType' of the highest node must be 'SourceUnit'.");
m_currentSourceName = srcPair.first;
m_sourceUnits[srcPair.first] = createSourceUnit(srcPair.second, srcPair.first);
}
return m_sourceUnits;
}
// ============ private ===========================
// =========== general creation functions ==============
template <typename T, typename... Args>
ASTPointer<T> ASTJsonImporter::createASTNode(Json::Value const& _node, Args&&... _args)
{
astAssert(member(_node, "id").isInt64(), "'id'-field must be 64bit integer.");
int64_t id = _node["id"].asInt64();
astAssert(m_usedIDs.insert(id).second, "Found duplicate node ID!");
auto n = make_shared<T>(
id,
createSourceLocation(_node),
forward<Args>(_args)...
);
return n;
}
SourceLocation const ASTJsonImporter::createSourceLocation(Json::Value const& _node)
{
astAssert(member(_node, "src").isString(), "'src' must be a string");
return solidity::langutil::parseSourceLocation(_node["src"].asString(), m_currentSourceName, int(m_sourceLocations.size()));
}
template<class T>
ASTPointer<T> ASTJsonImporter::convertJsonToASTNode(Json::Value const& _node)
{
ASTPointer<T> ret = dynamic_pointer_cast<T>(convertJsonToASTNode(_node));
astAssert(ret, "cast of converted json-node must not be nullptr");
return ret;
}
ASTPointer<ASTNode> ASTJsonImporter::convertJsonToASTNode(Json::Value const& _json)
{
astAssert(_json["nodeType"].isString() && _json.isMember("id"), "JSON-Node needs to have 'nodeType' and 'id' fields.");
string nodeType = _json["nodeType"].asString();
if (nodeType == "PragmaDirective")
return createPragmaDirective(_json);
if (nodeType == "ImportDirective")
return createImportDirective(_json);
if (nodeType == "ContractDefinition")
return createContractDefinition(_json);
if (nodeType == "InheritanceSpecifier")
return createInheritanceSpecifier(_json);
if (nodeType == "UsingForDirective")
return createUsingForDirective(_json);
if (nodeType == "StructDefinition")
return createStructDefinition(_json);
if (nodeType == "EnumDefinition")
return createEnumDefinition(_json);
if (nodeType == "EnumValue")
return createEnumValue(_json);
if (nodeType == "ParameterList")
return createParameterList(_json);
if (nodeType == "OverrideSpecifier")
return createOverrideSpecifier(_json);
if (nodeType == "FunctionDefinition")
return createFunctionDefinition(_json);
if (nodeType == "VariableDeclaration")
return createVariableDeclaration(_json);
if (nodeType == "ModifierDefinition")
return createModifierDefinition(_json);
if (nodeType == "ModifierInvocation")
return createModifierInvocation(_json);
if (nodeType == "EventDefinition")
return createEventDefinition(_json);
if (nodeType == "ElementaryTypeName")
return createElementaryTypeName(_json);
if (nodeType == "UserDefinedTypeName")
return createUserDefinedTypeName(_json);
if (nodeType == "FunctionTypeName")
return createFunctionTypeName(_json);
if (nodeType == "Mapping")
return createMapping(_json);
if (nodeType == "ArrayTypeName")
return createArrayTypeName(_json);
if (nodeType == "InlineAssembly")
return createInlineAssembly(_json);
if (nodeType == "Block")
return createBlock(_json);
if (nodeType == "PlaceholderStatement")
return createPlaceholderStatement(_json);
if (nodeType == "IfStatement")
return createIfStatement(_json);
if (nodeType == "TryCatchClause")
return createTryCatchClause(_json);
if (nodeType == "TryStatement")
return createTryStatement(_json);
if (nodeType == "WhileStatement")
return createWhileStatement(_json, false);
if (nodeType == "DoWhileStatement")
return createWhileStatement(_json, true);
if (nodeType == "ForStatement")
return createForStatement(_json);
if (nodeType == "Continue")
return createContinue(_json);
if (nodeType == "Break")
return createBreak(_json);
if (nodeType == "Return")
return createReturn(_json);
if (nodeType == "EmitStatement")
return createEmitStatement(_json);
if (nodeType == "Throw")
return createThrow(_json);
if (nodeType == "VariableDeclarationStatement")
return createVariableDeclarationStatement(_json);
if (nodeType == "ExpressionStatement")
return createExpressionStatement(_json);
if (nodeType == "Conditional")
return createConditional(_json);
if (nodeType == "Assignment")
return createAssignment(_json);
if (nodeType == "TupleExpression")
return createTupleExpression(_json);
if (nodeType == "UnaryOperation")
return createUnaryOperation(_json);
if (nodeType == "BinaryOperation")
return createBinaryOperation(_json);
if (nodeType == "FunctionCall")
return createFunctionCall(_json);
if (nodeType == "NewExpression")
return createNewExpression(_json);
if (nodeType == "MemberAccess")
return createMemberAccess(_json);
if (nodeType == "IndexAccess")
return createIndexAccess(_json);
if (nodeType == "IndexRangeAccess")
return createIndexRangeAccess(_json);
if (nodeType == "Identifier")
return createIdentifier(_json);
if (nodeType == "ElementaryTypeNameExpression")
return createElementaryTypeNameExpression(_json);
if (nodeType == "Literal")
return createLiteral(_json);
else
astAssert(false, "Unknown type of ASTNode: " + nodeType);
}
// ============ functions to instantiate the AST-Nodes from Json-Nodes ==============
ASTPointer<SourceUnit> ASTJsonImporter::createSourceUnit(Json::Value const& _node, string const& _srcName)
{
vector<ASTPointer<ASTNode>> nodes;
for (auto& child: member(_node, "nodes"))
nodes.emplace_back(convertJsonToASTNode(child));
ASTPointer<SourceUnit> tmp = createASTNode<SourceUnit>(_node, nodes);
tmp->annotation().path = _srcName;
return tmp;
}
ASTPointer<PragmaDirective> ASTJsonImporter::createPragmaDirective(Json::Value const& _node)
{
vector<Token> tokens;
vector<ASTString> literals;
for (auto const& lit: member(_node, "literals"))
{
string l = lit.asString();
literals.push_back(l);
tokens.push_back(scanSingleToken(l));
}
return createASTNode<PragmaDirective>(_node, tokens, literals);
}
ASTPointer<ImportDirective> ASTJsonImporter::createImportDirective(Json::Value const& _node)
{
ASTPointer<ASTString> unitAlias = memberAsASTString(_node, "unitAlias");
ASTPointer<ASTString> path = memberAsASTString(_node, "file");
ImportDirective::SymbolAliasList symbolAliases;
for (auto& tuple: member(_node, "symbolAliases"))
{
astAssert(tuple["local"].isNull() || tuple["local"].isString(), "expected 'local' to be a string or null!");
symbolAliases.push_back({
createIdentifier(tuple["foreign"]),
tuple["local"].isNull() ? nullptr : make_shared<ASTString>(tuple["local"].asString()),
createSourceLocation(tuple["foreign"])}
);
}
ASTPointer<ImportDirective> tmp = createASTNode<ImportDirective>(
_node,
path,
unitAlias,
move(symbolAliases)
);
astAssert(_node["absolutePath"].isString(), "Expected 'absolutePath' to be a string!");
tmp->annotation().absolutePath = _node["absolutePath"].asString();
return tmp;
}
ASTPointer<ContractDefinition> ASTJsonImporter::createContractDefinition(Json::Value const& _node)
{
astAssert(_node["name"].isString(), "Expected 'name' to be a string!");
std::vector<ASTPointer<InheritanceSpecifier>> baseContracts;
for (auto& base: _node["baseContracts"])
baseContracts.push_back(createInheritanceSpecifier(base));
std::vector<ASTPointer<ASTNode>> subNodes;
for (auto& subnode: _node["nodes"])
subNodes.push_back(convertJsonToASTNode(subnode));
return createASTNode<ContractDefinition>(
_node,
make_shared<ASTString>(_node["name"].asString()),
nullOrASTString(_node, "documentation"),
baseContracts,
subNodes,
contractKind(_node),
memberAsBool(_node, "abstract")
);
}
ASTPointer<InheritanceSpecifier> ASTJsonImporter::createInheritanceSpecifier(Json::Value const& _node)
{
std::vector<ASTPointer<Expression>> arguments;
for (auto& arg: member(_node, "arguments"))
arguments.push_back(convertJsonToASTNode<Expression>(arg));
return createASTNode<InheritanceSpecifier>(
_node,
createUserDefinedTypeName(member(_node, "baseName")),
member(_node, "arguments").isNull() ? nullptr : make_unique<std::vector<ASTPointer<Expression>>>(arguments)
);
}
ASTPointer<UsingForDirective> ASTJsonImporter::createUsingForDirective(Json::Value const& _node)
{
return createASTNode<UsingForDirective>(
_node,
createUserDefinedTypeName(member(_node, "libraryName")),
_node["typeName"].isNull() ? nullptr : convertJsonToASTNode<TypeName>(_node["typeName"])
);
}
ASTPointer<ASTNode> ASTJsonImporter::createStructDefinition(Json::Value const& _node)
{
std::vector<ASTPointer<VariableDeclaration>> members;
for (auto& member: _node["members"])
members.push_back(createVariableDeclaration(member));
return createASTNode<StructDefinition>(
_node,
memberAsASTString(_node, "name"),
members
);
}
ASTPointer<EnumDefinition> ASTJsonImporter::createEnumDefinition(Json::Value const& _node)
{
std::vector<ASTPointer<EnumValue>> members;
for (auto& member: _node["members"])
members.push_back(createEnumValue(member));
return createASTNode<EnumDefinition>(
_node,
memberAsASTString(_node, "name"),
members
);
}
ASTPointer<EnumValue> ASTJsonImporter::createEnumValue(Json::Value const& _node)
{
return createASTNode<EnumValue>(
_node,
memberAsASTString(_node, "name")
);
}
ASTPointer<ParameterList> ASTJsonImporter::createParameterList(Json::Value const& _node)
{
std::vector<ASTPointer<VariableDeclaration>> parameters;
for (auto& param: _node["parameters"])
parameters.push_back(createVariableDeclaration(param));
return createASTNode<ParameterList>(
_node,
parameters
);
}
ASTPointer<OverrideSpecifier> ASTJsonImporter::createOverrideSpecifier(Json::Value const& _node)
{
std::vector<ASTPointer<UserDefinedTypeName>> overrides;
for (auto& param: _node["overrides"])
overrides.push_back(createUserDefinedTypeName(param));
return createASTNode<OverrideSpecifier>(
_node,
overrides
);
}
ASTPointer<FunctionDefinition> ASTJsonImporter::createFunctionDefinition(Json::Value const& _node)
{
astAssert(_node["kind"].isString(), "Expected 'kind' to be a string!");
Token kind;
string kindStr = member(_node, "kind").asString();
if (kindStr == "constructor")
kind = Token::Constructor;
else if (kindStr == "function")
kind = Token::Function;
else if (kindStr == "fallback")
kind = Token::Fallback;
else if (kindStr == "receive")
kind = Token::Receive;
else
astAssert(false, "Expected 'kind' to be one of [constructor, function, fallback, receive]");
std::vector<ASTPointer<ModifierInvocation>> modifiers;
for (auto& mod: member(_node, "modifiers"))
modifiers.push_back(createModifierInvocation(mod));
return createASTNode<FunctionDefinition>(
_node,
memberAsASTString(_node, "name"),
visibility(_node),
stateMutability(_node),
kind,
memberAsBool(_node, "virtual"),
_node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")),
nullOrASTString(_node, "documentation"),
createParameterList(member(_node, "parameters")),
modifiers,
createParameterList(member(_node, "returnParameters")),
memberAsBool(_node, "implemented") ? createBlock(member(_node, "body")) : nullptr
);
}
ASTPointer<VariableDeclaration> ASTJsonImporter::createVariableDeclaration(Json::Value const& _node)
{
astAssert(_node["name"].isString(), "Expected 'name' to be a string!");
return createASTNode<VariableDeclaration>(
_node,
nullOrCast<TypeName>(member(_node, "typeName")),
make_shared<ASTString>(member(_node, "name").asString()),
nullOrCast<Expression>(member(_node, "value")),
visibility(_node),
memberAsBool(_node, "stateVariable"),
_node.isMember("indexed") ? memberAsBool(_node, "indexed") : false,
memberAsBool(_node, "constant"),
_node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")),
location(_node)
);
}
ASTPointer<ModifierDefinition> ASTJsonImporter::createModifierDefinition(Json::Value const& _node)
{
return createASTNode<ModifierDefinition>(
_node,
memberAsASTString(_node, "name"),
nullOrASTString(_node,"documentation"),
createParameterList(member(_node, "parameters")),
memberAsBool(_node, "virtual"),
_node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")),
createBlock(member(_node, "body"))
);
}
ASTPointer<ModifierInvocation> ASTJsonImporter::createModifierInvocation(Json::Value const& _node)
{
std::vector<ASTPointer<Expression>> arguments;
for (auto& arg: member(_node, "arguments"))
arguments.push_back(convertJsonToASTNode<Expression>(arg));
return createASTNode<ModifierInvocation>(
_node,
createIdentifier(member(_node, "modifierName")),
member(_node, "arguments").isNull() ? nullptr : make_unique<std::vector<ASTPointer<Expression>>>(arguments)
);
}
ASTPointer<EventDefinition> ASTJsonImporter::createEventDefinition(Json::Value const& _node)
{
return createASTNode<EventDefinition>(
_node,
memberAsASTString(_node, "name"),
nullOrASTString(_node, "documentation"),
createParameterList(member(_node, "parameters")),
memberAsBool(_node, "anonymous")
);
}
ASTPointer<ElementaryTypeName> ASTJsonImporter::createElementaryTypeName(Json::Value const& _node)
{
unsigned short firstNum;
unsigned short secondNum;
astAssert(_node["name"].isString(), "Expected 'name' to be a string!");
string name = member(_node, "name").asString();
Token token;
tie(token, firstNum, secondNum) = TokenTraits::fromIdentifierOrKeyword(name);
ElementaryTypeNameToken elem(token, firstNum, secondNum);
std::optional<StateMutability> mutability = {};
if (_node.isMember("stateMutability"))
mutability = stateMutability(_node);
return createASTNode<ElementaryTypeName>(_node, elem, mutability);
}
ASTPointer<UserDefinedTypeName> ASTJsonImporter::createUserDefinedTypeName(Json::Value const& _node)
{
astAssert(_node["name"].isString(), "Expected 'name' to be a string!");
vector<ASTString> namePath;
vector<string> strs;
string nameString = member(_node, "name").asString();
boost::algorithm::split(strs, nameString, boost::is_any_of("."));
for (string s: strs)
namePath.push_back(ASTString(s));
return createASTNode<UserDefinedTypeName>(
_node,
namePath
);
}
ASTPointer<FunctionTypeName> ASTJsonImporter::createFunctionTypeName(Json::Value const& _node)
{
return createASTNode<FunctionTypeName>(
_node,
createParameterList(member(_node, "parameterTypes")),
createParameterList(member(_node, "returnParameterTypes")),
visibility(_node),
stateMutability(_node)
);
}
ASTPointer<Mapping> ASTJsonImporter::createMapping(Json::Value const& _node)
{
return createASTNode<Mapping>(
_node,
createElementaryTypeName(member(_node, "keyType")),
convertJsonToASTNode<TypeName>(member(_node, "valueType"))
);
}
ASTPointer<ArrayTypeName> ASTJsonImporter::createArrayTypeName(Json::Value const& _node)
{
return createASTNode<ArrayTypeName>(
_node,
convertJsonToASTNode<TypeName>(member(_node, "baseType")),
nullOrCast<Expression>(member(_node, "length"))
);
}
ASTPointer<InlineAssembly> ASTJsonImporter::createInlineAssembly(Json::Value const& _node)
{
astAssert(_node["evmVersion"].isString(), "Expected evmVersion to be a string!");
auto evmVersion = langutil::EVMVersion::fromString(_node["evmVersion"].asString());
astAssert(evmVersion.has_value(), "Invalid EVM version!");
astAssert(m_evmVersion == evmVersion, "Imported tree evm version differs from configured evm version!");
yul::Dialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(evmVersion.value());
shared_ptr<yul::Block> operations = make_shared<yul::Block>(AsmJsonImporter(m_currentSourceName).createBlock(member(_node, "AST")));
return createASTNode<InlineAssembly>(
_node,
nullOrASTString(_node, "documentation"),
dialect,
operations
);
}
ASTPointer<Block> ASTJsonImporter::createBlock(Json::Value const& _node)
{
std::vector<ASTPointer<Statement>> statements;
for (auto& stat: member(_node, "statements"))
statements.push_back(convertJsonToASTNode<Statement>(stat));
return createASTNode<Block>(
_node,
nullOrASTString(_node, "documentation"),
statements
);
}
ASTPointer<PlaceholderStatement> ASTJsonImporter::createPlaceholderStatement(Json::Value const& _node)
{
return createASTNode<PlaceholderStatement>(
_node,
nullOrASTString(_node, "documentation")
);
}
ASTPointer<IfStatement> ASTJsonImporter::createIfStatement(Json::Value const& _node)
{
return createASTNode<IfStatement>(
_node,
nullOrASTString(_node, "documentation"),
convertJsonToASTNode<Expression>(member(_node, "condition")),
convertJsonToASTNode<Statement>(member(_node, "trueBody")),
nullOrCast<Statement>(member(_node, "falseBody"))
);
}
ASTPointer<TryCatchClause> ASTJsonImporter::createTryCatchClause(Json::Value const& _node)
{
return createASTNode<TryCatchClause>(
_node,
memberAsASTString(_node, "errorName"),
nullOrCast<ParameterList>(member(_node, "parameters")),
convertJsonToASTNode<Block>(member(_node, "block"))
);
}
ASTPointer<TryStatement> ASTJsonImporter::createTryStatement(Json::Value const& _node)
{
vector<ASTPointer<TryCatchClause>> clauses;
for (auto& param: _node["clauses"])
clauses.emplace_back(createTryCatchClause(param));
return createASTNode<TryStatement>(
_node,
nullOrASTString(_node, "documentation"),
convertJsonToASTNode<Expression>(member(_node, "externalCall")),
clauses
);
}
ASTPointer<WhileStatement> ASTJsonImporter::createWhileStatement(Json::Value const& _node, bool _isDoWhile=false)
{
return createASTNode<WhileStatement>(
_node,
nullOrASTString(_node, "documentation"),
convertJsonToASTNode<Expression>(member(_node, "condition")),
convertJsonToASTNode<Statement>(member(_node, "body")),
_isDoWhile
);
}
ASTPointer<ForStatement> ASTJsonImporter::createForStatement(Json::Value const& _node)
{
return createASTNode<ForStatement>(
_node,
nullOrASTString(_node, "documentation"),
nullOrCast<Statement>(member(_node, "initializationExpression")),
nullOrCast<Expression>(member(_node, "condition")),
nullOrCast<ExpressionStatement>(member(_node, "loopExpression")),
convertJsonToASTNode<Statement>(member(_node, "body"))
);
}
ASTPointer<Continue> ASTJsonImporter::createContinue(Json::Value const& _node)
{
return createASTNode<Continue>(
_node,
nullOrASTString(_node, "documentation")
);
}
ASTPointer<Break> ASTJsonImporter::createBreak(Json::Value const& _node)
{
return createASTNode<Break>(
_node,
nullOrASTString(_node, "documentation")
);
}
ASTPointer<Return> ASTJsonImporter::createReturn(Json::Value const& _node)
{
return createASTNode<Return>(
_node,
nullOrASTString(_node, "documentation"),
nullOrCast<Expression>(member(_node, "expression"))
);
}
ASTPointer<Throw> ASTJsonImporter::createThrow(Json::Value const& _node)
{
return createASTNode<Throw>(
_node,
nullOrASTString(_node, "documentation")
);
}
ASTPointer<EmitStatement> ASTJsonImporter::createEmitStatement(Json::Value const& _node)
{
return createASTNode<EmitStatement>(
_node,
nullOrASTString(_node, "documentation"),
createFunctionCall(member(_node, "eventCall"))
);
}
ASTPointer<VariableDeclarationStatement> ASTJsonImporter::createVariableDeclarationStatement(Json::Value const& _node)
{
std::vector<ASTPointer<VariableDeclaration>> variables;
for (auto& var: member(_node, "declarations"))
variables.push_back(var.isNull() ? nullptr : createVariableDeclaration(var)); //unnamed components are empty pointers
return createASTNode<VariableDeclarationStatement>(
_node,
nullOrASTString(_node, "documentation"),
variables,
nullOrCast<Expression>(member(_node, "initialValue"))
);
}
ASTPointer<ExpressionStatement> ASTJsonImporter::createExpressionStatement(Json::Value const& _node)
{
return createASTNode<ExpressionStatement>(
_node,
nullOrASTString(_node, "documentation"),
convertJsonToASTNode<Expression>(member(_node, "expression"))
);
}
ASTPointer<Conditional> ASTJsonImporter::createConditional(Json::Value const& _node)
{
return createASTNode<Conditional>(
_node,
convertJsonToASTNode<Expression>(member(_node, "condition")),
convertJsonToASTNode<Expression>(member(_node, "trueExpression")),
convertJsonToASTNode<Expression>(member(_node, "falseExpression"))
);
}
ASTPointer<Assignment> ASTJsonImporter::createAssignment(Json::Value const& _node)
{
return createASTNode<Assignment>(
_node,
convertJsonToASTNode<Expression>(member(_node, "leftHandSide")),
scanSingleToken(member(_node, "operator")),
convertJsonToASTNode<Expression>(member(_node, "rightHandSide"))
);
}
ASTPointer<TupleExpression> ASTJsonImporter::createTupleExpression(Json::Value const& _node)
{
std::vector<ASTPointer<Expression>> components;
for (auto& comp: member(_node, "components"))
components.push_back(nullOrCast<Expression>(comp));
return createASTNode<TupleExpression>(
_node,
components,
memberAsBool(_node, "isInlineArray")
);
}
ASTPointer<UnaryOperation> ASTJsonImporter::createUnaryOperation(Json::Value const& _node)
{
return createASTNode<UnaryOperation>(
_node,
scanSingleToken(member(_node, "operator")),
convertJsonToASTNode<Expression>(member(_node, "subExpression")),
memberAsBool(_node, "prefix")
);
}
ASTPointer<BinaryOperation> ASTJsonImporter::createBinaryOperation(Json::Value const& _node)
{
return createASTNode<BinaryOperation>(
_node,
convertJsonToASTNode<Expression>(member(_node, "leftExpression")),
scanSingleToken(member(_node, "operator")),
convertJsonToASTNode<Expression>(member(_node, "rightExpression"))
);
}
ASTPointer<FunctionCall> ASTJsonImporter::createFunctionCall(Json::Value const& _node)
{
std::vector<ASTPointer<Expression>> arguments;
for (auto& arg: member(_node, "arguments"))
arguments.push_back(convertJsonToASTNode<Expression>(arg));
std::vector<ASTPointer<ASTString>> names;
for (auto& name: member(_node, "names"))
{
astAssert(name.isString(), "Expected 'names' members to be strings!");
names.push_back(make_shared<ASTString>(name.asString()));
}
return createASTNode<FunctionCall>(
_node,
convertJsonToASTNode<Expression>(member(_node, "expression")),
arguments,
names
);
}
ASTPointer<NewExpression> ASTJsonImporter::createNewExpression(Json::Value const& _node)
{
return createASTNode<NewExpression>(
_node,
convertJsonToASTNode<TypeName>(member(_node, "typeName"))
);
}
ASTPointer<MemberAccess> ASTJsonImporter::createMemberAccess(Json::Value const& _node)
{
return createASTNode<MemberAccess>(
_node,
convertJsonToASTNode<Expression>(member(_node, "expression")),
memberAsASTString(_node, "memberName")
);
}
ASTPointer<IndexAccess> ASTJsonImporter::createIndexAccess(Json::Value const& _node)
{
return createASTNode<IndexAccess>(
_node,
convertJsonToASTNode<Expression>(member(_node, "baseExpression")),
nullOrCast<Expression>(member(_node, "indexExpression"))
);
}
ASTPointer<IndexRangeAccess> ASTJsonImporter::createIndexRangeAccess(Json::Value const& _node)
{
return createASTNode<IndexRangeAccess>(
_node,
convertJsonToASTNode<Expression>(member(_node, "baseExpression")),
nullOrCast<Expression>(member(_node, "startExpression")),
nullOrCast<Expression>(member(_node, "endExpression"))
);
}
ASTPointer<Identifier> ASTJsonImporter::createIdentifier(Json::Value const& _node)
{
return createASTNode<Identifier>(_node, memberAsASTString(_node, "name"));
}
ASTPointer<ElementaryTypeNameExpression> ASTJsonImporter::createElementaryTypeNameExpression(Json::Value const& _node)
{
return createASTNode<ElementaryTypeNameExpression>(
_node,
createElementaryTypeName(member(_node, "typeName"))
);
}
ASTPointer<ASTNode> ASTJsonImporter::createLiteral(Json::Value const& _node)
{
static string const valStr = "value";
static string const hexValStr = "hexValue";
astAssert(member(_node, valStr).isString() || member(_node, hexValStr).isString(), "Literal-value is unset.");
ASTPointer<ASTString> value = _node.isMember(hexValStr) ?
make_shared<ASTString>(util::asString(util::fromHex(_node[hexValStr].asString()))) :
make_shared<ASTString>(_node[valStr].asString());
return createASTNode<Literal>(
_node,
literalTokenKind(_node),
value,
member(_node, "subdenomination").isNull() ? Literal::SubDenomination::None : subdenomination(_node)
);
}
// ===== helper functions ==========
Json::Value ASTJsonImporter::member(Json::Value const& _node, string const& _name)
{
astAssert(_node.isMember(_name), "Node '" + _node["nodeType"].asString() + "' (id " + _node["id"].asString() + ") is missing field '" + _name + "'.");
return _node[_name];
}
Token ASTJsonImporter::scanSingleToken(Json::Value const& _node)
{
langutil::Scanner scanner{langutil::CharStream(_node.asString(), "")};
astAssert(scanner.peekNextToken() == Token::EOS, "Token string is too long.");
return scanner.currentToken();
}
ASTPointer<ASTString> ASTJsonImporter::nullOrASTString(Json::Value const& _json, string const& _name)
{
return _json[_name].isString() ? memberAsASTString(_json, _name) : nullptr;
}
ASTPointer<ASTString> ASTJsonImporter::memberAsASTString(Json::Value const& _node, string const& _name)
{
Json::Value value = member(_node, _name);
astAssert(value.isString(), "field " + _name + " must be of type string.");
return make_shared<ASTString>(_node[_name].asString());
}
bool ASTJsonImporter::memberAsBool(Json::Value const& _node, string const& _name)
{
Json::Value value = member(_node, _name);
astAssert(value.isBool(), "field " + _name + " must be of type boolean.");
return _node[_name].asBool();
}
// =========== JSON to definition helpers =======================
ContractKind ASTJsonImporter::contractKind(Json::Value const& _node)
{
ContractKind kind;
astAssert(!member(_node, "contractKind").isNull(), "'Contract-kind' can not be null.");
if (_node["contractKind"].asString() == "interface")
kind = ContractKind::Interface;
else if (_node["contractKind"].asString() == "contract")
kind = ContractKind::Contract;
else if (_node["contractKind"].asString() == "library")
kind = ContractKind::Library;
else
astAssert(false, "Unknown ContractKind");
return kind;
}
Token ASTJsonImporter::literalTokenKind(Json::Value const& _node)
{
astAssert(member(_node, "kind").isString(), "Token-'kind' expected to be a string.");
Token tok;
if (_node["kind"].asString() == "number")
tok = Token::Number;
else if (_node["kind"].asString() == "string")
tok = Token::StringLiteral;
else if (_node["kind"].asString() == "bool")
tok = (member(_node, "value").asString() == "true") ? Token::TrueLiteral : Token::FalseLiteral;
else
astAssert(false, "Unknown kind of literalString");
return tok;
}
Visibility ASTJsonImporter::visibility(Json::Value const& _node)
{
Json::Value visibility = member(_node, "visibility");
astAssert(visibility.isString(), "'visibility' expected to be a string.");
string const visibilityStr = visibility.asString();
if (visibilityStr == "default")
return Visibility::Default;
else if (visibilityStr == "private")
return Visibility::Private;
else if ( visibilityStr == "internal")
return Visibility::Internal;
else if (visibilityStr == "public")
return Visibility::Public;
else if (visibilityStr == "external")
return Visibility::External;
else
astAssert(false, "Unknown visibility declaration");
}
VariableDeclaration::Location ASTJsonImporter::location(Json::Value const& _node)
{
Json::Value storageLoc = member(_node, "storageLocation");
astAssert(storageLoc.isString(), "'storageLocation' expected to be a string.");
string const storageLocStr = storageLoc.asString();
if (storageLocStr == "default")
return VariableDeclaration::Location::Unspecified;
else if (storageLocStr == "storage")
return VariableDeclaration::Location::Storage;
else if (storageLocStr == "memory")
return VariableDeclaration::Location::Memory;
else if (storageLocStr == "calldata")
return VariableDeclaration::Location::CallData;
else
astAssert(false, "Unknown location declaration");
}
Literal::SubDenomination ASTJsonImporter::subdenomination(Json::Value const& _node)
{
Json::Value subDen = member(_node, "subdenomination");
if (subDen.isNull())
return Literal::SubDenomination::None;
astAssert(subDen.isString(), "'subDenomination' expected to be string.");
string const subDenStr = subDen.asString();
if (subDenStr == "wei")
return Literal::SubDenomination::Wei;
else if (subDenStr == "szabo")
return Literal::SubDenomination::Szabo;
else if (subDenStr == "finney")
return Literal::SubDenomination::Finney;
else if (subDenStr == "ether")
return Literal::SubDenomination::Ether;
else if (subDenStr == "seconds")
return Literal::SubDenomination::Second;
else if (subDenStr == "minutes")
return Literal::SubDenomination::Minute;
else if (subDenStr == "hours")
return Literal::SubDenomination::Hour;
else if (subDenStr == "days")
return Literal::SubDenomination::Day;
else if (subDenStr == "weeks")
return Literal::SubDenomination::Week;
else if (subDenStr == "years")
return Literal::SubDenomination::Year;
else
astAssert(false, "Unknown subdenomination");
}
StateMutability ASTJsonImporter::stateMutability(Json::Value const& _node)
{
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");
}
}

View File

@ -0,0 +1,161 @@
/*
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/>.
*/
/**
* @author julius <djudju@protonmail.com>
* @date 2019
* Converts the AST from JSON format to ASTNode
*/
#pragma once
#include <vector>
#include <libsolidity/ast/AST.h>
#include <json/json.h>
#include <libsolidity/ast/ASTAnnotations.h>
#include <liblangutil/EVMVersion.h>
#include <liblangutil/Exceptions.h>
#include <liblangutil/SourceLocation.h>
namespace solidity::frontend
{
/**
* Component that imports an AST from json format to the internal format
*/
class ASTJsonImporter
{
public:
ASTJsonImporter(langutil::EVMVersion _evmVersion)
:m_evmVersion(_evmVersion)
{}
/// Converts the AST from JSON-format to ASTPointer
/// @a _sourceList used to provide source names for the ASTs
/// @returns map of sourcenames to their respective ASTs
std::map<std::string, ASTPointer<SourceUnit>> jsonToSourceUnit(std::map<std::string, Json::Value> const& _sourceList);
private:
// =========== general creation functions ==============
/// Sets the source location and nodeID
/// @returns the ASTNode Object class of the respective JSON node,
template <typename T, typename... Args>
ASTPointer<T> createASTNode(Json::Value const& _node, Args&&... _args);
/// @returns the sourceLocation-object created from the string in the JSON node
langutil::SourceLocation const createSourceLocation(Json::Value const& _node);
/// Creates an ASTNode for a given JSON-ast of unknown type
/// @returns Pointer to a new created ASTNode
ASTPointer<ASTNode> convertJsonToASTNode(Json::Value const& _ast);
/// @returns a pointer to the more specific subclass of ASTNode
/// as indicated by the nodeType field of the json
template<class T>
ASTPointer<T> convertJsonToASTNode(Json::Value const& _node);
/// \defgroup nodeCreators JSON to AST-Nodes
///@{
ASTPointer<SourceUnit> createSourceUnit(Json::Value const& _node, std::string const& _srcName);
ASTPointer<PragmaDirective> createPragmaDirective(Json::Value const& _node);
ASTPointer<ImportDirective> createImportDirective(Json::Value const& _node);
ASTPointer<ContractDefinition> createContractDefinition(Json::Value const& _node);
ASTPointer<InheritanceSpecifier> createInheritanceSpecifier(Json::Value const& _node);
ASTPointer<UsingForDirective> createUsingForDirective(Json::Value const& _node);
ASTPointer<ASTNode> createStructDefinition(Json::Value const& _node);
ASTPointer<EnumDefinition> createEnumDefinition(Json::Value const& _node);
ASTPointer<EnumValue> createEnumValue(Json::Value const& _node);
ASTPointer<ParameterList> createParameterList(Json::Value const& _node);
ASTPointer<OverrideSpecifier> createOverrideSpecifier(Json::Value const& _node);
ASTPointer<FunctionDefinition> createFunctionDefinition(Json::Value const& _node);
ASTPointer<VariableDeclaration> createVariableDeclaration(Json::Value const& _node);
ASTPointer<ModifierDefinition> createModifierDefinition(Json::Value const& _node);
ASTPointer<ModifierInvocation> createModifierInvocation(Json::Value const& _node);
ASTPointer<EventDefinition> createEventDefinition(Json::Value const& _node);
ASTPointer<ElementaryTypeName> createElementaryTypeName(Json::Value const& _node);
ASTPointer<UserDefinedTypeName> createUserDefinedTypeName(Json::Value const& _node);
ASTPointer<FunctionTypeName> createFunctionTypeName(Json::Value const& _node);
ASTPointer<Mapping> createMapping(Json::Value const& _node);
ASTPointer<ArrayTypeName> createArrayTypeName(Json::Value const& _node);
ASTPointer<InlineAssembly> createInlineAssembly(Json::Value const& _node);
ASTPointer<Block> createBlock(Json::Value const& _node);
ASTPointer<PlaceholderStatement> createPlaceholderStatement(Json::Value const& _node);
ASTPointer<IfStatement> createIfStatement(Json::Value const& _node);
ASTPointer<TryCatchClause> createTryCatchClause(Json::Value const& _node);
ASTPointer<TryStatement> createTryStatement(Json::Value const& _node);
ASTPointer<WhileStatement> createWhileStatement(Json::Value const& _node, bool _isDoWhile);
ASTPointer<ForStatement> createForStatement(Json::Value const& _node);
ASTPointer<Continue> createContinue(Json::Value const& _node);
ASTPointer<Break> createBreak(Json::Value const& _node);
ASTPointer<Return> createReturn(Json::Value const& _node);
ASTPointer<Throw> createThrow(Json::Value const& _node);
ASTPointer<EmitStatement> createEmitStatement(Json::Value const& _node);
ASTPointer<VariableDeclarationStatement> createVariableDeclarationStatement(Json::Value const& _node);
ASTPointer<ExpressionStatement> createExpressionStatement(Json::Value const& _node);
ASTPointer<Conditional> createConditional(Json::Value const& _node);
ASTPointer<Assignment> createAssignment(Json::Value const& _node);
ASTPointer<TupleExpression> createTupleExpression(Json::Value const& _node);
ASTPointer<UnaryOperation> createUnaryOperation(Json::Value const& _node);
ASTPointer<BinaryOperation> createBinaryOperation(Json::Value const& _node);
ASTPointer<FunctionCall> createFunctionCall(Json::Value const& _node);
ASTPointer<NewExpression> createNewExpression(Json::Value const& _node);
ASTPointer<MemberAccess> createMemberAccess(Json::Value const& _node);
ASTPointer<IndexAccess> createIndexAccess(Json::Value const& _node);
ASTPointer<IndexRangeAccess> createIndexRangeAccess(Json::Value const& _node);
ASTPointer<Identifier> createIdentifier(Json::Value const& _node);
ASTPointer<ElementaryTypeNameExpression> createElementaryTypeNameExpression(Json::Value const& _node);
ASTPointer<ASTNode> createLiteral(Json::Value const& _node);
///@}
// =============== general helper functions ===================
/// @returns the member of a given JSON object, throws if member does not exist
Json::Value member(Json::Value const& _node, std::string const& _name);
/// @returns the appropriate TokenObject used in parsed Strings (pragma directive or operator)
Token scanSingleToken(Json::Value const& _node);
template<class T>
///@returns nullptr or an ASTPointer cast to a specific Class
ASTPointer<T> nullOrCast(Json::Value const& _json);
/// @returns nullptr or ASTString, given an JSON string or an empty field
ASTPointer<ASTString> nullOrASTString(Json::Value const& _json, std::string const& _name);
// ============== JSON to definition helpers ===============
/// \defgroup typeHelpers Json to ast-datatype helpers
/// {@
ASTPointer<ASTString> memberAsASTString(Json::Value const& _node, std::string const& _name);
bool memberAsBool(Json::Value const& _node, std::string const& _name);
Visibility visibility(Json::Value const& _node);
StateMutability stateMutability(Json::Value const& _node);
VariableDeclaration::Location location(Json::Value const& _node);
ContractKind contractKind(Json::Value const& _node);
Token literalTokenKind(Json::Value const& _node);
Literal::SubDenomination subdenomination(Json::Value const& _node);
///@}
// =========== member variables ===============
/// Stores filepath as sourcenames to AST in JSON format
std::map<std::string, Json::Value> m_sourceList;
/// list of filepaths (used as sourcenames)
std::vector<std::shared_ptr<std::string const>> m_sourceLocations;
/// filepath to AST
std::map<std::string, ASTPointer<SourceUnit>> m_sourceUnits;
std::string m_currentSourceName;
/// IDs already used by the nodes
std::set<int64_t> m_usedIDs;
/// Configured EVM version
langutil::EVMVersion m_evmVersion;
};
}

View File

@ -0,0 +1,303 @@
/*
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/>.
*/
/**
* @author julius <djudju@protonmail.com>
* @date 2019
* Converts an inlineAssembly AST from JSON format to AsmData
*/
#include <libsolidity/ast/AsmJsonImporter.h>
#include <libsolidity/ast/ASTJsonImporter.h>
#include <libsolidity/ast/Types.h>
#include <libyul/AsmData.h>
#include <libyul/AsmDataForward.h>
#include <liblangutil/Exceptions.h>
#include <liblangutil/Scanner.h>
#include <vector>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string.hpp>
using namespace std;
using namespace solidity::yul;
namespace solidity::frontend
{
using SourceLocation = langutil::SourceLocation;
SourceLocation const AsmJsonImporter::createSourceLocation(Json::Value const& _node)
{
astAssert(member(_node, "src").isString(), "'src' must be a string");
return solidity::langutil::parseSourceLocation(_node["src"].asString(), m_sourceName);
}
template <class T>
T AsmJsonImporter::createAsmNode(Json::Value const& _node)
{
T r;
r.location = createSourceLocation(_node);
astAssert(!r.location.isEmpty() || !r.location.source, "Invalid source location in Asm AST");
return r;
}
Json::Value AsmJsonImporter::member(Json::Value const& _node, string const& _name)
{
astAssert(_node.isMember(_name), "Node is missing field '" + _name + "'.");
return _node[_name];
}
yul::TypedName AsmJsonImporter::createTypedName(Json::Value const& _node)
{
auto typedName = createAsmNode<yul::TypedName>(_node);
typedName.type = YulString{member(_node, "type").asString()};
typedName.name = YulString{member(_node, "name").asString()};
return typedName;
}
yul::Statement AsmJsonImporter::createStatement(Json::Value const& _node)
{
Json::Value jsonNodeType = member(_node, "nodeType");
astAssert(jsonNodeType.isString(), "Expected \"nodeType\" to be of type string!");
string nodeType = jsonNodeType.asString();
astAssert(nodeType.substr(0, 3) == "Yul", "Invalid nodeType prefix");
nodeType = nodeType.substr(3);
if (nodeType == "ExpressionStatement")
return createExpressionStatement(_node);
else if (nodeType == "Assignment")
return createAssignment(_node);
else if (nodeType == "VariableDeclaration")
return createVariableDeclaration(_node);
else if (nodeType == "FunctionDefinition")
return createFunctionDefinition(_node);
else if (nodeType == "If")
return createIf(_node);
else if (nodeType == "Switch")
return createSwitch(_node);
else if (nodeType == "ForLoop")
return createForLoop(_node);
else if (nodeType == "Break")
return createBreak(_node);
else if (nodeType == "Continue")
return createContinue(_node);
else if (nodeType == "Leave")
return createLeave(_node);
else
astAssert(false, "Invalid nodeType as statement");
}
yul::Expression AsmJsonImporter::createExpression(Json::Value const& _node)
{
Json::Value jsonNodeType = member(_node, "nodeType");
astAssert(jsonNodeType.isString(), "Expected \"nodeType\" to be of type string!");
string nodeType = jsonNodeType.asString();
astAssert(nodeType.substr(0, 3) == "Yul", "Invalid nodeType prefix");
nodeType = nodeType.substr(3);
if (nodeType == "FunctionCall")
return createFunctionCall(_node);
else if (nodeType == "Identifier")
return createIdentifier(_node);
else if (nodeType == "Literal")
return createLiteral(_node);
else
astAssert(false, "Invalid nodeType as expression");
}
vector<yul::Expression> AsmJsonImporter::createExpressionVector(Json::Value const& _array)
{
vector<yul::Expression> ret;
for (auto& var: _array)
ret.emplace_back(createExpression(var));
return ret;
}
vector<yul::Statement> AsmJsonImporter::createStatementVector(Json::Value const& _array)
{
vector<yul::Statement> ret;
for (auto& var: _array)
ret.emplace_back(createStatement(var));
return ret;
}
yul::Block AsmJsonImporter::createBlock(Json::Value const& _node)
{
auto block = createAsmNode<yul::Block>(_node);
block.statements = createStatementVector(_node["statements"]);
return block;
}
yul::Literal AsmJsonImporter::createLiteral(Json::Value const& _node)
{
auto lit = createAsmNode<yul::Literal>(_node);
string kind = member(_node, "kind").asString();
lit.value = YulString{member(_node, "value").asString()};
lit.type= YulString{member(_node, "type").asString()};
langutil::Scanner scanner{langutil::CharStream(lit.value.str(), "")};
if (kind == "number")
{
lit.kind = yul::LiteralKind::Number;
astAssert(
scanner.currentToken() == Token::Number,
"Expected number but got " + langutil::TokenTraits::friendlyName(scanner.currentToken()) + string(" while scanning ") + lit.value.str()
);
}
else if (kind == "bool")
{
lit.kind = yul::LiteralKind::Boolean;
astAssert(
scanner.currentToken() == Token::TrueLiteral ||
scanner.currentToken() == Token::FalseLiteral,
"Expected true/false literal!"
);
}
else if (kind == "string")
{
lit.kind = yul::LiteralKind::String;
astAssert(scanner.currentToken() == Token::StringLiteral, "Expected string literal!");
}
else
solAssert(false, "unknown type of literal");
return lit;
}
yul::Leave AsmJsonImporter::createLeave(Json::Value const& _node)
{
return createAsmNode<yul::Leave>(_node);
}
yul::Identifier AsmJsonImporter::createIdentifier(Json::Value const& _node)
{
auto identifier = createAsmNode<yul::Identifier>(_node);
identifier.name = YulString(member(_node, "name").asString());
return identifier;
}
yul::Assignment AsmJsonImporter::createAssignment(Json::Value const& _node)
{
auto assignment = createAsmNode<yul::Assignment>(_node);
if (_node.isMember("variableNames"))
for (auto const& var: member(_node, "variableNames"))
assignment.variableNames.emplace_back(createIdentifier(var));
assignment.value = make_unique<yul::Expression>(createExpression(member(_node, "value")));
return assignment;
}
yul::FunctionCall AsmJsonImporter::createFunctionCall(Json::Value const& _node)
{
auto functionCall = createAsmNode<yul::FunctionCall>(_node);
for (auto const& var: member(_node, "arguments"))
functionCall.arguments.emplace_back(createExpression(var));
functionCall.functionName = createIdentifier(member(_node, "functionName"));
return functionCall;
}
yul::ExpressionStatement AsmJsonImporter::createExpressionStatement(Json::Value const& _node)
{
auto statement = createAsmNode<yul::ExpressionStatement>(_node);
statement.expression = createExpression(member(_node, "expression"));
return statement;
}
yul::VariableDeclaration AsmJsonImporter::createVariableDeclaration(Json::Value const& _node)
{
auto varDec = createAsmNode<yul::VariableDeclaration>(_node);
for (auto const& var: member(_node, "variables"))
varDec.variables.emplace_back(createTypedName(var));
varDec.value = make_unique<yul::Expression>(createExpression(member(_node, "value")));
return varDec;
}
yul::FunctionDefinition AsmJsonImporter::createFunctionDefinition(Json::Value const& _node)
{
auto funcDef = createAsmNode<yul::FunctionDefinition>(_node);
funcDef.name = YulString{member(_node, "name").asString()};
if (_node.isMember("parameters"))
for (auto const& var: member(_node, "parameters"))
funcDef.parameters.emplace_back(createTypedName(var));
if (_node.isMember("returnVariables"))
for (auto const& var: member(_node, "returnVariables"))
funcDef.returnVariables.emplace_back(createTypedName(var));
funcDef.body = createBlock(member(_node, "body"));
return funcDef;
}
yul::If AsmJsonImporter::createIf(Json::Value const& _node)
{
auto ifStatement = createAsmNode<yul::If>(_node);
ifStatement.condition = make_unique<yul::Expression>(createExpression(member(_node, "condition")));
ifStatement.body = createBlock(member(_node, "body"));
return ifStatement;
}
yul::Case AsmJsonImporter::createCase(Json::Value const& _node)
{
auto caseStatement = createAsmNode<yul::Case>(_node);
caseStatement.value = member(_node, "value").asString() == "default" ? nullptr : make_unique<yul::Literal>(createLiteral(member(_node, "value")));
caseStatement.body = createBlock(member(_node, "body"));
return caseStatement;
}
yul::Switch AsmJsonImporter::createSwitch(Json::Value const& _node)
{
auto switchStatement = createAsmNode<yul::Switch>(_node);
switchStatement.expression = make_unique<yul::Expression>(createExpression(member(_node, "value")));
for (auto const& var: member(_node, "cases"))
switchStatement.cases.emplace_back(createCase(var));
return switchStatement;
}
yul::ForLoop AsmJsonImporter::createForLoop(Json::Value const& _node)
{
auto forLoop = createAsmNode<yul::ForLoop>(_node);
forLoop.pre = createBlock(member(_node, "pre"));
forLoop.condition = make_unique<yul::Expression>(createExpression(member(_node, "condition")));
forLoop.post = createBlock(member(_node, "post"));
forLoop.body = createBlock(member(_node, "body"));
return forLoop;
}
yul::Break AsmJsonImporter::createBreak(Json::Value const& _node)
{
return createAsmNode<yul::Break>(_node);
}
yul::Continue AsmJsonImporter::createContinue(Json::Value const& _node)
{
return createAsmNode<yul::Continue>(_node);
}
}

View File

@ -0,0 +1,74 @@
/*
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/>.
*/
/**
* @author julius <djudju@protonmail.com>
* @date 2019
* Converts an inlineAssembly AST from JSON format to AsmData
*/
#pragma once
#include <json/json.h>
#include <liblangutil/SourceLocation.h>
#include <libyul/AsmDataForward.h>
namespace solidity::frontend
{
/**
* Component that imports an AST from json format to the internal format
*/
class AsmJsonImporter
{
public:
explicit AsmJsonImporter(std::string _sourceName) : m_sourceName(_sourceName) {}
yul::Block createBlock(Json::Value const& _node);
private:
langutil::SourceLocation const createSourceLocation(Json::Value const& _node);
template <class T>
T createAsmNode(Json::Value const& _node);
/// helper function to access member functions of the JSON
/// and throw an error if it does not exist
Json::Value member(Json::Value const& _node, std::string const& _name);
yul::Statement createStatement(Json::Value const& _node);
yul::Expression createExpression(Json::Value const& _node);
std::vector<yul::Statement> createStatementVector(Json::Value const& _array);
std::vector<yul::Expression> createExpressionVector(Json::Value const& _array);
yul::TypedName createTypedName(Json::Value const& _node);
yul::Literal createLiteral(Json::Value const& _node);
yul::Leave createLeave(Json::Value const& _node);
yul::Identifier createIdentifier(Json::Value const& _node);
yul::Assignment createAssignment(Json::Value const& _node);
yul::FunctionCall createFunctionCall(Json::Value const& _node);
yul::ExpressionStatement createExpressionStatement(Json::Value const& _node);
yul::VariableDeclaration createVariableDeclaration(Json::Value const& _node);
yul::FunctionDefinition createFunctionDefinition(Json::Value const& _node);
yul::If createIf(Json::Value const& _node);
yul::Case createCase(Json::Value const& _node);
yul::Switch createSwitch(Json::Value const& _node);
yul::ForLoop createForLoop(Json::Value const& _node);
yul::Break createBreak(Json::Value const& _node);
yul::Continue createContinue(Json::Value const& _node);
std::string m_sourceName;
};
}

View File

@ -38,6 +38,7 @@
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/TypeProvider.h>
#include <libsolidity/ast/ASTJsonImporter.h>
#include <libsolidity/codegen/Compiler.h>
#include <libsolidity/formal/ModelChecker.h>
#include <libsolidity/interface/ABI.h>
@ -233,11 +234,12 @@ bool CompilerStack::parse()
if (m_stackState != SourcesSet)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must call parse only after the SourcesSet state."));
m_errorReporter.clear();
ASTNode::resetID();
if (SemVerVersion{string(VersionString)}.isPrerelease())
m_errorReporter.warning("This is a pre-release compiler version, please do not use it in production.");
Parser parser{m_errorReporter, m_evmVersion, m_parserErrorRecovery};
vector<string> sourcesToParse;
for (auto const& s: m_sources)
sourcesToParse.push_back(s.first);
@ -246,7 +248,7 @@ bool CompilerStack::parse()
string const& path = sourcesToParse[i];
Source& source = m_sources[path];
source.scanner->reset();
source.ast = Parser(m_errorReporter, m_evmVersion, m_parserErrorRecovery).parse(source.scanner);
source.ast = parser.parse(source.scanner);
if (!source.ast)
solAssert(!Error::containsOnlyWarnings(m_errorReporter.errors()), "Parser returned null but did not report error.");
else
@ -268,6 +270,26 @@ bool CompilerStack::parse()
return !m_hasError;
}
void CompilerStack::importASTs(map<string, Json::Value> const& _sources)
{
if (m_stackState != Empty)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must call importASTs only before the SourcesSet state."));
m_sourceJsons = _sources;
map<string, ASTPointer<SourceUnit>> reconstructedSources = ASTJsonImporter(m_evmVersion).jsonToSourceUnit(m_sourceJsons);
for (auto& src: reconstructedSources)
{
string const& path = src.first;
Source source;
source.ast = src.second;
string srcString = util::jsonCompactPrint(m_sourceJsons[src.first]);
ASTPointer<Scanner> scanner = make_shared<Scanner>(langutil::CharStream(srcString, src.first));
source.scanner = scanner;
m_sources[path] = source;
}
m_stackState = ParsingPerformed;
m_importedSources = true;
}
bool CompilerStack::analyze()
{
if (m_stackState != ParsingPerformed || m_stackState >= AnalysisPerformed)
@ -1158,7 +1180,7 @@ string CompilerStack::createMetadata(Contract const& _contract) const
{
Json::Value meta;
meta["version"] = 1;
meta["language"] = "Solidity";
meta["language"] = m_importedSources ? "SolidityAST" : "Solidity";
meta["compiler"]["version"] = VersionStringStrict;
/// All the source files (including self), which should be included in the metadata.

View File

@ -199,6 +199,10 @@ public:
/// @returns false on error.
bool parse();
/// Imports given SourceUnits so they can be analyzed. Leads to the same internal state as parse().
/// Will throw errors if the import fails
void importASTs(std::map<std::string, Json::Value> const& _sources);
/// Performs the analysis steps (imports, scopesetting, syntaxCheck, referenceResolving,
/// typechecking, staticAnalysis) on previously parsed sources.
/// @returns false on error.
@ -440,6 +444,8 @@ private:
/// "context:prefix=target"
std::vector<Remapping> m_remappings;
std::map<std::string const, Source> m_sources;
// if imported, store AST-JSONS for each filename
std::map<std::string, Json::Value> m_sourceJsons;
std::vector<std::string> m_unhandledSMTLib2Queries;
std::map<util::h256, std::string> m_smtlib2Responses;
std::shared_ptr<GlobalContext> m_globalContext;
@ -453,6 +459,7 @@ private:
MetadataHash m_metadataHash = MetadataHash::IPFS;
bool m_parserErrorRecovery = false;
State m_stackState = Empty;
bool m_importedSources = false;
/// Whether or not there has been an error during processing.
/// If this is true, the stack will refuse to generate code.
bool m_hasError = false;

View File

@ -44,9 +44,9 @@ namespace solidity::frontend
class Parser::ASTNodeFactory
{
public:
explicit ASTNodeFactory(Parser const& _parser):
explicit ASTNodeFactory(Parser& _parser):
m_parser(_parser), m_location{_parser.position(), -1, _parser.source()} {}
ASTNodeFactory(Parser const& _parser, ASTPointer<ASTNode> const& _childNode):
ASTNodeFactory(Parser& _parser, ASTPointer<ASTNode> const& _childNode):
m_parser(_parser), m_location{_childNode->location()} {}
void markEndPosition() { m_location.end = m_parser.endPosition(); }
@ -61,18 +61,19 @@ public:
solAssert(m_location.source, "");
if (m_location.end < 0)
markEndPosition();
return make_shared<NodeType>(m_location, std::forward<Args>(_args)...);
return make_shared<NodeType>(m_parser.nextID(), m_location, std::forward<Args>(_args)...);
}
SourceLocation const& location() const noexcept { return m_location; }
private:
Parser const& m_parser;
Parser& m_parser;
SourceLocation m_location;
};
ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
{
solAssert(!m_insideModifier, "");
try
{
m_recursionDepth = 0;
@ -1193,7 +1194,7 @@ ASTPointer<InlineAssembly> Parser::parseInlineAssembly(ASTPointer<ASTString> con
BOOST_THROW_EXCEPTION(FatalError());
location.end = block->location.end;
return make_shared<InlineAssembly>(location, _docString, dialect, block);
return make_shared<InlineAssembly>(nextID(), location, _docString, dialect, block);
}
ASTPointer<IfStatement> Parser::parseIfStatement(ASTPointer<ASTString> const& _docString)

View File

@ -173,6 +173,9 @@ private:
bool empty() const;
};
/// Returns the next AST node ID
int64_t nextID() { return ++m_currentNodeID; }
std::pair<LookAheadInfo, IndexAccessedPath> tryParseIndexAccessedPath();
/// Performs limited look-ahead to distinguish between variable declaration and expression statement.
/// For source code of the form "a[][8]" ("IndexAccessStructure"), this is not possible to
@ -198,6 +201,8 @@ private:
/// Flag that signifies whether '_' is parsed as a PlaceholderStatement or a regular identifier.
bool m_insideModifier = false;
langutil::EVMVersion m_evmVersion;
/// Counter for the next AST node ID
int64_t m_currentNodeID = 0;
};
}

113
scripts/ASTImportTest.sh Executable file
View File

@ -0,0 +1,113 @@
#!/usr/bin/env bash
# Bash script to test the ast-import option of the compiler by
# first exporting a .sol file to JSON, then loading it into the compiler
# and exporting it again. The second JSON should be identical to the first
REPO_ROOT=$(realpath "$(dirname "$0")"/..)
SOLIDITY_BUILD_DIR=${SOLIDITY_BUILD_DIR:-build}
SOLC=${REPO_ROOT}/${SOLIDITY_BUILD_DIR}/solc/solc
SPLITSOURCES=${REPO_ROOT}/scripts/splitSources.py
SYNTAXTESTS_DIR="${REPO_ROOT}/test/libsolidity/syntaxTests"
NSOURCES="$(find $SYNTAXTESTS_DIR -type f | wc -l)"
# DEV_DIR="${REPO_ROOT}/../tmp/contracts/"
# NSOURCES="$(find $DEV_DIR -type f | wc -l)" #TODO use find command
FAILED=0
UNCOMPILABLE=0
TESTED=0
if [ $(ls | wc -l) -ne 0 ]; then
echo "Test directory not empty. Skipping!"
exit -1
fi
# function tests whether exporting and importing again leaves the JSON ast unchanged
# Results are recorded by adding to FAILED or UNCOMPILABLE.
# Also, in case of a mismatch a diff and the respective ASTs are printed
# Expected parameters:
# $1 name of the file to be exported and imported
# $2 any files needed to do so that might be in parent directories
function testImportExportEquivalence {
if $SOLC $1 $2 > /dev/null 2>&1
then
# save exported json as expected result (silently)
$SOLC --combined-json ast,compact-format --pretty-json $1 $2> expected.json 2> /dev/null
# import it, and export it again as obtained result (silently)
$SOLC --import-ast --combined-json ast,compact-format --pretty-json expected.json > obtained.json 2> /dev/null
if [ $? -ne 0 ]
then
# For investigating, use exit 1 here so the script stops at the
# first failing test
# exit 1
FAILED=$((FAILED + 1))
return 1
fi
DIFF="$(diff expected.json obtained.json)"
if [ "$DIFF" != "" ]
then
if [ "$DIFFVIEW" == "" ]
then
echo -e "ERROR: JSONS differ for $1: \n $DIFF \n"
echo "Expected:"
echo "$(cat ./expected.json)"
echo "Obtained:"
echo "$(cat ./obtained.json)"
else
# Use user supplied diff view binary
$DIFFVIEW expected.json obtained.json
fi
FAILED=$((FAILED + 1))
return 2
fi
TESTED=$((TESTED + 1))
rm expected.json obtained.json
else
# echo "contract $solfile could not be compiled "
UNCOMPILABLE=$((UNCOMPILABLE + 1))
fi
# return 0
}
echo "Looking at $NSOURCES .sol files..."
WORKINGDIR=$PWD
# for solfile in $(find $DEV_DIR -name *.sol)
for solfile in $(find $SYNTAXTESTS_DIR -name *.sol)
do
echo -n "."
# create a temporary sub-directory
FILETMP=$(mktemp -d -p $WORKINGDIR)
cd $FILETMP
OUTPUT=$($SPLITSOURCES $solfile)
if [ $? != 1 ]
then
# echo $OUTPUT
NSOURCES=$((NSOURCES - 1))
for i in $OUTPUT;
do
testImportExportEquivalence $i $OUTPUT
NSOURCES=$((NSOURCES + 1))
done
else
testImportExportEquivalence $solfile
fi
cd $WORKINGDIR
# Delete temporary files
rm -rf $FILETMP
done
echo ""
if [ "$FAILED" = 0 ]
then
echo "SUCCESS: $TESTED syntaxTests passed, $FAILED failed, $UNCOMPILABLE could not be compiled ($NSOURCES sources total)."
else
echo "FAILURE: Out of $NSOURCES sources, $FAILED failed, ($UNCOMPILABLE could not be compiled)."
exit 1
fi

62
scripts/splitSources.py Executable file
View File

@ -0,0 +1,62 @@
#!/usr/bin/env python2
#
# This script reads a syntaxTest file and writes all
# sources into their own files. If one source-name specifies subdirectories
# those will be created too.
# Usage: scripts/splitSources.py pathToTestfile
# as a result prints
# - string of created files separated by whitespaces
# - 'false' if the file only had one source
import sys
import os
hasMultipleSources = False
createdSources = []
def extractSourceName(line):
if line.find("/") > -1:
filePath = line[13: line.rindex("/")]
# fileName = line[line.rindex("/")+1: line.find(" ====")]
srcName = line[line.find(":")+2: line.find(" ====")]
return filePath, srcName
return False, line[line.find(":")+2 : line.find(" ====")]
# expects the first line of lines to be "==== Source: sourceName ===="
# writes the following source into a file named sourceName
def writeSourceToFile(lines):
filePath, srcName = extractSourceName(lines[0])
# print "sourceName is", srcName
# print "filePath is", filePath
if filePath != False:
os.system("mkdir -p " + filePath)
f = open(srcName, 'a+')
createdSources.append(srcName)
i = 0
for idx, line in enumerate(lines[1:]):
# write to file
if line[:12] != "==== Source:":
f.write(line)
# recursive call if there is another source
else:
writeSourceToFile(lines[1+idx:])
break
if __name__ == '__main__':
filePath = sys.argv[1]
# decide if file has multiple sources
lines = open(filePath, 'rb').read().splitlines()
if lines[0][:12] == "==== Source:":
hasMultipleSources = True
writeSourceToFile(lines)
if hasMultipleSources:
srcString = ""
for src in createdSources:
srcString += src + ' '
print srcString
else:
sys.exit(1)

View File

@ -28,6 +28,7 @@
#include <libsolidity/interface/Version.h>
#include <libsolidity/parsing/Parser.h>
#include <libsolidity/ast/ASTJsonConverter.h>
#include <libsolidity/ast/ASTJsonImporter.h>
#include <libsolidity/analysis/NameAndTypeResolver.h>
#include <libsolidity/interface/CompilerStack.h>
#include <libsolidity/interface/StandardCompiler.h>
@ -120,6 +121,7 @@ static string const g_strEVMVersion = "evm-version";
static string const g_strEwasm = "ewasm";
static string const g_strGas = "gas";
static string const g_strHelp = "help";
static string const g_strImportAst = "import-ast";
static string const g_strInputFile = "input-file";
static string const g_strInterface = "interface";
static string const g_strYul = "yul";
@ -184,6 +186,7 @@ static string const g_argCompactJSON = g_strCompactJSON;
static string const g_argErrorRecovery = g_strErrorRecovery;
static string const g_argGas = g_strGas;
static string const g_argHelp = g_strHelp;
static string const g_argImportAst = g_strImportAst;
static string const g_argInputFile = g_strInputFile;
static string const g_argYul = g_strYul;
static string const g_argIR = g_strIR;
@ -623,6 +626,33 @@ bool CommandLineInterface::parseLibraryOption(string const& _input)
return true;
}
map<string, Json::Value> CommandLineInterface::parseAstFromInput()
{
map<string, Json::Value> sourceJsons;
map<string, string> tmpSources;
for (auto const& srcPair: m_sourceCodes)
{
Json::Value ast;
astAssert(jsonParseStrict(srcPair.second, ast), "Input file could not be parsed to JSON");
astAssert(ast.isMember("sources"), "Invalid Format for import-JSON: Must have 'sources'-object");
for (auto& src: ast["sources"].getMemberNames())
{
std::string astKey = ast["sources"][src].isMember("ast") ? "ast" : "AST";
astAssert(ast["sources"][src].isMember(astKey), "astkey is not member");
astAssert(ast["sources"][src][astKey]["nodeType"].asString() == "SourceUnit", "Top-level node should be a 'SourceUnit'");
astAssert(sourceJsons.count(src) == 0, "All sources must have unique names");
sourceJsons.emplace(src, move(ast["sources"][src][astKey]));
tmpSources[src] = util::jsonCompactPrint(ast);
}
}
m_sourceCodes = std::move(tmpSources);
return sourceJsons;
}
void CommandLineInterface::createFile(string const& _fileName, string const& _data)
{
namespace fs = boost::filesystem;
@ -722,6 +752,12 @@ Allowed options)",
"Switch to Standard JSON input / output mode, ignoring all options. "
"It reads from standard input and provides the result on the standard output."
)
(
g_argImportAst.c_str(),
"Import ASTs to be compiled, assumes input holds the AST in compact JSON format."
" Supported Inputs is the output of the standard-json or the one produced by --combined-json ast,compact-format"
)
(
g_argAssemble.c_str(),
"Switch to assembly mode, ignoring all options except --machine, --yul-dialect and --optimize and assumes input is assembly."
@ -1065,10 +1101,9 @@ bool CommandLineInterface::processInput()
m_compiler->setMetadataHash(m_metadataHash);
if (m_args.count(g_argInputFile))
m_compiler->setRemappings(m_remappings);
m_compiler->setSources(m_sourceCodes);
if (m_args.count(g_argLibraries))
m_compiler->setLibraries(m_libraries);
m_compiler->setParserErrorRecovery(m_args.count(g_argErrorRecovery));
m_compiler->setEVMVersion(m_evmVersion);
m_compiler->setRevertStringBehaviour(m_revertStrings);
// TODO: Perhaps we should not compile unless requested
@ -1082,6 +1117,32 @@ bool CommandLineInterface::processInput()
settings.optimizeStackAllocation = settings.runYulOptimiser;
m_compiler->setOptimiserSettings(settings);
if (m_args.count(g_argImportAst))
{
try
{
m_compiler->importASTs(parseAstFromInput());
if (!m_compiler->analyze())
{
for (auto const& error: m_compiler->errors())
formatter->printErrorInformation(*error);
astAssert(false, "Analysis of the AST failed");
}
}
catch (Exception const& _exc)
{
serr() << string("Failed to import AST: ") << _exc.what() << endl;
return false;
}
}
else
{
m_compiler->setSources(m_sourceCodes);
if (m_args.count(g_argErrorRecovery))
m_compiler->setParserErrorRecovery(true);
}
bool successful = m_compiler->compile();
for (auto const& error: m_compiler->errors())

View File

@ -80,6 +80,12 @@ private:
/// It then tries to parse the contents and appends to m_libraries.
bool parseLibraryOption(std::string const& _input);
/// Tries to read @ m_sourceCodes as a JSONs holding ASTs
/// such that they can be imported into the compiler (importASTs())
/// (produced by --combined-json ast,compact-format <file.sol>
/// or standard-json output
std::map<std::string, Json::Value> parseAstFromInput();
/// Create a file in the given directory
/// @arg _fileName the name of the file
/// @arg _data to be written

View File

@ -437,6 +437,19 @@ SOLTMPDIR=$(mktemp -d)
fi
)
printTask "Testing AST import..."
SOLTMPDIR=$(mktemp -d)
(
cd "$SOLTMPDIR"
$REPO_ROOT/scripts/ASTImportTest.sh
if [ $? -ne 0 ]
then
rm -rf "$SOLTMPDIR"
exit 1
fi
)
rm -rf "$SOLTMPDIR"
printTask "Testing soljson via the fuzzer..."
SOLTMPDIR=$(mktemp -d)
(

View File

@ -282,7 +282,7 @@
"name": "this",
"nodeType": "Identifier",
"overloadedDeclarations": [],
"referencedDeclaration": 68,
"referencedDeclaration": -28,
"src": "217:4:1",
"typeDescriptions":
{

View File

@ -446,7 +446,7 @@
[
null
],
"referencedDeclaration": 68,
"referencedDeclaration": -28,
"type": "contract C",
"value": "this"
},

View File

@ -124,6 +124,7 @@
}
]
},
"evmVersion": %EVMVERSION%,
"externalReferences": [],
"id": 3,
"nodeType": "InlineAssembly",

View File

@ -89,6 +89,7 @@
{
"attributes":
{
"evmVersion": %EVMVERSION%,
"externalReferences":
[
null

View File

@ -111,6 +111,7 @@
}
]
},
"evmVersion": %EVMVERSION%,
"externalReferences": [],
"id": 3,
"nodeType": "InlineAssembly",

View File

@ -89,6 +89,7 @@
{
"attributes":
{
"evmVersion": %EVMVERSION%,
"externalReferences":
[
null

View File

@ -61,6 +61,7 @@
}
]
},
"evmVersion": %EVMVERSION%,
"externalReferences": [],
"id": 3,
"nodeType": "InlineAssembly",

View File

@ -89,6 +89,7 @@
{
"attributes":
{
"evmVersion": %EVMVERSION%,
"externalReferences":
[
null

View File

@ -124,6 +124,7 @@
}
]
},
"evmVersion": %EVMVERSION%,
"externalReferences": [],
"id": 3,
"nodeType": "InlineAssembly",

View File

@ -89,6 +89,7 @@
{
"attributes":
{
"evmVersion": %EVMVERSION%,
"externalReferences":
[
null

View File

@ -176,6 +176,7 @@
}
]
},
"evmVersion": %EVMVERSION%,
"externalReferences":
[
{

View File

@ -166,6 +166,7 @@
{
"attributes":
{
"evmVersion": %EVMVERSION%,
"externalReferences":
[
{

View File

@ -65,6 +65,7 @@
}
]
},
"evmVersion": %EVMVERSION%,
"externalReferences": [],
"id": 3,
"nodeType": "InlineAssembly",

View File

@ -89,6 +89,7 @@
{
"attributes":
{
"evmVersion": %EVMVERSION%,
"externalReferences":
[
null

View File

@ -157,6 +157,7 @@
}
]
},
"evmVersion": %EVMVERSION%,
"externalReferences": [],
"id": 3,
"nodeType": "InlineAssembly",

View File

@ -89,6 +89,7 @@
{
"attributes":
{
"evmVersion": %EVMVERSION%,
"externalReferences":
[
null

View File

@ -107,6 +107,7 @@
}
]
},
"evmVersion": %EVMVERSION%,
"externalReferences":
[
{

View File

@ -135,6 +135,7 @@
{
"attributes":
{
"evmVersion": %EVMVERSION%,
"externalReferences":
[
{

View File

@ -15,6 +15,7 @@
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
#include <boost/algorithm/string/replace.hpp>
#include <test/libsolidity/ASTJSONTest.h>
#include <test/Options.h>
#include <libsolutil/AnsiColorized.h>
@ -38,6 +39,30 @@ using namespace std;
namespace fs = boost::filesystem;
using namespace boost::unit_test;
namespace
{
void replaceVersionWithTag(string& _input)
{
boost::algorithm::replace_all(
_input,
"\"" + solidity::test::Options::get().evmVersion().name() + "\"",
"%EVMVERSION%"
);
}
void replaceTagWithVersion(string& _input)
{
boost::algorithm::replace_all(
_input,
"%EVMVERSION%",
"\"" + solidity::test::Options::get().evmVersion().name() + "\""
);
}
}
ASTJSONTest::ASTJSONTest(string const& _filename)
{
if (!boost::algorithm::ends_with(_filename, ".sol"))
@ -126,6 +151,8 @@ TestCase::TestResult ASTJSONTest::run(ostream& _stream, string const& _linePrefi
bool resultsMatch = true;
replaceTagWithVersion(m_expectation);
if (m_expectation != m_result)
{
string nextIndentLevel = _linePrefix + " ";
@ -158,6 +185,8 @@ TestCase::TestResult ASTJSONTest::run(ostream& _stream, string const& _linePrefi
m_resultLegacy += "\n";
}
replaceTagWithVersion(m_expectationLegacy);
if (m_expectationLegacy != m_resultLegacy)
{
string nextIndentLevel = _linePrefix + " ";
@ -202,12 +231,21 @@ void ASTJSONTest::printUpdatedExpectations(std::ostream&, std::string const&) co
ofstream file(m_astFilename.c_str());
if (!file) BOOST_THROW_EXCEPTION(runtime_error("Cannot write AST expectation to \"" + m_astFilename + "\"."));
file.exceptions(ios::badbit);
file << m_result;
string replacedResult = m_result;
replaceVersionWithTag(replacedResult);
file << replacedResult;
file.flush();
file.close();
file.open(m_legacyAstFilename.c_str());
if (!file) BOOST_THROW_EXCEPTION(runtime_error("Cannot write legacy AST expectation to \"" + m_legacyAstFilename + "\"."));
file << m_resultLegacy;
string replacedResultLegacy = m_resultLegacy;
replaceVersionWithTag(replacedResultLegacy);
file << replacedResultLegacy;
file.flush();
file.close();
}

View File

@ -145,7 +145,8 @@ BOOST_AUTO_TEST_CASE(type_identifier_escaping)
BOOST_AUTO_TEST_CASE(type_identifiers)
{
ASTNode::resetID();
int64_t id = 0;
BOOST_CHECK_EQUAL(TypeProvider::fromElementaryTypeName("uint128")->identifier(), "t_uint128");
BOOST_CHECK_EQUAL(TypeProvider::fromElementaryTypeName("int128")->identifier(), "t_int128");
BOOST_CHECK_EQUAL(TypeProvider::fromElementaryTypeName("address")->identifier(), "t_address");
@ -157,7 +158,7 @@ BOOST_AUTO_TEST_CASE(type_identifiers)
BOOST_CHECK_EQUAL(RationalNumberType(rational(2 * 200, 2 * 77)).identifier(), "t_rational_200_by_77");
BOOST_CHECK_EQUAL(RationalNumberType(rational(-2 * 200, 2 * 77)).identifier(), "t_rational_minus_200_by_77");
BOOST_CHECK_EQUAL(
StringLiteralType(Literal(SourceLocation{}, Token::StringLiteral, make_shared<string>("abc - def"))).identifier(),
StringLiteralType(Literal(++id, SourceLocation{}, Token::StringLiteral, make_shared<string>("abc - def"))).identifier(),
"t_stringliteral_196a9142ee0d40e274a6482393c762b16dd8315713207365e1e13d8d85b74fc4"
);
BOOST_CHECK_EQUAL(TypeProvider::fromElementaryTypeName("byte")->identifier(), "t_bytes1");
@ -178,14 +179,14 @@ BOOST_AUTO_TEST_CASE(type_identifiers)
TypePointer multiArray = TypeProvider::array(DataLocation::Storage, stringArray);
BOOST_CHECK_EQUAL(multiArray->identifier(), "t_array$_t_array$_t_string_storage_$20_storage_$dyn_storage_ptr");
ContractDefinition c(SourceLocation{}, make_shared<string>("MyContract$"), {}, {}, {}, ContractKind::Contract);
ContractDefinition c(++id, SourceLocation{}, make_shared<string>("MyContract$"), {}, {}, {}, ContractKind::Contract);
BOOST_CHECK_EQUAL(c.type()->identifier(), "t_type$_t_contract$_MyContract$$$_$2_$");
BOOST_CHECK_EQUAL(ContractType(c, true).identifier(), "t_super$_MyContract$$$_$2");
StructDefinition s({}, make_shared<string>("Struct"), {});
StructDefinition s(++id, {}, make_shared<string>("Struct"), {});
BOOST_CHECK_EQUAL(s.type()->identifier(), "t_type$_t_struct$_Struct_$3_storage_ptr_$");
EnumDefinition e({}, make_shared<string>("Enum"), {});
EnumDefinition e(++id, {}, make_shared<string>("Enum"), {});
BOOST_CHECK_EQUAL(e.type()->identifier(), "t_type$_t_enum$_Enum_$4_$");
TupleType t({e.type(), s.type(), stringArray, nullptr});
@ -203,11 +204,11 @@ BOOST_AUTO_TEST_CASE(type_identifiers)
// TypeType is tested with contract
auto emptyParams = make_shared<ParameterList>(SourceLocation(), std::vector<ASTPointer<VariableDeclaration>>());
ModifierDefinition mod(SourceLocation{}, make_shared<string>("modif"), {}, emptyParams, {}, {}, {});
auto emptyParams = make_shared<ParameterList>(++id, SourceLocation(), std::vector<ASTPointer<VariableDeclaration>>());
ModifierDefinition mod(++id, SourceLocation{}, make_shared<string>("modif"), {}, emptyParams, {}, {}, {});
BOOST_CHECK_EQUAL(ModifierType(mod).identifier(), "t_modifier$__$");
SourceUnit su({}, {});
SourceUnit su(++id, {}, {});
BOOST_CHECK_EQUAL(ModuleType(su).identifier(), "t_module_7");
BOOST_CHECK_EQUAL(MagicType(MagicType::Kind::Block).identifier(), "t_magic_block");
BOOST_CHECK_EQUAL(MagicType(MagicType::Kind::Message).identifier(), "t_magic_message");