/*
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 .
*/
// SPDX-License-Identifier: GPL-3.0
/**
* @author julius
* @date 2019
*Component that imports an AST from json format to the internal format
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace solidity::frontend
{
using SourceLocation = langutil::SourceLocation;
template
ASTPointer ASTJsonImporter::nullOrCast(Json::Value const& _json)
{
if (_json.isNull())
return nullptr;
else
return std::dynamic_pointer_cast(convertJsonToASTNode(_json));
}
// ============ public ===========================
std::map> ASTJsonImporter::jsonToSourceUnit(std::map const& _sourceList)
{
for (auto const& src: _sourceList)
m_sourceNames.emplace_back(std::make_shared(src.first));
for (auto const& srcPair: _sourceList)
{
astAssert(!srcPair.second.isNull());
astAssert(member(srcPair.second,"nodeType") == "SourceUnit", "The 'nodeType' of the highest node must be 'SourceUnit'.");
m_sourceUnits[srcPair.first] = createSourceUnit(srcPair.second, srcPair.first);
}
return m_sourceUnits;
}
// ============ private ===========================
// =========== general creation functions ==============
template
ASTPointer 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 = std::make_shared(
id,
createSourceLocation(_node),
std::forward(_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_sourceNames);
}
std::optional> ASTJsonImporter::createSourceLocations(Json::Value const& _node) const
{
std::vector locations;
if (_node.isMember("nameLocations") && _node["nameLocations"].isArray())
{
for (auto const& val: _node["nameLocations"])
locations.emplace_back(langutil::parseSourceLocation(val.asString(), m_sourceNames));
return locations;
}
return std::nullopt;
}
SourceLocation ASTJsonImporter::createNameSourceLocation(Json::Value const& _node)
{
astAssert(member(_node, "nameLocation").isString(), "'nameLocation' must be a string");
return solidity::langutil::parseSourceLocation(_node["nameLocation"].asString(), m_sourceNames);
}
SourceLocation ASTJsonImporter::createKeyNameSourceLocation(Json::Value const& _node)
{
astAssert(member(_node, "keyNameLocation").isString(), "'keyNameLocation' must be a string");
return solidity::langutil::parseSourceLocation(_node["keyNameLocation"].asString(), m_sourceNames);
}
SourceLocation ASTJsonImporter::createValueNameSourceLocation(Json::Value const& _node)
{
astAssert(member(_node, "valueNameLocation").isString(), "'valueNameLocation' must be a string");
return solidity::langutil::parseSourceLocation(_node["valueNameLocation"].asString(), m_sourceNames);
}
template
ASTPointer ASTJsonImporter::convertJsonToASTNode(Json::Value const& _node)
{
ASTPointer ret = std::dynamic_pointer_cast(convertJsonToASTNode(_node));
astAssert(ret, "cast of converted json-node must not be nullptr");
return ret;
}
ASTPointer ASTJsonImporter::convertJsonToASTNode(Json::Value const& _json)
{
astAssert(_json["nodeType"].isString() && _json.isMember("id"), "JSON-Node needs to have 'nodeType' and 'id' fields.");
std::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 == "IdentifierPath")
return createIdentifierPath(_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 == "UserDefinedValueTypeDefinition")
return createUserDefinedValueTypeDefinition(_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 == "ErrorDefinition")
return createErrorDefinition(_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, false);
if (nodeType == "UncheckedBlock")
return createBlock(_json, true);
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 == "RevertStatement")
return createRevertStatement(_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 == "FunctionCallOptions")
return createFunctionCallOptions(_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);
if (nodeType == "StructuredDocumentation")
return createDocumentation(_json);
else
astAssert(false, "Unknown type of ASTNode: " + nodeType);
// FIXME: Workaround for spurious GCC 12.1 warning (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105794)
util::unreachable();
}
// ============ functions to instantiate the AST-Nodes from Json-Nodes ==============
ASTPointer ASTJsonImporter::createSourceUnit(Json::Value const& _node, std::string const& _srcName)
{
std::optional license;
if (_node.isMember("license") && !_node["license"].isNull())
license = _node["license"].asString();
bool experimentalSolidity = false;
if (_node.isMember("experimentalSolidity") && !_node["experimentalSolidity"].isNull())
experimentalSolidity = _node["experimentalSolidity"].asBool();
std::vector> nodes;
for (auto& child: member(_node, "nodes"))
nodes.emplace_back(convertJsonToASTNode(child));
ASTPointer tmp = createASTNode(_node, license, nodes, experimentalSolidity);
tmp->annotation().path = _srcName;
return tmp;
}
ASTPointer ASTJsonImporter::createPragmaDirective(Json::Value const& _node)
{
std::vector tokens;
std::vector literals;
for (auto const& lit: member(_node, "literals"))
{
std::string l = lit.asString();
literals.push_back(l);
tokens.push_back(scanSingleToken(l));
}
return createASTNode(_node, tokens, literals);
}
ASTPointer ASTJsonImporter::createImportDirective(Json::Value const& _node)
{
ASTPointer unitAlias = memberAsASTString(_node, "unitAlias");
ASTPointer 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 : std::make_shared(tuple["local"].asString()),
createSourceLocation(tuple["foreign"])}
);
}
ASTPointer tmp = createASTNode(
_node,
path,
unitAlias,
createNameSourceLocation(_node),
std::move(symbolAliases)
);
astAssert(_node["absolutePath"].isString(), "Expected 'absolutePath' to be a string!");
tmp->annotation().absolutePath = _node["absolutePath"].asString();
return tmp;
}
ASTPointer ASTJsonImporter::createContractDefinition(Json::Value const& _node)
{
astAssert(_node["name"].isString(), "Expected 'name' to be a string!");
std::vector> baseContracts;
for (auto& base: _node["baseContracts"])
baseContracts.push_back(createInheritanceSpecifier(base));
std::vector> subNodes;
for (auto& subnode: _node["nodes"])
subNodes.push_back(convertJsonToASTNode(subnode));
return createASTNode(
_node,
std::make_shared(_node["name"].asString()),
createNameSourceLocation(_node),
_node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")),
baseContracts,
subNodes,
contractKind(_node),
memberAsBool(_node, "abstract")
);
}
ASTPointer ASTJsonImporter::createIdentifierPath(Json::Value const& _node)
{
astAssert(_node["name"].isString(), "Expected 'name' to be a string!");
std::vector namePath;
std::vector namePathLocations;
std::vector strs;
std::string nameString = member(_node, "name").asString();
boost::algorithm::split(strs, nameString, boost::is_any_of("."));
astAssert(!strs.empty(), "Expected at least one element in IdentifierPath.");
for (std::string s: strs)
{
astAssert(!s.empty(), "Expected non-empty string for IdentifierPath element.");
namePath.emplace_back(s);
}
if (_node.isMember("nameLocations") && _node["nameLocations"].isArray())
for (auto const& val: _node["nameLocations"])
namePathLocations.emplace_back(langutil::parseSourceLocation(val.asString(), m_sourceNames));
else
namePathLocations.resize(namePath.size());
astAssert(
namePath.size() == namePathLocations.size(),
"SourceLocations don't match name paths."
);
return createASTNode(
_node,
namePath,
namePathLocations
);
}
ASTPointer ASTJsonImporter::createInheritanceSpecifier(Json::Value const& _node)
{
std::vector> arguments;
for (auto& arg: member(_node, "arguments"))
arguments.push_back(convertJsonToASTNode(arg));
return createASTNode(
_node,
createIdentifierPath(member(_node, "baseName")),
member(_node, "arguments").isNull() ? nullptr : std::make_unique>>(arguments)
);
}
ASTPointer ASTJsonImporter::createUsingForDirective(Json::Value const& _node)
{
std::vector> functions;
std::vector> operators;
if (_node.isMember("libraryName"))
{
astAssert(!_node["libraryName"].isArray());
astAssert(!_node["libraryName"]["operator"]);
functions.emplace_back(createIdentifierPath(_node["libraryName"]));
operators.emplace_back(std::nullopt);
}
else if (_node.isMember("functionList"))
for (Json::Value const& function: _node["functionList"])
{
if (function.isMember("function"))
{
astAssert(!function.isMember("operator"));
astAssert(!function.isMember("definition"));
functions.emplace_back(createIdentifierPath(function["function"]));
operators.emplace_back(std::nullopt);
}
else
{
astAssert(function.isMember("operator"));
astAssert(function.isMember("definition"));
Token const operatorName = scanSingleToken(function["operator"]);
astAssert(util::contains(frontend::userDefinableOperators, operatorName));
functions.emplace_back(createIdentifierPath(function["definition"]));
operators.emplace_back(operatorName);
}
}
return createASTNode(
_node,
std::move(functions),
std::move(operators),
!_node.isMember("libraryName"),
_node["typeName"].isNull() ? nullptr : convertJsonToASTNode(_node["typeName"]),
memberAsBool(_node, "global")
);
}
ASTPointer ASTJsonImporter::createStructDefinition(Json::Value const& _node)
{
std::vector> members;
for (auto& member: _node["members"])
members.push_back(createVariableDeclaration(member));
return createASTNode(
_node,
memberAsASTString(_node, "name"),
createNameSourceLocation(_node),
members,
_node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation"))
);
}
ASTPointer ASTJsonImporter::createEnumDefinition(Json::Value const& _node)
{
std::vector> members;
for (auto& member: _node["members"])
members.push_back(createEnumValue(member));
return createASTNode(
_node,
memberAsASTString(_node, "name"),
createNameSourceLocation(_node),
members,
_node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation"))
);
}
ASTPointer ASTJsonImporter::createEnumValue(Json::Value const& _node)
{
return createASTNode(
_node,
memberAsASTString(_node, "name")
);
}
ASTPointer ASTJsonImporter::createUserDefinedValueTypeDefinition(Json::Value const& _node)
{
return createASTNode(
_node,
memberAsASTString(_node, "name"),
createNameSourceLocation(_node),
convertJsonToASTNode(member(_node, "underlyingType"))
);
}
ASTPointer ASTJsonImporter::createParameterList(Json::Value const& _node)
{
std::vector> parameters;
for (auto& param: _node["parameters"])
parameters.push_back(createVariableDeclaration(param));
return createASTNode(
_node,
parameters
);
}
ASTPointer ASTJsonImporter::createOverrideSpecifier(Json::Value const& _node)
{
std::vector> overrides;
for (auto& param: _node["overrides"])
overrides.push_back(createIdentifierPath(param));
return createASTNode(
_node,
overrides
);
}
ASTPointer ASTJsonImporter::createFunctionDefinition(Json::Value const& _node)
{
astAssert(_node["kind"].isString(), "Expected 'kind' to be a string!");
Token kind;
bool freeFunction = false;
std::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 if (kindStr == "freeFunction")
{
kind = Token::Function;
freeFunction = true;
}
else
astAssert(false, "Expected 'kind' to be one of [constructor, function, fallback, receive]");
std::vector> modifiers;
for (auto& mod: member(_node, "modifiers"))
modifiers.push_back(createModifierInvocation(mod));
Visibility vis = Visibility::Default;
// Ignore public visibility for constructors
if (kind == Token::Constructor)
vis = (visibility(_node) == Visibility::Public) ? Visibility::Default : visibility(_node);
else if (!freeFunction)
vis = visibility(_node);
return createASTNode(
_node,
memberAsASTString(_node, "name"),
createNameSourceLocation(_node),
vis,
stateMutability(_node),
freeFunction,
kind,
memberAsBool(_node, "virtual"),
_node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")),
_node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")),
createParameterList(member(_node, "parameters")),
modifiers,
createParameterList(member(_node, "returnParameters")),
memberAsBool(_node, "implemented") ? createBlock(member(_node, "body"), false) : nullptr
);
}
ASTPointer ASTJsonImporter::createVariableDeclaration(Json::Value const& _node)
{
astAssert(_node["name"].isString(), "Expected 'name' to be a string!");
VariableDeclaration::Mutability mutability{};
astAssert(member(_node, "mutability").isString(), "'mutability' expected to be string.");
std::string const mutabilityStr = member(_node, "mutability").asString();
if (mutabilityStr == "constant")
{
mutability = VariableDeclaration::Mutability::Constant;
astAssert(memberAsBool(_node, "constant"));
}
else
{
astAssert(!memberAsBool(_node, "constant"));
if (mutabilityStr == "mutable")
mutability = VariableDeclaration::Mutability::Mutable;
else if (mutabilityStr == "immutable")
mutability = VariableDeclaration::Mutability::Immutable;
else
astAssert(false);
}
return createASTNode(
_node,
nullOrCast(member(_node, "typeName")),
std::make_shared(member(_node, "name").asString()),
createNameSourceLocation(_node),
nullOrCast(member(_node, "value")),
visibility(_node),
_node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")),
_node.isMember("indexed") ? memberAsBool(_node, "indexed") : false,
mutability,
_node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")),
location(_node)
);
}
ASTPointer ASTJsonImporter::createModifierDefinition(Json::Value const& _node)
{
return createASTNode(
_node,
memberAsASTString(_node, "name"),
createNameSourceLocation(_node),
_node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")),
createParameterList(member(_node, "parameters")),
memberAsBool(_node, "virtual"),
_node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")),
_node["body"].isNull() ? nullptr: createBlock(member(_node, "body"), false)
);
}
ASTPointer ASTJsonImporter::createModifierInvocation(Json::Value const& _node)
{
std::vector> arguments;
for (auto& arg: member(_node, "arguments"))
arguments.push_back(convertJsonToASTNode(arg));
return createASTNode(
_node,
createIdentifierPath(member(_node, "modifierName")),
member(_node, "arguments").isNull() ? nullptr : std::make_unique>>(arguments)
);
}
ASTPointer ASTJsonImporter::createEventDefinition(Json::Value const& _node)
{
return createASTNode(
_node,
memberAsASTString(_node, "name"),
createNameSourceLocation(_node),
_node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")),
createParameterList(member(_node, "parameters")),
memberAsBool(_node, "anonymous")
);
}
ASTPointer ASTJsonImporter::createErrorDefinition(Json::Value const& _node)
{
return createASTNode(
_node,
memberAsASTString(_node, "name"),
createNameSourceLocation(_node),
_node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")),
createParameterList(member(_node, "parameters"))
);
}
ASTPointer ASTJsonImporter::createElementaryTypeName(Json::Value const& _node)
{
unsigned short firstNum;
unsigned short secondNum;
astAssert(_node["name"].isString(), "Expected 'name' to be a string!");
std::string name = member(_node, "name").asString();
Token token;
std::tie(token, firstNum, secondNum) = TokenTraits::fromIdentifierOrKeyword(name);
ElementaryTypeNameToken elem(token, firstNum, secondNum);
std::optional mutability = {};
if (_node.isMember("stateMutability"))
mutability = stateMutability(_node);
return createASTNode(_node, elem, mutability);
}
ASTPointer ASTJsonImporter::createUserDefinedTypeName(Json::Value const& _node)
{
return createASTNode(
_node,
createIdentifierPath(member(_node, "pathNode"))
);
}
ASTPointer ASTJsonImporter::createFunctionTypeName(Json::Value const& _node)
{
return createASTNode(
_node,
createParameterList(member(_node, "parameterTypes")),
createParameterList(member(_node, "returnParameterTypes")),
visibility(_node),
stateMutability(_node)
);
}
ASTPointer ASTJsonImporter::createMapping(Json::Value const& _node)
{
return createASTNode(
_node,
convertJsonToASTNode(member(_node, "keyType")),
memberAsASTString(_node, "keyName"),
createKeyNameSourceLocation(_node),
convertJsonToASTNode(member(_node, "valueType")),
memberAsASTString(_node, "valueName"),
createValueNameSourceLocation(_node)
);
}
ASTPointer ASTJsonImporter::createArrayTypeName(Json::Value const& _node)
{
return createASTNode(
_node,
convertJsonToASTNode(member(_node, "baseType")),
nullOrCast(member(_node, "length"))
);
}
ASTPointer 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());
ASTPointer>> flags;
if (_node.isMember("flags"))
{
flags = std::make_shared>>();
Json::Value const& flagsNode = _node["flags"];
astAssert(flagsNode.isArray(), "Assembly flags must be an array.");
for (Json::ArrayIndex i = 0; i < flagsNode.size(); ++i)
{
astAssert(flagsNode[i].isString(), "Assembly flag must be a string.");
flags->emplace_back(std::make_shared(flagsNode[i].asString()));
}
}
std::shared_ptr operations = std::make_shared(yul::AsmJsonImporter(m_sourceNames).createBlock(member(_node, "AST")));
return createASTNode(
_node,
nullOrASTString(_node, "documentation"),
dialect,
std::move(flags),
operations
);
}
ASTPointer ASTJsonImporter::createBlock(Json::Value const& _node, bool _unchecked)
{
std::vector> statements;
for (auto& stat: member(_node, "statements"))
statements.push_back(convertJsonToASTNode(stat));
return createASTNode(
_node,
nullOrASTString(_node, "documentation"),
_unchecked,
statements
);
}
ASTPointer ASTJsonImporter::createPlaceholderStatement(Json::Value const& _node)
{
return createASTNode(
_node,
nullOrASTString(_node, "documentation")
);
}
ASTPointer ASTJsonImporter::createIfStatement(Json::Value const& _node)
{
return createASTNode(
_node,
nullOrASTString(_node, "documentation"),
convertJsonToASTNode(member(_node, "condition")),
convertJsonToASTNode(member(_node, "trueBody")),
nullOrCast(member(_node, "falseBody"))
);
}
ASTPointer ASTJsonImporter::createTryCatchClause(Json::Value const& _node)
{
return createASTNode(
_node,
memberAsASTString(_node, "errorName"),
nullOrCast(member(_node, "parameters")),
convertJsonToASTNode(member(_node, "block"))
);
}
ASTPointer ASTJsonImporter::createTryStatement(Json::Value const& _node)
{
std::vector> clauses;
for (auto& param: _node["clauses"])
clauses.emplace_back(createTryCatchClause(param));
return createASTNode(
_node,
nullOrASTString(_node, "documentation"),
convertJsonToASTNode(member(_node, "externalCall")),
clauses
);
}
ASTPointer ASTJsonImporter::createWhileStatement(Json::Value const& _node, bool _isDoWhile=false)
{
return createASTNode(
_node,
nullOrASTString(_node, "documentation"),
convertJsonToASTNode(member(_node, "condition")),
convertJsonToASTNode(member(_node, "body")),
_isDoWhile
);
}
ASTPointer ASTJsonImporter::createForStatement(Json::Value const& _node)
{
return createASTNode(
_node,
nullOrASTString(_node, "documentation"),
nullOrCast(member(_node, "initializationExpression")),
nullOrCast(member(_node, "condition")),
nullOrCast(member(_node, "loopExpression")),
convertJsonToASTNode(member(_node, "body"))
);
}
ASTPointer ASTJsonImporter::createContinue(Json::Value const& _node)
{
return createASTNode(
_node,
nullOrASTString(_node, "documentation")
);
}
ASTPointer ASTJsonImporter::createBreak(Json::Value const& _node)
{
return createASTNode(
_node,
nullOrASTString(_node, "documentation")
);
}
ASTPointer ASTJsonImporter::createReturn(Json::Value const& _node)
{
return createASTNode(
_node,
nullOrASTString(_node, "documentation"),
nullOrCast(member(_node, "expression"))
);
}
ASTPointer ASTJsonImporter::createThrow(Json::Value const& _node)
{
return createASTNode(
_node,
nullOrASTString(_node, "documentation")
);
}
ASTPointer ASTJsonImporter::createEmitStatement(Json::Value const& _node)
{
return createASTNode(
_node,
nullOrASTString(_node, "documentation"),
createFunctionCall(member(_node, "eventCall"))
);
}
ASTPointer ASTJsonImporter::createRevertStatement(Json::Value const& _node)
{
return createASTNode(
_node,
nullOrASTString(_node, "documentation"),
createFunctionCall(member(_node, "errorCall"))
);
}
ASTPointer ASTJsonImporter::createVariableDeclarationStatement(Json::Value const& _node)
{
std::vector> variables;
for (auto& var: member(_node, "declarations"))
variables.push_back(var.isNull() ? nullptr : createVariableDeclaration(var)); //unnamed components are empty pointers
return createASTNode(
_node,
nullOrASTString(_node, "documentation"),
variables,
nullOrCast(member(_node, "initialValue"))
);
}
ASTPointer ASTJsonImporter::createExpressionStatement(Json::Value const& _node)
{
return createASTNode(
_node,
nullOrASTString(_node, "documentation"),
convertJsonToASTNode(member(_node, "expression"))
);
}
ASTPointer ASTJsonImporter::createConditional(Json::Value const& _node)
{
return createASTNode(
_node,
convertJsonToASTNode(member(_node, "condition")),
convertJsonToASTNode(member(_node, "trueExpression")),
convertJsonToASTNode(member(_node, "falseExpression"))
);
}
ASTPointer ASTJsonImporter::createAssignment(Json::Value const& _node)
{
return createASTNode(
_node,
convertJsonToASTNode(member(_node, "leftHandSide")),
scanSingleToken(member(_node, "operator")),
convertJsonToASTNode(member(_node, "rightHandSide"))
);
}
ASTPointer ASTJsonImporter::createTupleExpression(Json::Value const& _node)
{
std::vector> components;
for (auto& comp: member(_node, "components"))
components.push_back(nullOrCast(comp));
return createASTNode(
_node,
components,
memberAsBool(_node, "isInlineArray")
);
}
ASTPointer ASTJsonImporter::createUnaryOperation(Json::Value const& _node)
{
return createASTNode(
_node,
scanSingleToken(member(_node, "operator")),
convertJsonToASTNode(member(_node, "subExpression")),
memberAsBool(_node, "prefix")
);
}
ASTPointer ASTJsonImporter::createBinaryOperation(Json::Value const& _node)
{
return createASTNode(
_node,
convertJsonToASTNode(member(_node, "leftExpression")),
scanSingleToken(member(_node, "operator")),
convertJsonToASTNode(member(_node, "rightExpression"))
);
}
ASTPointer ASTJsonImporter::createFunctionCall(Json::Value const& _node)
{
std::vector> arguments;
for (auto& arg: member(_node, "arguments"))
arguments.push_back(convertJsonToASTNode(arg));
std::vector> names;
for (auto& name: member(_node, "names"))
{
astAssert(name.isString(), "Expected 'names' members to be strings!");
names.push_back(std::make_shared(name.asString()));
}
std::optional> sourceLocations = createSourceLocations(_node);
return createASTNode(
_node,
convertJsonToASTNode(member(_node, "expression")),
arguments,
names,
sourceLocations ?
*sourceLocations :
std::vector(names.size())
);
}
ASTPointer ASTJsonImporter::createFunctionCallOptions(Json::Value const& _node)
{
std::vector> options;
for (auto& option: member(_node, "options"))
options.push_back(convertJsonToASTNode(option));
std::vector> names;
for (auto& name: member(_node, "names"))
{
astAssert(name.isString(), "Expected 'names' members to be strings!");
names.push_back(std::make_shared(name.asString()));
}
return createASTNode(
_node,
convertJsonToASTNode(member(_node, "expression")),
options,
names
);
}
ASTPointer ASTJsonImporter::createNewExpression(Json::Value const& _node)
{
return createASTNode(
_node,
convertJsonToASTNode(member(_node, "typeName"))
);
}
ASTPointer ASTJsonImporter::createMemberAccess(Json::Value const& _node)
{
SourceLocation memberLocation;
if (member(_node, "memberLocation").isString())
memberLocation = solidity::langutil::parseSourceLocation(_node["memberLocation"].asString(), m_sourceNames);
return createASTNode(
_node,
convertJsonToASTNode(member(_node, "expression")),
memberAsASTString(_node, "memberName"),
std::move(memberLocation)
);
}
ASTPointer ASTJsonImporter::createIndexAccess(Json::Value const& _node)
{
return createASTNode(
_node,
convertJsonToASTNode(member(_node, "baseExpression")),
nullOrCast(member(_node, "indexExpression"))
);
}
ASTPointer ASTJsonImporter::createIndexRangeAccess(Json::Value const& _node)
{
return createASTNode(
_node,
convertJsonToASTNode(member(_node, "baseExpression")),
nullOrCast(member(_node, "startExpression")),
nullOrCast(member(_node, "endExpression"))
);
}
ASTPointer ASTJsonImporter::createIdentifier(Json::Value const& _node)
{
return createASTNode(_node, memberAsASTString(_node, "name"));
}
ASTPointer ASTJsonImporter::createElementaryTypeNameExpression(Json::Value const& _node)
{
return createASTNode(
_node,
createElementaryTypeName(member(_node, "typeName"))
);
}
ASTPointer ASTJsonImporter::createLiteral(Json::Value const& _node)
{
static std::string const valStr = "value";
static std::string const hexValStr = "hexValue";
astAssert(member(_node, valStr).isString() || member(_node, hexValStr).isString(), "Literal-value is unset.");
ASTPointer value = _node.isMember(hexValStr) ?
std::make_shared(util::asString(util::fromHex(_node[hexValStr].asString()))) :
std::make_shared(_node[valStr].asString());
return createASTNode(
_node,
literalTokenKind(_node),
value,
member(_node, "subdenomination").isNull() ? Literal::SubDenomination::None : subdenomination(_node)
);
}
ASTPointer ASTJsonImporter::createDocumentation(Json::Value const& _node)
{
static std::string const textString = "text";
astAssert(member(_node, textString).isString(), "'text' must be a string");
return createASTNode(
_node,
std::make_shared(_node[textString].asString())
);
}
// ===== helper functions ==========
Json::Value ASTJsonImporter::member(Json::Value const& _node, std::string const& _name)
{
if (!_node.isMember(_name))
return Json::nullValue;
return _node[_name];
}
Token ASTJsonImporter::scanSingleToken(Json::Value const& _node)
{
langutil::CharStream charStream(_node.asString(), "");
langutil::Scanner scanner{charStream};
astAssert(scanner.peekNextToken() == Token::EOS, "Token string is too long.");
return scanner.currentToken();
}
ASTPointer ASTJsonImporter::nullOrASTString(Json::Value const& _json, std::string const& _name)
{
return _json[_name].isString() ? memberAsASTString(_json, _name) : nullptr;
}
ASTPointer ASTJsonImporter::memberAsASTString(Json::Value const& _node, std::string const& _name)
{
Json::Value value = member(_node, _name);
astAssert(value.isString(), "field " + _name + " must be of type string.");
return std::make_shared(_node[_name].asString());
}
bool ASTJsonImporter::memberAsBool(Json::Value const& _node, std::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() == "unicodeString")
tok = Token::UnicodeStringLiteral;
else if (_node["kind"].asString() == "hexString")
tok = Token::HexStringLiteral;
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.");
std::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");
// FIXME: Workaround for spurious GCC 12.1 warning (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105794)
util::unreachable();
}
VariableDeclaration::Location ASTJsonImporter::location(Json::Value const& _node)
{
Json::Value storageLoc = member(_node, "storageLocation");
astAssert(storageLoc.isString(), "'storageLocation' expected to be a string.");
std::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");
// FIXME: Workaround for spurious GCC 12.1 warning (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105794)
util::unreachable();
}
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.");
std::string const subDenStr = subDen.asString();
if (subDenStr == "wei")
return Literal::SubDenomination::Wei;
else if (subDenStr == "gwei")
return Literal::SubDenomination::Gwei;
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");
// FIXME: Workaround for spurious GCC 12.1 warning (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105794)
util::unreachable();
}
StateMutability ASTJsonImporter::stateMutability(Json::Value const& _node)
{
astAssert(member(_node, "stateMutability").isString(), "StateMutability' expected to be string.");
std::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");
// FIXME: Workaround for spurious GCC 12.1 warning (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105794)
util::unreachable();
}
}