mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Add EVMJIT.
This commit is contained in:
parent
89d84edb16
commit
9b3886ec19
636
AST.cpp
636
AST.cpp
@ -1,636 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Solidity abstract syntax tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <libsolidity/Utils.h>
|
|
||||||
#include <libsolidity/AST.h>
|
|
||||||
#include <libsolidity/ASTVisitor.h>
|
|
||||||
#include <libsolidity/Exceptions.h>
|
|
||||||
#include <libsolidity/AST_accept.h>
|
|
||||||
|
|
||||||
#include <libdevcrypto/SHA3.h>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
TypeError ASTNode::createTypeError(string const& _description) const
|
|
||||||
{
|
|
||||||
return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description);
|
|
||||||
}
|
|
||||||
|
|
||||||
TypePointer ContractDefinition::getType(ContractDefinition const* _currentContract) const
|
|
||||||
{
|
|
||||||
return make_shared<TypeType>(make_shared<ContractType>(*this), _currentContract);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ContractDefinition::checkTypeRequirements()
|
|
||||||
{
|
|
||||||
for (ASTPointer<InheritanceSpecifier> const& baseSpecifier: getBaseContracts())
|
|
||||||
baseSpecifier->checkTypeRequirements();
|
|
||||||
|
|
||||||
checkIllegalOverrides();
|
|
||||||
|
|
||||||
FunctionDefinition const* constructor = getConstructor();
|
|
||||||
if (constructor && !constructor->getReturnParameters().empty())
|
|
||||||
BOOST_THROW_EXCEPTION(constructor->getReturnParameterList()->createTypeError(
|
|
||||||
"Non-empty \"returns\" directive for constructor."));
|
|
||||||
|
|
||||||
FunctionDefinition const* fallbackFunction = nullptr;
|
|
||||||
for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions())
|
|
||||||
if (function->getName().empty())
|
|
||||||
{
|
|
||||||
if (fallbackFunction)
|
|
||||||
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Only one fallback function is allowed."));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fallbackFunction = function.get();
|
|
||||||
if (!fallbackFunction->getParameters().empty())
|
|
||||||
BOOST_THROW_EXCEPTION(fallbackFunction->getParameterList().createTypeError("Fallback function cannot take parameters."));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (ASTPointer<ModifierDefinition> const& modifier: getFunctionModifiers())
|
|
||||||
modifier->checkTypeRequirements();
|
|
||||||
|
|
||||||
for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions())
|
|
||||||
function->checkTypeRequirements();
|
|
||||||
|
|
||||||
// check for hash collisions in function signatures
|
|
||||||
set<FixedHash<4>> hashes;
|
|
||||||
for (auto const& it: getInterfaceFunctionList())
|
|
||||||
{
|
|
||||||
FixedHash<4> const& hash = it.first;
|
|
||||||
if (hashes.count(hash))
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError(
|
|
||||||
std::string("Function signature hash collision for ") +
|
|
||||||
it.second->getCanonicalSignature()));
|
|
||||||
hashes.insert(hash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
map<FixedHash<4>, FunctionTypePointer> ContractDefinition::getInterfaceFunctions() const
|
|
||||||
{
|
|
||||||
auto exportedFunctionList = getInterfaceFunctionList();
|
|
||||||
|
|
||||||
map<FixedHash<4>, FunctionTypePointer> exportedFunctions;
|
|
||||||
for (auto const& it: exportedFunctionList)
|
|
||||||
exportedFunctions.insert(it);
|
|
||||||
|
|
||||||
solAssert(exportedFunctionList.size() == exportedFunctions.size(),
|
|
||||||
"Hash collision at Function Definition Hash calculation");
|
|
||||||
|
|
||||||
return exportedFunctions;
|
|
||||||
}
|
|
||||||
|
|
||||||
FunctionDefinition const* ContractDefinition::getConstructor() const
|
|
||||||
{
|
|
||||||
for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions)
|
|
||||||
if (f->isConstructor())
|
|
||||||
return f.get();
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
FunctionDefinition const* ContractDefinition::getFallbackFunction() const
|
|
||||||
{
|
|
||||||
for (ContractDefinition const* contract: getLinearizedBaseContracts())
|
|
||||||
for (ASTPointer<FunctionDefinition> const& f: contract->getDefinedFunctions())
|
|
||||||
if (f->getName().empty())
|
|
||||||
return f.get();
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ContractDefinition::checkIllegalOverrides() const
|
|
||||||
{
|
|
||||||
// TODO unify this at a later point. for this we need to put the constness and the access specifier
|
|
||||||
// into the types
|
|
||||||
map<string, FunctionDefinition const*> functions;
|
|
||||||
map<string, ModifierDefinition const*> modifiers;
|
|
||||||
|
|
||||||
// We search from derived to base, so the stored item causes the error.
|
|
||||||
for (ContractDefinition const* contract: getLinearizedBaseContracts())
|
|
||||||
{
|
|
||||||
for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions())
|
|
||||||
{
|
|
||||||
if (function->isConstructor())
|
|
||||||
continue; // constructors can neither be overridden nor override anything
|
|
||||||
string const& name = function->getName();
|
|
||||||
if (modifiers.count(name))
|
|
||||||
BOOST_THROW_EXCEPTION(modifiers[name]->createTypeError("Override changes function to modifier."));
|
|
||||||
FunctionDefinition const*& override = functions[name];
|
|
||||||
if (!override)
|
|
||||||
override = function.get();
|
|
||||||
else if (override->getVisibility() != function->getVisibility() ||
|
|
||||||
override->isDeclaredConst() != function->isDeclaredConst() ||
|
|
||||||
FunctionType(*override) != FunctionType(*function))
|
|
||||||
BOOST_THROW_EXCEPTION(override->createTypeError("Override changes extended function signature."));
|
|
||||||
}
|
|
||||||
for (ASTPointer<ModifierDefinition> const& modifier: contract->getFunctionModifiers())
|
|
||||||
{
|
|
||||||
string const& name = modifier->getName();
|
|
||||||
if (functions.count(name))
|
|
||||||
BOOST_THROW_EXCEPTION(functions[name]->createTypeError("Override changes modifier to function."));
|
|
||||||
ModifierDefinition const*& override = modifiers[name];
|
|
||||||
if (!override)
|
|
||||||
override = modifier.get();
|
|
||||||
else if (ModifierType(*override) != ModifierType(*modifier))
|
|
||||||
BOOST_THROW_EXCEPTION(override->createTypeError("Override changes modifier signature."));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<ASTPointer<EventDefinition>> const& ContractDefinition::getInterfaceEvents() const
|
|
||||||
{
|
|
||||||
if (!m_interfaceEvents)
|
|
||||||
{
|
|
||||||
set<string> eventsSeen;
|
|
||||||
m_interfaceEvents.reset(new std::vector<ASTPointer<EventDefinition>>());
|
|
||||||
for (ContractDefinition const* contract: getLinearizedBaseContracts())
|
|
||||||
for (ASTPointer<EventDefinition> const& e: contract->getEvents())
|
|
||||||
if (eventsSeen.count(e->getName()) == 0)
|
|
||||||
{
|
|
||||||
eventsSeen.insert(e->getName());
|
|
||||||
m_interfaceEvents->push_back(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return *m_interfaceEvents;
|
|
||||||
}
|
|
||||||
|
|
||||||
vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::getInterfaceFunctionList() const
|
|
||||||
{
|
|
||||||
if (!m_interfaceFunctionList)
|
|
||||||
{
|
|
||||||
set<string> functionsSeen;
|
|
||||||
m_interfaceFunctionList.reset(new vector<pair<FixedHash<4>, FunctionTypePointer>>());
|
|
||||||
for (ContractDefinition const* contract: getLinearizedBaseContracts())
|
|
||||||
{
|
|
||||||
for (ASTPointer<FunctionDefinition> const& f: contract->getDefinedFunctions())
|
|
||||||
if (f->isPublic() && !f->isConstructor() && !f->getName().empty() && functionsSeen.count(f->getName()) == 0)
|
|
||||||
{
|
|
||||||
functionsSeen.insert(f->getName());
|
|
||||||
FixedHash<4> hash(dev::sha3(f->getCanonicalSignature()));
|
|
||||||
m_interfaceFunctionList->push_back(make_pair(hash, make_shared<FunctionType>(*f, false)));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (ASTPointer<VariableDeclaration> const& v: contract->getStateVariables())
|
|
||||||
if (v->isPublic() && functionsSeen.count(v->getName()) == 0)
|
|
||||||
{
|
|
||||||
FunctionType ftype(*v);
|
|
||||||
functionsSeen.insert(v->getName());
|
|
||||||
FixedHash<4> hash(dev::sha3(ftype.getCanonicalSignature(v->getName())));
|
|
||||||
m_interfaceFunctionList->push_back(make_pair(hash, make_shared<FunctionType>(*v)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return *m_interfaceFunctionList;
|
|
||||||
}
|
|
||||||
|
|
||||||
TypePointer EnumValue::getType(ContractDefinition const*) const
|
|
||||||
{
|
|
||||||
EnumDefinition const* parentDef = dynamic_cast<EnumDefinition const*>(getScope());
|
|
||||||
solAssert(parentDef, "Enclosing Scope of EnumValue was not set");
|
|
||||||
return make_shared<EnumType>(*parentDef);
|
|
||||||
}
|
|
||||||
|
|
||||||
void InheritanceSpecifier::checkTypeRequirements()
|
|
||||||
{
|
|
||||||
m_baseName->checkTypeRequirements();
|
|
||||||
for (ASTPointer<Expression> const& argument: m_arguments)
|
|
||||||
argument->checkTypeRequirements();
|
|
||||||
|
|
||||||
ContractDefinition const* base = dynamic_cast<ContractDefinition const*>(m_baseName->getReferencedDeclaration());
|
|
||||||
solAssert(base, "Base contract not available.");
|
|
||||||
TypePointers parameterTypes = ContractType(*base).getConstructorType()->getParameterTypes();
|
|
||||||
if (parameterTypes.size() != m_arguments.size())
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for constructor call."));
|
|
||||||
for (size_t i = 0; i < m_arguments.size(); ++i)
|
|
||||||
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i]))
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in constructer call."));
|
|
||||||
}
|
|
||||||
|
|
||||||
TypePointer StructDefinition::getType(ContractDefinition const*) const
|
|
||||||
{
|
|
||||||
return make_shared<TypeType>(make_shared<StructType>(*this));
|
|
||||||
}
|
|
||||||
|
|
||||||
void StructDefinition::checkMemberTypes() const
|
|
||||||
{
|
|
||||||
for (ASTPointer<VariableDeclaration> const& member: getMembers())
|
|
||||||
if (!member->getType()->canBeStored())
|
|
||||||
BOOST_THROW_EXCEPTION(member->createTypeError("Type cannot be used in struct."));
|
|
||||||
}
|
|
||||||
|
|
||||||
void StructDefinition::checkRecursion() const
|
|
||||||
{
|
|
||||||
set<StructDefinition const*> definitionsSeen;
|
|
||||||
vector<StructDefinition const*> queue = {this};
|
|
||||||
while (!queue.empty())
|
|
||||||
{
|
|
||||||
StructDefinition const* def = queue.back();
|
|
||||||
queue.pop_back();
|
|
||||||
if (definitionsSeen.count(def))
|
|
||||||
BOOST_THROW_EXCEPTION(ParserError() << errinfo_sourceLocation(def->getLocation())
|
|
||||||
<< errinfo_comment("Recursive struct definition."));
|
|
||||||
definitionsSeen.insert(def);
|
|
||||||
for (ASTPointer<VariableDeclaration> const& member: def->getMembers())
|
|
||||||
if (member->getType()->getCategory() == Type::Category::Struct)
|
|
||||||
{
|
|
||||||
UserDefinedTypeName const& typeName = dynamic_cast<UserDefinedTypeName const&>(*member->getTypeName());
|
|
||||||
queue.push_back(&dynamic_cast<StructDefinition const&>(*typeName.getReferencedDeclaration()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TypePointer EnumDefinition::getType(ContractDefinition const*) const
|
|
||||||
{
|
|
||||||
return make_shared<TypeType>(make_shared<EnumType>(*this));
|
|
||||||
}
|
|
||||||
|
|
||||||
TypePointer FunctionDefinition::getType(ContractDefinition const*) const
|
|
||||||
{
|
|
||||||
return make_shared<FunctionType>(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FunctionDefinition::checkTypeRequirements()
|
|
||||||
{
|
|
||||||
for (ASTPointer<VariableDeclaration> const& var: getParameters() + getReturnParameters())
|
|
||||||
if (!var->getType()->canLiveOutsideStorage())
|
|
||||||
BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage."));
|
|
||||||
for (ASTPointer<ModifierInvocation> const& modifier: m_functionModifiers)
|
|
||||||
modifier->checkTypeRequirements();
|
|
||||||
|
|
||||||
m_body->checkTypeRequirements();
|
|
||||||
}
|
|
||||||
|
|
||||||
string FunctionDefinition::getCanonicalSignature() const
|
|
||||||
{
|
|
||||||
return FunctionType(*this).getCanonicalSignature(getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VariableDeclaration::isLValue() const
|
|
||||||
{
|
|
||||||
// External function parameters are Read-Only
|
|
||||||
return !isExternalFunctionParameter();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VariableDeclaration::isExternalFunctionParameter() const
|
|
||||||
{
|
|
||||||
auto const* function = dynamic_cast<FunctionDefinition const*>(getScope());
|
|
||||||
if (!function || function->getVisibility() != Declaration::Visibility::External)
|
|
||||||
return false;
|
|
||||||
for (auto const& variable: function->getParameters())
|
|
||||||
if (variable.get() == this)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
TypePointer ModifierDefinition::getType(ContractDefinition const*) const
|
|
||||||
{
|
|
||||||
return make_shared<ModifierType>(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModifierDefinition::checkTypeRequirements()
|
|
||||||
{
|
|
||||||
m_body->checkTypeRequirements();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModifierInvocation::checkTypeRequirements()
|
|
||||||
{
|
|
||||||
m_modifierName->checkTypeRequirements();
|
|
||||||
for (ASTPointer<Expression> const& argument: m_arguments)
|
|
||||||
argument->checkTypeRequirements();
|
|
||||||
|
|
||||||
ModifierDefinition const* modifier = dynamic_cast<ModifierDefinition const*>(m_modifierName->getReferencedDeclaration());
|
|
||||||
solAssert(modifier, "Function modifier not found.");
|
|
||||||
vector<ASTPointer<VariableDeclaration>> const& parameters = modifier->getParameters();
|
|
||||||
if (parameters.size() != m_arguments.size())
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for modifier invocation."));
|
|
||||||
for (size_t i = 0; i < m_arguments.size(); ++i)
|
|
||||||
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameters[i]->getType()))
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in modifier invocation."));
|
|
||||||
}
|
|
||||||
|
|
||||||
void EventDefinition::checkTypeRequirements()
|
|
||||||
{
|
|
||||||
int numIndexed = 0;
|
|
||||||
for (ASTPointer<VariableDeclaration> const& var: getParameters())
|
|
||||||
{
|
|
||||||
if (var->isIndexed())
|
|
||||||
numIndexed++;
|
|
||||||
if (!var->getType()->canLiveOutsideStorage())
|
|
||||||
BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage."));
|
|
||||||
}
|
|
||||||
if (numIndexed > 3)
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("More than 3 indexed arguments for event."));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Block::checkTypeRequirements()
|
|
||||||
{
|
|
||||||
for (shared_ptr<Statement> const& statement: m_statements)
|
|
||||||
statement->checkTypeRequirements();
|
|
||||||
}
|
|
||||||
|
|
||||||
void IfStatement::checkTypeRequirements()
|
|
||||||
{
|
|
||||||
m_condition->expectType(BoolType());
|
|
||||||
m_trueBody->checkTypeRequirements();
|
|
||||||
if (m_falseBody)
|
|
||||||
m_falseBody->checkTypeRequirements();
|
|
||||||
}
|
|
||||||
|
|
||||||
void WhileStatement::checkTypeRequirements()
|
|
||||||
{
|
|
||||||
m_condition->expectType(BoolType());
|
|
||||||
m_body->checkTypeRequirements();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ForStatement::checkTypeRequirements()
|
|
||||||
{
|
|
||||||
if (m_initExpression)
|
|
||||||
m_initExpression->checkTypeRequirements();
|
|
||||||
if (m_condExpression)
|
|
||||||
m_condExpression->expectType(BoolType());
|
|
||||||
if (m_loopExpression)
|
|
||||||
m_loopExpression->checkTypeRequirements();
|
|
||||||
m_body->checkTypeRequirements();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Return::checkTypeRequirements()
|
|
||||||
{
|
|
||||||
if (!m_expression)
|
|
||||||
return;
|
|
||||||
if (!m_returnParameters)
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Return arguments not allowed."));
|
|
||||||
if (m_returnParameters->getParameters().size() != 1)
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Different number of arguments in return statement "
|
|
||||||
"than in returns declaration."));
|
|
||||||
// this could later be changed such that the paramaters type is an anonymous struct type,
|
|
||||||
// but for now, we only allow one return parameter
|
|
||||||
m_expression->expectType(*m_returnParameters->getParameters().front()->getType());
|
|
||||||
}
|
|
||||||
|
|
||||||
void VariableDefinition::checkTypeRequirements()
|
|
||||||
{
|
|
||||||
// Variables can be declared without type (with "var"), in which case the first assignment
|
|
||||||
// sets the type.
|
|
||||||
// Note that assignments before the first declaration are legal because of the special scoping
|
|
||||||
// rules inherited from JavaScript.
|
|
||||||
if (m_value)
|
|
||||||
{
|
|
||||||
if (m_variable->getType())
|
|
||||||
m_value->expectType(*m_variable->getType());
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// no type declared and no previous assignment, infer the type
|
|
||||||
m_value->checkTypeRequirements();
|
|
||||||
TypePointer type = m_value->getType();
|
|
||||||
if (type->getCategory() == Type::Category::IntegerConstant)
|
|
||||||
{
|
|
||||||
auto intType = dynamic_pointer_cast<IntegerConstantType const>(type)->getIntegerType();
|
|
||||||
if (!intType)
|
|
||||||
BOOST_THROW_EXCEPTION(m_value->createTypeError("Invalid integer constant " + type->toString()));
|
|
||||||
type = intType;
|
|
||||||
}
|
|
||||||
else if (type->getCategory() == Type::Category::Void)
|
|
||||||
BOOST_THROW_EXCEPTION(m_variable->createTypeError("var cannot be void type"));
|
|
||||||
m_variable->setType(type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Assignment::checkTypeRequirements()
|
|
||||||
{
|
|
||||||
m_leftHandSide->checkTypeRequirements();
|
|
||||||
m_leftHandSide->requireLValue();
|
|
||||||
if (m_leftHandSide->getType()->getCategory() == Type::Category::Mapping)
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Mappings cannot be assigned to."));
|
|
||||||
m_type = m_leftHandSide->getType();
|
|
||||||
if (m_assigmentOperator == Token::Assign)
|
|
||||||
m_rightHandSide->expectType(*m_type);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// compound assignment
|
|
||||||
m_rightHandSide->checkTypeRequirements();
|
|
||||||
TypePointer resultType = m_type->binaryOperatorResult(Token::AssignmentToBinaryOp(m_assigmentOperator),
|
|
||||||
m_rightHandSide->getType());
|
|
||||||
if (!resultType || *resultType != *m_type)
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Operator " + string(Token::toString(m_assigmentOperator)) +
|
|
||||||
" not compatible with types " +
|
|
||||||
m_type->toString() + " and " +
|
|
||||||
m_rightHandSide->getType()->toString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ExpressionStatement::checkTypeRequirements()
|
|
||||||
{
|
|
||||||
m_expression->checkTypeRequirements();
|
|
||||||
if (m_expression->getType()->getCategory() == Type::Category::IntegerConstant)
|
|
||||||
if (!dynamic_pointer_cast<IntegerConstantType const>(m_expression->getType())->getIntegerType())
|
|
||||||
BOOST_THROW_EXCEPTION(m_expression->createTypeError("Invalid integer constant."));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Expression::expectType(Type const& _expectedType)
|
|
||||||
{
|
|
||||||
checkTypeRequirements();
|
|
||||||
Type const& type = *getType();
|
|
||||||
if (!type.isImplicitlyConvertibleTo(_expectedType))
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Type " + type.toString() +
|
|
||||||
" not implicitly convertible to expected type "
|
|
||||||
+ _expectedType.toString() + "."));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Expression::requireLValue()
|
|
||||||
{
|
|
||||||
if (!isLValue())
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Expression has to be an lvalue."));
|
|
||||||
m_lvalueRequested = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnaryOperation::checkTypeRequirements()
|
|
||||||
{
|
|
||||||
// Inc, Dec, Add, Sub, Not, BitNot, Delete
|
|
||||||
m_subExpression->checkTypeRequirements();
|
|
||||||
if (m_operator == Token::Value::Inc || m_operator == Token::Value::Dec || m_operator == Token::Value::Delete)
|
|
||||||
m_subExpression->requireLValue();
|
|
||||||
m_type = m_subExpression->getType()->unaryOperatorResult(m_operator);
|
|
||||||
if (!m_type)
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Unary operator not compatible with type."));
|
|
||||||
}
|
|
||||||
|
|
||||||
void BinaryOperation::checkTypeRequirements()
|
|
||||||
{
|
|
||||||
m_left->checkTypeRequirements();
|
|
||||||
m_right->checkTypeRequirements();
|
|
||||||
m_commonType = m_left->getType()->binaryOperatorResult(m_operator, m_right->getType());
|
|
||||||
if (!m_commonType)
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Operator " + string(Token::toString(m_operator)) +
|
|
||||||
" not compatible with types " +
|
|
||||||
m_left->getType()->toString() + " and " +
|
|
||||||
m_right->getType()->toString()));
|
|
||||||
m_type = Token::isCompareOp(m_operator) ? make_shared<BoolType>() : m_commonType;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FunctionCall::checkTypeRequirements()
|
|
||||||
{
|
|
||||||
m_expression->checkTypeRequirements();
|
|
||||||
for (ASTPointer<Expression> const& argument: m_arguments)
|
|
||||||
argument->checkTypeRequirements();
|
|
||||||
|
|
||||||
Type const* expressionType = m_expression->getType().get();
|
|
||||||
if (isTypeConversion())
|
|
||||||
{
|
|
||||||
TypeType const& type = dynamic_cast<TypeType const&>(*expressionType);
|
|
||||||
//@todo for structs, we have to check the number of arguments to be equal to the
|
|
||||||
// number of non-mapping members
|
|
||||||
if (m_arguments.size() != 1)
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("More than one argument for explicit type conversion."));
|
|
||||||
if (!m_names.empty())
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Type conversion cannot allow named arguments."));
|
|
||||||
if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type.getActualType()))
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed."));
|
|
||||||
m_type = type.getActualType();
|
|
||||||
}
|
|
||||||
else if (FunctionType const* functionType = dynamic_cast<FunctionType const*>(expressionType))
|
|
||||||
{
|
|
||||||
//@todo would be nice to create a struct type from the arguments
|
|
||||||
// and then ask if that is implicitly convertible to the struct represented by the
|
|
||||||
// function parameters
|
|
||||||
TypePointers const& parameterTypes = functionType->getParameterTypes();
|
|
||||||
if (!functionType->takesArbitraryParameters() && parameterTypes.size() != m_arguments.size())
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call."));
|
|
||||||
|
|
||||||
if (m_names.empty())
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < m_arguments.size(); ++i)
|
|
||||||
if (!functionType->takesArbitraryParameters() &&
|
|
||||||
!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i]))
|
|
||||||
BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError("Invalid type for argument in function call."));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (functionType->takesArbitraryParameters())
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Named arguments cannnot be used for functions "
|
|
||||||
"that take arbitrary parameters."));
|
|
||||||
auto const& parameterNames = functionType->getParameterNames();
|
|
||||||
if (parameterNames.size() != m_names.size())
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Some argument names are missing."));
|
|
||||||
|
|
||||||
// check duplicate names
|
|
||||||
for (size_t i = 0; i < m_names.size(); i++)
|
|
||||||
for (size_t j = i + 1; j < m_names.size(); j++)
|
|
||||||
if (*m_names[i] == *m_names[j])
|
|
||||||
BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError("Duplicate named argument."));
|
|
||||||
|
|
||||||
for (size_t i = 0; i < m_names.size(); i++) {
|
|
||||||
bool found = false;
|
|
||||||
for (size_t j = 0; j < parameterNames.size(); j++) {
|
|
||||||
if (parameterNames[j] == *m_names[i]) {
|
|
||||||
// check type convertible
|
|
||||||
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[j]))
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in function call."));
|
|
||||||
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found)
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Named argument does not match function declaration."));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// @todo actually the return type should be an anonymous struct,
|
|
||||||
// but we change it to the type of the first return value until we have structs
|
|
||||||
if (functionType->getReturnParameterTypes().empty())
|
|
||||||
m_type = make_shared<VoidType>();
|
|
||||||
else
|
|
||||||
m_type = functionType->getReturnParameterTypes().front();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Type is not callable."));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FunctionCall::isTypeConversion() const
|
|
||||||
{
|
|
||||||
return m_expression->getType()->getCategory() == Type::Category::TypeType;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NewExpression::checkTypeRequirements()
|
|
||||||
{
|
|
||||||
m_contractName->checkTypeRequirements();
|
|
||||||
m_contract = dynamic_cast<ContractDefinition const*>(m_contractName->getReferencedDeclaration());
|
|
||||||
if (!m_contract)
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract."));
|
|
||||||
shared_ptr<ContractType const> contractType = make_shared<ContractType>(*m_contract);
|
|
||||||
TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes();
|
|
||||||
m_type = make_shared<FunctionType>(parameterTypes, TypePointers{contractType},
|
|
||||||
FunctionType::Location::Creation);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MemberAccess::checkTypeRequirements()
|
|
||||||
{
|
|
||||||
m_expression->checkTypeRequirements();
|
|
||||||
Type const& type = *m_expression->getType();
|
|
||||||
m_type = type.getMemberType(*m_memberName);
|
|
||||||
if (!m_type)
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found or not "
|
|
||||||
"visible in " + type.toString()));
|
|
||||||
m_isLValue = (type.getCategory() == Type::Category::Struct);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IndexAccess::checkTypeRequirements()
|
|
||||||
{
|
|
||||||
m_base->checkTypeRequirements();
|
|
||||||
if (m_base->getType()->getCategory() != Type::Category::Mapping)
|
|
||||||
BOOST_THROW_EXCEPTION(m_base->createTypeError("Indexed expression has to be a mapping (is " +
|
|
||||||
m_base->getType()->toString() + ")"));
|
|
||||||
MappingType const& type = dynamic_cast<MappingType const&>(*m_base->getType());
|
|
||||||
m_index->expectType(*type.getKeyType());
|
|
||||||
m_type = type.getValueType();
|
|
||||||
m_isLValue = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Identifier::checkTypeRequirements()
|
|
||||||
{
|
|
||||||
solAssert(m_referencedDeclaration, "Identifier not resolved.");
|
|
||||||
|
|
||||||
m_isLValue = m_referencedDeclaration->isLValue();
|
|
||||||
m_type = m_referencedDeclaration->getType(m_currentContract);
|
|
||||||
if (!m_type)
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Declaration referenced before type could be determined."));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ElementaryTypeNameExpression::checkTypeRequirements()
|
|
||||||
{
|
|
||||||
m_type = make_shared<TypeType>(Type::fromElementaryTypeName(m_typeToken));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Literal::checkTypeRequirements()
|
|
||||||
{
|
|
||||||
m_type = Type::forLiteral(*this);
|
|
||||||
if (!m_type)
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Invalid literal value."));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
93
ASTForward.h
93
ASTForward.h
@ -1,93 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Forward-declarations of AST classes.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
// Forward-declare all AST node types
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
class ASTNode;
|
|
||||||
class SourceUnit;
|
|
||||||
class ImportDirective;
|
|
||||||
class Declaration;
|
|
||||||
class ContractDefinition;
|
|
||||||
class InheritanceSpecifier;
|
|
||||||
class StructDefinition;
|
|
||||||
class EnumDefinition;
|
|
||||||
class EnumValue;
|
|
||||||
class ParameterList;
|
|
||||||
class FunctionDefinition;
|
|
||||||
class VariableDeclaration;
|
|
||||||
class ModifierDefinition;
|
|
||||||
class ModifierInvocation;
|
|
||||||
class EventDefinition;
|
|
||||||
class MagicVariableDeclaration;
|
|
||||||
class TypeName;
|
|
||||||
class ElementaryTypeName;
|
|
||||||
class UserDefinedTypeName;
|
|
||||||
class Mapping;
|
|
||||||
class Statement;
|
|
||||||
class Block;
|
|
||||||
class PlaceholderStatement;
|
|
||||||
class IfStatement;
|
|
||||||
class BreakableStatement;
|
|
||||||
class WhileStatement;
|
|
||||||
class ForStatement;
|
|
||||||
class Continue;
|
|
||||||
class Break;
|
|
||||||
class Return;
|
|
||||||
class VariableDefinition;
|
|
||||||
class ExpressionStatement;
|
|
||||||
class Expression;
|
|
||||||
class Assignment;
|
|
||||||
class UnaryOperation;
|
|
||||||
class BinaryOperation;
|
|
||||||
class FunctionCall;
|
|
||||||
class NewExpression;
|
|
||||||
class MemberAccess;
|
|
||||||
class IndexAccess;
|
|
||||||
class PrimaryExpression;
|
|
||||||
class Identifier;
|
|
||||||
class ElementaryTypeNameExpression;
|
|
||||||
class Literal;
|
|
||||||
|
|
||||||
class VariableScope;
|
|
||||||
|
|
||||||
// Used as pointers to AST nodes, to be replaced by more clever pointers, e.g. pointers which do
|
|
||||||
// not do reference counting but point to a special memory area that is completely released
|
|
||||||
// explicitly.
|
|
||||||
template <class T>
|
|
||||||
using ASTPointer = std::shared_ptr<T>;
|
|
||||||
|
|
||||||
using ASTString = std::string;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,469 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Lefteris <lefteris@ethdev.com>
|
|
||||||
* @date 2015
|
|
||||||
* Converts the AST into json format
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <libsolidity/ASTJsonConverter.h>
|
|
||||||
#include <libsolidity/AST.h>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
void ASTJsonConverter::addKeyValue(Json::Value& _obj, string const& _key, string const& _val)
|
|
||||||
{
|
|
||||||
// special handling for booleans
|
|
||||||
if (_key == "const" || _key == "public" || _key == "local" ||
|
|
||||||
_key == "lvalue" || _key == "local_lvalue" || _key == "prefix")
|
|
||||||
_obj[_key] = (_val == "1") ? true : false;
|
|
||||||
else
|
|
||||||
// else simply add it as a string
|
|
||||||
_obj[_key] = _val;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::addJsonNode(string const& _nodeName,
|
|
||||||
initializer_list<pair<string const, string const>> _list,
|
|
||||||
bool _hasChildren = false)
|
|
||||||
{
|
|
||||||
Json::Value node;
|
|
||||||
|
|
||||||
node["name"] = _nodeName;
|
|
||||||
if (_list.size() != 0)
|
|
||||||
{
|
|
||||||
Json::Value attrs;
|
|
||||||
for (auto& e: _list)
|
|
||||||
addKeyValue(attrs, e.first, e.second);
|
|
||||||
node["attributes"] = attrs;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_jsonNodePtrs.top()->append(node);
|
|
||||||
|
|
||||||
if (_hasChildren)
|
|
||||||
{
|
|
||||||
Json::Value& addedNode = (*m_jsonNodePtrs.top())[m_jsonNodePtrs.top()->size() - 1];
|
|
||||||
Json::Value children(Json::arrayValue);
|
|
||||||
addedNode["children"] = children;
|
|
||||||
m_jsonNodePtrs.push(&addedNode["children"]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTJsonConverter::ASTJsonConverter(ASTNode const& _ast): m_ast(&_ast)
|
|
||||||
{
|
|
||||||
Json::Value children(Json::arrayValue);
|
|
||||||
|
|
||||||
m_astJson["name"] = "root";
|
|
||||||
m_astJson["children"] = children;
|
|
||||||
m_jsonNodePtrs.push(&m_astJson["children"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::print(ostream& _stream)
|
|
||||||
{
|
|
||||||
m_ast->accept(*this);
|
|
||||||
_stream << m_astJson;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(ImportDirective const& _node)
|
|
||||||
{
|
|
||||||
addJsonNode("Import", { make_pair("file", _node.getIdentifier())});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(ContractDefinition const& _node)
|
|
||||||
{
|
|
||||||
addJsonNode("Contract", { make_pair("name", _node.getName()) }, true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(StructDefinition const& _node)
|
|
||||||
{
|
|
||||||
addJsonNode("Struct", { make_pair("name", _node.getName()) }, true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(ParameterList const&)
|
|
||||||
{
|
|
||||||
addJsonNode("ParameterList", {}, true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(FunctionDefinition const& _node)
|
|
||||||
{
|
|
||||||
addJsonNode("Function",
|
|
||||||
{ make_pair("name", _node.getName()),
|
|
||||||
make_pair("public", boost::lexical_cast<std::string>(_node.isPublic())),
|
|
||||||
make_pair("const", boost::lexical_cast<std::string>(_node.isDeclaredConst())) },
|
|
||||||
true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(VariableDeclaration const& _node)
|
|
||||||
{
|
|
||||||
addJsonNode("VariableDeclaration", { make_pair("name", _node.getName()) }, true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(TypeName const&)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(ElementaryTypeName const& _node)
|
|
||||||
{
|
|
||||||
addJsonNode("ElementaryTypeName", { make_pair("name", Token::toString(_node.getTypeName())) });
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(UserDefinedTypeName const& _node)
|
|
||||||
{
|
|
||||||
addJsonNode("UserDefinedTypeName", { make_pair("name", _node.getName()) });
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(Mapping const&)
|
|
||||||
{
|
|
||||||
addJsonNode("Mapping", {}, true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(Statement const&)
|
|
||||||
{
|
|
||||||
addJsonNode("Statement", {}, true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(Block const&)
|
|
||||||
{
|
|
||||||
addJsonNode("Block", {}, true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(IfStatement const&)
|
|
||||||
{
|
|
||||||
addJsonNode("IfStatement", {}, true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(BreakableStatement const&)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(WhileStatement const&)
|
|
||||||
{
|
|
||||||
addJsonNode("WhileStatement", {}, true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(ForStatement const&)
|
|
||||||
{
|
|
||||||
addJsonNode("ForStatement", {}, true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(Continue const&)
|
|
||||||
{
|
|
||||||
addJsonNode("Continue", {});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(Break const&)
|
|
||||||
{
|
|
||||||
addJsonNode("Break", {});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(Return const&)
|
|
||||||
{
|
|
||||||
addJsonNode("Return", {}, true);;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(VariableDefinition const&)
|
|
||||||
{
|
|
||||||
addJsonNode("VariableDefinition", {}, true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(ExpressionStatement const&)
|
|
||||||
{
|
|
||||||
addJsonNode("ExpressionStatement", {}, true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(Expression const& _node)
|
|
||||||
{
|
|
||||||
addJsonNode(
|
|
||||||
"Expression",
|
|
||||||
{ make_pair("type", getType(_node)),
|
|
||||||
make_pair("lvalue", boost::lexical_cast<std::string>(_node.isLValue())) },
|
|
||||||
true
|
|
||||||
);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(Assignment const& _node)
|
|
||||||
{
|
|
||||||
addJsonNode("Assignment",
|
|
||||||
{ make_pair("operator", Token::toString(_node.getAssignmentOperator())),
|
|
||||||
make_pair("type", getType(_node)) },
|
|
||||||
true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(UnaryOperation const& _node)
|
|
||||||
{
|
|
||||||
addJsonNode("UnaryOperation",
|
|
||||||
{ make_pair("prefix", boost::lexical_cast<std::string>(_node.isPrefixOperation())),
|
|
||||||
make_pair("operator", Token::toString(_node.getOperator())),
|
|
||||||
make_pair("type", getType(_node)) },
|
|
||||||
true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(BinaryOperation const& _node)
|
|
||||||
{
|
|
||||||
addJsonNode("BinaryOperation",
|
|
||||||
{ make_pair("operator", Token::toString(_node.getOperator())),
|
|
||||||
make_pair("type", getType(_node))},
|
|
||||||
true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(FunctionCall const& _node)
|
|
||||||
{
|
|
||||||
addJsonNode("FunctionCall",
|
|
||||||
{ make_pair("type_conversion", boost::lexical_cast<std::string>(_node.isTypeConversion())),
|
|
||||||
make_pair("type", getType(_node)) },
|
|
||||||
true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(NewExpression const& _node)
|
|
||||||
{
|
|
||||||
addJsonNode("NewExpression", { make_pair("type", getType(_node)) }, true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(MemberAccess const& _node)
|
|
||||||
{
|
|
||||||
addJsonNode("MemberAccess",
|
|
||||||
{ make_pair("member_name", _node.getMemberName()),
|
|
||||||
make_pair("type", getType(_node)) },
|
|
||||||
true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(IndexAccess const& _node)
|
|
||||||
{
|
|
||||||
addJsonNode("IndexAccess", { make_pair("type", getType(_node)) }, true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(PrimaryExpression const&)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(Identifier const& _node)
|
|
||||||
{
|
|
||||||
addJsonNode("Identifier",
|
|
||||||
{ make_pair("value", _node.getName()), make_pair("type", getType(_node)) });
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(ElementaryTypeNameExpression const& _node)
|
|
||||||
{
|
|
||||||
addJsonNode("ElementaryTypenameExpression",
|
|
||||||
{ make_pair("value", Token::toString(_node.getTypeToken())), make_pair("type", getType(_node)) });
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(Literal const& _node)
|
|
||||||
{
|
|
||||||
char const* tokenString = Token::toString(_node.getToken());
|
|
||||||
addJsonNode("Literal",
|
|
||||||
{ make_pair("string", (tokenString) ? tokenString : "null"),
|
|
||||||
make_pair("value", _node.getValue()),
|
|
||||||
make_pair("type", getType(_node)) });
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(ImportDirective const&)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(ContractDefinition const&)
|
|
||||||
{
|
|
||||||
goUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(StructDefinition const&)
|
|
||||||
{
|
|
||||||
goUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(ParameterList const&)
|
|
||||||
{
|
|
||||||
goUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(FunctionDefinition const&)
|
|
||||||
{
|
|
||||||
goUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(VariableDeclaration const&)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(TypeName const&)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(ElementaryTypeName const&)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(UserDefinedTypeName const&)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(Mapping const&)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(Statement const&)
|
|
||||||
{
|
|
||||||
goUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(Block const&)
|
|
||||||
{
|
|
||||||
goUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(IfStatement const&)
|
|
||||||
{
|
|
||||||
goUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(BreakableStatement const&)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(WhileStatement const&)
|
|
||||||
{
|
|
||||||
goUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(ForStatement const&)
|
|
||||||
{
|
|
||||||
goUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(Continue const&)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(Break const&)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(Return const&)
|
|
||||||
{
|
|
||||||
goUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(VariableDefinition const&)
|
|
||||||
{
|
|
||||||
goUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(ExpressionStatement const&)
|
|
||||||
{
|
|
||||||
goUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(Expression const&)
|
|
||||||
{
|
|
||||||
goUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(Assignment const&)
|
|
||||||
{
|
|
||||||
goUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(UnaryOperation const&)
|
|
||||||
{
|
|
||||||
goUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(BinaryOperation const&)
|
|
||||||
{
|
|
||||||
goUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(FunctionCall const&)
|
|
||||||
{
|
|
||||||
goUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(NewExpression const&)
|
|
||||||
{
|
|
||||||
goUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(MemberAccess const&)
|
|
||||||
{
|
|
||||||
goUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(IndexAccess const&)
|
|
||||||
{
|
|
||||||
goUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(PrimaryExpression const&)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(Identifier const&)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(ElementaryTypeNameExpression const&)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(Literal const&)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
string ASTJsonConverter::getType(Expression const& _expression)
|
|
||||||
{
|
|
||||||
return (_expression.getType()) ? _expression.getType()->toString() : "Unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,135 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Lefteris <lefteris@ethdev.com>
|
|
||||||
* @date 2015
|
|
||||||
* Converts the AST into json format
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <ostream>
|
|
||||||
#include <stack>
|
|
||||||
#include <libsolidity/ASTVisitor.h>
|
|
||||||
#include <libsolidity/Exceptions.h>
|
|
||||||
#include <libsolidity/Utils.h>
|
|
||||||
#include <json/json.h>
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converter of the AST into JSON format
|
|
||||||
*/
|
|
||||||
class ASTJsonConverter: public ASTConstVisitor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/// Create a converter to JSON for the given abstract syntax tree.
|
|
||||||
ASTJsonConverter(ASTNode const& _ast);
|
|
||||||
/// Output the json representation of the AST to _stream.
|
|
||||||
void print(std::ostream& _stream);
|
|
||||||
|
|
||||||
bool visit(ImportDirective const& _node) override;
|
|
||||||
bool visit(ContractDefinition const& _node) override;
|
|
||||||
bool visit(StructDefinition const& _node) override;
|
|
||||||
bool visit(ParameterList const& _node) override;
|
|
||||||
bool visit(FunctionDefinition const& _node) override;
|
|
||||||
bool visit(VariableDeclaration const& _node) override;
|
|
||||||
bool visit(TypeName const& _node) override;
|
|
||||||
bool visit(ElementaryTypeName const& _node) override;
|
|
||||||
bool visit(UserDefinedTypeName const& _node) override;
|
|
||||||
bool visit(Mapping const& _node) override;
|
|
||||||
bool visit(Statement const& _node) override;
|
|
||||||
bool visit(Block const& _node) override;
|
|
||||||
bool visit(IfStatement const& _node) override;
|
|
||||||
bool visit(BreakableStatement const& _node) override;
|
|
||||||
bool visit(WhileStatement const& _node) override;
|
|
||||||
bool visit(ForStatement const& _node) override;
|
|
||||||
bool visit(Continue const& _node) override;
|
|
||||||
bool visit(Break const& _node) override;
|
|
||||||
bool visit(Return const& _node) override;
|
|
||||||
bool visit(VariableDefinition const& _node) override;
|
|
||||||
bool visit(ExpressionStatement const& _node) override;
|
|
||||||
bool visit(Expression const& _node) override;
|
|
||||||
bool visit(Assignment const& _node) override;
|
|
||||||
bool visit(UnaryOperation const& _node) override;
|
|
||||||
bool visit(BinaryOperation const& _node) override;
|
|
||||||
bool visit(FunctionCall const& _node) override;
|
|
||||||
bool visit(NewExpression const& _node) override;
|
|
||||||
bool visit(MemberAccess const& _node) override;
|
|
||||||
bool visit(IndexAccess const& _node) override;
|
|
||||||
bool visit(PrimaryExpression const& _node) override;
|
|
||||||
bool visit(Identifier const& _node) override;
|
|
||||||
bool visit(ElementaryTypeNameExpression const& _node) override;
|
|
||||||
bool visit(Literal const& _node) override;
|
|
||||||
|
|
||||||
void endVisit(ImportDirective const&) override;
|
|
||||||
void endVisit(ContractDefinition const&) override;
|
|
||||||
void endVisit(StructDefinition const&) override;
|
|
||||||
void endVisit(ParameterList const&) override;
|
|
||||||
void endVisit(FunctionDefinition const&) override;
|
|
||||||
void endVisit(VariableDeclaration const&) override;
|
|
||||||
void endVisit(TypeName const&) override;
|
|
||||||
void endVisit(ElementaryTypeName const&) override;
|
|
||||||
void endVisit(UserDefinedTypeName const&) override;
|
|
||||||
void endVisit(Mapping const&) override;
|
|
||||||
void endVisit(Statement const&) override;
|
|
||||||
void endVisit(Block const&) override;
|
|
||||||
void endVisit(IfStatement const&) override;
|
|
||||||
void endVisit(BreakableStatement const&) override;
|
|
||||||
void endVisit(WhileStatement const&) override;
|
|
||||||
void endVisit(ForStatement const&) override;
|
|
||||||
void endVisit(Continue const&) override;
|
|
||||||
void endVisit(Break const&) override;
|
|
||||||
void endVisit(Return const&) override;
|
|
||||||
void endVisit(VariableDefinition const&) override;
|
|
||||||
void endVisit(ExpressionStatement const&) override;
|
|
||||||
void endVisit(Expression const&) override;
|
|
||||||
void endVisit(Assignment const&) override;
|
|
||||||
void endVisit(UnaryOperation const&) override;
|
|
||||||
void endVisit(BinaryOperation const&) override;
|
|
||||||
void endVisit(FunctionCall const&) override;
|
|
||||||
void endVisit(NewExpression const&) override;
|
|
||||||
void endVisit(MemberAccess const&) override;
|
|
||||||
void endVisit(IndexAccess const&) override;
|
|
||||||
void endVisit(PrimaryExpression const&) override;
|
|
||||||
void endVisit(Identifier const&) override;
|
|
||||||
void endVisit(ElementaryTypeNameExpression const&) override;
|
|
||||||
void endVisit(Literal const&) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void addKeyValue(Json::Value& _obj, std::string const& _key, std::string const& _val);
|
|
||||||
void addJsonNode(std::string const& _nodeName,
|
|
||||||
std::initializer_list<std::pair<std::string const, std::string const>> _list,
|
|
||||||
bool _hasChildren);
|
|
||||||
std::string getType(Expression const& _expression);
|
|
||||||
inline void goUp()
|
|
||||||
{
|
|
||||||
solAssert(!m_jsonNodePtrs.empty(), "Uneven json nodes stack. Internal error.");
|
|
||||||
m_jsonNodePtrs.pop();
|
|
||||||
};
|
|
||||||
|
|
||||||
Json::Value m_astJson;
|
|
||||||
std::stack<Json::Value*> m_jsonNodePtrs;
|
|
||||||
std::string m_source;
|
|
||||||
ASTNode const* m_ast;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
571
ASTPrinter.cpp
571
ASTPrinter.cpp
@ -1,571 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Pretty-printer for the abstract syntax tree (the "pretty" is arguable), used for debugging.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <libsolidity/ASTPrinter.h>
|
|
||||||
#include <libsolidity/AST.h>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
ASTPrinter::ASTPrinter(ASTNode const& _ast, string const& _source):
|
|
||||||
m_indentation(0), m_source(_source), m_ast(&_ast)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::print(ostream& _stream)
|
|
||||||
{
|
|
||||||
m_ostream = &_stream;
|
|
||||||
m_ast->accept(*this);
|
|
||||||
m_ostream = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(ImportDirective const& _node)
|
|
||||||
{
|
|
||||||
writeLine("ImportDirective \"" + _node.getIdentifier() + "\"");
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(ContractDefinition const& _node)
|
|
||||||
{
|
|
||||||
writeLine("ContractDefinition \"" + _node.getName() + "\"");
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(InheritanceSpecifier const& _node)
|
|
||||||
{
|
|
||||||
writeLine("InheritanceSpecifier \"" + _node.getName()->getName() + "\"");
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(StructDefinition const& _node)
|
|
||||||
{
|
|
||||||
writeLine("StructDefinition \"" + _node.getName() + "\"");
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(EnumDefinition const& _node)
|
|
||||||
{
|
|
||||||
writeLine("EnumDefinition \"" + _node.getName() + "\"");
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(EnumValue const& _node)
|
|
||||||
{
|
|
||||||
writeLine("EnumValue \"" + _node.getName() + "\"");
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(ParameterList const& _node)
|
|
||||||
{
|
|
||||||
writeLine("ParameterList");
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(FunctionDefinition const& _node)
|
|
||||||
{
|
|
||||||
writeLine("FunctionDefinition \"" + _node.getName() + "\"" +
|
|
||||||
(_node.isPublic() ? " - public" : "") +
|
|
||||||
(_node.isDeclaredConst() ? " - const" : ""));
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(VariableDeclaration const& _node)
|
|
||||||
{
|
|
||||||
writeLine("VariableDeclaration \"" + _node.getName() + "\"");
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(ModifierDefinition const& _node)
|
|
||||||
{
|
|
||||||
writeLine("ModifierDefinition \"" + _node.getName() + "\"");
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(ModifierInvocation const& _node)
|
|
||||||
{
|
|
||||||
writeLine("ModifierInvocation \"" + _node.getName()->getName() + "\"");
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(EventDefinition const& _node)
|
|
||||||
{
|
|
||||||
writeLine("EventDefinition \"" + _node.getName() + "\"");
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(TypeName const& _node)
|
|
||||||
{
|
|
||||||
writeLine("TypeName");
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(ElementaryTypeName const& _node)
|
|
||||||
{
|
|
||||||
writeLine(string("ElementaryTypeName ") + Token::toString(_node.getTypeName()));
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(UserDefinedTypeName const& _node)
|
|
||||||
{
|
|
||||||
writeLine("UserDefinedTypeName \"" + _node.getName() + "\"");
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(Mapping const& _node)
|
|
||||||
{
|
|
||||||
writeLine("Mapping");
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(Statement const& _node)
|
|
||||||
{
|
|
||||||
writeLine("Statement");
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(Block const& _node)
|
|
||||||
{
|
|
||||||
writeLine("Block");
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(PlaceholderStatement const& _node)
|
|
||||||
{
|
|
||||||
writeLine("PlaceholderStatement");
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(IfStatement const& _node)
|
|
||||||
{
|
|
||||||
writeLine("IfStatement");
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(BreakableStatement const& _node)
|
|
||||||
{
|
|
||||||
writeLine("BreakableStatement");
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(WhileStatement const& _node)
|
|
||||||
{
|
|
||||||
writeLine("WhileStatement");
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(ForStatement const& _node)
|
|
||||||
{
|
|
||||||
writeLine("ForStatement");
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(Continue const& _node)
|
|
||||||
{
|
|
||||||
writeLine("Continue");
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(Break const& _node)
|
|
||||||
{
|
|
||||||
writeLine("Break");
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(Return const& _node)
|
|
||||||
{
|
|
||||||
writeLine("Return");
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(VariableDefinition const& _node)
|
|
||||||
{
|
|
||||||
writeLine("VariableDefinition");
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(ExpressionStatement const& _node)
|
|
||||||
{
|
|
||||||
writeLine("ExpressionStatement");
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(Expression const& _node)
|
|
||||||
{
|
|
||||||
writeLine("Expression");
|
|
||||||
printType(_node);
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(Assignment const& _node)
|
|
||||||
{
|
|
||||||
writeLine(string("Assignment using operator ") + Token::toString(_node.getAssignmentOperator()));
|
|
||||||
printType(_node);
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(UnaryOperation const& _node)
|
|
||||||
{
|
|
||||||
writeLine(string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") +
|
|
||||||
") " + Token::toString(_node.getOperator()));
|
|
||||||
printType(_node);
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(BinaryOperation const& _node)
|
|
||||||
{
|
|
||||||
writeLine(string("BinaryOperation using operator ") + Token::toString(_node.getOperator()));
|
|
||||||
printType(_node);
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(FunctionCall const& _node)
|
|
||||||
{
|
|
||||||
writeLine("FunctionCall");
|
|
||||||
printType(_node);
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(NewExpression const& _node)
|
|
||||||
{
|
|
||||||
writeLine("NewExpression");
|
|
||||||
printType(_node);
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(MemberAccess const& _node)
|
|
||||||
{
|
|
||||||
writeLine("MemberAccess to member " + _node.getMemberName());
|
|
||||||
printType(_node);
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(IndexAccess const& _node)
|
|
||||||
{
|
|
||||||
writeLine("IndexAccess");
|
|
||||||
printType(_node);
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(PrimaryExpression const& _node)
|
|
||||||
{
|
|
||||||
writeLine("PrimaryExpression");
|
|
||||||
printType(_node);
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(Identifier const& _node)
|
|
||||||
{
|
|
||||||
writeLine(string("Identifier ") + _node.getName());
|
|
||||||
printType(_node);
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(ElementaryTypeNameExpression const& _node)
|
|
||||||
{
|
|
||||||
writeLine(string("ElementaryTypeNameExpression ") + Token::toString(_node.getTypeToken()));
|
|
||||||
printType(_node);
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASTPrinter::visit(Literal const& _node)
|
|
||||||
{
|
|
||||||
char const* tokenString = Token::toString(_node.getToken());
|
|
||||||
if (!tokenString)
|
|
||||||
tokenString = "[no token]";
|
|
||||||
writeLine(string("Literal, token: ") + tokenString + " value: " + _node.getValue());
|
|
||||||
printType(_node);
|
|
||||||
printSourcePart(_node);
|
|
||||||
return goDeeper();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(ImportDirective const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(ContractDefinition const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(InheritanceSpecifier const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(StructDefinition const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(EnumDefinition const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(EnumValue const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(ParameterList const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(FunctionDefinition const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(VariableDeclaration const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(ModifierDefinition const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(ModifierInvocation const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(EventDefinition const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(TypeName const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(ElementaryTypeName const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(UserDefinedTypeName const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(Mapping const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(Statement const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(Block const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(PlaceholderStatement const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(IfStatement const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(BreakableStatement const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(WhileStatement const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(ForStatement const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(Continue const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(Break const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(Return const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(VariableDefinition const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(ExpressionStatement const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(Expression const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(Assignment const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(UnaryOperation const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(BinaryOperation const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(FunctionCall const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(NewExpression const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(MemberAccess const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(IndexAccess const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(PrimaryExpression const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(Identifier const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(ElementaryTypeNameExpression const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::endVisit(Literal const&)
|
|
||||||
{
|
|
||||||
m_indentation--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::printSourcePart(ASTNode const& _node)
|
|
||||||
{
|
|
||||||
if (!m_source.empty())
|
|
||||||
{
|
|
||||||
Location const& location(_node.getLocation());
|
|
||||||
*m_ostream << getIndentation() << " Source: "
|
|
||||||
<< escaped(m_source.substr(location.start, location.end - location.start), false) << endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::printType(Expression const& _expression)
|
|
||||||
{
|
|
||||||
if (_expression.getType())
|
|
||||||
*m_ostream << getIndentation() << " Type: " << _expression.getType()->toString() << "\n";
|
|
||||||
else
|
|
||||||
*m_ostream << getIndentation() << " Type unknown.\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
string ASTPrinter::getIndentation() const
|
|
||||||
{
|
|
||||||
return string(m_indentation * 2, ' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASTPrinter::writeLine(string const& _line)
|
|
||||||
{
|
|
||||||
*m_ostream << getIndentation() << _line << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
141
ASTPrinter.h
141
ASTPrinter.h
@ -1,141 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Pretty-printer for the abstract syntax tree (the "pretty" is arguable), used for debugging.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <ostream>
|
|
||||||
#include <libsolidity/ASTVisitor.h>
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pretty-printer for the abstract syntax tree (the "pretty" is arguable) for debugging purposes.
|
|
||||||
*/
|
|
||||||
class ASTPrinter: public ASTConstVisitor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/// Create a printer for the given abstract syntax tree. If the source is specified,
|
|
||||||
/// the corresponding parts of the source are printed with each node.
|
|
||||||
ASTPrinter(ASTNode const& _ast, std::string const& _source = std::string());
|
|
||||||
/// Output the string representation of the AST to _stream.
|
|
||||||
void print(std::ostream& _stream);
|
|
||||||
|
|
||||||
bool visit(ImportDirective const& _node) override;
|
|
||||||
bool visit(ContractDefinition const& _node) override;
|
|
||||||
bool visit(InheritanceSpecifier const& _node) override;
|
|
||||||
bool visit(StructDefinition const& _node) override;
|
|
||||||
bool visit(EnumDefinition const& _node) override;
|
|
||||||
bool visit(EnumValue const& _node) override;
|
|
||||||
bool visit(ParameterList const& _node) override;
|
|
||||||
bool visit(FunctionDefinition const& _node) override;
|
|
||||||
bool visit(VariableDeclaration const& _node) override;
|
|
||||||
bool visit(ModifierDefinition const& _node) override;
|
|
||||||
bool visit(ModifierInvocation const& _node) override;
|
|
||||||
bool visit(EventDefinition const& _node) override;
|
|
||||||
bool visit(TypeName const& _node) override;
|
|
||||||
bool visit(ElementaryTypeName const& _node) override;
|
|
||||||
bool visit(UserDefinedTypeName const& _node) override;
|
|
||||||
bool visit(Mapping const& _node) override;
|
|
||||||
bool visit(Statement const& _node) override;
|
|
||||||
bool visit(Block const& _node) override;
|
|
||||||
bool visit(PlaceholderStatement const& _node) override;
|
|
||||||
bool visit(IfStatement const& _node) override;
|
|
||||||
bool visit(BreakableStatement const& _node) override;
|
|
||||||
bool visit(WhileStatement const& _node) override;
|
|
||||||
bool visit(ForStatement const& _node) override;
|
|
||||||
bool visit(Continue const& _node) override;
|
|
||||||
bool visit(Break const& _node) override;
|
|
||||||
bool visit(Return const& _node) override;
|
|
||||||
bool visit(VariableDefinition const& _node) override;
|
|
||||||
bool visit(ExpressionStatement const& _node) override;
|
|
||||||
bool visit(Expression const& _node) override;
|
|
||||||
bool visit(Assignment const& _node) override;
|
|
||||||
bool visit(UnaryOperation const& _node) override;
|
|
||||||
bool visit(BinaryOperation const& _node) override;
|
|
||||||
bool visit(FunctionCall const& _node) override;
|
|
||||||
bool visit(NewExpression const& _node) override;
|
|
||||||
bool visit(MemberAccess const& _node) override;
|
|
||||||
bool visit(IndexAccess const& _node) override;
|
|
||||||
bool visit(PrimaryExpression const& _node) override;
|
|
||||||
bool visit(Identifier const& _node) override;
|
|
||||||
bool visit(ElementaryTypeNameExpression const& _node) override;
|
|
||||||
bool visit(Literal const& _node) override;
|
|
||||||
|
|
||||||
void endVisit(ImportDirective const&) override;
|
|
||||||
void endVisit(ContractDefinition const&) override;
|
|
||||||
void endVisit(InheritanceSpecifier const&) override;
|
|
||||||
void endVisit(StructDefinition const&) override;
|
|
||||||
void endVisit(EnumDefinition const&) override;
|
|
||||||
void endVisit(EnumValue const&) override;
|
|
||||||
void endVisit(ParameterList const&) override;
|
|
||||||
void endVisit(FunctionDefinition const&) override;
|
|
||||||
void endVisit(VariableDeclaration const&) override;
|
|
||||||
void endVisit(ModifierDefinition const&) override;
|
|
||||||
void endVisit(ModifierInvocation const&) override;
|
|
||||||
void endVisit(EventDefinition const&) override;
|
|
||||||
void endVisit(TypeName const&) override;
|
|
||||||
void endVisit(ElementaryTypeName const&) override;
|
|
||||||
void endVisit(UserDefinedTypeName const&) override;
|
|
||||||
void endVisit(Mapping const&) override;
|
|
||||||
void endVisit(Statement const&) override;
|
|
||||||
void endVisit(Block const&) override;
|
|
||||||
void endVisit(PlaceholderStatement const&) override;
|
|
||||||
void endVisit(IfStatement const&) override;
|
|
||||||
void endVisit(BreakableStatement const&) override;
|
|
||||||
void endVisit(WhileStatement const&) override;
|
|
||||||
void endVisit(ForStatement const&) override;
|
|
||||||
void endVisit(Continue const&) override;
|
|
||||||
void endVisit(Break const&) override;
|
|
||||||
void endVisit(Return const&) override;
|
|
||||||
void endVisit(VariableDefinition const&) override;
|
|
||||||
void endVisit(ExpressionStatement const&) override;
|
|
||||||
void endVisit(Expression const&) override;
|
|
||||||
void endVisit(Assignment const&) override;
|
|
||||||
void endVisit(UnaryOperation const&) override;
|
|
||||||
void endVisit(BinaryOperation const&) override;
|
|
||||||
void endVisit(FunctionCall const&) override;
|
|
||||||
void endVisit(NewExpression const&) override;
|
|
||||||
void endVisit(MemberAccess const&) override;
|
|
||||||
void endVisit(IndexAccess const&) override;
|
|
||||||
void endVisit(PrimaryExpression const&) override;
|
|
||||||
void endVisit(Identifier const&) override;
|
|
||||||
void endVisit(ElementaryTypeNameExpression const&) override;
|
|
||||||
void endVisit(Literal const&) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void printSourcePart(ASTNode const& _node);
|
|
||||||
void printType(Expression const& _expression);
|
|
||||||
std::string getIndentation() const;
|
|
||||||
void writeLine(std::string const& _line);
|
|
||||||
bool goDeeper() { m_indentation++; return true; }
|
|
||||||
|
|
||||||
int m_indentation;
|
|
||||||
std::string m_source;
|
|
||||||
ASTNode const* m_ast;
|
|
||||||
std::ostream* m_ostream;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
222
ASTVisitor.h
222
ASTVisitor.h
@ -1,222 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* AST visitor base class.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <libsolidity/ASTForward.h>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Visitor interface for the abstract syntax tree. This class is tightly bound to the
|
|
||||||
* implementation of @ref ASTNode::accept and its overrides. After a call to
|
|
||||||
* @ref ASTNode::accept, the function visit for the appropriate parameter is called and then
|
|
||||||
* (if it returns true) this continues recursively for all child nodes in document order
|
|
||||||
* (there is an exception for contracts). After all child nodes have been visited, endVisit is
|
|
||||||
* called for the node.
|
|
||||||
*/
|
|
||||||
class ASTVisitor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual bool visit(ASTNode&) { return true; }
|
|
||||||
virtual bool visit(SourceUnit&) { return true; }
|
|
||||||
virtual bool visit(ImportDirective&) { return true; }
|
|
||||||
virtual bool visit(ContractDefinition&) { return true; }
|
|
||||||
virtual bool visit(InheritanceSpecifier&) { return true; }
|
|
||||||
virtual bool visit(StructDefinition&) { return true; }
|
|
||||||
virtual bool visit(EnumDefinition&) { return true; }
|
|
||||||
virtual bool visit(EnumValue&) { return true; }
|
|
||||||
virtual bool visit(ParameterList&) { return true; }
|
|
||||||
virtual bool visit(FunctionDefinition&) { return true; }
|
|
||||||
virtual bool visit(VariableDeclaration&) { return true; }
|
|
||||||
virtual bool visit(ModifierDefinition&) { return true; }
|
|
||||||
virtual bool visit(ModifierInvocation&) { return true; }
|
|
||||||
virtual bool visit(EventDefinition&) { return true; }
|
|
||||||
virtual bool visit(TypeName&) { return true; }
|
|
||||||
virtual bool visit(ElementaryTypeName&) { return true; }
|
|
||||||
virtual bool visit(UserDefinedTypeName&) { return true; }
|
|
||||||
virtual bool visit(Mapping&) { return true; }
|
|
||||||
virtual bool visit(Statement&) { return true; }
|
|
||||||
virtual bool visit(Block&) { return true; }
|
|
||||||
virtual bool visit(PlaceholderStatement&) { return true; }
|
|
||||||
virtual bool visit(IfStatement&) { return true; }
|
|
||||||
virtual bool visit(BreakableStatement&) { return true; }
|
|
||||||
virtual bool visit(WhileStatement&) { return true; }
|
|
||||||
virtual bool visit(ForStatement&) { return true; }
|
|
||||||
virtual bool visit(Continue&) { return true; }
|
|
||||||
virtual bool visit(Break&) { return true; }
|
|
||||||
virtual bool visit(Return&) { return true; }
|
|
||||||
virtual bool visit(VariableDefinition&) { return true; }
|
|
||||||
virtual bool visit(ExpressionStatement&) { return true; }
|
|
||||||
virtual bool visit(Expression&) { return true; }
|
|
||||||
virtual bool visit(Assignment&) { return true; }
|
|
||||||
virtual bool visit(UnaryOperation&) { return true; }
|
|
||||||
virtual bool visit(BinaryOperation&) { return true; }
|
|
||||||
virtual bool visit(FunctionCall&) { return true; }
|
|
||||||
virtual bool visit(NewExpression&) { return true; }
|
|
||||||
virtual bool visit(MemberAccess&) { return true; }
|
|
||||||
virtual bool visit(IndexAccess&) { return true; }
|
|
||||||
virtual bool visit(PrimaryExpression&) { return true; }
|
|
||||||
virtual bool visit(Identifier&) { return true; }
|
|
||||||
virtual bool visit(ElementaryTypeNameExpression&) { return true; }
|
|
||||||
virtual bool visit(Literal&) { return true; }
|
|
||||||
|
|
||||||
virtual void endVisit(ASTNode&) { }
|
|
||||||
virtual void endVisit(SourceUnit&) { }
|
|
||||||
virtual void endVisit(ImportDirective&) { }
|
|
||||||
virtual void endVisit(ContractDefinition&) { }
|
|
||||||
virtual void endVisit(InheritanceSpecifier&) { }
|
|
||||||
virtual void endVisit(StructDefinition&) { }
|
|
||||||
virtual void endVisit(EnumDefinition&) { }
|
|
||||||
virtual void endVisit(EnumValue&) { }
|
|
||||||
virtual void endVisit(ParameterList&) { }
|
|
||||||
virtual void endVisit(FunctionDefinition&) { }
|
|
||||||
virtual void endVisit(VariableDeclaration&) { }
|
|
||||||
virtual void endVisit(ModifierDefinition&) { }
|
|
||||||
virtual void endVisit(ModifierInvocation&) { }
|
|
||||||
virtual void endVisit(EventDefinition&) { }
|
|
||||||
virtual void endVisit(TypeName&) { }
|
|
||||||
virtual void endVisit(ElementaryTypeName&) { }
|
|
||||||
virtual void endVisit(UserDefinedTypeName&) { }
|
|
||||||
virtual void endVisit(Mapping&) { }
|
|
||||||
virtual void endVisit(Statement&) { }
|
|
||||||
virtual void endVisit(Block&) { }
|
|
||||||
virtual void endVisit(PlaceholderStatement&) { }
|
|
||||||
virtual void endVisit(IfStatement&) { }
|
|
||||||
virtual void endVisit(BreakableStatement&) { }
|
|
||||||
virtual void endVisit(WhileStatement&) { }
|
|
||||||
virtual void endVisit(ForStatement&) { }
|
|
||||||
virtual void endVisit(Continue&) { }
|
|
||||||
virtual void endVisit(Break&) { }
|
|
||||||
virtual void endVisit(Return&) { }
|
|
||||||
virtual void endVisit(VariableDefinition&) { }
|
|
||||||
virtual void endVisit(ExpressionStatement&) { }
|
|
||||||
virtual void endVisit(Expression&) { }
|
|
||||||
virtual void endVisit(Assignment&) { }
|
|
||||||
virtual void endVisit(UnaryOperation&) { }
|
|
||||||
virtual void endVisit(BinaryOperation&) { }
|
|
||||||
virtual void endVisit(FunctionCall&) { }
|
|
||||||
virtual void endVisit(NewExpression&) { }
|
|
||||||
virtual void endVisit(MemberAccess&) { }
|
|
||||||
virtual void endVisit(IndexAccess&) { }
|
|
||||||
virtual void endVisit(PrimaryExpression&) { }
|
|
||||||
virtual void endVisit(Identifier&) { }
|
|
||||||
virtual void endVisit(ElementaryTypeNameExpression&) { }
|
|
||||||
virtual void endVisit(Literal&) { }
|
|
||||||
};
|
|
||||||
|
|
||||||
class ASTConstVisitor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual bool visit(ASTNode const&) { return true; }
|
|
||||||
virtual bool visit(SourceUnit const&) { return true; }
|
|
||||||
virtual bool visit(ImportDirective const&) { return true; }
|
|
||||||
virtual bool visit(ContractDefinition const&) { return true; }
|
|
||||||
virtual bool visit(InheritanceSpecifier const&) { return true; }
|
|
||||||
virtual bool visit(StructDefinition const&) { return true; }
|
|
||||||
virtual bool visit(EnumDefinition const&) { return true; }
|
|
||||||
virtual bool visit(EnumValue const&) { return true; }
|
|
||||||
virtual bool visit(ParameterList const&) { return true; }
|
|
||||||
virtual bool visit(FunctionDefinition const&) { return true; }
|
|
||||||
virtual bool visit(VariableDeclaration const&) { return true; }
|
|
||||||
virtual bool visit(ModifierDefinition const&) { return true; }
|
|
||||||
virtual bool visit(ModifierInvocation const&) { return true; }
|
|
||||||
virtual bool visit(EventDefinition const&) { return true; }
|
|
||||||
virtual bool visit(TypeName const&) { return true; }
|
|
||||||
virtual bool visit(ElementaryTypeName const&) { return true; }
|
|
||||||
virtual bool visit(UserDefinedTypeName const&) { return true; }
|
|
||||||
virtual bool visit(Mapping const&) { return true; }
|
|
||||||
virtual bool visit(Statement const&) { return true; }
|
|
||||||
virtual bool visit(Block const&) { return true; }
|
|
||||||
virtual bool visit(PlaceholderStatement const&) { return true; }
|
|
||||||
virtual bool visit(IfStatement const&) { return true; }
|
|
||||||
virtual bool visit(BreakableStatement const&) { return true; }
|
|
||||||
virtual bool visit(WhileStatement const&) { return true; }
|
|
||||||
virtual bool visit(ForStatement const&) { return true; }
|
|
||||||
virtual bool visit(Continue const&) { return true; }
|
|
||||||
virtual bool visit(Break const&) { return true; }
|
|
||||||
virtual bool visit(Return const&) { return true; }
|
|
||||||
virtual bool visit(VariableDefinition const&) { return true; }
|
|
||||||
virtual bool visit(ExpressionStatement const&) { return true; }
|
|
||||||
virtual bool visit(Expression const&) { return true; }
|
|
||||||
virtual bool visit(Assignment const&) { return true; }
|
|
||||||
virtual bool visit(UnaryOperation const&) { return true; }
|
|
||||||
virtual bool visit(BinaryOperation const&) { return true; }
|
|
||||||
virtual bool visit(FunctionCall const&) { return true; }
|
|
||||||
virtual bool visit(NewExpression const&) { return true; }
|
|
||||||
virtual bool visit(MemberAccess const&) { return true; }
|
|
||||||
virtual bool visit(IndexAccess const&) { return true; }
|
|
||||||
virtual bool visit(PrimaryExpression const&) { return true; }
|
|
||||||
virtual bool visit(Identifier const&) { return true; }
|
|
||||||
virtual bool visit(ElementaryTypeNameExpression const&) { return true; }
|
|
||||||
virtual bool visit(Literal const&) { return true; }
|
|
||||||
|
|
||||||
virtual void endVisit(ASTNode const&) { }
|
|
||||||
virtual void endVisit(SourceUnit const&) { }
|
|
||||||
virtual void endVisit(ImportDirective const&) { }
|
|
||||||
virtual void endVisit(ContractDefinition const&) { }
|
|
||||||
virtual void endVisit(InheritanceSpecifier const&) { }
|
|
||||||
virtual void endVisit(StructDefinition const&) { }
|
|
||||||
virtual void endVisit(EnumDefinition const&) { }
|
|
||||||
virtual void endVisit(EnumValue const&) { }
|
|
||||||
virtual void endVisit(ParameterList const&) { }
|
|
||||||
virtual void endVisit(FunctionDefinition const&) { }
|
|
||||||
virtual void endVisit(VariableDeclaration const&) { }
|
|
||||||
virtual void endVisit(ModifierDefinition const&) { }
|
|
||||||
virtual void endVisit(ModifierInvocation const&) { }
|
|
||||||
virtual void endVisit(EventDefinition const&) { }
|
|
||||||
virtual void endVisit(TypeName const&) { }
|
|
||||||
virtual void endVisit(ElementaryTypeName const&) { }
|
|
||||||
virtual void endVisit(UserDefinedTypeName const&) { }
|
|
||||||
virtual void endVisit(Mapping const&) { }
|
|
||||||
virtual void endVisit(Statement const&) { }
|
|
||||||
virtual void endVisit(Block const&) { }
|
|
||||||
virtual void endVisit(PlaceholderStatement const&) { }
|
|
||||||
virtual void endVisit(IfStatement const&) { }
|
|
||||||
virtual void endVisit(BreakableStatement const&) { }
|
|
||||||
virtual void endVisit(WhileStatement const&) { }
|
|
||||||
virtual void endVisit(ForStatement const&) { }
|
|
||||||
virtual void endVisit(Continue const&) { }
|
|
||||||
virtual void endVisit(Break const&) { }
|
|
||||||
virtual void endVisit(Return const&) { }
|
|
||||||
virtual void endVisit(VariableDefinition const&) { }
|
|
||||||
virtual void endVisit(ExpressionStatement const&) { }
|
|
||||||
virtual void endVisit(Expression const&) { }
|
|
||||||
virtual void endVisit(Assignment const&) { }
|
|
||||||
virtual void endVisit(UnaryOperation const&) { }
|
|
||||||
virtual void endVisit(BinaryOperation const&) { }
|
|
||||||
virtual void endVisit(FunctionCall const&) { }
|
|
||||||
virtual void endVisit(NewExpression const&) { }
|
|
||||||
virtual void endVisit(MemberAccess const&) { }
|
|
||||||
virtual void endVisit(IndexAccess const&) { }
|
|
||||||
virtual void endVisit(PrimaryExpression const&) { }
|
|
||||||
virtual void endVisit(Identifier const&) { }
|
|
||||||
virtual void endVisit(ElementaryTypeNameExpression const&) { }
|
|
||||||
virtual void endVisit(Literal const&) { }
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
659
AST_accept.h
659
AST_accept.h
@ -1,659 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Implementation of the accept functions of AST nodes, included by AST.cpp to not clutter that
|
|
||||||
* file with these mechanical implementations.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <libsolidity/AST.h>
|
|
||||||
#include <libsolidity/ASTVisitor.h>
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
void SourceUnit::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
listAccept(m_nodes, _visitor);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SourceUnit::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
listAccept(m_nodes, _visitor);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImportDirective::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
_visitor.visit(*this);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImportDirective::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
_visitor.visit(*this);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ContractDefinition::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
{
|
|
||||||
listAccept(m_baseContracts, _visitor);
|
|
||||||
listAccept(m_definedStructs, _visitor);
|
|
||||||
listAccept(m_definedEnums, _visitor);
|
|
||||||
listAccept(m_stateVariables, _visitor);
|
|
||||||
listAccept(m_events, _visitor);
|
|
||||||
listAccept(m_functionModifiers, _visitor);
|
|
||||||
listAccept(m_definedFunctions, _visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ContractDefinition::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
{
|
|
||||||
listAccept(m_baseContracts, _visitor);
|
|
||||||
listAccept(m_definedStructs, _visitor);
|
|
||||||
listAccept(m_definedEnums, _visitor);
|
|
||||||
listAccept(m_stateVariables, _visitor);
|
|
||||||
listAccept(m_events, _visitor);
|
|
||||||
listAccept(m_functionModifiers, _visitor);
|
|
||||||
listAccept(m_definedFunctions, _visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void InheritanceSpecifier::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
{
|
|
||||||
m_baseName->accept(_visitor);
|
|
||||||
listAccept(m_arguments, _visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void InheritanceSpecifier::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
{
|
|
||||||
m_baseName->accept(_visitor);
|
|
||||||
listAccept(m_arguments, _visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EnumDefinition::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
listAccept(m_members, _visitor);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EnumDefinition::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
listAccept(m_members, _visitor);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EnumValue::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
_visitor.visit(*this);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EnumValue::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
_visitor.visit(*this);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void StructDefinition::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
listAccept(m_members, _visitor);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void StructDefinition::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
listAccept(m_members, _visitor);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void StructDefinition::checkValidityOfMembers() const
|
|
||||||
{
|
|
||||||
checkMemberTypes();
|
|
||||||
checkRecursion();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ParameterList::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
listAccept(m_parameters, _visitor);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ParameterList::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
listAccept(m_parameters, _visitor);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FunctionDefinition::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
{
|
|
||||||
m_parameters->accept(_visitor);
|
|
||||||
if (m_returnParameters)
|
|
||||||
m_returnParameters->accept(_visitor);
|
|
||||||
listAccept(m_functionModifiers, _visitor);
|
|
||||||
m_body->accept(_visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FunctionDefinition::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
{
|
|
||||||
m_parameters->accept(_visitor);
|
|
||||||
if (m_returnParameters)
|
|
||||||
m_returnParameters->accept(_visitor);
|
|
||||||
listAccept(m_functionModifiers, _visitor);
|
|
||||||
m_body->accept(_visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VariableDeclaration::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
if (m_typeName)
|
|
||||||
m_typeName->accept(_visitor);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VariableDeclaration::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
if (m_typeName)
|
|
||||||
m_typeName->accept(_visitor);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModifierDefinition::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
{
|
|
||||||
m_parameters->accept(_visitor);
|
|
||||||
m_body->accept(_visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModifierDefinition::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
{
|
|
||||||
m_parameters->accept(_visitor);
|
|
||||||
m_body->accept(_visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModifierInvocation::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
{
|
|
||||||
m_modifierName->accept(_visitor);
|
|
||||||
listAccept(m_arguments, _visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModifierInvocation::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
{
|
|
||||||
m_modifierName->accept(_visitor);
|
|
||||||
listAccept(m_arguments, _visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EventDefinition::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
m_parameters->accept(_visitor);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EventDefinition::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
m_parameters->accept(_visitor);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TypeName::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
_visitor.visit(*this);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TypeName::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
_visitor.visit(*this);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ElementaryTypeName::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
_visitor.visit(*this);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ElementaryTypeName::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
_visitor.visit(*this);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UserDefinedTypeName::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
_visitor.visit(*this);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UserDefinedTypeName::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
_visitor.visit(*this);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mapping::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
{
|
|
||||||
m_keyType->accept(_visitor);
|
|
||||||
m_valueType->accept(_visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mapping::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
{
|
|
||||||
m_keyType->accept(_visitor);
|
|
||||||
m_valueType->accept(_visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Block::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
listAccept(m_statements, _visitor);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Block::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
listAccept(m_statements, _visitor);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlaceholderStatement::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
_visitor.visit(*this);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlaceholderStatement::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
_visitor.visit(*this);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IfStatement::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
{
|
|
||||||
m_condition->accept(_visitor);
|
|
||||||
m_trueBody->accept(_visitor);
|
|
||||||
if (m_falseBody)
|
|
||||||
m_falseBody->accept(_visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IfStatement::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
{
|
|
||||||
m_condition->accept(_visitor);
|
|
||||||
m_trueBody->accept(_visitor);
|
|
||||||
if (m_falseBody)
|
|
||||||
m_falseBody->accept(_visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WhileStatement::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
{
|
|
||||||
m_condition->accept(_visitor);
|
|
||||||
m_body->accept(_visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WhileStatement::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
{
|
|
||||||
m_condition->accept(_visitor);
|
|
||||||
m_body->accept(_visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ForStatement::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
{
|
|
||||||
if (m_initExpression)
|
|
||||||
m_initExpression->accept(_visitor);
|
|
||||||
if (m_condExpression)
|
|
||||||
m_condExpression->accept(_visitor);
|
|
||||||
if (m_loopExpression)
|
|
||||||
m_loopExpression->accept(_visitor);
|
|
||||||
m_body->accept(_visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ForStatement::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
{
|
|
||||||
if (m_initExpression)
|
|
||||||
m_initExpression->accept(_visitor);
|
|
||||||
if (m_condExpression)
|
|
||||||
m_condExpression->accept(_visitor);
|
|
||||||
if (m_loopExpression)
|
|
||||||
m_loopExpression->accept(_visitor);
|
|
||||||
m_body->accept(_visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Continue::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
_visitor.visit(*this);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Continue::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
_visitor.visit(*this);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Break::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
_visitor.visit(*this);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Break::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
_visitor.visit(*this);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Return::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
if (m_expression)
|
|
||||||
m_expression->accept(_visitor);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Return::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
if (m_expression)
|
|
||||||
m_expression->accept(_visitor);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ExpressionStatement::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
if (m_expression)
|
|
||||||
m_expression->accept(_visitor);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ExpressionStatement::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
if (m_expression)
|
|
||||||
m_expression->accept(_visitor);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VariableDefinition::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
{
|
|
||||||
m_variable->accept(_visitor);
|
|
||||||
if (m_value)
|
|
||||||
m_value->accept(_visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VariableDefinition::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
{
|
|
||||||
m_variable->accept(_visitor);
|
|
||||||
if (m_value)
|
|
||||||
m_value->accept(_visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Assignment::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
{
|
|
||||||
m_leftHandSide->accept(_visitor);
|
|
||||||
m_rightHandSide->accept(_visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Assignment::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
{
|
|
||||||
m_leftHandSide->accept(_visitor);
|
|
||||||
m_rightHandSide->accept(_visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnaryOperation::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
m_subExpression->accept(_visitor);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnaryOperation::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
m_subExpression->accept(_visitor);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BinaryOperation::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
{
|
|
||||||
m_left->accept(_visitor);
|
|
||||||
m_right->accept(_visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BinaryOperation::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
{
|
|
||||||
m_left->accept(_visitor);
|
|
||||||
m_right->accept(_visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FunctionCall::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
{
|
|
||||||
m_expression->accept(_visitor);
|
|
||||||
listAccept(m_arguments, _visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FunctionCall::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
{
|
|
||||||
m_expression->accept(_visitor);
|
|
||||||
listAccept(m_arguments, _visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NewExpression::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
m_contractName->accept(_visitor);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NewExpression::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
m_contractName->accept(_visitor);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MemberAccess::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
m_expression->accept(_visitor);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MemberAccess::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
m_expression->accept(_visitor);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IndexAccess::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
{
|
|
||||||
m_base->accept(_visitor);
|
|
||||||
m_index->accept(_visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IndexAccess::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
if (_visitor.visit(*this))
|
|
||||||
{
|
|
||||||
m_base->accept(_visitor);
|
|
||||||
m_index->accept(_visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Identifier::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
_visitor.visit(*this);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Identifier::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
_visitor.visit(*this);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ElementaryTypeNameExpression::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
_visitor.visit(*this);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ElementaryTypeNameExpression::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
_visitor.visit(*this);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Literal::accept(ASTVisitor& _visitor)
|
|
||||||
{
|
|
||||||
_visitor.visit(*this);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Literal::accept(ASTConstVisitor& _visitor) const
|
|
||||||
{
|
|
||||||
_visitor.visit(*this);
|
|
||||||
_visitor.endVisit(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
60
BaseTypes.h
60
BaseTypes.h
@ -1,60 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Some elementary types for the parser.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <ostream>
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Representation of an interval of source positions.
|
|
||||||
* The interval includes start and excludes end.
|
|
||||||
*/
|
|
||||||
struct Location
|
|
||||||
{
|
|
||||||
Location(int _start, int _end, std::shared_ptr<std::string const> _sourceName):
|
|
||||||
start(_start), end(_end), sourceName(_sourceName) { }
|
|
||||||
Location(): start(-1), end(-1) { }
|
|
||||||
|
|
||||||
bool isEmpty() const { return start == -1 && end == -1; }
|
|
||||||
|
|
||||||
int start;
|
|
||||||
int end;
|
|
||||||
std::shared_ptr<std::string const> sourceName;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Stream output for Location (used e.g. in boost exceptions).
|
|
||||||
inline std::ostream& operator<<(std::ostream& _out, Location const& _location)
|
|
||||||
{
|
|
||||||
if (_location.isEmpty())
|
|
||||||
return _out << "NO_LOCATION_SPECIFIED";
|
|
||||||
return _out << *_location.sourceName << "[" << _location.start << "," << _location.end << ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
cmake_policy(SET CMP0015 NEW)
|
|
||||||
# this policy was introduced in cmake 3.0
|
|
||||||
# remove if, once 3.0 will be used on unix
|
|
||||||
if (${CMAKE_MAJOR_VERSION} GREATER 2)
|
|
||||||
# old policy do not use MACOSX_RPATH
|
|
||||||
cmake_policy(SET CMP0042 OLD)
|
|
||||||
endif()
|
|
||||||
set(CMAKE_AUTOMOC OFF)
|
|
||||||
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB")
|
|
||||||
|
|
||||||
aux_source_directory(. SRC_LIST)
|
|
||||||
|
|
||||||
include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
|
|
||||||
include_directories(BEFORE ..)
|
|
||||||
include_directories(${Boost_INCLUDE_DIRS})
|
|
||||||
|
|
||||||
set(EXECUTABLE solidity)
|
|
||||||
|
|
||||||
file(GLOB HEADERS "*.h")
|
|
||||||
|
|
||||||
if (ETH_STATIC)
|
|
||||||
add_library(${EXECUTABLE} STATIC ${SRC_LIST} ${HEADERS})
|
|
||||||
else()
|
|
||||||
add_library(${EXECUTABLE} SHARED ${SRC_LIST} ${HEADERS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_link_libraries(${EXECUTABLE} ${JSONCPP_LIBRARIES})
|
|
||||||
target_link_libraries(${EXECUTABLE} evmcore)
|
|
||||||
target_link_libraries(${EXECUTABLE} devcore)
|
|
||||||
target_link_libraries(${EXECUTABLE} devcrypto)
|
|
||||||
|
|
||||||
install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
|
|
||||||
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )
|
|
||||||
|
|
498
Compiler.cpp
498
Compiler.cpp
@ -1,498 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Solidity compiler.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
|
||||||
#include <libevmcore/Instruction.h>
|
|
||||||
#include <libevmcore/Assembly.h>
|
|
||||||
#include <libsolidity/AST.h>
|
|
||||||
#include <libsolidity/Compiler.h>
|
|
||||||
#include <libsolidity/ExpressionCompiler.h>
|
|
||||||
#include <libsolidity/CompilerUtils.h>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace dev {
|
|
||||||
namespace solidity {
|
|
||||||
|
|
||||||
void Compiler::compileContract(ContractDefinition const& _contract,
|
|
||||||
map<ContractDefinition const*, bytes const*> const& _contracts)
|
|
||||||
{
|
|
||||||
m_context = CompilerContext(); // clear it just in case
|
|
||||||
initializeContext(_contract, _contracts);
|
|
||||||
appendFunctionSelector(_contract);
|
|
||||||
set<Declaration const*> functions = m_context.getFunctionsWithoutCode();
|
|
||||||
while (!functions.empty())
|
|
||||||
{
|
|
||||||
for (Declaration const* function: functions)
|
|
||||||
function->accept(*this);
|
|
||||||
functions = m_context.getFunctionsWithoutCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Swap the runtime context with the creation-time context
|
|
||||||
swap(m_context, m_runtimeContext);
|
|
||||||
initializeContext(_contract, _contracts);
|
|
||||||
packIntoContractCreator(_contract, m_runtimeContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Compiler::initializeContext(ContractDefinition const& _contract,
|
|
||||||
map<ContractDefinition const*, bytes const*> const& _contracts)
|
|
||||||
{
|
|
||||||
m_context.setCompiledContracts(_contracts);
|
|
||||||
m_context.setInheritanceHierarchy(_contract.getLinearizedBaseContracts());
|
|
||||||
registerStateVariables(_contract);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Compiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext)
|
|
||||||
{
|
|
||||||
// arguments for base constructors, filled in derived-to-base order
|
|
||||||
map<ContractDefinition const*, vector<ASTPointer<Expression>> const*> baseArguments;
|
|
||||||
|
|
||||||
// Determine the arguments that are used for the base constructors.
|
|
||||||
std::vector<ContractDefinition const*> const& bases = _contract.getLinearizedBaseContracts();
|
|
||||||
for (ContractDefinition const* contract: bases)
|
|
||||||
for (ASTPointer<InheritanceSpecifier> const& base: contract->getBaseContracts())
|
|
||||||
{
|
|
||||||
ContractDefinition const* baseContract = dynamic_cast<ContractDefinition const*>(
|
|
||||||
base->getName()->getReferencedDeclaration());
|
|
||||||
solAssert(baseContract, "");
|
|
||||||
if (baseArguments.count(baseContract) == 0)
|
|
||||||
baseArguments[baseContract] = &base->getArguments();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call constructors in base-to-derived order.
|
|
||||||
// The Constructor for the most derived contract is called later.
|
|
||||||
for (unsigned i = 1; i < bases.size(); i++)
|
|
||||||
{
|
|
||||||
ContractDefinition const* base = bases[bases.size() - i];
|
|
||||||
solAssert(base, "");
|
|
||||||
FunctionDefinition const* baseConstructor = base->getConstructor();
|
|
||||||
if (!baseConstructor)
|
|
||||||
continue;
|
|
||||||
solAssert(baseArguments[base], "");
|
|
||||||
appendBaseConstructorCall(*baseConstructor, *baseArguments[base]);
|
|
||||||
}
|
|
||||||
if (_contract.getConstructor())
|
|
||||||
appendConstructorCall(*_contract.getConstructor());
|
|
||||||
|
|
||||||
eth::AssemblyItem sub = m_context.addSubroutine(_runtimeContext.getAssembly());
|
|
||||||
// stack contains sub size
|
|
||||||
m_context << eth::Instruction::DUP1 << sub << u256(0) << eth::Instruction::CODECOPY;
|
|
||||||
m_context << u256(0) << eth::Instruction::RETURN;
|
|
||||||
|
|
||||||
// note that we have to include the functions again because of absolute jump labels
|
|
||||||
set<Declaration const*> functions = m_context.getFunctionsWithoutCode();
|
|
||||||
while (!functions.empty())
|
|
||||||
{
|
|
||||||
for (Declaration const* function: functions)
|
|
||||||
function->accept(*this);
|
|
||||||
functions = m_context.getFunctionsWithoutCode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Compiler::appendBaseConstructorCall(FunctionDefinition const& _constructor,
|
|
||||||
vector<ASTPointer<Expression>> const& _arguments)
|
|
||||||
{
|
|
||||||
FunctionType constructorType(_constructor);
|
|
||||||
eth::AssemblyItem returnLabel = m_context.pushNewTag();
|
|
||||||
for (unsigned i = 0; i < _arguments.size(); ++i)
|
|
||||||
compileExpression(*_arguments[i], constructorType.getParameterTypes()[i]);
|
|
||||||
m_context.appendJumpTo(m_context.getFunctionEntryLabel(_constructor));
|
|
||||||
m_context << returnLabel;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Compiler::appendConstructorCall(FunctionDefinition const& _constructor)
|
|
||||||
{
|
|
||||||
eth::AssemblyItem returnTag = m_context.pushNewTag();
|
|
||||||
// copy constructor arguments from code to memory and then to stack, they are supplied after the actual program
|
|
||||||
unsigned argumentSize = 0;
|
|
||||||
for (ASTPointer<VariableDeclaration> const& var: _constructor.getParameters())
|
|
||||||
argumentSize += CompilerUtils::getPaddedSize(var->getType()->getCalldataEncodedSize());
|
|
||||||
|
|
||||||
if (argumentSize > 0)
|
|
||||||
{
|
|
||||||
m_context << u256(argumentSize);
|
|
||||||
m_context.appendProgramSize();
|
|
||||||
m_context << u256(CompilerUtils::dataStartOffset); // copy it to byte four as expected for ABI calls
|
|
||||||
m_context << eth::Instruction::CODECOPY;
|
|
||||||
appendCalldataUnpacker(FunctionType(_constructor).getParameterTypes(), true);
|
|
||||||
}
|
|
||||||
m_context.appendJumpTo(m_context.getFunctionEntryLabel(_constructor));
|
|
||||||
m_context << returnTag;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
|
|
||||||
{
|
|
||||||
map<FixedHash<4>, FunctionTypePointer> interfaceFunctions = _contract.getInterfaceFunctions();
|
|
||||||
map<FixedHash<4>, const eth::AssemblyItem> callDataUnpackerEntryPoints;
|
|
||||||
|
|
||||||
// retrieve the function signature hash from the calldata
|
|
||||||
if (!interfaceFunctions.empty())
|
|
||||||
CompilerUtils(m_context).loadFromMemory(0, IntegerType(CompilerUtils::dataStartOffset * 8), true);
|
|
||||||
|
|
||||||
// stack now is: 1 0 <funhash>
|
|
||||||
for (auto const& it: interfaceFunctions)
|
|
||||||
{
|
|
||||||
callDataUnpackerEntryPoints.insert(std::make_pair(it.first, m_context.newTag()));
|
|
||||||
m_context << eth::dupInstruction(1) << u256(FixedHash<4>::Arith(it.first)) << eth::Instruction::EQ;
|
|
||||||
m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.at(it.first));
|
|
||||||
}
|
|
||||||
if (FunctionDefinition const* fallback = _contract.getFallbackFunction())
|
|
||||||
{
|
|
||||||
eth::AssemblyItem returnTag = m_context.pushNewTag();
|
|
||||||
fallback->accept(*this);
|
|
||||||
m_context << returnTag;
|
|
||||||
appendReturnValuePacker(FunctionType(*fallback).getReturnParameterTypes());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
m_context << eth::Instruction::STOP; // function not found
|
|
||||||
|
|
||||||
for (auto const& it: interfaceFunctions)
|
|
||||||
{
|
|
||||||
FunctionTypePointer const& functionType = it.second;
|
|
||||||
m_context << callDataUnpackerEntryPoints.at(it.first);
|
|
||||||
eth::AssemblyItem returnTag = m_context.pushNewTag();
|
|
||||||
appendCalldataUnpacker(functionType->getParameterTypes());
|
|
||||||
m_context.appendJumpTo(m_context.getFunctionEntryLabel(it.second->getDeclaration()));
|
|
||||||
m_context << returnTag;
|
|
||||||
appendReturnValuePacker(functionType->getReturnParameterTypes());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory)
|
|
||||||
{
|
|
||||||
// We do not check the calldata size, everything is zero-padded.
|
|
||||||
unsigned offset(CompilerUtils::dataStartOffset);
|
|
||||||
bool const c_padToWords = true;
|
|
||||||
|
|
||||||
unsigned dynamicParameterCount = 0;
|
|
||||||
for (TypePointer const& type: _typeParameters)
|
|
||||||
if (type->isDynamicallySized())
|
|
||||||
dynamicParameterCount++;
|
|
||||||
offset += dynamicParameterCount * 32;
|
|
||||||
unsigned currentDynamicParameter = 0;
|
|
||||||
for (TypePointer const& type: _typeParameters)
|
|
||||||
if (type->isDynamicallySized())
|
|
||||||
{
|
|
||||||
// value on stack: [calldata_offset] (only if we are already in dynamic mode)
|
|
||||||
if (currentDynamicParameter == 0)
|
|
||||||
// switch from static to dynamic
|
|
||||||
m_context << u256(offset);
|
|
||||||
// retrieve length
|
|
||||||
CompilerUtils(m_context).loadFromMemory(
|
|
||||||
CompilerUtils::dataStartOffset + currentDynamicParameter * 32,
|
|
||||||
IntegerType(256), !_fromMemory, c_padToWords);
|
|
||||||
// stack: offset length
|
|
||||||
// add 32-byte padding to copy of length
|
|
||||||
m_context << u256(32) << eth::Instruction::DUP1 << u256(31)
|
|
||||||
<< eth::Instruction::DUP4 << eth::Instruction::ADD
|
|
||||||
<< eth::Instruction::DIV << eth::Instruction::MUL;
|
|
||||||
// stack: offset length padded_length
|
|
||||||
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
|
|
||||||
currentDynamicParameter++;
|
|
||||||
// stack: offset length next_calldata_offset
|
|
||||||
}
|
|
||||||
else if (currentDynamicParameter == 0)
|
|
||||||
// we can still use static load
|
|
||||||
offset += CompilerUtils(m_context).loadFromMemory(offset, *type, !_fromMemory, c_padToWords);
|
|
||||||
else
|
|
||||||
CompilerUtils(m_context).loadFromMemoryDynamic(*type, !_fromMemory, c_padToWords);
|
|
||||||
if (dynamicParameterCount > 0)
|
|
||||||
m_context << eth::Instruction::POP;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters)
|
|
||||||
{
|
|
||||||
//@todo this can be also done more efficiently
|
|
||||||
unsigned dataOffset = 0;
|
|
||||||
unsigned stackDepth = 0;
|
|
||||||
for (TypePointer const& type: _typeParameters)
|
|
||||||
stackDepth += type->getSizeOnStack();
|
|
||||||
|
|
||||||
for (TypePointer const& type: _typeParameters)
|
|
||||||
{
|
|
||||||
CompilerUtils(m_context).copyToStackTop(stackDepth, *type);
|
|
||||||
ExpressionCompiler::appendTypeConversion(m_context, *type, *type, true);
|
|
||||||
bool const c_padToWords = true;
|
|
||||||
dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, *type, c_padToWords);
|
|
||||||
stackDepth -= type->getSizeOnStack();
|
|
||||||
}
|
|
||||||
// note that the stack is not cleaned up here
|
|
||||||
m_context << u256(dataOffset) << u256(0) << eth::Instruction::RETURN;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Compiler::registerStateVariables(ContractDefinition const& _contract)
|
|
||||||
{
|
|
||||||
for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.getLinearizedBaseContracts()))
|
|
||||||
for (ASTPointer<VariableDeclaration> const& variable: contract->getStateVariables())
|
|
||||||
m_context.addStateVariable(*variable);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Compiler::visit(VariableDeclaration const& _variableDeclaration)
|
|
||||||
{
|
|
||||||
solAssert(_variableDeclaration.isStateVariable(), "Compiler visit to non-state variable declaration.");
|
|
||||||
|
|
||||||
m_context.startFunction(_variableDeclaration);
|
|
||||||
m_breakTags.clear();
|
|
||||||
m_continueTags.clear();
|
|
||||||
|
|
||||||
m_context << m_context.getFunctionEntryLabel(_variableDeclaration);
|
|
||||||
ExpressionCompiler::appendStateVariableAccessor(m_context, _variableDeclaration);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Compiler::visit(FunctionDefinition const& _function)
|
|
||||||
{
|
|
||||||
//@todo to simplify this, the calling convention could by changed such that
|
|
||||||
// caller puts: [retarg0] ... [retargm] [return address] [arg0] ... [argn]
|
|
||||||
// although note that this reduces the size of the visible stack
|
|
||||||
|
|
||||||
m_context.startFunction(_function);
|
|
||||||
m_returnTag = m_context.newTag();
|
|
||||||
m_breakTags.clear();
|
|
||||||
m_continueTags.clear();
|
|
||||||
m_stackCleanupForReturn = 0;
|
|
||||||
m_currentFunction = &_function;
|
|
||||||
m_modifierDepth = 0;
|
|
||||||
|
|
||||||
// stack upon entry: [return address] [arg0] [arg1] ... [argn]
|
|
||||||
// reserve additional slots: [retarg0] ... [retargm] [localvar0] ... [localvarp]
|
|
||||||
|
|
||||||
unsigned parametersSize = CompilerUtils::getSizeOnStack(_function.getParameters());
|
|
||||||
m_context.adjustStackOffset(parametersSize);
|
|
||||||
for (ASTPointer<VariableDeclaration const> const& variable: _function.getParameters())
|
|
||||||
{
|
|
||||||
m_context.addVariable(*variable, parametersSize);
|
|
||||||
parametersSize -= variable->getType()->getSizeOnStack();
|
|
||||||
}
|
|
||||||
for (ASTPointer<VariableDeclaration const> const& variable: _function.getReturnParameters())
|
|
||||||
m_context.addAndInitializeVariable(*variable);
|
|
||||||
for (VariableDeclaration const* localVariable: _function.getLocalVariables())
|
|
||||||
m_context.addAndInitializeVariable(*localVariable);
|
|
||||||
|
|
||||||
appendModifierOrFunctionCode();
|
|
||||||
|
|
||||||
m_context << m_returnTag;
|
|
||||||
|
|
||||||
// Now we need to re-shuffle the stack. For this we keep a record of the stack layout
|
|
||||||
// that shows the target positions of the elements, where "-1" denotes that this element needs
|
|
||||||
// to be removed from the stack.
|
|
||||||
// Note that the fact that the return arguments are of increasing index is vital for this
|
|
||||||
// algorithm to work.
|
|
||||||
|
|
||||||
unsigned const c_argumentsSize = CompilerUtils::getSizeOnStack(_function.getParameters());
|
|
||||||
unsigned const c_returnValuesSize = CompilerUtils::getSizeOnStack(_function.getReturnParameters());
|
|
||||||
unsigned const c_localVariablesSize = CompilerUtils::getSizeOnStack(_function.getLocalVariables());
|
|
||||||
|
|
||||||
vector<int> stackLayout;
|
|
||||||
stackLayout.push_back(c_returnValuesSize); // target of return address
|
|
||||||
stackLayout += vector<int>(c_argumentsSize, -1); // discard all arguments
|
|
||||||
for (unsigned i = 0; i < c_returnValuesSize; ++i)
|
|
||||||
stackLayout.push_back(i);
|
|
||||||
stackLayout += vector<int>(c_localVariablesSize, -1);
|
|
||||||
|
|
||||||
while (stackLayout.back() != int(stackLayout.size() - 1))
|
|
||||||
if (stackLayout.back() < 0)
|
|
||||||
{
|
|
||||||
m_context << eth::Instruction::POP;
|
|
||||||
stackLayout.pop_back();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_context << eth::swapInstruction(stackLayout.size() - stackLayout.back() - 1);
|
|
||||||
swap(stackLayout[stackLayout.back()], stackLayout.back());
|
|
||||||
}
|
|
||||||
//@todo assert that everything is in place now
|
|
||||||
|
|
||||||
m_context << eth::Instruction::JUMP;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Compiler::visit(IfStatement const& _ifStatement)
|
|
||||||
{
|
|
||||||
compileExpression(_ifStatement.getCondition());
|
|
||||||
eth::AssemblyItem trueTag = m_context.appendConditionalJump();
|
|
||||||
if (_ifStatement.getFalseStatement())
|
|
||||||
_ifStatement.getFalseStatement()->accept(*this);
|
|
||||||
eth::AssemblyItem endTag = m_context.appendJumpToNew();
|
|
||||||
m_context << trueTag;
|
|
||||||
_ifStatement.getTrueStatement().accept(*this);
|
|
||||||
m_context << endTag;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Compiler::visit(WhileStatement const& _whileStatement)
|
|
||||||
{
|
|
||||||
eth::AssemblyItem loopStart = m_context.newTag();
|
|
||||||
eth::AssemblyItem loopEnd = m_context.newTag();
|
|
||||||
m_continueTags.push_back(loopStart);
|
|
||||||
m_breakTags.push_back(loopEnd);
|
|
||||||
|
|
||||||
m_context << loopStart;
|
|
||||||
compileExpression(_whileStatement.getCondition());
|
|
||||||
m_context << eth::Instruction::ISZERO;
|
|
||||||
m_context.appendConditionalJumpTo(loopEnd);
|
|
||||||
|
|
||||||
_whileStatement.getBody().accept(*this);
|
|
||||||
|
|
||||||
m_context.appendJumpTo(loopStart);
|
|
||||||
m_context << loopEnd;
|
|
||||||
|
|
||||||
m_continueTags.pop_back();
|
|
||||||
m_breakTags.pop_back();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Compiler::visit(ForStatement const& _forStatement)
|
|
||||||
{
|
|
||||||
eth::AssemblyItem loopStart = m_context.newTag();
|
|
||||||
eth::AssemblyItem loopEnd = m_context.newTag();
|
|
||||||
m_continueTags.push_back(loopStart);
|
|
||||||
m_breakTags.push_back(loopEnd);
|
|
||||||
|
|
||||||
if (_forStatement.getInitializationExpression())
|
|
||||||
_forStatement.getInitializationExpression()->accept(*this);
|
|
||||||
|
|
||||||
m_context << loopStart;
|
|
||||||
|
|
||||||
// if there is no terminating condition in for, default is to always be true
|
|
||||||
if (_forStatement.getCondition())
|
|
||||||
{
|
|
||||||
compileExpression(*_forStatement.getCondition());
|
|
||||||
m_context << eth::Instruction::ISZERO;
|
|
||||||
m_context.appendConditionalJumpTo(loopEnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
_forStatement.getBody().accept(*this);
|
|
||||||
|
|
||||||
// for's loop expression if existing
|
|
||||||
if (_forStatement.getLoopExpression())
|
|
||||||
_forStatement.getLoopExpression()->accept(*this);
|
|
||||||
|
|
||||||
m_context.appendJumpTo(loopStart);
|
|
||||||
m_context << loopEnd;
|
|
||||||
|
|
||||||
m_continueTags.pop_back();
|
|
||||||
m_breakTags.pop_back();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Compiler::visit(Continue const&)
|
|
||||||
{
|
|
||||||
if (!m_continueTags.empty())
|
|
||||||
m_context.appendJumpTo(m_continueTags.back());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Compiler::visit(Break const&)
|
|
||||||
{
|
|
||||||
if (!m_breakTags.empty())
|
|
||||||
m_context.appendJumpTo(m_breakTags.back());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Compiler::visit(Return const& _return)
|
|
||||||
{
|
|
||||||
//@todo modifications are needed to make this work with functions returning multiple values
|
|
||||||
if (Expression const* expression = _return.getExpression())
|
|
||||||
{
|
|
||||||
solAssert(_return.getFunctionReturnParameters(), "Invalid return parameters pointer.");
|
|
||||||
VariableDeclaration const& firstVariable = *_return.getFunctionReturnParameters()->getParameters().front();
|
|
||||||
compileExpression(*expression, firstVariable.getType());
|
|
||||||
CompilerUtils(m_context).moveToStackVariable(firstVariable);
|
|
||||||
}
|
|
||||||
for (unsigned i = 0; i < m_stackCleanupForReturn; ++i)
|
|
||||||
m_context << eth::Instruction::POP;
|
|
||||||
m_context.appendJumpTo(m_returnTag);
|
|
||||||
m_context.adjustStackOffset(m_stackCleanupForReturn);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Compiler::visit(VariableDefinition const& _variableDefinition)
|
|
||||||
{
|
|
||||||
if (Expression const* expression = _variableDefinition.getExpression())
|
|
||||||
{
|
|
||||||
compileExpression(*expression, _variableDefinition.getDeclaration().getType());
|
|
||||||
CompilerUtils(m_context).moveToStackVariable(_variableDefinition.getDeclaration());
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Compiler::visit(ExpressionStatement const& _expressionStatement)
|
|
||||||
{
|
|
||||||
Expression const& expression = _expressionStatement.getExpression();
|
|
||||||
compileExpression(expression);
|
|
||||||
CompilerUtils(m_context).popStackElement(*expression.getType());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Compiler::visit(PlaceholderStatement const&)
|
|
||||||
{
|
|
||||||
++m_modifierDepth;
|
|
||||||
appendModifierOrFunctionCode();
|
|
||||||
--m_modifierDepth;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Compiler::appendModifierOrFunctionCode()
|
|
||||||
{
|
|
||||||
solAssert(m_currentFunction, "");
|
|
||||||
if (m_modifierDepth >= m_currentFunction->getModifiers().size())
|
|
||||||
m_currentFunction->getBody().accept(*this);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ASTPointer<ModifierInvocation> const& modifierInvocation = m_currentFunction->getModifiers()[m_modifierDepth];
|
|
||||||
|
|
||||||
ModifierDefinition const& modifier = m_context.getFunctionModifier(modifierInvocation->getName()->getName());
|
|
||||||
solAssert(modifier.getParameters().size() == modifierInvocation->getArguments().size(), "");
|
|
||||||
for (unsigned i = 0; i < modifier.getParameters().size(); ++i)
|
|
||||||
{
|
|
||||||
m_context.addVariable(*modifier.getParameters()[i]);
|
|
||||||
compileExpression(*modifierInvocation->getArguments()[i],
|
|
||||||
modifier.getParameters()[i]->getType());
|
|
||||||
}
|
|
||||||
for (VariableDeclaration const* localVariable: modifier.getLocalVariables())
|
|
||||||
m_context.addAndInitializeVariable(*localVariable);
|
|
||||||
|
|
||||||
unsigned const c_stackSurplus = CompilerUtils::getSizeOnStack(modifier.getParameters()) +
|
|
||||||
CompilerUtils::getSizeOnStack(modifier.getLocalVariables());
|
|
||||||
m_stackCleanupForReturn += c_stackSurplus;
|
|
||||||
|
|
||||||
modifier.getBody().accept(*this);
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < c_stackSurplus; ++i)
|
|
||||||
m_context << eth::Instruction::POP;
|
|
||||||
m_stackCleanupForReturn -= c_stackSurplus;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Compiler::compileExpression(Expression const& _expression, TypePointer const& _targetType)
|
|
||||||
{
|
|
||||||
ExpressionCompiler::compileExpression(m_context, _expression, m_optimize);
|
|
||||||
if (_targetType)
|
|
||||||
ExpressionCompiler::appendTypeConversion(m_context, *_expression.getType(), *_targetType);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
93
Compiler.h
93
Compiler.h
@ -1,93 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Solidity AST to EVM bytecode compiler.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <ostream>
|
|
||||||
#include <functional>
|
|
||||||
#include <libsolidity/ASTVisitor.h>
|
|
||||||
#include <libsolidity/CompilerContext.h>
|
|
||||||
|
|
||||||
namespace dev {
|
|
||||||
namespace solidity {
|
|
||||||
|
|
||||||
class Compiler: private ASTConstVisitor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit Compiler(bool _optimize = false): m_optimize(_optimize), m_context(),
|
|
||||||
m_returnTag(m_context.newTag()) {}
|
|
||||||
|
|
||||||
void compileContract(ContractDefinition const& _contract,
|
|
||||||
std::map<ContractDefinition const*, bytes const*> const& _contracts);
|
|
||||||
bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); }
|
|
||||||
bytes getRuntimeBytecode() { return m_runtimeContext.getAssembledBytecode(m_optimize);}
|
|
||||||
void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// Registers the non-function objects inside the contract with the context.
|
|
||||||
void initializeContext(ContractDefinition const& _contract,
|
|
||||||
std::map<ContractDefinition const*, bytes const*> const& _contracts);
|
|
||||||
/// Adds the code that is run at creation time. Should be run after exchanging the run-time context
|
|
||||||
/// with a new and initialized context. Adds the constructor code.
|
|
||||||
void packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext);
|
|
||||||
void appendBaseConstructorCall(FunctionDefinition const& _constructor,
|
|
||||||
std::vector<ASTPointer<Expression>> const& _arguments);
|
|
||||||
void appendConstructorCall(FunctionDefinition const& _constructor);
|
|
||||||
void appendFunctionSelector(ContractDefinition const& _contract);
|
|
||||||
/// Creates code that unpacks the arguments for the given function represented by a vector of TypePointers.
|
|
||||||
/// From memory if @a _fromMemory is true, otherwise from call data.
|
|
||||||
void appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory = false);
|
|
||||||
void appendReturnValuePacker(TypePointers const& _typeParameters);
|
|
||||||
|
|
||||||
void registerStateVariables(ContractDefinition const& _contract);
|
|
||||||
|
|
||||||
virtual bool visit(VariableDeclaration const& _variableDeclaration) override;
|
|
||||||
virtual bool visit(FunctionDefinition const& _function) override;
|
|
||||||
virtual bool visit(IfStatement const& _ifStatement) override;
|
|
||||||
virtual bool visit(WhileStatement const& _whileStatement) override;
|
|
||||||
virtual bool visit(ForStatement const& _forStatement) override;
|
|
||||||
virtual bool visit(Continue const& _continue) override;
|
|
||||||
virtual bool visit(Break const& _break) override;
|
|
||||||
virtual bool visit(Return const& _return) override;
|
|
||||||
virtual bool visit(VariableDefinition const& _variableDefinition) override;
|
|
||||||
virtual bool visit(ExpressionStatement const& _expressionStatement) override;
|
|
||||||
virtual bool visit(PlaceholderStatement const&) override;
|
|
||||||
|
|
||||||
/// Appends one layer of function modifier code of the current function, or the function
|
|
||||||
/// body itself if the last modifier was reached.
|
|
||||||
void appendModifierOrFunctionCode();
|
|
||||||
|
|
||||||
void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer());
|
|
||||||
|
|
||||||
bool const m_optimize;
|
|
||||||
CompilerContext m_context;
|
|
||||||
CompilerContext m_runtimeContext;
|
|
||||||
std::vector<eth::AssemblyItem> m_breakTags; ///< tag to jump to for a "break" statement
|
|
||||||
std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" statement
|
|
||||||
eth::AssemblyItem m_returnTag; ///< tag to jump to for a "return" statement
|
|
||||||
unsigned m_modifierDepth = 0;
|
|
||||||
FunctionDefinition const* m_currentFunction;
|
|
||||||
unsigned m_stackCleanupForReturn; ///< this number of stack elements need to be removed before jump to m_returnTag
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,165 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Utilities for the solidity compiler.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
#include <numeric>
|
|
||||||
#include <libsolidity/AST.h>
|
|
||||||
#include <libsolidity/Compiler.h>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
void CompilerContext::addMagicGlobal(MagicVariableDeclaration const& _declaration)
|
|
||||||
{
|
|
||||||
m_magicGlobals.insert(&_declaration);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompilerContext::addStateVariable(VariableDeclaration const& _declaration)
|
|
||||||
{
|
|
||||||
m_stateVariables[&_declaration] = m_stateVariablesSize;
|
|
||||||
m_stateVariablesSize += _declaration.getType()->getStorageSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompilerContext::startFunction(Declaration const& _function)
|
|
||||||
{
|
|
||||||
m_functionsWithCode.insert(&_function);
|
|
||||||
m_localVariables.clear();
|
|
||||||
m_asm.setDeposit(0);
|
|
||||||
*this << getFunctionEntryLabel(_function);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompilerContext::addVariable(VariableDeclaration const& _declaration,
|
|
||||||
unsigned _offsetToCurrent)
|
|
||||||
{
|
|
||||||
solAssert(m_asm.deposit() >= 0 && unsigned(m_asm.deposit()) >= _offsetToCurrent, "");
|
|
||||||
m_localVariables[&_declaration] = unsigned(m_asm.deposit()) - _offsetToCurrent;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompilerContext::addAndInitializeVariable(VariableDeclaration const& _declaration)
|
|
||||||
{
|
|
||||||
addVariable(_declaration);
|
|
||||||
|
|
||||||
int const size = _declaration.getType()->getSizeOnStack();
|
|
||||||
for (int i = 0; i < size; ++i)
|
|
||||||
*this << u256(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes const& CompilerContext::getCompiledContract(const ContractDefinition& _contract) const
|
|
||||||
{
|
|
||||||
auto ret = m_compiledContracts.find(&_contract);
|
|
||||||
solAssert(ret != m_compiledContracts.end(), "Compiled contract not found.");
|
|
||||||
return *ret->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CompilerContext::isLocalVariable(Declaration const* _declaration) const
|
|
||||||
{
|
|
||||||
return !!m_localVariables.count(_declaration);
|
|
||||||
}
|
|
||||||
|
|
||||||
eth::AssemblyItem CompilerContext::getFunctionEntryLabel(Declaration const& _declaration)
|
|
||||||
{
|
|
||||||
auto res = m_functionEntryLabels.find(&_declaration);
|
|
||||||
if (res == m_functionEntryLabels.end())
|
|
||||||
{
|
|
||||||
eth::AssemblyItem tag(m_asm.newTag());
|
|
||||||
m_functionEntryLabels.insert(make_pair(&_declaration, tag));
|
|
||||||
return tag.tag();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return res->second.tag();
|
|
||||||
}
|
|
||||||
|
|
||||||
eth::AssemblyItem CompilerContext::getVirtualFunctionEntryLabel(FunctionDefinition const& _function)
|
|
||||||
{
|
|
||||||
solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");
|
|
||||||
for (ContractDefinition const* contract: m_inheritanceHierarchy)
|
|
||||||
for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions())
|
|
||||||
if (!function->isConstructor() && function->getName() == _function.getName())
|
|
||||||
return getFunctionEntryLabel(*function);
|
|
||||||
solAssert(false, "Virtual function " + _function.getName() + " not found.");
|
|
||||||
return m_asm.newTag(); // not reached
|
|
||||||
}
|
|
||||||
|
|
||||||
eth::AssemblyItem CompilerContext::getSuperFunctionEntryLabel(string const& _name, ContractDefinition const& _base)
|
|
||||||
{
|
|
||||||
// search for first contract after _base
|
|
||||||
solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");
|
|
||||||
auto it = find(m_inheritanceHierarchy.begin(), m_inheritanceHierarchy.end(), &_base);
|
|
||||||
solAssert(it != m_inheritanceHierarchy.end(), "Base not found in inheritance hierarchy.");
|
|
||||||
for (++it; it != m_inheritanceHierarchy.end(); ++it)
|
|
||||||
for (ASTPointer<FunctionDefinition> const& function: (*it)->getDefinedFunctions())
|
|
||||||
if (!function->isConstructor() && function->getName() == _name)
|
|
||||||
return getFunctionEntryLabel(*function);
|
|
||||||
solAssert(false, "Super function " + _name + " not found.");
|
|
||||||
return m_asm.newTag(); // not reached
|
|
||||||
}
|
|
||||||
|
|
||||||
set<Declaration const*> CompilerContext::getFunctionsWithoutCode()
|
|
||||||
{
|
|
||||||
set<Declaration const*> functions;
|
|
||||||
for (auto const& it: m_functionEntryLabels)
|
|
||||||
if (m_functionsWithCode.count(it.first) == 0)
|
|
||||||
functions.insert(it.first);
|
|
||||||
return move(functions);
|
|
||||||
}
|
|
||||||
|
|
||||||
ModifierDefinition const& CompilerContext::getFunctionModifier(string const& _name) const
|
|
||||||
{
|
|
||||||
solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");
|
|
||||||
for (ContractDefinition const* contract: m_inheritanceHierarchy)
|
|
||||||
for (ASTPointer<ModifierDefinition> const& modifier: contract->getFunctionModifiers())
|
|
||||||
if (modifier->getName() == _name)
|
|
||||||
return *modifier.get();
|
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError()
|
|
||||||
<< errinfo_comment("Function modifier " + _name + " not found."));
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned CompilerContext::getBaseStackOffsetOfVariable(Declaration const& _declaration) const
|
|
||||||
{
|
|
||||||
auto res = m_localVariables.find(&_declaration);
|
|
||||||
solAssert(res != m_localVariables.end(), "Variable not found on stack.");
|
|
||||||
return res->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned CompilerContext::baseToCurrentStackOffset(unsigned _baseOffset) const
|
|
||||||
{
|
|
||||||
return m_asm.deposit() - _baseOffset - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned CompilerContext::currentToBaseStackOffset(unsigned _offset) const
|
|
||||||
{
|
|
||||||
return m_asm.deposit() - _offset - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
u256 CompilerContext::getStorageLocationOfVariable(const Declaration& _declaration) const
|
|
||||||
{
|
|
||||||
auto it = m_stateVariables.find(&_declaration);
|
|
||||||
solAssert(it != m_stateVariables.end(), "Variable not found in storage.");
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,135 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Utilities for the solidity compiler.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <ostream>
|
|
||||||
#include <libevmcore/Instruction.h>
|
|
||||||
#include <libevmcore/Assembly.h>
|
|
||||||
#include <libsolidity/ASTForward.h>
|
|
||||||
#include <libsolidity/Types.h>
|
|
||||||
|
|
||||||
namespace dev {
|
|
||||||
namespace solidity {
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Context to be shared by all units that compile the same contract.
|
|
||||||
* It stores the generated bytecode and the position of identifiers in memory and on the stack.
|
|
||||||
*/
|
|
||||||
class CompilerContext
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void addMagicGlobal(MagicVariableDeclaration const& _declaration);
|
|
||||||
void addStateVariable(VariableDeclaration const& _declaration);
|
|
||||||
void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0);
|
|
||||||
void addAndInitializeVariable(VariableDeclaration const& _declaration);
|
|
||||||
|
|
||||||
void setCompiledContracts(std::map<ContractDefinition const*, bytes const*> const& _contracts) { m_compiledContracts = _contracts; }
|
|
||||||
bytes const& getCompiledContract(ContractDefinition const& _contract) const;
|
|
||||||
|
|
||||||
void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); }
|
|
||||||
unsigned getStackHeight() { solAssert(m_asm.deposit() >= 0, ""); return unsigned(m_asm.deposit()); }
|
|
||||||
|
|
||||||
bool isMagicGlobal(Declaration const* _declaration) const { return m_magicGlobals.count(_declaration) != 0; }
|
|
||||||
bool isLocalVariable(Declaration const* _declaration) const;
|
|
||||||
bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration) != 0; }
|
|
||||||
|
|
||||||
eth::AssemblyItem getFunctionEntryLabel(Declaration const& _declaration);
|
|
||||||
void setInheritanceHierarchy(std::vector<ContractDefinition const*> const& _hierarchy) { m_inheritanceHierarchy = _hierarchy; }
|
|
||||||
/// @returns the entry label of the given function and takes overrides into account.
|
|
||||||
eth::AssemblyItem getVirtualFunctionEntryLabel(FunctionDefinition const& _function);
|
|
||||||
/// @returns the entry label of function with the given name from the most derived class just
|
|
||||||
/// above _base in the current inheritance hierarchy.
|
|
||||||
eth::AssemblyItem getSuperFunctionEntryLabel(std::string const& _name, ContractDefinition const& _base);
|
|
||||||
/// @returns the set of functions for which we still need to generate code
|
|
||||||
std::set<Declaration const*> getFunctionsWithoutCode();
|
|
||||||
/// Resets function specific members, inserts the function entry label and marks the function
|
|
||||||
/// as "having code".
|
|
||||||
void startFunction(Declaration const& _function);
|
|
||||||
|
|
||||||
ModifierDefinition const& getFunctionModifier(std::string const& _name) const;
|
|
||||||
/// Returns the distance of the given local variable from the bottom of the stack (of the current function).
|
|
||||||
unsigned getBaseStackOffsetOfVariable(Declaration const& _declaration) const;
|
|
||||||
/// If supplied by a value returned by @ref getBaseStackOffsetOfVariable(variable), returns
|
|
||||||
/// the distance of that variable from the current top of the stack.
|
|
||||||
unsigned baseToCurrentStackOffset(unsigned _baseOffset) const;
|
|
||||||
/// Converts an offset relative to the current stack height to a value that can be used later
|
|
||||||
/// with baseToCurrentStackOffset to point to the same stack element.
|
|
||||||
unsigned currentToBaseStackOffset(unsigned _offset) const;
|
|
||||||
u256 getStorageLocationOfVariable(Declaration const& _declaration) const;
|
|
||||||
|
|
||||||
/// Appends a JUMPI instruction to a new tag and @returns the tag
|
|
||||||
eth::AssemblyItem appendConditionalJump() { return m_asm.appendJumpI().tag(); }
|
|
||||||
/// Appends a JUMPI instruction to @a _tag
|
|
||||||
CompilerContext& appendConditionalJumpTo(eth::AssemblyItem const& _tag) { m_asm.appendJumpI(_tag); return *this; }
|
|
||||||
/// Appends a JUMP to a new tag and @returns the tag
|
|
||||||
eth::AssemblyItem appendJumpToNew() { return m_asm.appendJump().tag(); }
|
|
||||||
/// Appends a JUMP to a tag already on the stack
|
|
||||||
CompilerContext& appendJump() { return *this << eth::Instruction::JUMP; }
|
|
||||||
/// Appends a JUMP to a specific tag
|
|
||||||
CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm.appendJump(_tag); return *this; }
|
|
||||||
/// Appends pushing of a new tag and @returns the new tag.
|
|
||||||
eth::AssemblyItem pushNewTag() { return m_asm.append(m_asm.newPushTag()).tag(); }
|
|
||||||
/// @returns a new tag without pushing any opcodes or data
|
|
||||||
eth::AssemblyItem newTag() { return m_asm.newTag(); }
|
|
||||||
/// Adds a subroutine to the code (in the data section) and pushes its size (via a tag)
|
|
||||||
/// on the stack. @returns the assembly item corresponding to the pushed subroutine, i.e. its offset.
|
|
||||||
eth::AssemblyItem addSubroutine(eth::Assembly const& _assembly) { return m_asm.appendSubSize(_assembly); }
|
|
||||||
/// Pushes the size of the final program
|
|
||||||
void appendProgramSize() { return m_asm.appendProgramSize(); }
|
|
||||||
/// Adds data to the data section, pushes a reference to the stack
|
|
||||||
eth::AssemblyItem appendData(bytes const& _data) { return m_asm.append(_data); }
|
|
||||||
|
|
||||||
/// Append elements to the current instruction list and adjust @a m_stackOffset.
|
|
||||||
CompilerContext& operator<<(eth::AssemblyItem const& _item) { m_asm.append(_item); return *this; }
|
|
||||||
CompilerContext& operator<<(eth::Instruction _instruction) { m_asm.append(_instruction); return *this; }
|
|
||||||
CompilerContext& operator<<(u256 const& _value) { m_asm.append(_value); return *this; }
|
|
||||||
CompilerContext& operator<<(bytes const& _data) { m_asm.append(_data); return *this; }
|
|
||||||
|
|
||||||
eth::Assembly const& getAssembly() const { return m_asm; }
|
|
||||||
void streamAssembly(std::ostream& _stream) const { _stream << m_asm; }
|
|
||||||
bytes getAssembledBytecode(bool _optimize = false) { return m_asm.optimise(_optimize).assemble(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
eth::Assembly m_asm;
|
|
||||||
|
|
||||||
/// Magic global variables like msg, tx or this, distinguished by type.
|
|
||||||
std::set<Declaration const*> m_magicGlobals;
|
|
||||||
/// Other already compiled contracts to be used in contract creation calls.
|
|
||||||
std::map<ContractDefinition const*, bytes const*> m_compiledContracts;
|
|
||||||
/// Size of the state variables, offset of next variable to be added.
|
|
||||||
u256 m_stateVariablesSize = 0;
|
|
||||||
/// Storage offsets of state variables
|
|
||||||
std::map<Declaration const*, u256> m_stateVariables;
|
|
||||||
/// Offsets of local variables on the stack (relative to stack base).
|
|
||||||
std::map<Declaration const*, unsigned> m_localVariables;
|
|
||||||
/// Labels pointing to the entry points of functions.
|
|
||||||
std::map<Declaration const*, eth::AssemblyItem> m_functionEntryLabels;
|
|
||||||
/// Set of functions for which we did not yet generate code.
|
|
||||||
std::set<Declaration const*> m_functionsWithCode;
|
|
||||||
/// List of current inheritance hierarchy from derived to base.
|
|
||||||
std::vector<ContractDefinition const*> m_inheritanceHierarchy;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,370 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @author Gav Wood <g@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Full-stack compiler that converts a source code string to bytecode.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
|
||||||
#include <libsolidity/AST.h>
|
|
||||||
#include <libsolidity/Scanner.h>
|
|
||||||
#include <libsolidity/Parser.h>
|
|
||||||
#include <libsolidity/GlobalContext.h>
|
|
||||||
#include <libsolidity/NameAndTypeResolver.h>
|
|
||||||
#include <libsolidity/Compiler.h>
|
|
||||||
#include <libsolidity/CompilerStack.h>
|
|
||||||
#include <libsolidity/InterfaceHandler.h>
|
|
||||||
|
|
||||||
#include <libdevcrypto/SHA3.h>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
const map<string, string> StandardSources = map<string, string>{
|
|
||||||
{"coin", R"(import "CoinReg";import "Config";import "configUser";contract coin is configUser{function coin(string3 name, uint denom) {CoinReg(Config(configAddr()).lookup(3)).register(name, denom);}})"},
|
|
||||||
{"Coin", R"(contract Coin{function isApprovedFor(address _target,address _proxy)constant returns(bool _r){}function isApproved(address _proxy)constant returns(bool _r){}function sendCoinFrom(address _from,uint256 _val,address _to){}function coinBalanceOf(address _a)constant returns(uint256 _r){}function sendCoin(uint256 _val,address _to){}function coinBalance()constant returns(uint256 _r){}function approve(address _a){}})"},
|
|
||||||
{"CoinReg", R"(contract CoinReg{function count()constant returns(uint256 r){}function info(uint256 i)constant returns(address addr,string3 name,uint256 denom){}function register(string3 name,uint256 denom){}function unregister(){}})"},
|
|
||||||
{"configUser", R"(contract configUser{function configAddr()constant returns(address a){ return 0xc6d9d2cd449a754c494264e1809c50e34d64562b;}})"},
|
|
||||||
{"Config", R"(contract Config{function lookup(uint256 service)constant returns(address a){}function kill(){}function unregister(uint256 id){}function register(uint256 id,address service){}})"},
|
|
||||||
{"mortal", R"(import "owned";contract mortal is owned {function kill() { if (msg.sender == owner) suicide(owner); }})"},
|
|
||||||
{"named", R"(import "Config";import "NameReg";import "configUser";contract named is configUser {function named(string32 name) {NameReg(Config(configAddr()).lookup(1)).register(name);}})"},
|
|
||||||
{"NameReg", R"(contract NameReg{function register(string32 name){}function addressOf(string32 name)constant returns(address addr){}function unregister(){}function nameOf(address addr)constant returns(string32 name){}})"},
|
|
||||||
{"owned", R"(contract owned{function owned(){owner = msg.sender;}modifier onlyowner(){if(msg.sender==owner)_}address owner;})"},
|
|
||||||
{"service", R"(import "Config";import "configUser";contract service is configUser{function service(uint _n){Config(configAddr()).register(_n, this);}})"},
|
|
||||||
{"std", R"(import "owned";import "mortal";import "Config";import "configUser";import "NameReg";import "named";)"}
|
|
||||||
};
|
|
||||||
|
|
||||||
CompilerStack::CompilerStack(bool _addStandardSources):
|
|
||||||
m_addStandardSources(_addStandardSources), m_parseSuccessful(false)
|
|
||||||
{
|
|
||||||
if (m_addStandardSources)
|
|
||||||
addSources(StandardSources);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CompilerStack::addSource(string const& _name, string const& _content)
|
|
||||||
{
|
|
||||||
bool existed = m_sources.count(_name) != 0;
|
|
||||||
reset(true);
|
|
||||||
m_sources[_name].scanner = make_shared<Scanner>(CharStream(expanded(_content)), _name);
|
|
||||||
return existed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompilerStack::setSource(string const& _sourceCode)
|
|
||||||
{
|
|
||||||
reset();
|
|
||||||
addSource("", expanded(_sourceCode));
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompilerStack::parse()
|
|
||||||
{
|
|
||||||
for (auto& sourcePair: m_sources)
|
|
||||||
{
|
|
||||||
sourcePair.second.scanner->reset();
|
|
||||||
sourcePair.second.ast = Parser().parse(sourcePair.second.scanner);
|
|
||||||
}
|
|
||||||
resolveImports();
|
|
||||||
|
|
||||||
m_globalContext = make_shared<GlobalContext>();
|
|
||||||
NameAndTypeResolver resolver(m_globalContext->getDeclarations());
|
|
||||||
for (Source const* source: m_sourceOrder)
|
|
||||||
resolver.registerDeclarations(*source->ast);
|
|
||||||
for (Source const* source: m_sourceOrder)
|
|
||||||
for (ASTPointer<ASTNode> const& node: source->ast->getNodes())
|
|
||||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
|
||||||
{
|
|
||||||
m_globalContext->setCurrentContract(*contract);
|
|
||||||
resolver.updateDeclaration(*m_globalContext->getCurrentThis());
|
|
||||||
resolver.updateDeclaration(*m_globalContext->getCurrentSuper());
|
|
||||||
resolver.resolveNamesAndTypes(*contract);
|
|
||||||
m_contracts[contract->getName()].contract = contract;
|
|
||||||
}
|
|
||||||
for (Source const* source: m_sourceOrder)
|
|
||||||
for (ASTPointer<ASTNode> const& node: source->ast->getNodes())
|
|
||||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
|
||||||
{
|
|
||||||
m_globalContext->setCurrentContract(*contract);
|
|
||||||
resolver.updateDeclaration(*m_globalContext->getCurrentThis());
|
|
||||||
resolver.checkTypeRequirements(*contract);
|
|
||||||
m_contracts[contract->getName()].contract = contract;
|
|
||||||
}
|
|
||||||
m_parseSuccessful = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompilerStack::parse(string const& _sourceCode)
|
|
||||||
{
|
|
||||||
setSource(_sourceCode);
|
|
||||||
parse();
|
|
||||||
}
|
|
||||||
|
|
||||||
vector<string> CompilerStack::getContractNames() const
|
|
||||||
{
|
|
||||||
if (!m_parseSuccessful)
|
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
|
|
||||||
vector<string> contractNames;
|
|
||||||
for (auto const& contract: m_contracts)
|
|
||||||
contractNames.push_back(contract.first);
|
|
||||||
return contractNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
////// BEGIN: TEMPORARY ONLY
|
|
||||||
///
|
|
||||||
/// NOTE: THIS INVALIDATES SOURCE POINTERS AND CAN CRASH THE COMPILER
|
|
||||||
///
|
|
||||||
/// remove once import works properly and we have genesis contracts
|
|
||||||
|
|
||||||
string CompilerStack::expanded(string const& _sourceCode)
|
|
||||||
{
|
|
||||||
const map<string, string> c_standardSources = map<string, string>{
|
|
||||||
{ "Config", "contract Config{function lookup(uint256 service)constant returns(address a){}function kill(){}function unregister(uint256 id){}function register(uint256 id,address service){}}" },
|
|
||||||
{ "Coin", "contract Coin{function isApprovedFor(address _target,address _proxy)constant returns(bool _r){}function isApproved(address _proxy)constant returns(bool _r){}function sendCoinFrom(address _from,uint256 _val,address _to){}function coinBalanceOf(address _a)constant returns(uint256 _r){}function sendCoin(uint256 _val,address _to){}function coinBalance()constant returns(uint256 _r){}function approve(address _a){}}"},
|
|
||||||
{ "CoinReg", "contract CoinReg{function count()constant returns(uint256 r){}function info(uint256 i)constant returns(address addr,string3 name,uint256 denom){}function register(string3 name,uint256 denom){}function unregister(){}}" },
|
|
||||||
{ "coin", "#require CoinReg\ncontract coin {function coin(string3 name, uint denom) {CoinReg(Config().lookup(3)).register(name, denom);}}" },
|
|
||||||
{ "service", "#require Config\ncontract service{function service(uint _n){Config().register(_n, this);}}" },
|
|
||||||
{ "owned", "contract owned{function owned(){owner = msg.sender;}modifier onlyowner(){if(msg.sender==owner)_}address owner;}" },
|
|
||||||
{ "mortal", "#require owned\ncontract mortal is owned {function kill() { if (msg.sender == owner) suicide(owner); }}" },
|
|
||||||
{ "NameReg", "contract NameReg{function register(string32 name){}function addressOf(string32 name)constant returns(address addr){}function unregister(){}function nameOf(address addr)constant returns(string32 name){}}" },
|
|
||||||
{ "named", "#require Config NameReg\ncontract named {function named(string32 name) {NameReg(Config().lookup(1)).register(name);}}" },
|
|
||||||
{ "std", "#require owned mortal Config NameReg named" },
|
|
||||||
};
|
|
||||||
|
|
||||||
string sub;
|
|
||||||
set<string> got;
|
|
||||||
function<string(string const&)> localExpanded;
|
|
||||||
localExpanded = [&](string const& s) -> string
|
|
||||||
{
|
|
||||||
string ret = s;
|
|
||||||
for (size_t p = 0; p != string::npos;)
|
|
||||||
if ((p = ret.find("#require ")) != string::npos)
|
|
||||||
{
|
|
||||||
string n = ret.substr(p + 9, ret.find_first_of('\n', p + 9) - p - 9);
|
|
||||||
ret.replace(p, n.size() + 9, "");
|
|
||||||
vector<string> rs;
|
|
||||||
boost::split(rs, n, boost::is_any_of(" \t,"), boost::token_compress_on);
|
|
||||||
for (auto const& r: rs)
|
|
||||||
if (!got.count(r))
|
|
||||||
{
|
|
||||||
if (c_standardSources.count(r))
|
|
||||||
sub.append("\n" + localExpanded(c_standardSources.at(r)) + "\n");
|
|
||||||
got.insert(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO: remove once we have genesis contracts.
|
|
||||||
else if ((p = ret.find("Config()")) != string::npos)
|
|
||||||
ret.replace(p, 8, "Config(0xc6d9d2cd449a754c494264e1809c50e34d64562b)");
|
|
||||||
return ret;
|
|
||||||
};
|
|
||||||
return sub + localExpanded(_sourceCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
////// END: TEMPORARY ONLY
|
|
||||||
|
|
||||||
void CompilerStack::compile(bool _optimize)
|
|
||||||
{
|
|
||||||
if (!m_parseSuccessful)
|
|
||||||
parse();
|
|
||||||
|
|
||||||
map<ContractDefinition const*, bytes const*> contractBytecode;
|
|
||||||
for (Source const* source: m_sourceOrder)
|
|
||||||
for (ASTPointer<ASTNode> const& node: source->ast->getNodes())
|
|
||||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
|
||||||
{
|
|
||||||
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize);
|
|
||||||
compiler->compileContract(*contract, contractBytecode);
|
|
||||||
Contract& compiledContract = m_contracts[contract->getName()];
|
|
||||||
compiledContract.bytecode = compiler->getAssembledBytecode();
|
|
||||||
compiledContract.runtimeBytecode = compiler->getRuntimeBytecode();
|
|
||||||
compiledContract.compiler = move(compiler);
|
|
||||||
contractBytecode[compiledContract.contract] = &compiledContract.bytecode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes const& CompilerStack::compile(string const& _sourceCode, bool _optimize)
|
|
||||||
{
|
|
||||||
parse(_sourceCode);
|
|
||||||
compile(_optimize);
|
|
||||||
return getBytecode();
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes const& CompilerStack::getBytecode(string const& _contractName) const
|
|
||||||
{
|
|
||||||
return getContract(_contractName).bytecode;
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes const& CompilerStack::getRuntimeBytecode(string const& _contractName) const
|
|
||||||
{
|
|
||||||
return getContract(_contractName).runtimeBytecode;
|
|
||||||
}
|
|
||||||
|
|
||||||
dev::h256 CompilerStack::getContractCodeHash(string const& _contractName) const
|
|
||||||
{
|
|
||||||
return dev::sha3(getRuntimeBytecode(_contractName));
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName) const
|
|
||||||
{
|
|
||||||
getContract(_contractName).compiler->streamAssembly(_outStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
string const& CompilerStack::getInterface(string const& _contractName) const
|
|
||||||
{
|
|
||||||
return getMetadata(_contractName, DocumentationType::ABIInterface);
|
|
||||||
}
|
|
||||||
|
|
||||||
string const& CompilerStack::getSolidityInterface(string const& _contractName) const
|
|
||||||
{
|
|
||||||
return getMetadata(_contractName, DocumentationType::ABISolidityInterface);
|
|
||||||
}
|
|
||||||
|
|
||||||
string const& CompilerStack::getMetadata(string const& _contractName, DocumentationType _type) const
|
|
||||||
{
|
|
||||||
if (!m_parseSuccessful)
|
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
|
|
||||||
|
|
||||||
Contract const& contract = getContract(_contractName);
|
|
||||||
|
|
||||||
std::unique_ptr<string const>* doc;
|
|
||||||
switch (_type)
|
|
||||||
{
|
|
||||||
case DocumentationType::NatspecUser:
|
|
||||||
doc = &contract.userDocumentation;
|
|
||||||
break;
|
|
||||||
case DocumentationType::NatspecDev:
|
|
||||||
doc = &contract.devDocumentation;
|
|
||||||
break;
|
|
||||||
case DocumentationType::ABIInterface:
|
|
||||||
doc = &contract.interface;
|
|
||||||
break;
|
|
||||||
case DocumentationType::ABISolidityInterface:
|
|
||||||
doc = &contract.solidityInterface;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type."));
|
|
||||||
}
|
|
||||||
if (!*doc)
|
|
||||||
*doc = contract.interfaceHandler->getDocumentation(*contract.contract, _type);
|
|
||||||
return *(*doc);
|
|
||||||
}
|
|
||||||
|
|
||||||
Scanner const& CompilerStack::getScanner(string const& _sourceName) const
|
|
||||||
{
|
|
||||||
return *getSource(_sourceName).scanner;
|
|
||||||
}
|
|
||||||
|
|
||||||
SourceUnit const& CompilerStack::getAST(string const& _sourceName) const
|
|
||||||
{
|
|
||||||
return *getSource(_sourceName).ast;
|
|
||||||
}
|
|
||||||
|
|
||||||
ContractDefinition const& CompilerStack::getContractDefinition(string const& _contractName) const
|
|
||||||
{
|
|
||||||
return *getContract(_contractName).contract;
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize)
|
|
||||||
{
|
|
||||||
CompilerStack stack;
|
|
||||||
return stack.compile(_sourceCode, _optimize);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompilerStack::reset(bool _keepSources)
|
|
||||||
{
|
|
||||||
m_parseSuccessful = false;
|
|
||||||
if (_keepSources)
|
|
||||||
for (auto sourcePair: m_sources)
|
|
||||||
sourcePair.second.reset();
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_sources.clear();
|
|
||||||
if (m_addStandardSources)
|
|
||||||
addSources(StandardSources);
|
|
||||||
}
|
|
||||||
m_globalContext.reset();
|
|
||||||
m_sourceOrder.clear();
|
|
||||||
m_contracts.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompilerStack::resolveImports()
|
|
||||||
{
|
|
||||||
// topological sorting (depth first search) of the import graph, cutting potential cycles
|
|
||||||
vector<Source const*> sourceOrder;
|
|
||||||
set<Source const*> sourcesSeen;
|
|
||||||
|
|
||||||
function<void(Source const*)> toposort = [&](Source const* _source)
|
|
||||||
{
|
|
||||||
if (sourcesSeen.count(_source))
|
|
||||||
return;
|
|
||||||
sourcesSeen.insert(_source);
|
|
||||||
for (ASTPointer<ASTNode> const& node: _source->ast->getNodes())
|
|
||||||
if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get()))
|
|
||||||
{
|
|
||||||
string const& id = import->getIdentifier();
|
|
||||||
if (!m_sources.count(id))
|
|
||||||
BOOST_THROW_EXCEPTION(ParserError()
|
|
||||||
<< errinfo_sourceLocation(import->getLocation())
|
|
||||||
<< errinfo_comment("Source not found."));
|
|
||||||
toposort(&m_sources[id]);
|
|
||||||
}
|
|
||||||
sourceOrder.push_back(_source);
|
|
||||||
};
|
|
||||||
|
|
||||||
for (auto const& sourcePair: m_sources)
|
|
||||||
toposort(&sourcePair.second);
|
|
||||||
|
|
||||||
swap(m_sourceOrder, sourceOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string CompilerStack::defaultContractName() const
|
|
||||||
{
|
|
||||||
return getContract("").contract->getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
CompilerStack::Contract const& CompilerStack::getContract(string const& _contractName) const
|
|
||||||
{
|
|
||||||
if (m_contracts.empty())
|
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No compiled contracts found."));
|
|
||||||
string contractName = _contractName;
|
|
||||||
if (_contractName.empty())
|
|
||||||
// try to find some user-supplied contract
|
|
||||||
for (auto const& it: m_sources)
|
|
||||||
if (!StandardSources.count(it.first))
|
|
||||||
for (ASTPointer<ASTNode> const& node: it.second.ast->getNodes())
|
|
||||||
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
|
|
||||||
contractName = contract->getName();
|
|
||||||
auto it = m_contracts.find(contractName);
|
|
||||||
if (it == m_contracts.end())
|
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Contract " + _contractName + " not found."));
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
CompilerStack::Source const& CompilerStack::getSource(string const& _sourceName) const
|
|
||||||
{
|
|
||||||
auto it = m_sources.find(_sourceName);
|
|
||||||
if (it == m_sources.end())
|
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Given source file not found."));
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
CompilerStack::Contract::Contract(): interfaceHandler(make_shared<InterfaceHandler>()) {}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
165
CompilerStack.h
165
CompilerStack.h
@ -1,165 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @author Gav Wood <g@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Full-stack compiler that converts a source code string to bytecode.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <ostream>
|
|
||||||
#include <string>
|
|
||||||
#include <memory>
|
|
||||||
#include <boost/noncopyable.hpp>
|
|
||||||
#include <libdevcore/Common.h>
|
|
||||||
#include <libdevcore/FixedHash.h>
|
|
||||||
|
|
||||||
namespace dev {
|
|
||||||
namespace solidity {
|
|
||||||
|
|
||||||
// forward declarations
|
|
||||||
class Scanner;
|
|
||||||
class ContractDefinition;
|
|
||||||
class SourceUnit;
|
|
||||||
class Compiler;
|
|
||||||
class GlobalContext;
|
|
||||||
class InterfaceHandler;
|
|
||||||
|
|
||||||
enum class DocumentationType: uint8_t
|
|
||||||
{
|
|
||||||
NatspecUser = 1,
|
|
||||||
NatspecDev,
|
|
||||||
ABIInterface,
|
|
||||||
ABISolidityInterface
|
|
||||||
};
|
|
||||||
|
|
||||||
extern const std::map<std::string, std::string> StandardSources;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Easy to use and self-contained Solidity compiler with as few header dependencies as possible.
|
|
||||||
* It holds state and can be used to either step through the compilation stages (and abort e.g.
|
|
||||||
* before compilation to bytecode) or run the whole compilation in one call.
|
|
||||||
*/
|
|
||||||
class CompilerStack: boost::noncopyable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/// Creates a new compiler stack. Adds standard sources if @a _addStandardSources.
|
|
||||||
explicit CompilerStack(bool _addStandardSources = false);
|
|
||||||
|
|
||||||
/// Adds a source object (e.g. file) to the parser. After this, parse has to be called again.
|
|
||||||
/// @returns true if a source object by the name already existed and was replaced.
|
|
||||||
void addSources(std::map<std::string, std::string> const& _nameContents) { for (auto const& i: _nameContents) addSource(i.first, i.second); }
|
|
||||||
bool addSource(std::string const& _name, std::string const& _content);
|
|
||||||
void setSource(std::string const& _sourceCode);
|
|
||||||
/// Parses all source units that were added
|
|
||||||
void parse();
|
|
||||||
/// Sets the given source code as the only source unit apart from standard sources and parses it.
|
|
||||||
void parse(std::string const& _sourceCode);
|
|
||||||
/// Returns a list of the contract names in the sources.
|
|
||||||
std::vector<std::string> getContractNames() const;
|
|
||||||
std::string defaultContractName() const;
|
|
||||||
|
|
||||||
/// Compiles the source units that were previously added and parsed.
|
|
||||||
void compile(bool _optimize = false);
|
|
||||||
/// Parses and compiles the given source code.
|
|
||||||
/// @returns the compiled bytecode
|
|
||||||
bytes const& compile(std::string const& _sourceCode, bool _optimize = false);
|
|
||||||
|
|
||||||
/// @returns the assembled bytecode for a contract.
|
|
||||||
bytes const& getBytecode(std::string const& _contractName = "") const;
|
|
||||||
/// @returns the runtime bytecode for the contract, i.e. the code that is returned by the constructor.
|
|
||||||
bytes const& getRuntimeBytecode(std::string const& _contractName = "") const;
|
|
||||||
/// @returns hash of the runtime bytecode for the contract, i.e. the code that is returned by the constructor.
|
|
||||||
dev::h256 getContractCodeHash(std::string const& _contractName = "") const;
|
|
||||||
|
|
||||||
/// Streams a verbose version of the assembly to @a _outStream.
|
|
||||||
/// Prerequisite: Successful compilation.
|
|
||||||
void streamAssembly(std::ostream& _outStream, std::string const& _contractName = "") const;
|
|
||||||
|
|
||||||
/// Returns a string representing the contract interface in JSON.
|
|
||||||
/// Prerequisite: Successful call to parse or compile.
|
|
||||||
std::string const& getInterface(std::string const& _contractName = "") const;
|
|
||||||
/// Returns a string representing the contract interface in Solidity.
|
|
||||||
/// Prerequisite: Successful call to parse or compile.
|
|
||||||
std::string const& getSolidityInterface(std::string const& _contractName = "") const;
|
|
||||||
/// Returns a string representing the contract's documentation in JSON.
|
|
||||||
/// Prerequisite: Successful call to parse or compile.
|
|
||||||
/// @param type The type of the documentation to get.
|
|
||||||
/// Can be one of 4 types defined at @c DocumentationType
|
|
||||||
std::string const& getMetadata(std::string const& _contractName, DocumentationType _type) const;
|
|
||||||
|
|
||||||
/// @returns the previously used scanner, useful for counting lines during error reporting.
|
|
||||||
Scanner const& getScanner(std::string const& _sourceName = "") const;
|
|
||||||
/// @returns the parsed source unit with the supplied name.
|
|
||||||
SourceUnit const& getAST(std::string const& _sourceName = "") const;
|
|
||||||
/// @returns the parsed contract with the supplied name. Throws an exception if the contract
|
|
||||||
/// does not exist.
|
|
||||||
ContractDefinition const& getContractDefinition(std::string const& _contractName) const;
|
|
||||||
|
|
||||||
/// Compile the given @a _sourceCode to bytecode. If a scanner is provided, it is used for
|
|
||||||
/// scanning the source code - this is useful for printing exception information.
|
|
||||||
static bytes staticCompile(std::string const& _sourceCode, bool _optimize = false);
|
|
||||||
|
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* Information pertaining to one source unit, filled gradually during parsing and compilation.
|
|
||||||
*/
|
|
||||||
struct Source
|
|
||||||
{
|
|
||||||
std::shared_ptr<Scanner> scanner;
|
|
||||||
std::shared_ptr<SourceUnit> ast;
|
|
||||||
std::string interface;
|
|
||||||
void reset() { scanner.reset(); ast.reset(); interface.clear(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Contract
|
|
||||||
{
|
|
||||||
ContractDefinition const* contract = nullptr;
|
|
||||||
std::shared_ptr<Compiler> compiler;
|
|
||||||
bytes bytecode;
|
|
||||||
bytes runtimeBytecode;
|
|
||||||
std::shared_ptr<InterfaceHandler> interfaceHandler;
|
|
||||||
mutable std::unique_ptr<std::string const> interface;
|
|
||||||
mutable std::unique_ptr<std::string const> solidityInterface;
|
|
||||||
mutable std::unique_ptr<std::string const> userDocumentation;
|
|
||||||
mutable std::unique_ptr<std::string const> devDocumentation;
|
|
||||||
|
|
||||||
Contract();
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Expand source code with preprocessor-like includes.
|
|
||||||
/// @todo Replace with better framework.
|
|
||||||
std::string expanded(std::string const& _sourceCode);
|
|
||||||
|
|
||||||
void reset(bool _keepSources = false);
|
|
||||||
void resolveImports();
|
|
||||||
|
|
||||||
Contract const& getContract(std::string const& _contractName = "") const;
|
|
||||||
Source const& getSource(std::string const& _sourceName = "") const;
|
|
||||||
|
|
||||||
bool m_addStandardSources; ///< If true, standard sources are added.
|
|
||||||
bool m_parseSuccessful;
|
|
||||||
std::map<std::string const, Source> m_sources;
|
|
||||||
std::shared_ptr<GlobalContext> m_globalContext;
|
|
||||||
std::vector<Source const*> m_sourceOrder;
|
|
||||||
std::map<std::string const, Contract> m_contracts;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,376 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Routines used by both the compiler and the expression compiler.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <libsolidity/CompilerUtils.h>
|
|
||||||
#include <libsolidity/AST.h>
|
|
||||||
#include <libevmcore/Instruction.h>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
const unsigned int CompilerUtils::dataStartOffset = 4;
|
|
||||||
|
|
||||||
unsigned CompilerUtils::loadFromMemory(unsigned _offset, Type const& _type,
|
|
||||||
bool _fromCalldata, bool _padToWordBoundaries)
|
|
||||||
{
|
|
||||||
solAssert(_type.getCategory() != Type::Category::ByteArray, "Unable to statically load dynamic type.");
|
|
||||||
m_context << u256(_offset);
|
|
||||||
return loadFromMemoryHelper(_type, _fromCalldata, _padToWordBoundaries);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompilerUtils::loadFromMemoryDynamic(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries)
|
|
||||||
{
|
|
||||||
solAssert(_type.getCategory() != Type::Category::ByteArray, "Byte arrays not yet implemented.");
|
|
||||||
m_context << eth::Instruction::DUP1;
|
|
||||||
unsigned numBytes = loadFromMemoryHelper(_type, _fromCalldata, _padToWordBoundaries);
|
|
||||||
// update memory counter
|
|
||||||
for (unsigned i = 0; i < _type.getSizeOnStack(); ++i)
|
|
||||||
m_context << eth::swapInstruction(1 + i);
|
|
||||||
m_context << u256(numBytes) << eth::Instruction::ADD;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
unsigned CompilerUtils::storeInMemory(unsigned _offset, Type const& _type, bool _padToWordBoundaries)
|
|
||||||
{
|
|
||||||
solAssert(_type.getCategory() != Type::Category::ByteArray, "Unable to statically store dynamic type.");
|
|
||||||
unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries);
|
|
||||||
if (numBytes > 0)
|
|
||||||
m_context << u256(_offset) << eth::Instruction::MSTORE;
|
|
||||||
return numBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries)
|
|
||||||
{
|
|
||||||
if (_type.getCategory() == Type::Category::ByteArray)
|
|
||||||
{
|
|
||||||
auto const& type = dynamic_cast<ByteArrayType const&>(_type);
|
|
||||||
|
|
||||||
if (type.getLocation() == ByteArrayType::Location::CallData)
|
|
||||||
{
|
|
||||||
// stack: target source_offset source_len
|
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::DUP5
|
|
||||||
// stack: target source_offset source_len source_len source_offset target
|
|
||||||
<< eth::Instruction::CALLDATACOPY
|
|
||||||
<< eth::Instruction::DUP3 << eth::Instruction::ADD
|
|
||||||
<< eth::Instruction::SWAP2 << eth::Instruction::POP << eth::Instruction::POP;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
solAssert(type.getLocation() == ByteArrayType::Location::Storage, "Memory byte arrays not yet implemented.");
|
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;
|
|
||||||
// stack here: memory_offset storage_offset length_bytes
|
|
||||||
// jump to end if length is zero
|
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::ISZERO;
|
|
||||||
eth::AssemblyItem loopEnd = m_context.newTag();
|
|
||||||
m_context.appendConditionalJumpTo(loopEnd);
|
|
||||||
// compute memory end offset
|
|
||||||
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD << eth::Instruction::SWAP2;
|
|
||||||
// actual array data is stored at SHA3(storage_offset)
|
|
||||||
m_context << eth::Instruction::SWAP1;
|
|
||||||
CompilerUtils(m_context).computeHashStatic();
|
|
||||||
m_context << eth::Instruction::SWAP1;
|
|
||||||
|
|
||||||
// stack here: memory_end_offset storage_data_offset memory_offset
|
|
||||||
eth::AssemblyItem loopStart = m_context.newTag();
|
|
||||||
m_context << loopStart
|
|
||||||
// load and store
|
|
||||||
<< eth::Instruction::DUP2 << eth::Instruction::SLOAD
|
|
||||||
<< eth::Instruction::DUP2 << eth::Instruction::MSTORE
|
|
||||||
// increment storage_data_offset by 1
|
|
||||||
<< eth::Instruction::SWAP1 << u256(1) << eth::Instruction::ADD
|
|
||||||
// increment memory offset by 32
|
|
||||||
<< eth::Instruction::SWAP1 << u256(32) << eth::Instruction::ADD
|
|
||||||
// check for loop condition
|
|
||||||
<< eth::Instruction::DUP1 << eth::Instruction::DUP4 << eth::Instruction::GT;
|
|
||||||
m_context.appendConditionalJumpTo(loopStart);
|
|
||||||
m_context << loopEnd << eth::Instruction::POP << eth::Instruction::POP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries);
|
|
||||||
if (numBytes > 0)
|
|
||||||
{
|
|
||||||
solAssert(_type.getSizeOnStack() == 1, "Memory store of types with stack size != 1 not implemented.");
|
|
||||||
m_context << eth::Instruction::DUP2 << eth::Instruction::MSTORE;
|
|
||||||
m_context << u256(numBytes) << eth::Instruction::ADD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
|
|
||||||
{
|
|
||||||
unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(_variable));
|
|
||||||
unsigned const size = _variable.getType()->getSizeOnStack();
|
|
||||||
// move variable starting from its top end in the stack
|
|
||||||
if (stackPosition - size + 1 > 16)
|
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_variable.getLocation())
|
|
||||||
<< errinfo_comment("Stack too deep."));
|
|
||||||
for (unsigned i = 0; i < size; ++i)
|
|
||||||
m_context << eth::swapInstruction(stackPosition - size + 1) << eth::Instruction::POP;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompilerUtils::copyToStackTop(unsigned _stackDepth, Type const& _type)
|
|
||||||
{
|
|
||||||
if (_stackDepth > 16)
|
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Stack too deep."));
|
|
||||||
unsigned const size = _type.getSizeOnStack();
|
|
||||||
for (unsigned i = 0; i < size; ++i)
|
|
||||||
m_context << eth::dupInstruction(_stackDepth);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompilerUtils::popStackElement(Type const& _type)
|
|
||||||
{
|
|
||||||
unsigned const size = _type.getSizeOnStack();
|
|
||||||
for (unsigned i = 0; i < size; ++i)
|
|
||||||
m_context << eth::Instruction::POP;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned CompilerUtils::getSizeOnStack(vector<shared_ptr<Type const>> const& _variableTypes)
|
|
||||||
{
|
|
||||||
unsigned size = 0;
|
|
||||||
for (shared_ptr<Type const> const& type: _variableTypes)
|
|
||||||
size += type->getSizeOnStack();
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompilerUtils::computeHashStatic(Type const& _type, bool _padToWordBoundaries)
|
|
||||||
{
|
|
||||||
unsigned length = storeInMemory(0, _type, _padToWordBoundaries);
|
|
||||||
m_context << u256(length) << u256(0) << eth::Instruction::SHA3;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompilerUtils::copyByteArrayToStorage(ByteArrayType const& _targetType,
|
|
||||||
ByteArrayType const& _sourceType) const
|
|
||||||
{
|
|
||||||
// stack layout: [source_ref] target_ref (top)
|
|
||||||
// need to leave target_ref on the stack at the end
|
|
||||||
solAssert(_targetType.getLocation() == ByteArrayType::Location::Storage, "");
|
|
||||||
|
|
||||||
switch (_sourceType.getLocation())
|
|
||||||
{
|
|
||||||
case ByteArrayType::Location::CallData:
|
|
||||||
{
|
|
||||||
// This also assumes that after "length" we only have zeros, i.e. it cannot be used to
|
|
||||||
// slice a byte array from calldata.
|
|
||||||
|
|
||||||
// stack: source_offset source_len target_ref
|
|
||||||
// fetch old length and convert to words
|
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;
|
|
||||||
m_context << u256(31) << eth::Instruction::ADD
|
|
||||||
<< u256(32) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
|
|
||||||
// stack here: source_offset source_len target_ref target_length_words
|
|
||||||
// actual array data is stored at SHA3(storage_offset)
|
|
||||||
m_context << eth::Instruction::DUP2;
|
|
||||||
CompilerUtils(m_context).computeHashStatic();
|
|
||||||
// compute target_data_end
|
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::SWAP2 << eth::Instruction::ADD
|
|
||||||
<< eth::Instruction::SWAP1;
|
|
||||||
// stack here: source_offset source_len target_ref target_data_end target_data_ref
|
|
||||||
// store length (in bytes)
|
|
||||||
m_context << eth::Instruction::DUP4 << eth::Instruction::DUP1 << eth::Instruction::DUP5
|
|
||||||
<< eth::Instruction::SSTORE;
|
|
||||||
// jump to end if length is zero
|
|
||||||
m_context << eth::Instruction::ISZERO;
|
|
||||||
eth::AssemblyItem copyLoopEnd = m_context.newTag();
|
|
||||||
m_context.appendConditionalJumpTo(copyLoopEnd);
|
|
||||||
// store start offset
|
|
||||||
m_context << eth::Instruction::DUP5;
|
|
||||||
// stack now: source_offset source_len target_ref target_data_end target_data_ref calldata_offset
|
|
||||||
eth::AssemblyItem copyLoopStart = m_context.newTag();
|
|
||||||
m_context << copyLoopStart
|
|
||||||
// copy from calldata and store
|
|
||||||
<< eth::Instruction::DUP1 << eth::Instruction::CALLDATALOAD
|
|
||||||
<< eth::Instruction::DUP3 << eth::Instruction::SSTORE
|
|
||||||
// increment target_data_ref by 1
|
|
||||||
<< eth::Instruction::SWAP1 << u256(1) << eth::Instruction::ADD
|
|
||||||
// increment calldata_offset by 32
|
|
||||||
<< eth::Instruction::SWAP1 << u256(32) << eth::Instruction::ADD
|
|
||||||
// check for loop condition
|
|
||||||
<< eth::Instruction::DUP1 << eth::Instruction::DUP6 << eth::Instruction::GT;
|
|
||||||
m_context.appendConditionalJumpTo(copyLoopStart);
|
|
||||||
m_context << eth::Instruction::POP;
|
|
||||||
m_context << copyLoopEnd;
|
|
||||||
|
|
||||||
// now clear leftover bytes of the old value
|
|
||||||
// stack now: source_offset source_len target_ref target_data_end target_data_ref
|
|
||||||
clearStorageLoop();
|
|
||||||
// stack now: source_offset source_len target_ref target_data_end
|
|
||||||
|
|
||||||
m_context << eth::Instruction::POP << eth::Instruction::SWAP2
|
|
||||||
<< eth::Instruction::POP << eth::Instruction::POP;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ByteArrayType::Location::Storage:
|
|
||||||
{
|
|
||||||
// this copies source to target and also clears target if it was larger
|
|
||||||
|
|
||||||
// stack: source_ref target_ref
|
|
||||||
// store target_ref
|
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2;
|
|
||||||
// fetch lengthes
|
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP2
|
|
||||||
<< eth::Instruction::DUP1 << eth::Instruction::SLOAD;
|
|
||||||
// stack: target_ref target_len_bytes target_ref source_ref source_len_bytes
|
|
||||||
// store new target length
|
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP4 << eth::Instruction::SSTORE;
|
|
||||||
// compute hashes (data positions)
|
|
||||||
m_context << eth::Instruction::SWAP2;
|
|
||||||
CompilerUtils(m_context).computeHashStatic();
|
|
||||||
m_context << eth::Instruction::SWAP1;
|
|
||||||
CompilerUtils(m_context).computeHashStatic();
|
|
||||||
// stack: target_ref target_len_bytes source_len_bytes target_data_pos source_data_pos
|
|
||||||
// convert lengthes from bytes to storage slots
|
|
||||||
m_context << u256(31) << u256(32) << eth::Instruction::DUP1 << eth::Instruction::DUP3
|
|
||||||
<< eth::Instruction::DUP8 << eth::Instruction::ADD << eth::Instruction::DIV
|
|
||||||
<< eth::Instruction::SWAP2
|
|
||||||
<< eth::Instruction::DUP6 << eth::Instruction::ADD << eth::Instruction::DIV;
|
|
||||||
// stack: target_ref target_len_bytes source_len_bytes target_data_pos source_data_pos target_len source_len
|
|
||||||
// @todo we might be able to go without a third counter
|
|
||||||
m_context << u256(0);
|
|
||||||
// stack: target_ref target_len_bytes source_len_bytes target_data_pos source_data_pos target_len source_len counter
|
|
||||||
eth::AssemblyItem copyLoopStart = m_context.newTag();
|
|
||||||
m_context << copyLoopStart;
|
|
||||||
// check for loop condition
|
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3
|
|
||||||
<< eth::Instruction::GT << eth::Instruction::ISZERO;
|
|
||||||
eth::AssemblyItem copyLoopEnd = m_context.newTag();
|
|
||||||
m_context.appendConditionalJumpTo(copyLoopEnd);
|
|
||||||
// copy
|
|
||||||
m_context << eth::Instruction::DUP4 << eth::Instruction::DUP2 << eth::Instruction::ADD
|
|
||||||
<< eth::Instruction::SLOAD
|
|
||||||
<< eth::Instruction::DUP6 << eth::Instruction::DUP3 << eth::Instruction::ADD
|
|
||||||
<< eth::Instruction::SSTORE;
|
|
||||||
// increment
|
|
||||||
m_context << u256(1) << eth::Instruction::ADD;
|
|
||||||
m_context.appendJumpTo(copyLoopStart);
|
|
||||||
m_context << copyLoopEnd;
|
|
||||||
|
|
||||||
// zero-out leftovers in target
|
|
||||||
// stack: target_ref target_len_bytes source_len_bytes target_data_pos source_data_pos target_len source_len counter
|
|
||||||
// add counter to target_data_pos
|
|
||||||
m_context << eth::Instruction::DUP5 << eth::Instruction::ADD
|
|
||||||
<< eth::Instruction::SWAP5 << eth::Instruction::POP;
|
|
||||||
// stack: target_ref target_len_bytes target_data_pos_updated target_data_pos source_data_pos target_len source_len
|
|
||||||
// add length to target_data_pos to get target_data_end
|
|
||||||
m_context << eth::Instruction::POP << eth::Instruction::DUP3 << eth::Instruction::ADD
|
|
||||||
<< eth::Instruction::SWAP4
|
|
||||||
<< eth::Instruction::POP << eth::Instruction::POP << eth::Instruction::POP;
|
|
||||||
// stack: target_ref target_data_end target_data_pos_updated
|
|
||||||
clearStorageLoop();
|
|
||||||
m_context << eth::Instruction::POP;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
solAssert(false, "Given byte array location not implemented.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries)
|
|
||||||
{
|
|
||||||
unsigned _encodedSize = _type.getCalldataEncodedSize();
|
|
||||||
unsigned numBytes = _padToWordBoundaries ? getPaddedSize(_encodedSize) : _encodedSize;
|
|
||||||
bool leftAligned = _type.getCategory() == Type::Category::String;
|
|
||||||
if (numBytes == 0)
|
|
||||||
m_context << eth::Instruction::POP << u256(0);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
solAssert(numBytes <= 32, "Static memory load of more than 32 bytes requested.");
|
|
||||||
m_context << (_fromCalldata ? eth::Instruction::CALLDATALOAD : eth::Instruction::MLOAD);
|
|
||||||
if (numBytes != 32)
|
|
||||||
{
|
|
||||||
// add leading or trailing zeros by dividing/multiplying depending on alignment
|
|
||||||
u256 shiftFactor = u256(1) << ((32 - numBytes) * 8);
|
|
||||||
m_context << shiftFactor << eth::Instruction::SWAP1 << eth::Instruction::DIV;
|
|
||||||
if (leftAligned)
|
|
||||||
m_context << shiftFactor << eth::Instruction::MUL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return numBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompilerUtils::clearByteArray(ByteArrayType const& _type) const
|
|
||||||
{
|
|
||||||
solAssert(_type.getLocation() == ByteArrayType::Location::Storage, "");
|
|
||||||
|
|
||||||
// fetch length
|
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;
|
|
||||||
// set length to zero
|
|
||||||
m_context << u256(0) << eth::Instruction::DUP3 << eth::Instruction::SSTORE;
|
|
||||||
// convert length from bytes to storage slots
|
|
||||||
m_context << u256(31) << eth::Instruction::ADD
|
|
||||||
<< u256(32) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
|
|
||||||
// compute data positions
|
|
||||||
m_context << eth::Instruction::SWAP1;
|
|
||||||
CompilerUtils(m_context).computeHashStatic();
|
|
||||||
// stack: len data_pos
|
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2 << eth::Instruction::ADD
|
|
||||||
<< eth::Instruction::SWAP1;
|
|
||||||
clearStorageLoop();
|
|
||||||
// cleanup
|
|
||||||
m_context << eth::Instruction::POP;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const
|
|
||||||
{
|
|
||||||
unsigned _encodedSize = _type.getCalldataEncodedSize();
|
|
||||||
unsigned numBytes = _padToWordBoundaries ? getPaddedSize(_encodedSize) : _encodedSize;
|
|
||||||
bool leftAligned = _type.getCategory() == Type::Category::String;
|
|
||||||
if (numBytes == 0)
|
|
||||||
m_context << eth::Instruction::POP;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
solAssert(numBytes <= 32, "Memory store of more than 32 bytes requested.");
|
|
||||||
if (numBytes != 32 && !leftAligned && !_padToWordBoundaries)
|
|
||||||
// shift the value accordingly before storing
|
|
||||||
m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
|
|
||||||
}
|
|
||||||
return numBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompilerUtils::clearStorageLoop() const
|
|
||||||
{
|
|
||||||
// stack: end_pos pos
|
|
||||||
eth::AssemblyItem loopStart = m_context.newTag();
|
|
||||||
m_context << loopStart;
|
|
||||||
// check for loop condition
|
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3
|
|
||||||
<< eth::Instruction::GT << eth::Instruction::ISZERO;
|
|
||||||
eth::AssemblyItem zeroLoopEnd = m_context.newTag();
|
|
||||||
m_context.appendConditionalJumpTo(zeroLoopEnd);
|
|
||||||
// zero out
|
|
||||||
m_context << u256(0) << eth::Instruction::DUP2 << eth::Instruction::SSTORE;
|
|
||||||
// increment
|
|
||||||
m_context << u256(1) << eth::Instruction::ADD;
|
|
||||||
m_context.appendJumpTo(loopStart);
|
|
||||||
// cleanup
|
|
||||||
m_context << zeroLoopEnd;
|
|
||||||
m_context << eth::Instruction::POP;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
119
CompilerUtils.h
119
CompilerUtils.h
@ -1,119 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Routines used by both the compiler and the expression compiler.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <libsolidity/CompilerContext.h>
|
|
||||||
#include <libsolidity/ASTForward.h>
|
|
||||||
|
|
||||||
namespace dev {
|
|
||||||
namespace solidity {
|
|
||||||
|
|
||||||
class Type; // forward
|
|
||||||
|
|
||||||
class CompilerUtils
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CompilerUtils(CompilerContext& _context): m_context(_context) {}
|
|
||||||
|
|
||||||
/// Loads data from memory to the stack.
|
|
||||||
/// @param _offset offset in memory (or calldata)
|
|
||||||
/// @param _type data type to load
|
|
||||||
/// @param _fromCalldata if true, load from calldata, not from memory
|
|
||||||
/// @param _padToWordBoundaries if true, assume the data is padded to word (32 byte) boundaries
|
|
||||||
/// @returns the number of bytes consumed in memory.
|
|
||||||
unsigned loadFromMemory(unsigned _offset, Type const& _type = IntegerType(256),
|
|
||||||
bool _fromCalldata = false, bool _padToWordBoundaries = false);
|
|
||||||
/// Dynamic version of @see loadFromMemory, expects the memory offset on the stack.
|
|
||||||
/// Stack pre: memory_offset
|
|
||||||
/// Stack post: value... (memory_offset+length)
|
|
||||||
void loadFromMemoryDynamic(Type const& _type, bool _fromCalldata = false, bool _padToWordBoundaries = true);
|
|
||||||
/// Stores data from stack in memory.
|
|
||||||
/// @param _offset offset in memory
|
|
||||||
/// @param _type type of the data on the stack
|
|
||||||
/// @param _padToWordBoundaries if true, pad the data to word (32 byte) boundaries
|
|
||||||
/// @returns the number of bytes written to memory (can be different from _bytes if
|
|
||||||
/// _padToWordBoundaries is true)
|
|
||||||
unsigned storeInMemory(unsigned _offset, Type const& _type = IntegerType(256), bool _padToWordBoundaries = false);
|
|
||||||
/// Dynamic version of @see storeInMemory, expects the memory offset below the value on the stack
|
|
||||||
/// and also updates that.
|
|
||||||
/// Stack pre: memory_offset value...
|
|
||||||
/// Stack post: (memory_offset+length)
|
|
||||||
void storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries = true);
|
|
||||||
/// @returns _size rounded up to the next multiple of 32 (the number of bytes occupied in the
|
|
||||||
/// padded calldata)
|
|
||||||
static unsigned getPaddedSize(unsigned _size) { return ((_size + 31) / 32) * 32; }
|
|
||||||
|
|
||||||
/// Moves the value that is at the top of the stack to a stack variable.
|
|
||||||
void moveToStackVariable(VariableDeclaration const& _variable);
|
|
||||||
/// Copies a variable of type @a _type from a stack depth of @a _stackDepth to the top of the stack.
|
|
||||||
void copyToStackTop(unsigned _stackDepth, Type const& _type);
|
|
||||||
/// Removes the current value from the top of the stack.
|
|
||||||
void popStackElement(Type const& _type);
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
static unsigned getSizeOnStack(std::vector<T> const& _variables);
|
|
||||||
static unsigned getSizeOnStack(std::vector<std::shared_ptr<Type const>> const& _variableTypes);
|
|
||||||
|
|
||||||
/// Appends code that computes tha SHA3 hash of the topmost stack element of type @a _type.
|
|
||||||
/// If @a _pad is set, padds the type to muliples of 32 bytes.
|
|
||||||
/// @note Only works for types of fixed size.
|
|
||||||
void computeHashStatic(Type const& _type = IntegerType(256), bool _padToWordBoundaries = false);
|
|
||||||
|
|
||||||
/// Copies a byte array to a byte array in storage.
|
|
||||||
/// Stack pre: [source_reference] target_reference
|
|
||||||
/// Stack post: target_reference
|
|
||||||
void copyByteArrayToStorage(ByteArrayType const& _targetType, ByteArrayType const& _sourceType) const;
|
|
||||||
/// Clears the length and data elements of the byte array referenced on the stack.
|
|
||||||
/// Stack pre: reference
|
|
||||||
/// Stack post:
|
|
||||||
void clearByteArray(ByteArrayType const& _type) const;
|
|
||||||
|
|
||||||
/// Bytes we need to the start of call data.
|
|
||||||
/// - The size in bytes of the function (hash) identifier.
|
|
||||||
static const unsigned int dataStartOffset;
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// Prepares the given type for storing in memory by shifting it if necessary.
|
|
||||||
unsigned prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const;
|
|
||||||
/// Loads type from memory assuming memory offset is on stack top.
|
|
||||||
unsigned loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries);
|
|
||||||
/// Appends a loop that clears a sequence of storage slots (excluding end).
|
|
||||||
/// Stack pre: end_ref start_ref
|
|
||||||
/// Stack post: end_ref
|
|
||||||
void clearStorageLoop() const;
|
|
||||||
|
|
||||||
CompilerContext& m_context;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
unsigned CompilerUtils::getSizeOnStack(std::vector<T> const& _variables)
|
|
||||||
{
|
|
||||||
unsigned size = 0;
|
|
||||||
for (T const& variable: _variables)
|
|
||||||
size += variable->getType()->getSizeOnStack();
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Scope - object that holds declaration of names.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <libsolidity/DeclarationContainer.h>
|
|
||||||
#include <libsolidity/AST.h>
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
bool DeclarationContainer::registerDeclaration(Declaration const& _declaration, bool _invisible, bool _update)
|
|
||||||
{
|
|
||||||
ASTString const& name(_declaration.getName());
|
|
||||||
if (name.empty())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (!_update && (m_declarations.count(name) || m_invisibleDeclarations.count(name)))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (_invisible)
|
|
||||||
m_invisibleDeclarations.insert(name);
|
|
||||||
else
|
|
||||||
m_declarations[name] = &_declaration;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Declaration const* DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const
|
|
||||||
{
|
|
||||||
solAssert(!_name.empty(), "Attempt to resolve empty name.");
|
|
||||||
auto result = m_declarations.find(_name);
|
|
||||||
if (result != m_declarations.end())
|
|
||||||
return result->second;
|
|
||||||
if (_recursive && m_enclosingContainer)
|
|
||||||
return m_enclosingContainer->resolveName(_name, true);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Scope - object that holds declaration of names.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <set>
|
|
||||||
#include <boost/noncopyable.hpp>
|
|
||||||
|
|
||||||
#include <libsolidity/ASTForward.h>
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Container that stores mappings betwee names and declarations. It also contains a link to the
|
|
||||||
* enclosing scope.
|
|
||||||
*/
|
|
||||||
class DeclarationContainer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit DeclarationContainer(Declaration const* _enclosingDeclaration = nullptr,
|
|
||||||
DeclarationContainer const* _enclosingContainer = nullptr):
|
|
||||||
m_enclosingDeclaration(_enclosingDeclaration), m_enclosingContainer(_enclosingContainer) {}
|
|
||||||
/// Registers the declaration in the scope unless its name is already declared or the name is empty.
|
|
||||||
/// @param _invisible if true, registers the declaration, reports name clashes but does not return it in @a resolveName
|
|
||||||
/// @param _update if true, replaces a potential declaration that is already present
|
|
||||||
/// @returns false if the name was already declared.
|
|
||||||
bool registerDeclaration(Declaration const& _declaration, bool _invisible = false, bool _update = false);
|
|
||||||
Declaration const* resolveName(ASTString const& _name, bool _recursive = false) const;
|
|
||||||
Declaration const* getEnclosingDeclaration() const { return m_enclosingDeclaration; }
|
|
||||||
std::map<ASTString, Declaration const*> const& getDeclarations() const { return m_declarations; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
Declaration const* m_enclosingDeclaration;
|
|
||||||
DeclarationContainer const* m_enclosingContainer;
|
|
||||||
std::map<ASTString, Declaration const*> m_declarations;
|
|
||||||
std::set<ASTString> m_invisibleDeclarations;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
44
Exceptions.h
44
Exceptions.h
@ -1,44 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Solidity exception hierarchy.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <libdevcore/Exceptions.h>
|
|
||||||
#include <libsolidity/BaseTypes.h>
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
struct ParserError: virtual Exception {};
|
|
||||||
struct TypeError: virtual Exception {};
|
|
||||||
struct DeclarationError: virtual Exception {};
|
|
||||||
struct CompilerError: virtual Exception {};
|
|
||||||
struct InternalCompilerError: virtual Exception {};
|
|
||||||
struct DocstringParsingError: virtual Exception {};
|
|
||||||
|
|
||||||
using errinfo_sourceLocation = boost::error_info<struct tag_sourceLocation, Location>;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,180 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @author Gav Wood <g@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Solidity AST to EVM bytecode compiler for expressions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#include <boost/noncopyable.hpp>
|
|
||||||
#include <libdevcore/Common.h>
|
|
||||||
#include <libsolidity/BaseTypes.h>
|
|
||||||
#include <libsolidity/ASTVisitor.h>
|
|
||||||
|
|
||||||
namespace dev {
|
|
||||||
namespace eth
|
|
||||||
{
|
|
||||||
class AssemblyItem; // forward
|
|
||||||
}
|
|
||||||
namespace solidity {
|
|
||||||
|
|
||||||
// forward declarations
|
|
||||||
class CompilerContext;
|
|
||||||
class Type;
|
|
||||||
class IntegerType;
|
|
||||||
class ByteArrayType;
|
|
||||||
class StaticStringType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream
|
|
||||||
* of EVM instructions. It needs a compiler context that is the same for the whole compilation
|
|
||||||
* unit.
|
|
||||||
*/
|
|
||||||
class ExpressionCompiler: private ASTConstVisitor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/// Compile the given @a _expression into the @a _context.
|
|
||||||
static void compileExpression(CompilerContext& _context, Expression const& _expression, bool _optimize = false);
|
|
||||||
|
|
||||||
/// Appends code to remove dirty higher order bits in case of an implicit promotion to a wider type.
|
|
||||||
static void appendTypeConversion(CompilerContext& _context, Type const& _typeOnStack,
|
|
||||||
Type const& _targetType, bool _cleanupNeeded = false);
|
|
||||||
/// Appends code for a State Variable accessor function
|
|
||||||
static void appendStateVariableAccessor(CompilerContext& _context, VariableDeclaration const& _varDecl, bool _optimize = false);
|
|
||||||
|
|
||||||
private:
|
|
||||||
explicit ExpressionCompiler(CompilerContext& _compilerContext, bool _optimize = false):
|
|
||||||
m_optimize(_optimize), m_context(_compilerContext), m_currentLValue(m_context) {}
|
|
||||||
|
|
||||||
virtual bool visit(Assignment const& _assignment) override;
|
|
||||||
virtual bool visit(UnaryOperation const& _unaryOperation) override;
|
|
||||||
virtual bool visit(BinaryOperation const& _binaryOperation) override;
|
|
||||||
virtual bool visit(FunctionCall const& _functionCall) override;
|
|
||||||
virtual bool visit(NewExpression const& _newExpression) override;
|
|
||||||
virtual void endVisit(MemberAccess const& _memberAccess) override;
|
|
||||||
virtual bool visit(IndexAccess const& _indexAccess) override;
|
|
||||||
virtual void endVisit(Identifier const& _identifier) override;
|
|
||||||
virtual void endVisit(Literal const& _literal) override;
|
|
||||||
|
|
||||||
///@{
|
|
||||||
///@name Append code for various operator types
|
|
||||||
void appendAndOrOperatorCode(BinaryOperation const& _binaryOperation);
|
|
||||||
void appendCompareOperatorCode(Token::Value _operator, Type const& _type);
|
|
||||||
void appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type);
|
|
||||||
|
|
||||||
void appendArithmeticOperatorCode(Token::Value _operator, Type const& _type);
|
|
||||||
void appendBitOperatorCode(Token::Value _operator);
|
|
||||||
void appendShiftOperatorCode(Token::Value _operator);
|
|
||||||
/// @}
|
|
||||||
|
|
||||||
/// Appends an implicit or explicit type conversion. For now this comprises only erasing
|
|
||||||
/// higher-order bits (@see appendHighBitCleanup) when widening integer.
|
|
||||||
/// If @a _cleanupNeeded, high order bits cleanup is also done if no type conversion would be
|
|
||||||
/// necessary.
|
|
||||||
void appendTypeConversion(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded = false);
|
|
||||||
//// Appends code that cleans higher-order bits for integer types.
|
|
||||||
void appendHighBitsCleanup(IntegerType const& _typeOnStack);
|
|
||||||
|
|
||||||
/// Appends code to call a function of the given type with the given arguments.
|
|
||||||
void appendExternalFunctionCall(FunctionType const& _functionType, std::vector<ASTPointer<Expression const>> const& _arguments,
|
|
||||||
bool bare = false);
|
|
||||||
/// Appends code that evaluates the given arguments and moves the result to memory. The memory offset is
|
|
||||||
/// expected to be on the stack and is updated by this call.
|
|
||||||
void appendArgumentsCopyToMemory(std::vector<ASTPointer<Expression const>> const& _arguments,
|
|
||||||
TypePointers const& _types = {},
|
|
||||||
bool _padToWordBoundaries = true,
|
|
||||||
bool _padExceptionIfFourBytes = false);
|
|
||||||
/// Appends code that moves a stack element of the given type to memory. The memory offset is
|
|
||||||
/// expected below the stack element and is updated by this call.
|
|
||||||
void appendTypeMoveToMemory(Type const& _type, bool _padToWordBoundaries = true);
|
|
||||||
/// Appends code that evaluates a single expression and moves the result to memory. The memory offset is
|
|
||||||
/// expected to be on the stack and is updated by this call.
|
|
||||||
void appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression);
|
|
||||||
|
|
||||||
/// Appends code for a State Variable accessor function
|
|
||||||
void appendStateVariableAccessor(VariableDeclaration const& _varDecl);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper class to store and retrieve lvalues to and from various locations.
|
|
||||||
* All types except STACK store a reference in a slot on the stack, STACK just
|
|
||||||
* stores the base stack offset of the variable in @a m_baseStackOffset.
|
|
||||||
*/
|
|
||||||
class LValue
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum class LValueType { None, Stack, Memory, Storage };
|
|
||||||
|
|
||||||
explicit LValue(CompilerContext& _compilerContext): m_context(&_compilerContext) { reset(); }
|
|
||||||
LValue(CompilerContext& _compilerContext, LValueType _type,
|
|
||||||
std::shared_ptr<Type const> const& _dataType, unsigned _baseStackOffset = 0);
|
|
||||||
|
|
||||||
/// Set type according to the declaration and retrieve the reference.
|
|
||||||
/// @a _expression is the current expression
|
|
||||||
void fromIdentifier(Identifier const& _identifier, Declaration const& _declaration);
|
|
||||||
void reset() { m_type = LValueType::None; m_dataType.reset(); m_baseStackOffset = 0; m_size = 0; }
|
|
||||||
|
|
||||||
bool isValid() const { return m_type != LValueType::None; }
|
|
||||||
bool isInOnStack() const { return m_type == LValueType::Stack; }
|
|
||||||
bool isInMemory() const { return m_type == LValueType::Memory; }
|
|
||||||
bool isInStorage() const { return m_type == LValueType::Storage; }
|
|
||||||
|
|
||||||
/// @returns true if this lvalue reference type occupies a slot on the stack.
|
|
||||||
bool storesReferenceOnStack() const { return m_type == LValueType::Storage || m_type == LValueType::Memory; }
|
|
||||||
|
|
||||||
/// Copies the value of the current lvalue to the top of the stack and, if @a _remove is true,
|
|
||||||
/// also removes the reference from the stack (note that is does not reset the type to @a NONE).
|
|
||||||
/// @a _location source location of the current expression, used for error reporting.
|
|
||||||
void retrieveValue(Location const& _location, bool _remove = false) const;
|
|
||||||
/// Moves a value from the stack to the lvalue. Removes the value if @a _move is true.
|
|
||||||
/// @a _location is the source location of the expression that caused this operation.
|
|
||||||
/// Stack pre: value [lvalue_ref]
|
|
||||||
/// Stack post if !_move: value_of(lvalue_ref)
|
|
||||||
void storeValue(Type const& _sourceType, Location const& _location = Location(), bool _move = false) const;
|
|
||||||
/// Stores zero in the lvalue.
|
|
||||||
/// @a _location is the source location of the requested operation
|
|
||||||
void setToZero(Location const& _location = Location()) const;
|
|
||||||
/// Convenience function to convert the stored reference to a value and reset type to NONE if
|
|
||||||
/// the reference was not requested by @a _expression.
|
|
||||||
void retrieveValueIfLValueNotRequested(Expression const& _expression);
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// Convenience function to retrieve Value from Storage. Specific version of @ref retrieveValue
|
|
||||||
void retrieveValueFromStorage(bool _remove = false) const;
|
|
||||||
/// Copies from a byte array to a byte array in storage, both references on the stack.
|
|
||||||
void copyByteArrayToStorage(ByteArrayType const& _targetType, ByteArrayType const& _sourceType) const;
|
|
||||||
|
|
||||||
CompilerContext* m_context;
|
|
||||||
LValueType m_type = LValueType::None;
|
|
||||||
std::shared_ptr<Type const> m_dataType;
|
|
||||||
/// If m_type is STACK, this is base stack offset (@see
|
|
||||||
/// CompilerContext::getBaseStackOffsetOfVariable) of a local variable.
|
|
||||||
unsigned m_baseStackOffset = 0;
|
|
||||||
/// Size of the value of this lvalue on the stack or the storage.
|
|
||||||
unsigned m_size = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool m_optimize;
|
|
||||||
CompilerContext& m_context;
|
|
||||||
LValue m_currentLValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,95 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @author Gav Wood <g@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Container of the (implicit and explicit) global objects.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <libsolidity/GlobalContext.h>
|
|
||||||
#include <libsolidity/AST.h>
|
|
||||||
#include <libsolidity/Types.h>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
GlobalContext::GlobalContext():
|
|
||||||
m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{make_shared<MagicVariableDeclaration>("block", make_shared<MagicType>(MagicType::Kind::Block)),
|
|
||||||
make_shared<MagicVariableDeclaration>("msg", make_shared<MagicType>(MagicType::Kind::Message)),
|
|
||||||
make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::Transaction)),
|
|
||||||
make_shared<MagicVariableDeclaration>("suicide",
|
|
||||||
make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Location::Suicide)),
|
|
||||||
make_shared<MagicVariableDeclaration>("sha3",
|
|
||||||
make_shared<FunctionType>(strings(), strings{"hash"}, FunctionType::Location::SHA3, true)),
|
|
||||||
make_shared<MagicVariableDeclaration>("log0",
|
|
||||||
make_shared<FunctionType>(strings{"hash"},strings{}, FunctionType::Location::Log0)),
|
|
||||||
make_shared<MagicVariableDeclaration>("log1",
|
|
||||||
make_shared<FunctionType>(strings{"hash", "hash"},strings{}, FunctionType::Location::Log1)),
|
|
||||||
make_shared<MagicVariableDeclaration>("log2",
|
|
||||||
make_shared<FunctionType>(strings{"hash", "hash", "hash"},strings{}, FunctionType::Location::Log2)),
|
|
||||||
make_shared<MagicVariableDeclaration>("log3",
|
|
||||||
make_shared<FunctionType>(strings{"hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::Log3)),
|
|
||||||
make_shared<MagicVariableDeclaration>("log4",
|
|
||||||
make_shared<FunctionType>(strings{"hash", "hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::Log4)),
|
|
||||||
make_shared<MagicVariableDeclaration>("sha256",
|
|
||||||
make_shared<FunctionType>(strings(), strings{"hash"}, FunctionType::Location::SHA256, true)),
|
|
||||||
make_shared<MagicVariableDeclaration>("ecrecover",
|
|
||||||
make_shared<FunctionType>(strings{"hash", "hash8", "hash", "hash"}, strings{"address"}, FunctionType::Location::ECRecover)),
|
|
||||||
make_shared<MagicVariableDeclaration>("ripemd160",
|
|
||||||
make_shared<FunctionType>(strings(), strings{"hash160"}, FunctionType::Location::RIPEMD160, true))})
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void GlobalContext::setCurrentContract(ContractDefinition const& _contract)
|
|
||||||
{
|
|
||||||
m_currentContract = &_contract;
|
|
||||||
}
|
|
||||||
|
|
||||||
vector<Declaration const*> GlobalContext::getDeclarations() const
|
|
||||||
{
|
|
||||||
vector<Declaration const*> declarations;
|
|
||||||
declarations.reserve(m_magicVariables.size());
|
|
||||||
for (ASTPointer<Declaration const> const& variable: m_magicVariables)
|
|
||||||
declarations.push_back(variable.get());
|
|
||||||
return declarations;
|
|
||||||
}
|
|
||||||
|
|
||||||
MagicVariableDeclaration const* GlobalContext::getCurrentThis() const
|
|
||||||
{
|
|
||||||
if (!m_thisPointer[m_currentContract])
|
|
||||||
m_thisPointer[m_currentContract] = make_shared<MagicVariableDeclaration>(
|
|
||||||
"this", make_shared<ContractType>(*m_currentContract));
|
|
||||||
return m_thisPointer[m_currentContract].get();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
MagicVariableDeclaration const* GlobalContext::getCurrentSuper() const
|
|
||||||
{
|
|
||||||
if (!m_superPointer[m_currentContract])
|
|
||||||
m_superPointer[m_currentContract] = make_shared<MagicVariableDeclaration>(
|
|
||||||
"super", make_shared<ContractType>(*m_currentContract, true));
|
|
||||||
return m_superPointer[m_currentContract].get();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Container of the (implicit and explicit) global objects.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <map>
|
|
||||||
#include <memory>
|
|
||||||
#include <boost/noncopyable.hpp>
|
|
||||||
#include <libsolidity/ASTForward.h>
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
class Type; // forward
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Container for all global objects which look like AST nodes, but are not part of the AST
|
|
||||||
* that is currently being compiled.
|
|
||||||
* @note must not be destroyed or moved during compilation as its objects can be referenced from
|
|
||||||
* other objects.
|
|
||||||
*/
|
|
||||||
class GlobalContext: private boost::noncopyable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
GlobalContext();
|
|
||||||
void setCurrentContract(ContractDefinition const& _contract);
|
|
||||||
MagicVariableDeclaration const* getCurrentThis() const;
|
|
||||||
MagicVariableDeclaration const* getCurrentSuper() const;
|
|
||||||
|
|
||||||
/// @returns a vector of all implicit global declarations excluding "this".
|
|
||||||
std::vector<Declaration const*> getDeclarations() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<std::shared_ptr<MagicVariableDeclaration const>> m_magicVariables;
|
|
||||||
ContractDefinition const* m_currentContract = nullptr;
|
|
||||||
std::map<ContractDefinition const*, std::shared_ptr<MagicVariableDeclaration const>> mutable m_thisPointer;
|
|
||||||
std::map<ContractDefinition const*, std::shared_ptr<MagicVariableDeclaration const>> mutable m_superPointer;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,391 +0,0 @@
|
|||||||
|
|
||||||
#include <libsolidity/InterfaceHandler.h>
|
|
||||||
#include <libsolidity/AST.h>
|
|
||||||
#include <libsolidity/CompilerStack.h>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
/* -- public -- */
|
|
||||||
|
|
||||||
InterfaceHandler::InterfaceHandler()
|
|
||||||
{
|
|
||||||
m_lastTag = DocTagType::None;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefinition const& _contractDef,
|
|
||||||
DocumentationType _type)
|
|
||||||
{
|
|
||||||
switch(_type)
|
|
||||||
{
|
|
||||||
case DocumentationType::NatspecUser:
|
|
||||||
return getUserDocumentation(_contractDef);
|
|
||||||
case DocumentationType::NatspecDev:
|
|
||||||
return getDevDocumentation(_contractDef);
|
|
||||||
case DocumentationType::ABIInterface:
|
|
||||||
return getABIInterface(_contractDef);
|
|
||||||
case DocumentationType::ABISolidityInterface:
|
|
||||||
return getABISolidityInterface(_contractDef);
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type"));
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinition const& _contractDef)
|
|
||||||
{
|
|
||||||
Json::Value abi(Json::arrayValue);
|
|
||||||
for (auto const& it: _contractDef.getInterfaceFunctions())
|
|
||||||
{
|
|
||||||
auto populateParameters = [](vector<string> const& _paramNames, vector<string> const& _paramTypes)
|
|
||||||
{
|
|
||||||
Json::Value params(Json::arrayValue);
|
|
||||||
solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match");
|
|
||||||
for (unsigned i = 0; i < _paramNames.size(); ++i)
|
|
||||||
{
|
|
||||||
Json::Value param;
|
|
||||||
param["name"] = _paramNames[i];
|
|
||||||
param["type"] = _paramTypes[i];
|
|
||||||
params.append(param);
|
|
||||||
}
|
|
||||||
return params;
|
|
||||||
};
|
|
||||||
|
|
||||||
Json::Value method;
|
|
||||||
method["type"] = "function";
|
|
||||||
method["name"] = it.second->getDeclaration().getName();
|
|
||||||
method["constant"] = it.second->isConstant();
|
|
||||||
method["inputs"] = populateParameters(it.second->getParameterNames(),
|
|
||||||
it.second->getParameterTypeNames());
|
|
||||||
method["outputs"] = populateParameters(it.second->getReturnParameterNames(),
|
|
||||||
it.second->getReturnParameterTypeNames());
|
|
||||||
abi.append(method);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto const& it: _contractDef.getInterfaceEvents())
|
|
||||||
{
|
|
||||||
Json::Value event;
|
|
||||||
event["type"] = "event";
|
|
||||||
event["name"] = it->getName();
|
|
||||||
Json::Value params(Json::arrayValue);
|
|
||||||
for (auto const& p: it->getParameters())
|
|
||||||
{
|
|
||||||
Json::Value input;
|
|
||||||
input["name"] = p->getName();
|
|
||||||
input["type"] = p->getType()->toString();
|
|
||||||
input["indexed"] = p->isIndexed();
|
|
||||||
params.append(input);
|
|
||||||
}
|
|
||||||
event["inputs"] = params;
|
|
||||||
abi.append(event);
|
|
||||||
}
|
|
||||||
return std::unique_ptr<std::string>(new std::string(m_writer.write(abi)));
|
|
||||||
}
|
|
||||||
|
|
||||||
unique_ptr<string> InterfaceHandler::getABISolidityInterface(ContractDefinition const& _contractDef)
|
|
||||||
{
|
|
||||||
string ret = "contract " + _contractDef.getName() + "{";
|
|
||||||
for (auto const& it: _contractDef.getInterfaceFunctions())
|
|
||||||
{
|
|
||||||
auto populateParameters = [](vector<string> const& _paramNames,
|
|
||||||
vector<string> const& _paramTypes)
|
|
||||||
{
|
|
||||||
string r = "";
|
|
||||||
solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match");
|
|
||||||
for (unsigned i = 0; i < _paramNames.size(); ++i)
|
|
||||||
r += (r.size() ? "," : "(") + _paramTypes[i] + " " + _paramNames[i];
|
|
||||||
return r.size() ? r + ")" : "()";
|
|
||||||
};
|
|
||||||
ret += "function " + it.second->getDeclaration().getName() +
|
|
||||||
populateParameters(it.second->getParameterNames(), it.second->getParameterTypeNames()) +
|
|
||||||
(it.second->isConstant() ? "constant " : "");
|
|
||||||
if (it.second->getReturnParameterTypes().size())
|
|
||||||
ret += "returns" + populateParameters(it.second->getReturnParameterNames(), it.second->getReturnParameterTypeNames());
|
|
||||||
else if (ret.back() == ' ')
|
|
||||||
ret.pop_back();
|
|
||||||
ret += "{}";
|
|
||||||
}
|
|
||||||
|
|
||||||
return unique_ptr<string>(new string(ret + "}"));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefinition const& _contractDef)
|
|
||||||
{
|
|
||||||
Json::Value doc;
|
|
||||||
Json::Value methods(Json::objectValue);
|
|
||||||
|
|
||||||
for (auto const& it: _contractDef.getInterfaceFunctions())
|
|
||||||
{
|
|
||||||
Json::Value user;
|
|
||||||
auto strPtr = it.second->getDocumentation();
|
|
||||||
if (strPtr)
|
|
||||||
{
|
|
||||||
resetUser();
|
|
||||||
parseDocString(*strPtr, CommentOwner::Function);
|
|
||||||
if (!m_notice.empty())
|
|
||||||
{// since @notice is the only user tag if missing function should not appear
|
|
||||||
user["notice"] = Json::Value(m_notice);
|
|
||||||
methods[it.second->getCanonicalSignature()] = user;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
doc["methods"] = methods;
|
|
||||||
|
|
||||||
return std::unique_ptr<std::string>(new std::string(m_writer.write(doc)));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefinition const& _contractDef)
|
|
||||||
{
|
|
||||||
// LTODO: Somewhere in this function warnings for mismatch of param names
|
|
||||||
// should be thrown
|
|
||||||
Json::Value doc;
|
|
||||||
Json::Value methods(Json::objectValue);
|
|
||||||
|
|
||||||
auto contractDoc = _contractDef.getDocumentation();
|
|
||||||
if (contractDoc)
|
|
||||||
{
|
|
||||||
m_contractAuthor.clear();
|
|
||||||
m_title.clear();
|
|
||||||
parseDocString(*contractDoc, CommentOwner::Contract);
|
|
||||||
|
|
||||||
if (!m_contractAuthor.empty())
|
|
||||||
doc["author"] = m_contractAuthor;
|
|
||||||
|
|
||||||
if (!m_title.empty())
|
|
||||||
doc["title"] = m_title;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto const& it: _contractDef.getInterfaceFunctions())
|
|
||||||
{
|
|
||||||
Json::Value method;
|
|
||||||
auto strPtr = it.second->getDocumentation();
|
|
||||||
if (strPtr)
|
|
||||||
{
|
|
||||||
resetDev();
|
|
||||||
parseDocString(*strPtr, CommentOwner::Function);
|
|
||||||
|
|
||||||
if (!m_dev.empty())
|
|
||||||
method["details"] = Json::Value(m_dev);
|
|
||||||
|
|
||||||
if (!m_author.empty())
|
|
||||||
method["author"] = m_author;
|
|
||||||
|
|
||||||
Json::Value params(Json::objectValue);
|
|
||||||
for (auto const& pair: m_params)
|
|
||||||
params[pair.first] = pair.second;
|
|
||||||
|
|
||||||
if (!m_params.empty())
|
|
||||||
method["params"] = params;
|
|
||||||
|
|
||||||
if (!m_return.empty())
|
|
||||||
method["return"] = m_return;
|
|
||||||
|
|
||||||
if (!method.empty()) // add the function, only if we have any documentation to add
|
|
||||||
methods[it.second->getCanonicalSignature()] = method;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
doc["methods"] = methods;
|
|
||||||
|
|
||||||
return std::unique_ptr<std::string>(new std::string(m_writer.write(doc)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- private -- */
|
|
||||||
void InterfaceHandler::resetUser()
|
|
||||||
{
|
|
||||||
m_notice.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void InterfaceHandler::resetDev()
|
|
||||||
{
|
|
||||||
m_dev.clear();
|
|
||||||
m_author.clear();
|
|
||||||
m_return.clear();
|
|
||||||
m_params.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline std::string::const_iterator skipLineOrEOS(std::string::const_iterator _nlPos,
|
|
||||||
std::string::const_iterator _end)
|
|
||||||
{
|
|
||||||
return (_nlPos == _end) ? _end : ++_nlPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string::const_iterator InterfaceHandler::parseDocTagLine(std::string::const_iterator _pos,
|
|
||||||
std::string::const_iterator _end,
|
|
||||||
std::string& _tagString,
|
|
||||||
DocTagType _tagType,
|
|
||||||
bool _appending)
|
|
||||||
{
|
|
||||||
auto nlPos = std::find(_pos, _end, '\n');
|
|
||||||
if (_appending && _pos < _end && *_pos != ' ')
|
|
||||||
_tagString += " ";
|
|
||||||
std::copy(_pos, nlPos, back_inserter(_tagString));
|
|
||||||
m_lastTag = _tagType;
|
|
||||||
return skipLineOrEOS(nlPos, _end);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string::const_iterator InterfaceHandler::parseDocTagParam(std::string::const_iterator _pos,
|
|
||||||
std::string::const_iterator _end)
|
|
||||||
{
|
|
||||||
// find param name
|
|
||||||
auto currPos = std::find(_pos, _end, ' ');
|
|
||||||
if (currPos == _end)
|
|
||||||
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("End of param name not found" + std::string(_pos, _end)));
|
|
||||||
|
|
||||||
|
|
||||||
auto paramName = std::string(_pos, currPos);
|
|
||||||
|
|
||||||
currPos += 1;
|
|
||||||
auto nlPos = std::find(currPos, _end, '\n');
|
|
||||||
auto paramDesc = std::string(currPos, nlPos);
|
|
||||||
m_params.push_back(std::make_pair(paramName, paramDesc));
|
|
||||||
|
|
||||||
m_lastTag = DocTagType::Param;
|
|
||||||
return skipLineOrEOS(nlPos, _end);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string::const_iterator InterfaceHandler::appendDocTagParam(std::string::const_iterator _pos,
|
|
||||||
std::string::const_iterator _end)
|
|
||||||
{
|
|
||||||
// Should never be called with an empty vector
|
|
||||||
solAssert(!m_params.empty(), "Internal: Tried to append to empty parameter");
|
|
||||||
|
|
||||||
auto pair = m_params.back();
|
|
||||||
if (_pos < _end && *_pos != ' ')
|
|
||||||
pair.second += " ";
|
|
||||||
auto nlPos = std::find(_pos, _end, '\n');
|
|
||||||
std::copy(_pos, nlPos, back_inserter(pair.second));
|
|
||||||
|
|
||||||
m_params.at(m_params.size() - 1) = pair;
|
|
||||||
|
|
||||||
return skipLineOrEOS(nlPos, _end);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_iterator _pos,
|
|
||||||
std::string::const_iterator _end,
|
|
||||||
std::string const& _tag,
|
|
||||||
CommentOwner _owner)
|
|
||||||
{
|
|
||||||
// LTODO: need to check for @(start of a tag) between here and the end of line
|
|
||||||
// for all cases. Also somehow automate list of acceptable tags for each
|
|
||||||
// language construct since current way does not scale well.
|
|
||||||
if (m_lastTag == DocTagType::None || _tag != "")
|
|
||||||
{
|
|
||||||
if (_tag == "dev")
|
|
||||||
return parseDocTagLine(_pos, _end, m_dev, DocTagType::Dev, false);
|
|
||||||
else if (_tag == "notice")
|
|
||||||
return parseDocTagLine(_pos, _end, m_notice, DocTagType::Notice, false);
|
|
||||||
else if (_tag == "return")
|
|
||||||
return parseDocTagLine(_pos, _end, m_return, DocTagType::Return, false);
|
|
||||||
else if (_tag == "author")
|
|
||||||
{
|
|
||||||
if (_owner == CommentOwner::Contract)
|
|
||||||
return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::Author, false);
|
|
||||||
else if (_owner == CommentOwner::Function)
|
|
||||||
return parseDocTagLine(_pos, _end, m_author, DocTagType::Author, false);
|
|
||||||
else
|
|
||||||
// LTODO: for now this else makes no sense but later comments will go to more language constructs
|
|
||||||
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag is legal only for contracts"));
|
|
||||||
}
|
|
||||||
else if (_tag == "title")
|
|
||||||
{
|
|
||||||
if (_owner == CommentOwner::Contract)
|
|
||||||
return parseDocTagLine(_pos, _end, m_title, DocTagType::Title, false);
|
|
||||||
else
|
|
||||||
// LTODO: Unknown tag, throw some form of warning and not just an exception
|
|
||||||
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag is legal only for contracts"));
|
|
||||||
}
|
|
||||||
else if (_tag == "param")
|
|
||||||
return parseDocTagParam(_pos, _end);
|
|
||||||
else
|
|
||||||
// LTODO: Unknown tag, throw some form of warning and not just an exception
|
|
||||||
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("Unknown tag " + _tag + " encountered"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return appendDocTag(_pos, _end, _owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string::const_iterator InterfaceHandler::appendDocTag(std::string::const_iterator _pos,
|
|
||||||
std::string::const_iterator _end,
|
|
||||||
CommentOwner _owner)
|
|
||||||
{
|
|
||||||
switch (m_lastTag)
|
|
||||||
{
|
|
||||||
case DocTagType::Dev:
|
|
||||||
return parseDocTagLine(_pos, _end, m_dev, DocTagType::Dev, true);
|
|
||||||
case DocTagType::Notice:
|
|
||||||
return parseDocTagLine(_pos, _end, m_notice, DocTagType::Notice, true);
|
|
||||||
case DocTagType::Return:
|
|
||||||
return parseDocTagLine(_pos, _end, m_return, DocTagType::Return, true);
|
|
||||||
case DocTagType::Author:
|
|
||||||
if (_owner == CommentOwner::Contract)
|
|
||||||
return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::Author, true);
|
|
||||||
else if (_owner == CommentOwner::Function)
|
|
||||||
return parseDocTagLine(_pos, _end, m_author, DocTagType::Author, true);
|
|
||||||
else
|
|
||||||
// LTODO: Unknown tag, throw some form of warning and not just an exception
|
|
||||||
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag in illegal comment"));
|
|
||||||
case DocTagType::Title:
|
|
||||||
if (_owner == CommentOwner::Contract)
|
|
||||||
return parseDocTagLine(_pos, _end, m_title, DocTagType::Title, true);
|
|
||||||
else
|
|
||||||
// LTODO: Unknown tag, throw some form of warning and not just an exception
|
|
||||||
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag in illegal comment"));
|
|
||||||
case DocTagType::Param:
|
|
||||||
return appendDocTagParam(_pos, _end);
|
|
||||||
default:
|
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Internal: Illegal documentation tag type"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline std::string::const_iterator getFirstSpaceOrNl(std::string::const_iterator _pos,
|
|
||||||
std::string::const_iterator _end)
|
|
||||||
{
|
|
||||||
auto spacePos = std::find(_pos, _end, ' ');
|
|
||||||
auto nlPos = std::find(_pos, _end, '\n');
|
|
||||||
return (spacePos < nlPos) ? spacePos : nlPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InterfaceHandler::parseDocString(std::string const& _string, CommentOwner _owner)
|
|
||||||
{
|
|
||||||
auto currPos = _string.begin();
|
|
||||||
auto end = _string.end();
|
|
||||||
|
|
||||||
while (currPos != end)
|
|
||||||
{
|
|
||||||
auto tagPos = std::find(currPos, end, '@');
|
|
||||||
auto nlPos = std::find(currPos, end, '\n');
|
|
||||||
|
|
||||||
if (tagPos != end && tagPos < nlPos)
|
|
||||||
{
|
|
||||||
// we found a tag
|
|
||||||
auto tagNameEndPos = getFirstSpaceOrNl(tagPos, end);
|
|
||||||
if (tagNameEndPos == end)
|
|
||||||
BOOST_THROW_EXCEPTION(DocstringParsingError() <<
|
|
||||||
errinfo_comment("End of tag " + std::string(tagPos, tagNameEndPos) + "not found"));
|
|
||||||
|
|
||||||
currPos = parseDocTag(tagNameEndPos + 1, end, std::string(tagPos + 1, tagNameEndPos), _owner);
|
|
||||||
}
|
|
||||||
else if (m_lastTag != DocTagType::None) // continuation of the previous tag
|
|
||||||
currPos = appendDocTag(currPos, end, _owner);
|
|
||||||
else if (currPos != end)
|
|
||||||
{
|
|
||||||
// if it begins without a tag then consider it as @notice
|
|
||||||
if (currPos == _string.begin())
|
|
||||||
{
|
|
||||||
currPos = parseDocTag(currPos, end, "notice", CommentOwner::Function);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if (nlPos == end) //end of text
|
|
||||||
return;
|
|
||||||
// else skip the line if a newline was found and we get here
|
|
||||||
currPos = nlPos + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} //solidity NS
|
|
||||||
} // dev NS
|
|
@ -1,125 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Lefteris <lefteris@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Takes the parsed AST and produces the Natspec
|
|
||||||
* documentation and the ABI interface
|
|
||||||
* https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format
|
|
||||||
*
|
|
||||||
* Can generally deal with JSON files
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <memory>
|
|
||||||
#include <json/json.h>
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
// Forward declarations
|
|
||||||
class ContractDefinition;
|
|
||||||
enum class DocumentationType: uint8_t;
|
|
||||||
|
|
||||||
enum class DocTagType: uint8_t
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
Dev,
|
|
||||||
Notice,
|
|
||||||
Param,
|
|
||||||
Return,
|
|
||||||
Author,
|
|
||||||
Title
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class CommentOwner
|
|
||||||
{
|
|
||||||
Contract,
|
|
||||||
Function
|
|
||||||
};
|
|
||||||
|
|
||||||
class InterfaceHandler
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
InterfaceHandler();
|
|
||||||
|
|
||||||
/// Get the given type of documentation
|
|
||||||
/// @param _contractDef The contract definition
|
|
||||||
/// @param _type The type of the documentation. Can be one of the
|
|
||||||
/// types provided by @c DocumentationType
|
|
||||||
/// @return A unique pointer contained string with the json
|
|
||||||
/// representation of provided type
|
|
||||||
std::unique_ptr<std::string> getDocumentation(ContractDefinition const& _contractDef,
|
|
||||||
DocumentationType _type);
|
|
||||||
/// Get the ABI Interface of the contract
|
|
||||||
/// @param _contractDef The contract definition
|
|
||||||
/// @return A unique pointer contained string with the json
|
|
||||||
/// representation of the contract's ABI Interface
|
|
||||||
std::unique_ptr<std::string> getABIInterface(ContractDefinition const& _contractDef);
|
|
||||||
std::unique_ptr<std::string> getABISolidityInterface(ContractDefinition const& _contractDef);
|
|
||||||
/// Get the User documentation of the contract
|
|
||||||
/// @param _contractDef The contract definition
|
|
||||||
/// @return A unique pointer contained string with the json
|
|
||||||
/// representation of the contract's user documentation
|
|
||||||
std::unique_ptr<std::string> getUserDocumentation(ContractDefinition const& _contractDef);
|
|
||||||
/// Get the Developer's documentation of the contract
|
|
||||||
/// @param _contractDef The contract definition
|
|
||||||
/// @return A unique pointer contained string with the json
|
|
||||||
/// representation of the contract's developer documentation
|
|
||||||
std::unique_ptr<std::string> getDevDocumentation(ContractDefinition const& _contractDef);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void resetUser();
|
|
||||||
void resetDev();
|
|
||||||
|
|
||||||
std::string::const_iterator parseDocTagLine(std::string::const_iterator _pos,
|
|
||||||
std::string::const_iterator _end,
|
|
||||||
std::string& _tagString,
|
|
||||||
DocTagType _tagType,
|
|
||||||
bool _appending);
|
|
||||||
std::string::const_iterator parseDocTagParam(std::string::const_iterator _pos,
|
|
||||||
std::string::const_iterator _end);
|
|
||||||
std::string::const_iterator appendDocTagParam(std::string::const_iterator _pos,
|
|
||||||
std::string::const_iterator _end);
|
|
||||||
void parseDocString(std::string const& _string, CommentOwner _owner);
|
|
||||||
std::string::const_iterator appendDocTag(std::string::const_iterator _pos,
|
|
||||||
std::string::const_iterator _end,
|
|
||||||
CommentOwner _owner);
|
|
||||||
std::string::const_iterator parseDocTag(std::string::const_iterator _pos,
|
|
||||||
std::string::const_iterator _end,
|
|
||||||
std::string const& _tag,
|
|
||||||
CommentOwner _owner);
|
|
||||||
|
|
||||||
Json::StyledWriter m_writer;
|
|
||||||
|
|
||||||
// internal state
|
|
||||||
DocTagType m_lastTag;
|
|
||||||
std::string m_notice;
|
|
||||||
std::string m_dev;
|
|
||||||
std::string m_return;
|
|
||||||
std::string m_contractAuthor;
|
|
||||||
std::string m_author;
|
|
||||||
std::string m_title;
|
|
||||||
std::vector<std::pair<std::string, std::string>> m_params;
|
|
||||||
};
|
|
||||||
|
|
||||||
} //solidity NS
|
|
||||||
} // dev NS
|
|
@ -1,384 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Parser part that determines the declarations corresponding to names and the types of expressions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <libsolidity/NameAndTypeResolver.h>
|
|
||||||
#include <libsolidity/AST.h>
|
|
||||||
#include <libsolidity/Exceptions.h>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
NameAndTypeResolver::NameAndTypeResolver(std::vector<Declaration const*> const& _globals)
|
|
||||||
{
|
|
||||||
for (Declaration const* declaration: _globals)
|
|
||||||
m_scopes[nullptr].registerDeclaration(*declaration);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit)
|
|
||||||
{
|
|
||||||
// The helper registers all declarations in m_scopes as a side-effect of its construction.
|
|
||||||
DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
|
|
||||||
{
|
|
||||||
m_currentScope = &m_scopes[nullptr];
|
|
||||||
|
|
||||||
for (ASTPointer<InheritanceSpecifier> const& baseContract: _contract.getBaseContracts())
|
|
||||||
ReferencesResolver resolver(*baseContract, *this, &_contract, nullptr);
|
|
||||||
|
|
||||||
m_currentScope = &m_scopes[&_contract];
|
|
||||||
|
|
||||||
linearizeBaseContracts(_contract);
|
|
||||||
for (ContractDefinition const* base: _contract.getLinearizedBaseContracts())
|
|
||||||
importInheritedScope(*base);
|
|
||||||
|
|
||||||
for (ASTPointer<StructDefinition> const& structDef: _contract.getDefinedStructs())
|
|
||||||
ReferencesResolver resolver(*structDef, *this, &_contract, nullptr);
|
|
||||||
for (ASTPointer<EnumDefinition> const& enumDef: _contract.getDefinedEnums())
|
|
||||||
ReferencesResolver resolver(*enumDef, *this, &_contract, nullptr);
|
|
||||||
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables())
|
|
||||||
ReferencesResolver resolver(*variable, *this, &_contract, nullptr);
|
|
||||||
for (ASTPointer<EventDefinition> const& event: _contract.getEvents())
|
|
||||||
ReferencesResolver resolver(*event, *this, &_contract, nullptr);
|
|
||||||
for (ASTPointer<ModifierDefinition> const& modifier: _contract.getFunctionModifiers())
|
|
||||||
{
|
|
||||||
m_currentScope = &m_scopes[modifier.get()];
|
|
||||||
ReferencesResolver resolver(*modifier, *this, &_contract, nullptr);
|
|
||||||
}
|
|
||||||
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
|
|
||||||
{
|
|
||||||
m_currentScope = &m_scopes[function.get()];
|
|
||||||
ReferencesResolver referencesResolver(*function, *this, &_contract,
|
|
||||||
function->getReturnParameterList().get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NameAndTypeResolver::checkTypeRequirements(ContractDefinition& _contract)
|
|
||||||
{
|
|
||||||
for (ASTPointer<StructDefinition> const& structDef: _contract.getDefinedStructs())
|
|
||||||
structDef->checkValidityOfMembers();
|
|
||||||
_contract.checkTypeRequirements();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
|
|
||||||
{
|
|
||||||
m_scopes[nullptr].registerDeclaration(_declaration, false, true);
|
|
||||||
solAssert(_declaration.getScope() == nullptr, "Updated declaration outside global scope.");
|
|
||||||
}
|
|
||||||
|
|
||||||
Declaration const* NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const
|
|
||||||
{
|
|
||||||
auto iterator = m_scopes.find(_scope);
|
|
||||||
if (iterator == end(m_scopes))
|
|
||||||
return nullptr;
|
|
||||||
return iterator->second.resolveName(_name, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Declaration const* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive)
|
|
||||||
{
|
|
||||||
return m_currentScope->resolveName(_name, _recursive);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base)
|
|
||||||
{
|
|
||||||
auto iterator = m_scopes.find(&_base);
|
|
||||||
solAssert(iterator != end(m_scopes), "");
|
|
||||||
for (auto const& nameAndDeclaration: iterator->second.getDeclarations())
|
|
||||||
{
|
|
||||||
Declaration const* declaration = nameAndDeclaration.second;
|
|
||||||
// Import if it was declared in the base, is not the constructor and is visible in derived classes
|
|
||||||
if (declaration->getScope() == &_base && declaration->getName() != _base.getName() &&
|
|
||||||
declaration->isVisibleInDerivedContracts())
|
|
||||||
m_currentScope->registerDeclaration(*declaration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract) const
|
|
||||||
{
|
|
||||||
// order in the lists is from derived to base
|
|
||||||
// list of lists to linearize, the last element is the list of direct bases
|
|
||||||
list<list<ContractDefinition const*>> input(1, {});
|
|
||||||
for (ASTPointer<InheritanceSpecifier> const& baseSpecifier: _contract.getBaseContracts())
|
|
||||||
{
|
|
||||||
ASTPointer<Identifier> baseName = baseSpecifier->getName();
|
|
||||||
ContractDefinition const* base = dynamic_cast<ContractDefinition const*>(
|
|
||||||
baseName->getReferencedDeclaration());
|
|
||||||
if (!base)
|
|
||||||
BOOST_THROW_EXCEPTION(baseName->createTypeError("Contract expected."));
|
|
||||||
// "push_front" has the effect that bases mentioned later can overwrite members of bases
|
|
||||||
// mentioned earlier
|
|
||||||
input.back().push_front(base);
|
|
||||||
vector<ContractDefinition const*> const& basesBases = base->getLinearizedBaseContracts();
|
|
||||||
if (basesBases.empty())
|
|
||||||
BOOST_THROW_EXCEPTION(baseName->createTypeError("Definition of base has to precede definition of derived contract"));
|
|
||||||
input.push_front(list<ContractDefinition const*>(basesBases.begin(), basesBases.end()));
|
|
||||||
}
|
|
||||||
input.back().push_front(&_contract);
|
|
||||||
vector<ContractDefinition const*> result = cThreeMerge(input);
|
|
||||||
if (result.empty())
|
|
||||||
BOOST_THROW_EXCEPTION(_contract.createTypeError("Linearization of inheritance graph impossible"));
|
|
||||||
_contract.setLinearizedBaseContracts(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class _T>
|
|
||||||
vector<_T const*> NameAndTypeResolver::cThreeMerge(list<list<_T const*>>& _toMerge)
|
|
||||||
{
|
|
||||||
// returns true iff _candidate appears only as last element of the lists
|
|
||||||
auto appearsOnlyAtHead = [&](_T const* _candidate) -> bool
|
|
||||||
{
|
|
||||||
for (list<_T const*> const& bases: _toMerge)
|
|
||||||
{
|
|
||||||
solAssert(!bases.empty(), "");
|
|
||||||
if (find(++bases.begin(), bases.end(), _candidate) != bases.end())
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
// returns the next candidate to append to the linearized list or nullptr on failure
|
|
||||||
auto nextCandidate = [&]() -> _T const*
|
|
||||||
{
|
|
||||||
for (list<_T const*> const& bases: _toMerge)
|
|
||||||
{
|
|
||||||
solAssert(!bases.empty(), "");
|
|
||||||
if (appearsOnlyAtHead(bases.front()))
|
|
||||||
return bases.front();
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
};
|
|
||||||
// removes the given contract from all lists
|
|
||||||
auto removeCandidate = [&](_T const* _candidate)
|
|
||||||
{
|
|
||||||
for (auto it = _toMerge.begin(); it != _toMerge.end();)
|
|
||||||
{
|
|
||||||
it->remove(_candidate);
|
|
||||||
if (it->empty())
|
|
||||||
it = _toMerge.erase(it);
|
|
||||||
else
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
_toMerge.remove_if([](list<_T const*> const& _bases) { return _bases.empty(); });
|
|
||||||
vector<_T const*> result;
|
|
||||||
while (!_toMerge.empty())
|
|
||||||
{
|
|
||||||
_T const* candidate = nextCandidate();
|
|
||||||
if (!candidate)
|
|
||||||
return vector<_T const*>();
|
|
||||||
result.push_back(candidate);
|
|
||||||
removeCandidate(candidate);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
DeclarationRegistrationHelper::DeclarationRegistrationHelper(map<ASTNode const*, DeclarationContainer>& _scopes,
|
|
||||||
ASTNode& _astRoot):
|
|
||||||
m_scopes(_scopes), m_currentScope(nullptr)
|
|
||||||
{
|
|
||||||
_astRoot.accept(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract)
|
|
||||||
{
|
|
||||||
registerDeclaration(_contract, true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeclarationRegistrationHelper::endVisit(ContractDefinition&)
|
|
||||||
{
|
|
||||||
closeCurrentScope();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DeclarationRegistrationHelper::visit(StructDefinition& _struct)
|
|
||||||
{
|
|
||||||
registerDeclaration(_struct, true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeclarationRegistrationHelper::endVisit(StructDefinition&)
|
|
||||||
{
|
|
||||||
closeCurrentScope();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DeclarationRegistrationHelper::visit(EnumDefinition& _enum)
|
|
||||||
{
|
|
||||||
registerDeclaration(_enum, true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeclarationRegistrationHelper::endVisit(EnumDefinition&)
|
|
||||||
{
|
|
||||||
closeCurrentScope();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DeclarationRegistrationHelper::visit(EnumValue& _value)
|
|
||||||
{
|
|
||||||
registerDeclaration(_value, false);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DeclarationRegistrationHelper::visit(FunctionDefinition& _function)
|
|
||||||
{
|
|
||||||
registerDeclaration(_function, true);
|
|
||||||
m_currentFunction = &_function;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeclarationRegistrationHelper::endVisit(FunctionDefinition&)
|
|
||||||
{
|
|
||||||
m_currentFunction = nullptr;
|
|
||||||
closeCurrentScope();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DeclarationRegistrationHelper::visit(ModifierDefinition& _modifier)
|
|
||||||
{
|
|
||||||
registerDeclaration(_modifier, true);
|
|
||||||
m_currentFunction = &_modifier;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeclarationRegistrationHelper::endVisit(ModifierDefinition&)
|
|
||||||
{
|
|
||||||
m_currentFunction = nullptr;
|
|
||||||
closeCurrentScope();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeclarationRegistrationHelper::endVisit(VariableDefinition& _variableDefinition)
|
|
||||||
{
|
|
||||||
// Register the local variables with the function
|
|
||||||
// This does not fit here perfectly, but it saves us another AST visit.
|
|
||||||
solAssert(m_currentFunction, "Variable definition without function.");
|
|
||||||
m_currentFunction->addLocalVariable(_variableDefinition.getDeclaration());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration)
|
|
||||||
{
|
|
||||||
registerDeclaration(_declaration, false);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DeclarationRegistrationHelper::visit(EventDefinition& _event)
|
|
||||||
{
|
|
||||||
registerDeclaration(_event, true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeclarationRegistrationHelper::endVisit(EventDefinition&)
|
|
||||||
{
|
|
||||||
closeCurrentScope();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declaration)
|
|
||||||
{
|
|
||||||
map<ASTNode const*, DeclarationContainer>::iterator iter;
|
|
||||||
bool newlyAdded;
|
|
||||||
tie(iter, newlyAdded) = m_scopes.emplace(&_declaration, DeclarationContainer(m_currentScope, &m_scopes[m_currentScope]));
|
|
||||||
solAssert(newlyAdded, "Unable to add new scope.");
|
|
||||||
m_currentScope = &_declaration;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeclarationRegistrationHelper::closeCurrentScope()
|
|
||||||
{
|
|
||||||
solAssert(m_currentScope, "Closed non-existing scope.");
|
|
||||||
m_currentScope = m_scopes[m_currentScope].getEnclosingDeclaration();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope)
|
|
||||||
{
|
|
||||||
if (!m_scopes[m_currentScope].registerDeclaration(_declaration, !_declaration.isVisibleInContract()))
|
|
||||||
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_declaration.getLocation())
|
|
||||||
<< errinfo_comment("Identifier already declared."));
|
|
||||||
//@todo the exception should also contain the location of the first declaration
|
|
||||||
_declaration.setScope(m_currentScope);
|
|
||||||
if (_opensScope)
|
|
||||||
enterNewSubScope(_declaration);
|
|
||||||
}
|
|
||||||
|
|
||||||
ReferencesResolver::ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver,
|
|
||||||
ContractDefinition const* _currentContract,
|
|
||||||
ParameterList const* _returnParameters, bool _allowLazyTypes):
|
|
||||||
m_resolver(_resolver), m_currentContract(_currentContract),
|
|
||||||
m_returnParameters(_returnParameters), m_allowLazyTypes(_allowLazyTypes)
|
|
||||||
{
|
|
||||||
_root.accept(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReferencesResolver::endVisit(VariableDeclaration& _variable)
|
|
||||||
{
|
|
||||||
// endVisit because the internal type needs resolving if it is a user defined type
|
|
||||||
// or mapping
|
|
||||||
if (_variable.getTypeName())
|
|
||||||
{
|
|
||||||
TypePointer type = _variable.getTypeName()->toType();
|
|
||||||
// All byte array parameter types should point to call data
|
|
||||||
if (_variable.isExternalFunctionParameter())
|
|
||||||
if (auto const* byteArrayType = dynamic_cast<ByteArrayType const*>(type.get()))
|
|
||||||
type = byteArrayType->copyForLocation(ByteArrayType::Location::CallData);
|
|
||||||
_variable.setType(type);
|
|
||||||
|
|
||||||
if (!_variable.getType())
|
|
||||||
BOOST_THROW_EXCEPTION(_variable.getTypeName()->createTypeError("Invalid type name"));
|
|
||||||
}
|
|
||||||
else if (!m_allowLazyTypes)
|
|
||||||
BOOST_THROW_EXCEPTION(_variable.createTypeError("Explicit type needed."));
|
|
||||||
// otherwise we have a "var"-declaration whose type is resolved by the first assignment
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ReferencesResolver::visit(Return& _return)
|
|
||||||
{
|
|
||||||
_return.setFunctionReturnParameters(m_returnParameters);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ReferencesResolver::visit(Mapping&)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ReferencesResolver::visit(UserDefinedTypeName& _typeName)
|
|
||||||
{
|
|
||||||
Declaration const* declaration = m_resolver.getNameFromCurrentScope(_typeName.getName());
|
|
||||||
if (!declaration)
|
|
||||||
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_typeName.getLocation())
|
|
||||||
<< errinfo_comment("Undeclared identifier."));
|
|
||||||
_typeName.setReferencedDeclaration(*declaration);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ReferencesResolver::visit(Identifier& _identifier)
|
|
||||||
{
|
|
||||||
Declaration const* declaration = m_resolver.getNameFromCurrentScope(_identifier.getName());
|
|
||||||
if (!declaration)
|
|
||||||
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_identifier.getLocation())
|
|
||||||
<< errinfo_comment("Undeclared identifier."));
|
|
||||||
_identifier.setReferencedDeclaration(*declaration, m_currentContract);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,148 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Parser part that determines the declarations corresponding to names and the types of expressions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <list>
|
|
||||||
#include <boost/noncopyable.hpp>
|
|
||||||
|
|
||||||
#include <libsolidity/DeclarationContainer.h>
|
|
||||||
#include <libsolidity/ASTVisitor.h>
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolves name references, types and checks types of all expressions.
|
|
||||||
* Specifically, it checks that all operations are valid for the inferred types.
|
|
||||||
* An exception is throw on the first error.
|
|
||||||
*/
|
|
||||||
class NameAndTypeResolver: private boost::noncopyable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit NameAndTypeResolver(std::vector<Declaration const*> const& _globals);
|
|
||||||
/// Registers all declarations found in the source unit.
|
|
||||||
void registerDeclarations(SourceUnit& _sourceUnit);
|
|
||||||
/// Resolves all names and types referenced from the given contract.
|
|
||||||
void resolveNamesAndTypes(ContractDefinition& _contract);
|
|
||||||
/// Check all type requirements in the given contract.
|
|
||||||
void checkTypeRequirements(ContractDefinition& _contract);
|
|
||||||
/// Updates the given global declaration (used for "this"). Not to be used with declarations
|
|
||||||
/// that create their own scope.
|
|
||||||
void updateDeclaration(Declaration const& _declaration);
|
|
||||||
|
|
||||||
/// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted,
|
|
||||||
/// the global scope is used (i.e. the one containing only the contract).
|
|
||||||
/// @returns a pointer to the declaration on success or nullptr on failure.
|
|
||||||
Declaration const* resolveName(ASTString const& _name, Declaration const* _scope = nullptr) const;
|
|
||||||
|
|
||||||
/// Resolves a name in the "current" scope. Should only be called during the initial
|
|
||||||
/// resolving phase.
|
|
||||||
Declaration const* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void reset();
|
|
||||||
|
|
||||||
/// Imports all members declared directly in the given contract (i.e. does not import inherited
|
|
||||||
/// members) into the current scope if they are not present already.
|
|
||||||
void importInheritedScope(ContractDefinition const& _base);
|
|
||||||
|
|
||||||
/// Computes "C3-Linearization" of base contracts and stores it inside the contract.
|
|
||||||
void linearizeBaseContracts(ContractDefinition& _contract) const;
|
|
||||||
/// Computes the C3-merge of the given list of lists of bases.
|
|
||||||
/// @returns the linearized vector or an empty vector if linearization is not possible.
|
|
||||||
template <class _T>
|
|
||||||
static std::vector<_T const*> cThreeMerge(std::list<std::list<_T const*>>& _toMerge);
|
|
||||||
|
|
||||||
/// Maps nodes declaring a scope to scopes, i.e. ContractDefinition and FunctionDeclaration,
|
|
||||||
/// where nullptr denotes the global scope. Note that structs are not scope since they do
|
|
||||||
/// not contain code.
|
|
||||||
std::map<ASTNode const*, DeclarationContainer> m_scopes;
|
|
||||||
|
|
||||||
DeclarationContainer* m_currentScope = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Traverses the given AST upon construction and fills _scopes with all declarations inside the
|
|
||||||
* AST.
|
|
||||||
*/
|
|
||||||
class DeclarationRegistrationHelper: private ASTVisitor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
DeclarationRegistrationHelper(std::map<ASTNode const*, DeclarationContainer>& _scopes, ASTNode& _astRoot);
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool visit(ContractDefinition& _contract) override;
|
|
||||||
void endVisit(ContractDefinition& _contract) override;
|
|
||||||
bool visit(StructDefinition& _struct) override;
|
|
||||||
void endVisit(StructDefinition& _struct) override;
|
|
||||||
bool visit(EnumDefinition& _enum) override;
|
|
||||||
void endVisit(EnumDefinition& _enum) override;
|
|
||||||
bool visit(EnumValue& _value) override;
|
|
||||||
bool visit(FunctionDefinition& _function) override;
|
|
||||||
void endVisit(FunctionDefinition& _function) override;
|
|
||||||
bool visit(ModifierDefinition& _modifier) override;
|
|
||||||
void endVisit(ModifierDefinition& _modifier) override;
|
|
||||||
void endVisit(VariableDefinition& _variableDefinition) override;
|
|
||||||
bool visit(VariableDeclaration& _declaration) override;
|
|
||||||
bool visit(EventDefinition& _event) override;
|
|
||||||
void endVisit(EventDefinition& _event) override;
|
|
||||||
|
|
||||||
void enterNewSubScope(Declaration const& _declaration);
|
|
||||||
void closeCurrentScope();
|
|
||||||
void registerDeclaration(Declaration& _declaration, bool _opensScope);
|
|
||||||
|
|
||||||
std::map<ASTNode const*, DeclarationContainer>& m_scopes;
|
|
||||||
Declaration const* m_currentScope;
|
|
||||||
VariableScope* m_currentFunction;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolves references to declarations (of variables and types) and also establishes the link
|
|
||||||
* between a return statement and the return parameter list.
|
|
||||||
*/
|
|
||||||
class ReferencesResolver: private ASTVisitor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver,
|
|
||||||
ContractDefinition const* _currentContract,
|
|
||||||
ParameterList const* _returnParameters,
|
|
||||||
bool _allowLazyTypes = true);
|
|
||||||
|
|
||||||
private:
|
|
||||||
virtual void endVisit(VariableDeclaration& _variable) override;
|
|
||||||
virtual bool visit(Identifier& _identifier) override;
|
|
||||||
virtual bool visit(UserDefinedTypeName& _typeName) override;
|
|
||||||
virtual bool visit(Mapping&) override;
|
|
||||||
virtual bool visit(Return& _return) override;
|
|
||||||
|
|
||||||
NameAndTypeResolver& m_resolver;
|
|
||||||
ContractDefinition const* m_currentContract;
|
|
||||||
ParameterList const* m_returnParameters;
|
|
||||||
bool m_allowLazyTypes;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
883
Parser.cpp
883
Parser.cpp
@ -1,883 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Solidity parser.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <libdevcore/Log.h>
|
|
||||||
#include <libsolidity/BaseTypes.h>
|
|
||||||
#include <libsolidity/Parser.h>
|
|
||||||
#include <libsolidity/Scanner.h>
|
|
||||||
#include <libsolidity/Exceptions.h>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
/// AST node factory that also tracks the begin and end position of an AST node
|
|
||||||
/// while it is being parsed
|
|
||||||
class Parser::ASTNodeFactory
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ASTNodeFactory(Parser const& _parser):
|
|
||||||
m_parser(_parser), m_location(_parser.getPosition(), -1, _parser.getSourceName()) {}
|
|
||||||
|
|
||||||
void markEndPosition() { m_location.end = m_parser.getEndPosition(); }
|
|
||||||
void setLocationEmpty() { m_location.end = m_location.start; }
|
|
||||||
/// Set the end position to the one of the given node.
|
|
||||||
void setEndPositionFromNode(ASTPointer<ASTNode> const& _node) { m_location.end = _node->getLocation().end; }
|
|
||||||
|
|
||||||
template <class NodeType, typename... Args>
|
|
||||||
ASTPointer<NodeType> createNode(Args&& ... _args)
|
|
||||||
{
|
|
||||||
if (m_location.end < 0)
|
|
||||||
markEndPosition();
|
|
||||||
return make_shared<NodeType>(m_location, forward<Args>(_args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Parser const& m_parser;
|
|
||||||
Location m_location;
|
|
||||||
};
|
|
||||||
|
|
||||||
ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
|
|
||||||
{
|
|
||||||
m_scanner = _scanner;
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
vector<ASTPointer<ASTNode>> nodes;
|
|
||||||
while (_scanner->getCurrentToken() != Token::EOS)
|
|
||||||
{
|
|
||||||
switch (m_scanner->getCurrentToken())
|
|
||||||
{
|
|
||||||
case Token::Import:
|
|
||||||
nodes.push_back(parseImportDirective());
|
|
||||||
break;
|
|
||||||
case Token::Contract:
|
|
||||||
nodes.push_back(parseContractDefinition());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
BOOST_THROW_EXCEPTION(createParserError(std::string("Expected import directive or contract definition.")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nodeFactory.createNode<SourceUnit>(nodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<const string> const& Parser::getSourceName() const
|
|
||||||
{
|
|
||||||
return m_scanner->getSourceName();
|
|
||||||
}
|
|
||||||
|
|
||||||
int Parser::getPosition() const
|
|
||||||
{
|
|
||||||
return m_scanner->getCurrentLocation().start;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Parser::getEndPosition() const
|
|
||||||
{
|
|
||||||
return m_scanner->getCurrentLocation().end;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<ImportDirective> Parser::parseImportDirective()
|
|
||||||
{
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
expectToken(Token::Import);
|
|
||||||
if (m_scanner->getCurrentToken() != Token::StringLiteral)
|
|
||||||
BOOST_THROW_EXCEPTION(createParserError("Expected string literal (URL)."));
|
|
||||||
ASTPointer<ASTString> url = getLiteralAndAdvance();
|
|
||||||
nodeFactory.markEndPosition();
|
|
||||||
expectToken(Token::Semicolon);
|
|
||||||
return nodeFactory.createNode<ImportDirective>(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<ContractDefinition> Parser::parseContractDefinition()
|
|
||||||
{
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
ASTPointer<ASTString> docString;
|
|
||||||
if (m_scanner->getCurrentCommentLiteral() != "")
|
|
||||||
docString = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
|
|
||||||
expectToken(Token::Contract);
|
|
||||||
ASTPointer<ASTString> name = expectIdentifierToken();
|
|
||||||
vector<ASTPointer<InheritanceSpecifier>> baseContracts;
|
|
||||||
vector<ASTPointer<StructDefinition>> structs;
|
|
||||||
vector<ASTPointer<EnumDefinition>> enums;
|
|
||||||
vector<ASTPointer<VariableDeclaration>> stateVariables;
|
|
||||||
vector<ASTPointer<FunctionDefinition>> functions;
|
|
||||||
vector<ASTPointer<ModifierDefinition>> modifiers;
|
|
||||||
vector<ASTPointer<EventDefinition>> events;
|
|
||||||
if (m_scanner->getCurrentToken() == Token::Is)
|
|
||||||
do
|
|
||||||
{
|
|
||||||
m_scanner->next();
|
|
||||||
baseContracts.push_back(parseInheritanceSpecifier());
|
|
||||||
}
|
|
||||||
while (m_scanner->getCurrentToken() == Token::Comma);
|
|
||||||
expectToken(Token::LBrace);
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
Token::Value currentToken = m_scanner->getCurrentToken();
|
|
||||||
if (currentToken == Token::RBrace)
|
|
||||||
break;
|
|
||||||
else if (currentToken == Token::Function)
|
|
||||||
functions.push_back(parseFunctionDefinition(name.get()));
|
|
||||||
else if (currentToken == Token::Struct)
|
|
||||||
structs.push_back(parseStructDefinition());
|
|
||||||
else if (currentToken == Token::Enum)
|
|
||||||
enums.push_back(parseEnumDefinition());
|
|
||||||
else if (currentToken == Token::Identifier || currentToken == Token::Mapping ||
|
|
||||||
Token::isElementaryTypeName(currentToken))
|
|
||||||
{
|
|
||||||
VarDeclParserOptions options;
|
|
||||||
options.isStateVariable = true;
|
|
||||||
stateVariables.push_back(parseVariableDeclaration(options));
|
|
||||||
expectToken(Token::Semicolon);
|
|
||||||
}
|
|
||||||
else if (currentToken == Token::Modifier)
|
|
||||||
modifiers.push_back(parseModifierDefinition());
|
|
||||||
else if (currentToken == Token::Event)
|
|
||||||
events.push_back(parseEventDefinition());
|
|
||||||
else
|
|
||||||
BOOST_THROW_EXCEPTION(createParserError("Function, variable, struct or modifier declaration expected."));
|
|
||||||
}
|
|
||||||
nodeFactory.markEndPosition();
|
|
||||||
expectToken(Token::RBrace);
|
|
||||||
return nodeFactory.createNode<ContractDefinition>(name, docString, baseContracts, structs, enums,
|
|
||||||
stateVariables, functions, modifiers, events);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier()
|
|
||||||
{
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
ASTPointer<Identifier> name(parseIdentifier());
|
|
||||||
vector<ASTPointer<Expression>> arguments;
|
|
||||||
if (m_scanner->getCurrentToken() == Token::LParen)
|
|
||||||
{
|
|
||||||
m_scanner->next();
|
|
||||||
arguments = parseFunctionCallListArguments();
|
|
||||||
nodeFactory.markEndPosition();
|
|
||||||
expectToken(Token::RParen);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
nodeFactory.setEndPositionFromNode(name);
|
|
||||||
return nodeFactory.createNode<InheritanceSpecifier>(name, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
Declaration::Visibility Parser::parseVisibilitySpecifier(Token::Value _token)
|
|
||||||
{
|
|
||||||
Declaration::Visibility visibility(Declaration::Visibility::Default);
|
|
||||||
if (_token == Token::Public)
|
|
||||||
visibility = Declaration::Visibility::Public;
|
|
||||||
else if (_token == Token::Inheritable)
|
|
||||||
visibility = Declaration::Visibility::Inheritable;
|
|
||||||
else if (_token == Token::Private)
|
|
||||||
visibility = Declaration::Visibility::Private;
|
|
||||||
else if (_token == Token::External)
|
|
||||||
visibility = Declaration::Visibility::External;
|
|
||||||
else
|
|
||||||
solAssert(false, "Invalid visibility specifier.");
|
|
||||||
m_scanner->next();
|
|
||||||
return visibility;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const* _contractName)
|
|
||||||
{
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
ASTPointer<ASTString> docstring;
|
|
||||||
if (m_scanner->getCurrentCommentLiteral() != "")
|
|
||||||
docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
|
|
||||||
|
|
||||||
expectToken(Token::Function);
|
|
||||||
ASTPointer<ASTString> name;
|
|
||||||
if (m_scanner->getCurrentToken() == Token::LParen)
|
|
||||||
name = make_shared<ASTString>(); // anonymous function
|
|
||||||
else
|
|
||||||
name = expectIdentifierToken();
|
|
||||||
ASTPointer<ParameterList> parameters(parseParameterList());
|
|
||||||
bool isDeclaredConst = false;
|
|
||||||
Declaration::Visibility visibility(Declaration::Visibility::Default);
|
|
||||||
vector<ASTPointer<ModifierInvocation>> modifiers;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
Token::Value token = m_scanner->getCurrentToken();
|
|
||||||
if (token == Token::Const)
|
|
||||||
{
|
|
||||||
isDeclaredConst = true;
|
|
||||||
m_scanner->next();
|
|
||||||
}
|
|
||||||
else if (token == Token::Identifier)
|
|
||||||
modifiers.push_back(parseModifierInvocation());
|
|
||||||
else if (Token::isVisibilitySpecifier(token))
|
|
||||||
{
|
|
||||||
if (visibility != Declaration::Visibility::Default)
|
|
||||||
BOOST_THROW_EXCEPTION(createParserError("Multiple visibility specifiers."));
|
|
||||||
visibility = parseVisibilitySpecifier(token);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ASTPointer<ParameterList> returnParameters;
|
|
||||||
if (m_scanner->getCurrentToken() == Token::Returns)
|
|
||||||
{
|
|
||||||
bool const permitEmptyParameterList = false;
|
|
||||||
m_scanner->next();
|
|
||||||
returnParameters = parseParameterList(permitEmptyParameterList);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
returnParameters = createEmptyParameterList();
|
|
||||||
ASTPointer<Block> block = parseBlock();
|
|
||||||
nodeFactory.setEndPositionFromNode(block);
|
|
||||||
bool const c_isConstructor = (_contractName && *name == *_contractName);
|
|
||||||
return nodeFactory.createNode<FunctionDefinition>(name, visibility, c_isConstructor, docstring,
|
|
||||||
parameters, isDeclaredConst, modifiers,
|
|
||||||
returnParameters, block);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<StructDefinition> Parser::parseStructDefinition()
|
|
||||||
{
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
expectToken(Token::Struct);
|
|
||||||
ASTPointer<ASTString> name = expectIdentifierToken();
|
|
||||||
vector<ASTPointer<VariableDeclaration>> members;
|
|
||||||
expectToken(Token::LBrace);
|
|
||||||
while (m_scanner->getCurrentToken() != Token::RBrace)
|
|
||||||
{
|
|
||||||
members.push_back(parseVariableDeclaration());
|
|
||||||
expectToken(Token::Semicolon);
|
|
||||||
}
|
|
||||||
nodeFactory.markEndPosition();
|
|
||||||
expectToken(Token::RBrace);
|
|
||||||
return nodeFactory.createNode<StructDefinition>(name, members);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<EnumValue> Parser::parseEnumValue()
|
|
||||||
{
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
nodeFactory.markEndPosition();
|
|
||||||
return nodeFactory.createNode<EnumValue>(expectIdentifierToken());
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<EnumDefinition> Parser::parseEnumDefinition()
|
|
||||||
{
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
expectToken(Token::Enum);
|
|
||||||
ASTPointer<ASTString> name = expectIdentifierToken();
|
|
||||||
vector<ASTPointer<EnumValue>> members;
|
|
||||||
expectToken(Token::LBrace);
|
|
||||||
|
|
||||||
while (m_scanner->getCurrentToken() != Token::RBrace)
|
|
||||||
{
|
|
||||||
members.push_back(parseEnumValue());
|
|
||||||
if (m_scanner->getCurrentToken() == Token::RBrace)
|
|
||||||
break;
|
|
||||||
expectToken(Token::Comma);
|
|
||||||
if (m_scanner->getCurrentToken() != Token::Identifier)
|
|
||||||
BOOST_THROW_EXCEPTION(createParserError("Expected Identifier after ','"));
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeFactory.markEndPosition();
|
|
||||||
expectToken(Token::RBrace);
|
|
||||||
return nodeFactory.createNode<EnumDefinition>(name, members);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(VarDeclParserOptions const& _options)
|
|
||||||
{
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
ASTPointer<TypeName> type = parseTypeName(_options.allowVar);
|
|
||||||
if (type != nullptr)
|
|
||||||
nodeFactory.setEndPositionFromNode(type);
|
|
||||||
bool isIndexed = false;
|
|
||||||
ASTPointer<ASTString> identifier;
|
|
||||||
Token::Value token = m_scanner->getCurrentToken();
|
|
||||||
Declaration::Visibility visibility(Declaration::Visibility::Default);
|
|
||||||
if (_options.isStateVariable && Token::isVariableVisibilitySpecifier(token))
|
|
||||||
visibility = parseVisibilitySpecifier(token);
|
|
||||||
if (_options.allowIndexed && token == Token::Indexed)
|
|
||||||
{
|
|
||||||
isIndexed = true;
|
|
||||||
m_scanner->next();
|
|
||||||
}
|
|
||||||
nodeFactory.markEndPosition();
|
|
||||||
if (_options.allowEmptyName && m_scanner->getCurrentToken() != Token::Identifier)
|
|
||||||
{
|
|
||||||
identifier = make_shared<ASTString>("");
|
|
||||||
solAssert(type != nullptr, "");
|
|
||||||
nodeFactory.setEndPositionFromNode(type);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
identifier = expectIdentifierToken();
|
|
||||||
return nodeFactory.createNode<VariableDeclaration>(type, identifier,
|
|
||||||
visibility, _options.isStateVariable,
|
|
||||||
isIndexed);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
|
|
||||||
{
|
|
||||||
ScopeGuard resetModifierFlag([this]() { m_insideModifier = false; });
|
|
||||||
m_insideModifier = true;
|
|
||||||
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
ASTPointer<ASTString> docstring;
|
|
||||||
if (m_scanner->getCurrentCommentLiteral() != "")
|
|
||||||
docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
|
|
||||||
|
|
||||||
expectToken(Token::Modifier);
|
|
||||||
ASTPointer<ASTString> name(expectIdentifierToken());
|
|
||||||
ASTPointer<ParameterList> parameters;
|
|
||||||
if (m_scanner->getCurrentToken() == Token::LParen)
|
|
||||||
parameters = parseParameterList();
|
|
||||||
else
|
|
||||||
parameters = createEmptyParameterList();
|
|
||||||
ASTPointer<Block> block = parseBlock();
|
|
||||||
nodeFactory.setEndPositionFromNode(block);
|
|
||||||
return nodeFactory.createNode<ModifierDefinition>(name, docstring, parameters, block);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<EventDefinition> Parser::parseEventDefinition()
|
|
||||||
{
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
ASTPointer<ASTString> docstring;
|
|
||||||
if (m_scanner->getCurrentCommentLiteral() != "")
|
|
||||||
docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
|
|
||||||
|
|
||||||
expectToken(Token::Event);
|
|
||||||
ASTPointer<ASTString> name(expectIdentifierToken());
|
|
||||||
ASTPointer<ParameterList> parameters;
|
|
||||||
if (m_scanner->getCurrentToken() == Token::LParen)
|
|
||||||
parameters = parseParameterList(true, true);
|
|
||||||
else
|
|
||||||
parameters = createEmptyParameterList();
|
|
||||||
nodeFactory.markEndPosition();
|
|
||||||
expectToken(Token::Semicolon);
|
|
||||||
return nodeFactory.createNode<EventDefinition>(name, docstring, parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<ModifierInvocation> Parser::parseModifierInvocation()
|
|
||||||
{
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
ASTPointer<Identifier> name(parseIdentifier());
|
|
||||||
vector<ASTPointer<Expression>> arguments;
|
|
||||||
if (m_scanner->getCurrentToken() == Token::LParen)
|
|
||||||
{
|
|
||||||
m_scanner->next();
|
|
||||||
arguments = parseFunctionCallListArguments();
|
|
||||||
nodeFactory.markEndPosition();
|
|
||||||
expectToken(Token::RParen);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
nodeFactory.setEndPositionFromNode(name);
|
|
||||||
return nodeFactory.createNode<ModifierInvocation>(name, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<Identifier> Parser::parseIdentifier()
|
|
||||||
{
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
nodeFactory.markEndPosition();
|
|
||||||
return nodeFactory.createNode<Identifier>(expectIdentifierToken());
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
|
|
||||||
{
|
|
||||||
ASTPointer<TypeName> type;
|
|
||||||
Token::Value token = m_scanner->getCurrentToken();
|
|
||||||
if (Token::isElementaryTypeName(token))
|
|
||||||
{
|
|
||||||
type = ASTNodeFactory(*this).createNode<ElementaryTypeName>(token);
|
|
||||||
m_scanner->next();
|
|
||||||
}
|
|
||||||
else if (token == Token::Var)
|
|
||||||
{
|
|
||||||
if (!_allowVar)
|
|
||||||
BOOST_THROW_EXCEPTION(createParserError("Expected explicit type name."));
|
|
||||||
m_scanner->next();
|
|
||||||
}
|
|
||||||
else if (token == Token::Mapping)
|
|
||||||
{
|
|
||||||
type = parseMapping();
|
|
||||||
}
|
|
||||||
else if (token == Token::Identifier)
|
|
||||||
{
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
nodeFactory.markEndPosition();
|
|
||||||
type = nodeFactory.createNode<UserDefinedTypeName>(expectIdentifierToken());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
BOOST_THROW_EXCEPTION(createParserError("Expected type name"));
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<Mapping> Parser::parseMapping()
|
|
||||||
{
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
expectToken(Token::Mapping);
|
|
||||||
expectToken(Token::LParen);
|
|
||||||
if (!Token::isElementaryTypeName(m_scanner->getCurrentToken()))
|
|
||||||
BOOST_THROW_EXCEPTION(createParserError("Expected elementary type name for mapping key type"));
|
|
||||||
ASTPointer<ElementaryTypeName> keyType;
|
|
||||||
keyType = ASTNodeFactory(*this).createNode<ElementaryTypeName>(m_scanner->getCurrentToken());
|
|
||||||
m_scanner->next();
|
|
||||||
expectToken(Token::Arrow);
|
|
||||||
bool const allowVar = false;
|
|
||||||
ASTPointer<TypeName> valueType = parseTypeName(allowVar);
|
|
||||||
nodeFactory.markEndPosition();
|
|
||||||
expectToken(Token::RParen);
|
|
||||||
return nodeFactory.createNode<Mapping>(keyType, valueType);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty, bool _allowIndexed)
|
|
||||||
{
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
vector<ASTPointer<VariableDeclaration>> parameters;
|
|
||||||
VarDeclParserOptions options;
|
|
||||||
options.allowIndexed = _allowIndexed;
|
|
||||||
options.allowEmptyName = true;
|
|
||||||
expectToken(Token::LParen);
|
|
||||||
if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RParen)
|
|
||||||
{
|
|
||||||
parameters.push_back(parseVariableDeclaration(options));
|
|
||||||
while (m_scanner->getCurrentToken() != Token::RParen)
|
|
||||||
{
|
|
||||||
expectToken(Token::Comma);
|
|
||||||
parameters.push_back(parseVariableDeclaration(options));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nodeFactory.markEndPosition();
|
|
||||||
m_scanner->next();
|
|
||||||
return nodeFactory.createNode<ParameterList>(parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<Block> Parser::parseBlock()
|
|
||||||
{
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
expectToken(Token::LBrace);
|
|
||||||
vector<ASTPointer<Statement>> statements;
|
|
||||||
while (m_scanner->getCurrentToken() != Token::RBrace)
|
|
||||||
statements.push_back(parseStatement());
|
|
||||||
nodeFactory.markEndPosition();
|
|
||||||
expectToken(Token::RBrace);
|
|
||||||
return nodeFactory.createNode<Block>(statements);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<Statement> Parser::parseStatement()
|
|
||||||
{
|
|
||||||
ASTPointer<Statement> statement;
|
|
||||||
switch (m_scanner->getCurrentToken())
|
|
||||||
{
|
|
||||||
case Token::If:
|
|
||||||
return parseIfStatement();
|
|
||||||
case Token::While:
|
|
||||||
return parseWhileStatement();
|
|
||||||
case Token::For:
|
|
||||||
return parseForStatement();
|
|
||||||
case Token::LBrace:
|
|
||||||
return parseBlock();
|
|
||||||
// starting from here, all statements must be terminated by a semicolon
|
|
||||||
case Token::Continue:
|
|
||||||
statement = ASTNodeFactory(*this).createNode<Continue>();
|
|
||||||
m_scanner->next();
|
|
||||||
break;
|
|
||||||
case Token::Break:
|
|
||||||
statement = ASTNodeFactory(*this).createNode<Break>();
|
|
||||||
m_scanner->next();
|
|
||||||
break;
|
|
||||||
case Token::Return:
|
|
||||||
{
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
ASTPointer<Expression> expression;
|
|
||||||
if (m_scanner->next() != Token::Semicolon)
|
|
||||||
{
|
|
||||||
expression = parseExpression();
|
|
||||||
nodeFactory.setEndPositionFromNode(expression);
|
|
||||||
}
|
|
||||||
statement = nodeFactory.createNode<Return>(expression);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Token::Identifier:
|
|
||||||
if (m_insideModifier && m_scanner->getCurrentLiteral() == "_")
|
|
||||||
{
|
|
||||||
statement = ASTNodeFactory(*this).createNode<PlaceholderStatement>();
|
|
||||||
m_scanner->next();
|
|
||||||
return statement;
|
|
||||||
}
|
|
||||||
// fall-through
|
|
||||||
default:
|
|
||||||
statement = parseVarDefOrExprStmt();
|
|
||||||
}
|
|
||||||
expectToken(Token::Semicolon);
|
|
||||||
return statement;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<IfStatement> Parser::parseIfStatement()
|
|
||||||
{
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
expectToken(Token::If);
|
|
||||||
expectToken(Token::LParen);
|
|
||||||
ASTPointer<Expression> condition = parseExpression();
|
|
||||||
expectToken(Token::RParen);
|
|
||||||
ASTPointer<Statement> trueBody = parseStatement();
|
|
||||||
ASTPointer<Statement> falseBody;
|
|
||||||
if (m_scanner->getCurrentToken() == Token::Else)
|
|
||||||
{
|
|
||||||
m_scanner->next();
|
|
||||||
falseBody = parseStatement();
|
|
||||||
nodeFactory.setEndPositionFromNode(falseBody);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
nodeFactory.setEndPositionFromNode(trueBody);
|
|
||||||
return nodeFactory.createNode<IfStatement>(condition, trueBody, falseBody);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<WhileStatement> Parser::parseWhileStatement()
|
|
||||||
{
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
expectToken(Token::While);
|
|
||||||
expectToken(Token::LParen);
|
|
||||||
ASTPointer<Expression> condition = parseExpression();
|
|
||||||
expectToken(Token::RParen);
|
|
||||||
ASTPointer<Statement> body = parseStatement();
|
|
||||||
nodeFactory.setEndPositionFromNode(body);
|
|
||||||
return nodeFactory.createNode<WhileStatement>(condition, body);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<ForStatement> Parser::parseForStatement()
|
|
||||||
{
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
ASTPointer<Statement> initExpression;
|
|
||||||
ASTPointer<Expression> conditionExpression;
|
|
||||||
ASTPointer<ExpressionStatement> loopExpression;
|
|
||||||
expectToken(Token::For);
|
|
||||||
expectToken(Token::LParen);
|
|
||||||
|
|
||||||
// LTODO: Maybe here have some predicate like peekExpression() instead of checking for semicolon and RParen?
|
|
||||||
if (m_scanner->getCurrentToken() != Token::Semicolon)
|
|
||||||
initExpression = parseVarDefOrExprStmt();
|
|
||||||
expectToken(Token::Semicolon);
|
|
||||||
|
|
||||||
if (m_scanner->getCurrentToken() != Token::Semicolon)
|
|
||||||
conditionExpression = parseExpression();
|
|
||||||
expectToken(Token::Semicolon);
|
|
||||||
|
|
||||||
if (m_scanner->getCurrentToken() != Token::RParen)
|
|
||||||
loopExpression = parseExpressionStatement();
|
|
||||||
expectToken(Token::RParen);
|
|
||||||
|
|
||||||
ASTPointer<Statement> body = parseStatement();
|
|
||||||
nodeFactory.setEndPositionFromNode(body);
|
|
||||||
return nodeFactory.createNode<ForStatement>(initExpression,
|
|
||||||
conditionExpression,
|
|
||||||
loopExpression,
|
|
||||||
body);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<Statement> Parser::parseVarDefOrExprStmt()
|
|
||||||
{
|
|
||||||
if (peekVariableDefinition())
|
|
||||||
return parseVariableDefinition();
|
|
||||||
else
|
|
||||||
return parseExpressionStatement();
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<VariableDefinition> Parser::parseVariableDefinition()
|
|
||||||
{
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
VarDeclParserOptions options;
|
|
||||||
options.allowVar = true;
|
|
||||||
ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(options);
|
|
||||||
ASTPointer<Expression> value;
|
|
||||||
if (m_scanner->getCurrentToken() == Token::Assign)
|
|
||||||
{
|
|
||||||
m_scanner->next();
|
|
||||||
value = parseExpression();
|
|
||||||
nodeFactory.setEndPositionFromNode(value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
nodeFactory.setEndPositionFromNode(variable);
|
|
||||||
return nodeFactory.createNode<VariableDefinition>(variable, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<ExpressionStatement> Parser::parseExpressionStatement()
|
|
||||||
{
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
ASTPointer<Expression> expression = parseExpression();
|
|
||||||
nodeFactory.setEndPositionFromNode(expression);
|
|
||||||
return nodeFactory.createNode<ExpressionStatement>(expression);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<Expression> Parser::parseExpression()
|
|
||||||
{
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
ASTPointer<Expression> expression = parseBinaryExpression();
|
|
||||||
if (!Token::isAssignmentOp(m_scanner->getCurrentToken()))
|
|
||||||
return expression;
|
|
||||||
Token::Value assignmentOperator = expectAssignmentOperator();
|
|
||||||
ASTPointer<Expression> rightHandSide = parseExpression();
|
|
||||||
nodeFactory.setEndPositionFromNode(rightHandSide);
|
|
||||||
return nodeFactory.createNode<Assignment>(expression, assignmentOperator, rightHandSide);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence)
|
|
||||||
{
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
ASTPointer<Expression> expression = parseUnaryExpression();
|
|
||||||
int precedence = Token::precedence(m_scanner->getCurrentToken());
|
|
||||||
for (; precedence >= _minPrecedence; --precedence)
|
|
||||||
while (Token::precedence(m_scanner->getCurrentToken()) == precedence)
|
|
||||||
{
|
|
||||||
Token::Value op = m_scanner->getCurrentToken();
|
|
||||||
m_scanner->next();
|
|
||||||
ASTPointer<Expression> right = parseBinaryExpression(precedence + 1);
|
|
||||||
nodeFactory.setEndPositionFromNode(right);
|
|
||||||
expression = nodeFactory.createNode<BinaryOperation>(expression, op, right);
|
|
||||||
}
|
|
||||||
return expression;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<Expression> Parser::parseUnaryExpression()
|
|
||||||
{
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
Token::Value token = m_scanner->getCurrentToken();
|
|
||||||
if (Token::isUnaryOp(token) || Token::isCountOp(token))
|
|
||||||
{
|
|
||||||
// prefix expression
|
|
||||||
m_scanner->next();
|
|
||||||
ASTPointer<Expression> subExpression = parseUnaryExpression();
|
|
||||||
nodeFactory.setEndPositionFromNode(subExpression);
|
|
||||||
return nodeFactory.createNode<UnaryOperation>(token, subExpression, true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// potential postfix expression
|
|
||||||
ASTPointer<Expression> subExpression = parseLeftHandSideExpression();
|
|
||||||
token = m_scanner->getCurrentToken();
|
|
||||||
if (!Token::isCountOp(token))
|
|
||||||
return subExpression;
|
|
||||||
nodeFactory.markEndPosition();
|
|
||||||
m_scanner->next();
|
|
||||||
return nodeFactory.createNode<UnaryOperation>(token, subExpression, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<Expression> Parser::parseLeftHandSideExpression()
|
|
||||||
{
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
ASTPointer<Expression> expression;
|
|
||||||
if (m_scanner->getCurrentToken() == Token::New)
|
|
||||||
{
|
|
||||||
expectToken(Token::New);
|
|
||||||
ASTPointer<Identifier> contractName(parseIdentifier());
|
|
||||||
nodeFactory.setEndPositionFromNode(contractName);
|
|
||||||
expression = nodeFactory.createNode<NewExpression>(contractName);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
expression = parsePrimaryExpression();
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
switch (m_scanner->getCurrentToken())
|
|
||||||
{
|
|
||||||
case Token::LBrack:
|
|
||||||
{
|
|
||||||
m_scanner->next();
|
|
||||||
ASTPointer<Expression> index = parseExpression();
|
|
||||||
nodeFactory.markEndPosition();
|
|
||||||
expectToken(Token::RBrack);
|
|
||||||
expression = nodeFactory.createNode<IndexAccess>(expression, index);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Token::Period:
|
|
||||||
{
|
|
||||||
m_scanner->next();
|
|
||||||
nodeFactory.markEndPosition();
|
|
||||||
expression = nodeFactory.createNode<MemberAccess>(expression, expectIdentifierToken());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Token::LParen:
|
|
||||||
{
|
|
||||||
m_scanner->next();
|
|
||||||
vector<ASTPointer<Expression>> arguments;
|
|
||||||
vector<ASTPointer<ASTString>> names;
|
|
||||||
std::tie(arguments, names) = parseFunctionCallArguments();
|
|
||||||
nodeFactory.markEndPosition();
|
|
||||||
expectToken(Token::RParen);
|
|
||||||
expression = nodeFactory.createNode<FunctionCall>(expression, arguments, names);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return expression;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<Expression> Parser::parsePrimaryExpression()
|
|
||||||
{
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
Token::Value token = m_scanner->getCurrentToken();
|
|
||||||
ASTPointer<Expression> expression;
|
|
||||||
switch (token)
|
|
||||||
{
|
|
||||||
case Token::TrueLiteral:
|
|
||||||
case Token::FalseLiteral:
|
|
||||||
expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance());
|
|
||||||
break;
|
|
||||||
case Token::Number:
|
|
||||||
if (Token::isEtherSubdenomination(m_scanner->peekNextToken()))
|
|
||||||
{
|
|
||||||
ASTPointer<ASTString> literal = getLiteralAndAdvance();
|
|
||||||
nodeFactory.markEndPosition();
|
|
||||||
Literal::SubDenomination subdenomination = static_cast<Literal::SubDenomination>(m_scanner->getCurrentToken());
|
|
||||||
m_scanner->next();
|
|
||||||
expression = nodeFactory.createNode<Literal>(token, literal, subdenomination);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// fall-through
|
|
||||||
case Token::StringLiteral:
|
|
||||||
nodeFactory.markEndPosition();
|
|
||||||
expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance());
|
|
||||||
break;
|
|
||||||
case Token::Identifier:
|
|
||||||
nodeFactory.markEndPosition();
|
|
||||||
expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance());
|
|
||||||
break;
|
|
||||||
case Token::LParen:
|
|
||||||
{
|
|
||||||
m_scanner->next();
|
|
||||||
ASTPointer<Expression> expression = parseExpression();
|
|
||||||
expectToken(Token::RParen);
|
|
||||||
return expression;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
if (Token::isElementaryTypeName(token))
|
|
||||||
{
|
|
||||||
// used for casts
|
|
||||||
expression = nodeFactory.createNode<ElementaryTypeNameExpression>(token);
|
|
||||||
m_scanner->next();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
BOOST_THROW_EXCEPTION(createParserError("Expected primary expression."));
|
|
||||||
return ASTPointer<Expression>(); // this is not reached
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return expression;
|
|
||||||
}
|
|
||||||
|
|
||||||
vector<ASTPointer<Expression>> Parser::parseFunctionCallListArguments()
|
|
||||||
{
|
|
||||||
vector<ASTPointer<Expression>> arguments;
|
|
||||||
if (m_scanner->getCurrentToken() != Token::RParen)
|
|
||||||
{
|
|
||||||
arguments.push_back(parseExpression());
|
|
||||||
while (m_scanner->getCurrentToken() != Token::RParen)
|
|
||||||
{
|
|
||||||
expectToken(Token::Comma);
|
|
||||||
arguments.push_back(parseExpression());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return arguments;
|
|
||||||
}
|
|
||||||
|
|
||||||
pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> Parser::parseFunctionCallArguments()
|
|
||||||
{
|
|
||||||
pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> ret;
|
|
||||||
Token::Value token = m_scanner->getCurrentToken();
|
|
||||||
if (token == Token::LBrace)
|
|
||||||
{
|
|
||||||
// call({arg1 : 1, arg2 : 2 })
|
|
||||||
expectToken(Token::LBrace);
|
|
||||||
while (m_scanner->getCurrentToken() != Token::RBrace)
|
|
||||||
{
|
|
||||||
ret.second.push_back(expectIdentifierToken());
|
|
||||||
expectToken(Token::Colon);
|
|
||||||
ret.first.push_back(parseExpression());
|
|
||||||
|
|
||||||
if (m_scanner->getCurrentToken() == Token::Comma)
|
|
||||||
expectToken(Token::Comma);
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
expectToken(Token::RBrace);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ret.first = parseFunctionCallListArguments();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool Parser::peekVariableDefinition()
|
|
||||||
{
|
|
||||||
// distinguish between variable definition (and potentially assignment) and expression statement
|
|
||||||
// (which include assignments to other expressions and pre-declared variables)
|
|
||||||
// We have a variable definition if we get a keyword that specifies a type name, or
|
|
||||||
// in the case of a user-defined type, we have two identifiers following each other.
|
|
||||||
return (m_scanner->getCurrentToken() == Token::Mapping ||
|
|
||||||
m_scanner->getCurrentToken() == Token::Var ||
|
|
||||||
((Token::isElementaryTypeName(m_scanner->getCurrentToken()) ||
|
|
||||||
m_scanner->getCurrentToken() == Token::Identifier) &&
|
|
||||||
m_scanner->peekNextToken() == Token::Identifier));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Parser::expectToken(Token::Value _value)
|
|
||||||
{
|
|
||||||
if (m_scanner->getCurrentToken() != _value)
|
|
||||||
BOOST_THROW_EXCEPTION(createParserError(string("Expected token ") + string(Token::getName(_value))));
|
|
||||||
m_scanner->next();
|
|
||||||
}
|
|
||||||
|
|
||||||
Token::Value Parser::expectAssignmentOperator()
|
|
||||||
{
|
|
||||||
Token::Value op = m_scanner->getCurrentToken();
|
|
||||||
if (!Token::isAssignmentOp(op))
|
|
||||||
BOOST_THROW_EXCEPTION(createParserError("Expected assignment operator"));
|
|
||||||
m_scanner->next();
|
|
||||||
return op;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<ASTString> Parser::expectIdentifierToken()
|
|
||||||
{
|
|
||||||
if (m_scanner->getCurrentToken() != Token::Identifier)
|
|
||||||
BOOST_THROW_EXCEPTION(createParserError("Expected identifier"));
|
|
||||||
return getLiteralAndAdvance();
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<ASTString> Parser::getLiteralAndAdvance()
|
|
||||||
{
|
|
||||||
ASTPointer<ASTString> identifier = make_shared<ASTString>(m_scanner->getCurrentLiteral());
|
|
||||||
m_scanner->next();
|
|
||||||
return identifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPointer<ParameterList> Parser::createEmptyParameterList()
|
|
||||||
{
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
nodeFactory.setLocationEmpty();
|
|
||||||
return nodeFactory.createNode<ParameterList>(vector<ASTPointer<VariableDeclaration>>());
|
|
||||||
}
|
|
||||||
|
|
||||||
ParserError Parser::createParserError(string const& _description) const
|
|
||||||
{
|
|
||||||
return ParserError() << errinfo_sourceLocation(Location(getPosition(), getPosition(), getSourceName()))
|
|
||||||
<< errinfo_comment(_description);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
117
Parser.h
117
Parser.h
@ -1,117 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Solidity parser.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "libsolidity/AST.h"
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
class Scanner;
|
|
||||||
|
|
||||||
class Parser
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ASTPointer<SourceUnit> parse(std::shared_ptr<Scanner> const& _scanner);
|
|
||||||
std::shared_ptr<std::string const> const& getSourceName() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
class ASTNodeFactory;
|
|
||||||
|
|
||||||
/// Start position of the current token
|
|
||||||
int getPosition() const;
|
|
||||||
/// End position of the current token
|
|
||||||
int getEndPosition() const;
|
|
||||||
|
|
||||||
struct VarDeclParserOptions {
|
|
||||||
VarDeclParserOptions() {}
|
|
||||||
bool allowVar = false;
|
|
||||||
bool isStateVariable = false;
|
|
||||||
bool allowIndexed = false;
|
|
||||||
bool allowEmptyName = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
///@{
|
|
||||||
///@name Parsing functions for the AST nodes
|
|
||||||
ASTPointer<ImportDirective> parseImportDirective();
|
|
||||||
ASTPointer<ContractDefinition> parseContractDefinition();
|
|
||||||
ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier();
|
|
||||||
Declaration::Visibility parseVisibilitySpecifier(Token::Value _token);
|
|
||||||
ASTPointer<FunctionDefinition> parseFunctionDefinition(ASTString const* _contractName);
|
|
||||||
ASTPointer<StructDefinition> parseStructDefinition();
|
|
||||||
ASTPointer<EnumDefinition> parseEnumDefinition();
|
|
||||||
ASTPointer<EnumValue> parseEnumValue();
|
|
||||||
ASTPointer<VariableDeclaration> parseVariableDeclaration(VarDeclParserOptions const& _options = VarDeclParserOptions());
|
|
||||||
ASTPointer<ModifierDefinition> parseModifierDefinition();
|
|
||||||
ASTPointer<EventDefinition> parseEventDefinition();
|
|
||||||
ASTPointer<ModifierInvocation> parseModifierInvocation();
|
|
||||||
ASTPointer<Identifier> parseIdentifier();
|
|
||||||
ASTPointer<TypeName> parseTypeName(bool _allowVar);
|
|
||||||
ASTPointer<Mapping> parseMapping();
|
|
||||||
ASTPointer<ParameterList> parseParameterList(bool _allowEmpty = true, bool _allowIndexed = false);
|
|
||||||
ASTPointer<Block> parseBlock();
|
|
||||||
ASTPointer<Statement> parseStatement();
|
|
||||||
ASTPointer<IfStatement> parseIfStatement();
|
|
||||||
ASTPointer<WhileStatement> parseWhileStatement();
|
|
||||||
ASTPointer<ForStatement> parseForStatement();
|
|
||||||
ASTPointer<Statement> parseVarDefOrExprStmt();
|
|
||||||
ASTPointer<VariableDefinition> parseVariableDefinition();
|
|
||||||
ASTPointer<ExpressionStatement> parseExpressionStatement();
|
|
||||||
ASTPointer<Expression> parseExpression();
|
|
||||||
ASTPointer<Expression> parseBinaryExpression(int _minPrecedence = 4);
|
|
||||||
ASTPointer<Expression> parseUnaryExpression();
|
|
||||||
ASTPointer<Expression> parseLeftHandSideExpression();
|
|
||||||
ASTPointer<Expression> parsePrimaryExpression();
|
|
||||||
std::vector<ASTPointer<Expression>> parseFunctionCallListArguments();
|
|
||||||
std::pair<std::vector<ASTPointer<Expression>>, std::vector<ASTPointer<ASTString>>> parseFunctionCallArguments();
|
|
||||||
///@}
|
|
||||||
|
|
||||||
///@{
|
|
||||||
///@name Helper functions
|
|
||||||
|
|
||||||
/// Peeks ahead in the scanner to determine if a variable definition is going to follow
|
|
||||||
bool peekVariableDefinition();
|
|
||||||
|
|
||||||
/// If current token value is not _value, throw exception otherwise advance token.
|
|
||||||
void expectToken(Token::Value _value);
|
|
||||||
Token::Value expectAssignmentOperator();
|
|
||||||
ASTPointer<ASTString> expectIdentifierToken();
|
|
||||||
ASTPointer<ASTString> getLiteralAndAdvance();
|
|
||||||
///@}
|
|
||||||
|
|
||||||
/// Creates an empty ParameterList at the current location (used if parameters can be omitted).
|
|
||||||
ASTPointer<ParameterList> createEmptyParameterList();
|
|
||||||
|
|
||||||
/// Creates a @ref ParserError exception and annotates it with the current position and the
|
|
||||||
/// given @a _description.
|
|
||||||
ParserError createParserError(std::string const& _description) const;
|
|
||||||
|
|
||||||
std::shared_ptr<Scanner> m_scanner;
|
|
||||||
/// Flag that signifies whether '_' is parsed as a PlaceholderStatement or a regular identifier.
|
|
||||||
bool m_insideModifier = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
769
Scanner.cpp
769
Scanner.cpp
@ -1,769 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
This file is derived from the file "scanner.cc", which was part of the
|
|
||||||
V8 project. The original copyright header follows:
|
|
||||||
|
|
||||||
Copyright 2006-2012, the V8 project authors. All rights reserved.
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following
|
|
||||||
disclaimer in the documentation and/or other materials provided
|
|
||||||
with the distribution.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Solidity scanner.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <tuple>
|
|
||||||
#include <libsolidity/Utils.h>
|
|
||||||
#include <libsolidity/Scanner.h>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
bool isDecimalDigit(char c)
|
|
||||||
{
|
|
||||||
return '0' <= c && c <= '9';
|
|
||||||
}
|
|
||||||
bool isHexDigit(char c)
|
|
||||||
{
|
|
||||||
return isDecimalDigit(c)
|
|
||||||
|| ('a' <= c && c <= 'f')
|
|
||||||
|| ('A' <= c && c <= 'F');
|
|
||||||
}
|
|
||||||
bool isLineTerminator(char c)
|
|
||||||
{
|
|
||||||
return c == '\n';
|
|
||||||
}
|
|
||||||
bool isWhiteSpace(char c)
|
|
||||||
{
|
|
||||||
return c == ' ' || c == '\n' || c == '\t' || c == '\r';
|
|
||||||
}
|
|
||||||
bool isIdentifierStart(char c)
|
|
||||||
{
|
|
||||||
return c == '_' || c == '$' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
|
|
||||||
}
|
|
||||||
bool isIdentifierPart(char c)
|
|
||||||
{
|
|
||||||
return isIdentifierStart(c) || isDecimalDigit(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
int hexValue(char c)
|
|
||||||
{
|
|
||||||
if (c >= '0' && c <= '9')
|
|
||||||
return c - '0';
|
|
||||||
else if (c >= 'a' && c <= 'f')
|
|
||||||
return c - 'a' + 10;
|
|
||||||
else if (c >= 'A' && c <= 'F')
|
|
||||||
return c - 'A' + 10;
|
|
||||||
else return -1;
|
|
||||||
}
|
|
||||||
} // end anonymous namespace
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Scoped helper for literal recording. Automatically drops the literal
|
|
||||||
/// if aborting the scanning before it's complete.
|
|
||||||
enum LiteralType {
|
|
||||||
LITERAL_TYPE_STRING,
|
|
||||||
LITERAL_TYPE_NUMBER, // not really different from string type in behaviour
|
|
||||||
LITERAL_TYPE_COMMENT
|
|
||||||
};
|
|
||||||
|
|
||||||
class LiteralScope
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit LiteralScope(Scanner* _self, enum LiteralType _type): m_type(_type)
|
|
||||||
, m_scanner(_self)
|
|
||||||
, m_complete(false)
|
|
||||||
{
|
|
||||||
if (_type == LITERAL_TYPE_COMMENT)
|
|
||||||
m_scanner->m_nextSkippedComment.literal.clear();
|
|
||||||
else
|
|
||||||
m_scanner->m_nextToken.literal.clear();
|
|
||||||
}
|
|
||||||
~LiteralScope()
|
|
||||||
{
|
|
||||||
if (!m_complete)
|
|
||||||
{
|
|
||||||
if (m_type == LITERAL_TYPE_COMMENT)
|
|
||||||
m_scanner->m_nextSkippedComment.literal.clear();
|
|
||||||
else
|
|
||||||
m_scanner->m_nextToken.literal.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void complete() { m_complete = true; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
enum LiteralType m_type;
|
|
||||||
Scanner* m_scanner;
|
|
||||||
bool m_complete;
|
|
||||||
}; // end of LiteralScope class
|
|
||||||
|
|
||||||
|
|
||||||
void Scanner::reset(CharStream const& _source, string const& _sourceName)
|
|
||||||
{
|
|
||||||
m_source = _source;
|
|
||||||
m_sourceName = make_shared<string const>(_sourceName);
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Scanner::reset()
|
|
||||||
{
|
|
||||||
m_source.reset();
|
|
||||||
m_char = m_source.get();
|
|
||||||
skipWhitespace();
|
|
||||||
scanToken();
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Scanner::scanHexByte(char& o_scannedByte)
|
|
||||||
{
|
|
||||||
char x = 0;
|
|
||||||
for (int i = 0; i < 2; i++)
|
|
||||||
{
|
|
||||||
int d = hexValue(m_char);
|
|
||||||
if (d < 0)
|
|
||||||
{
|
|
||||||
rollback(i);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
x = x * 16 + d;
|
|
||||||
advance();
|
|
||||||
}
|
|
||||||
o_scannedByte = x;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Ensure that tokens can be stored in a byte.
|
|
||||||
BOOST_STATIC_ASSERT(Token::NUM_TOKENS <= 0x100);
|
|
||||||
|
|
||||||
Token::Value Scanner::next()
|
|
||||||
{
|
|
||||||
m_currentToken = m_nextToken;
|
|
||||||
m_skippedComment = m_nextSkippedComment;
|
|
||||||
scanToken();
|
|
||||||
|
|
||||||
return m_currentToken.token;
|
|
||||||
}
|
|
||||||
|
|
||||||
Token::Value Scanner::selectToken(char _next, Token::Value _then, Token::Value _else)
|
|
||||||
{
|
|
||||||
advance();
|
|
||||||
if (m_char == _next)
|
|
||||||
return selectToken(_then);
|
|
||||||
else
|
|
||||||
return _else;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Scanner::skipWhitespace()
|
|
||||||
{
|
|
||||||
int const startPosition = getSourcePos();
|
|
||||||
while (isWhiteSpace(m_char))
|
|
||||||
advance();
|
|
||||||
// Return whether or not we skipped any characters.
|
|
||||||
return getSourcePos() != startPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Scanner::skipWhitespaceExceptLF()
|
|
||||||
{
|
|
||||||
int const startPosition = getSourcePos();
|
|
||||||
while (isWhiteSpace(m_char) && !isLineTerminator(m_char))
|
|
||||||
advance();
|
|
||||||
// Return whether or not we skipped any characters.
|
|
||||||
return getSourcePos() != startPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
Token::Value Scanner::skipSingleLineComment()
|
|
||||||
{
|
|
||||||
// The line terminator at the end of the line is not considered
|
|
||||||
// to be part of the single-line comment; it is recognized
|
|
||||||
// separately by the lexical grammar and becomes part of the
|
|
||||||
// stream of input elements for the syntactic grammar
|
|
||||||
while (advance() && !isLineTerminator(m_char)) { };
|
|
||||||
return Token::Whitespace;
|
|
||||||
}
|
|
||||||
|
|
||||||
Token::Value Scanner::scanSingleLineDocComment()
|
|
||||||
{
|
|
||||||
LiteralScope literal(this, LITERAL_TYPE_COMMENT);
|
|
||||||
advance(); //consume the last '/' at ///
|
|
||||||
skipWhitespaceExceptLF();
|
|
||||||
while (!isSourcePastEndOfInput())
|
|
||||||
{
|
|
||||||
if (isLineTerminator(m_char))
|
|
||||||
{
|
|
||||||
// check if next line is also a documentation comment
|
|
||||||
skipWhitespace();
|
|
||||||
if (!m_source.isPastEndOfInput(3) &&
|
|
||||||
m_source.get(0) == '/' &&
|
|
||||||
m_source.get(1) == '/' &&
|
|
||||||
m_source.get(2) == '/')
|
|
||||||
{
|
|
||||||
addCommentLiteralChar('\n');
|
|
||||||
m_char = m_source.advanceAndGet(3);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
break; // next line is not a documentation comment, we are done
|
|
||||||
|
|
||||||
}
|
|
||||||
addCommentLiteralChar(m_char);
|
|
||||||
advance();
|
|
||||||
}
|
|
||||||
literal.complete();
|
|
||||||
return Token::CommentLiteral;
|
|
||||||
}
|
|
||||||
|
|
||||||
Token::Value Scanner::skipMultiLineComment()
|
|
||||||
{
|
|
||||||
advance();
|
|
||||||
while (!isSourcePastEndOfInput())
|
|
||||||
{
|
|
||||||
char ch = m_char;
|
|
||||||
advance();
|
|
||||||
|
|
||||||
// If we have reached the end of the multi-line comment, we
|
|
||||||
// consume the '/' and insert a whitespace. This way all
|
|
||||||
// multi-line comments are treated as whitespace.
|
|
||||||
if (ch == '*' && m_char == '/')
|
|
||||||
{
|
|
||||||
m_char = ' ';
|
|
||||||
return Token::Whitespace;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Unterminated multi-line comment.
|
|
||||||
return Token::Illegal;
|
|
||||||
}
|
|
||||||
|
|
||||||
Token::Value Scanner::scanMultiLineDocComment()
|
|
||||||
{
|
|
||||||
LiteralScope literal(this, LITERAL_TYPE_COMMENT);
|
|
||||||
bool endFound = false;
|
|
||||||
bool charsAdded = false;
|
|
||||||
|
|
||||||
while (!isSourcePastEndOfInput())
|
|
||||||
{
|
|
||||||
//handle newlines in multline comments
|
|
||||||
if (isLineTerminator(m_char))
|
|
||||||
{
|
|
||||||
skipWhitespace();
|
|
||||||
if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) != '/')
|
|
||||||
{ // skip first '*' in subsequent lines
|
|
||||||
if (charsAdded)
|
|
||||||
addCommentLiteralChar('\n');
|
|
||||||
m_char = m_source.advanceAndGet(2);
|
|
||||||
}
|
|
||||||
else if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) == '/')
|
|
||||||
{ // if after newline the comment ends, don't insert the newline
|
|
||||||
m_char = m_source.advanceAndGet(2);
|
|
||||||
endFound = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (charsAdded)
|
|
||||||
addCommentLiteralChar('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) == '/')
|
|
||||||
{
|
|
||||||
m_char = m_source.advanceAndGet(2);
|
|
||||||
endFound = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
addCommentLiteralChar(m_char);
|
|
||||||
charsAdded = true;
|
|
||||||
advance();
|
|
||||||
}
|
|
||||||
literal.complete();
|
|
||||||
if (!endFound)
|
|
||||||
return Token::Illegal;
|
|
||||||
else
|
|
||||||
return Token::CommentLiteral;
|
|
||||||
}
|
|
||||||
|
|
||||||
Token::Value Scanner::scanSlash()
|
|
||||||
{
|
|
||||||
int firstSlashPosition = getSourcePos();
|
|
||||||
advance();
|
|
||||||
if (m_char == '/')
|
|
||||||
{
|
|
||||||
if (!advance()) /* double slash comment directly before EOS */
|
|
||||||
return Token::Whitespace;
|
|
||||||
else if (m_char == '/')
|
|
||||||
{
|
|
||||||
// doxygen style /// comment
|
|
||||||
Token::Value comment;
|
|
||||||
m_nextSkippedComment.location.start = firstSlashPosition;
|
|
||||||
comment = scanSingleLineDocComment();
|
|
||||||
m_nextSkippedComment.location.end = getSourcePos();
|
|
||||||
m_nextSkippedComment.token = comment;
|
|
||||||
return Token::Whitespace;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return skipSingleLineComment();
|
|
||||||
}
|
|
||||||
else if (m_char == '*')
|
|
||||||
{
|
|
||||||
// doxygen style /** natspec comment
|
|
||||||
if (!advance()) /* slash star comment before EOS */
|
|
||||||
return Token::Whitespace;
|
|
||||||
else if (m_char == '*')
|
|
||||||
{
|
|
||||||
advance(); //consume the last '*' at /**
|
|
||||||
skipWhitespaceExceptLF();
|
|
||||||
|
|
||||||
// special case of a closed normal multiline comment
|
|
||||||
if (!m_source.isPastEndOfInput() && m_source.get(0) == '/')
|
|
||||||
advance(); //skip the closing slash
|
|
||||||
else // we actually have a multiline documentation comment
|
|
||||||
{
|
|
||||||
Token::Value comment;
|
|
||||||
m_nextSkippedComment.location.start = firstSlashPosition;
|
|
||||||
comment = scanMultiLineDocComment();
|
|
||||||
m_nextSkippedComment.location.end = getSourcePos();
|
|
||||||
m_nextSkippedComment.token = comment;
|
|
||||||
}
|
|
||||||
return Token::Whitespace;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return skipMultiLineComment();
|
|
||||||
}
|
|
||||||
else if (m_char == '=')
|
|
||||||
return selectToken(Token::AssignDiv);
|
|
||||||
else
|
|
||||||
return Token::Div;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Scanner::scanToken()
|
|
||||||
{
|
|
||||||
m_nextToken.literal.clear();
|
|
||||||
m_nextSkippedComment.literal.clear();
|
|
||||||
Token::Value token;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
// Remember the position of the next token
|
|
||||||
m_nextToken.location.start = getSourcePos();
|
|
||||||
switch (m_char)
|
|
||||||
{
|
|
||||||
case '\n': // fall-through
|
|
||||||
case ' ':
|
|
||||||
case '\t':
|
|
||||||
token = selectToken(Token::Whitespace);
|
|
||||||
break;
|
|
||||||
case '"':
|
|
||||||
case '\'':
|
|
||||||
token = scanString();
|
|
||||||
break;
|
|
||||||
case '<':
|
|
||||||
// < <= << <<=
|
|
||||||
advance();
|
|
||||||
if (m_char == '=')
|
|
||||||
token = selectToken(Token::LessThanOrEqual);
|
|
||||||
else if (m_char == '<')
|
|
||||||
token = selectToken('=', Token::AssignShl, Token::SHL);
|
|
||||||
else
|
|
||||||
token = Token::LessThan;
|
|
||||||
break;
|
|
||||||
case '>':
|
|
||||||
// > >= >> >>= >>> >>>=
|
|
||||||
advance();
|
|
||||||
if (m_char == '=')
|
|
||||||
token = selectToken(Token::GreaterThanOrEqual);
|
|
||||||
else if (m_char == '>')
|
|
||||||
{
|
|
||||||
// >> >>= >>> >>>=
|
|
||||||
advance();
|
|
||||||
if (m_char == '=')
|
|
||||||
token = selectToken(Token::AssignSar);
|
|
||||||
else if (m_char == '>')
|
|
||||||
token = selectToken('=', Token::AssignShr, Token::SHR);
|
|
||||||
else
|
|
||||||
token = Token::SAR;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
token = Token::GreaterThan;
|
|
||||||
break;
|
|
||||||
case '=':
|
|
||||||
// = == =>
|
|
||||||
advance();
|
|
||||||
if (m_char == '=')
|
|
||||||
token = selectToken(Token::Equal);
|
|
||||||
else if (m_char == '>')
|
|
||||||
token = selectToken(Token::Arrow);
|
|
||||||
else
|
|
||||||
token = Token::Assign;
|
|
||||||
break;
|
|
||||||
case '!':
|
|
||||||
// ! !=
|
|
||||||
advance();
|
|
||||||
if (m_char == '=')
|
|
||||||
token = selectToken(Token::NotEqual);
|
|
||||||
else
|
|
||||||
token = Token::Not;
|
|
||||||
break;
|
|
||||||
case '+':
|
|
||||||
// + ++ +=
|
|
||||||
advance();
|
|
||||||
if (m_char == '+')
|
|
||||||
token = selectToken(Token::Inc);
|
|
||||||
else if (m_char == '=')
|
|
||||||
token = selectToken(Token::AssignAdd);
|
|
||||||
else
|
|
||||||
token = Token::Add;
|
|
||||||
break;
|
|
||||||
case '-':
|
|
||||||
// - -- -=
|
|
||||||
advance();
|
|
||||||
if (m_char == '-')
|
|
||||||
token = selectToken(Token::Dec);
|
|
||||||
else if (m_char == '=')
|
|
||||||
token = selectToken(Token::AssignSub);
|
|
||||||
else
|
|
||||||
token = Token::Sub;
|
|
||||||
break;
|
|
||||||
case '*':
|
|
||||||
// * ** *=
|
|
||||||
advance();
|
|
||||||
if (m_char == '*')
|
|
||||||
token = selectToken(Token::Exp);
|
|
||||||
else if (m_char == '=')
|
|
||||||
token = selectToken(Token::AssignMul);
|
|
||||||
else
|
|
||||||
token = Token::Mul;
|
|
||||||
break;
|
|
||||||
case '%':
|
|
||||||
// % %=
|
|
||||||
token = selectToken('=', Token::AssignMod, Token::Mod);
|
|
||||||
break;
|
|
||||||
case '/':
|
|
||||||
// / // /* /=
|
|
||||||
token = scanSlash();
|
|
||||||
break;
|
|
||||||
case '&':
|
|
||||||
// & && &=
|
|
||||||
advance();
|
|
||||||
if (m_char == '&')
|
|
||||||
token = selectToken(Token::And);
|
|
||||||
else if (m_char == '=')
|
|
||||||
token = selectToken(Token::AssignBitAnd);
|
|
||||||
else
|
|
||||||
token = Token::BitAnd;
|
|
||||||
break;
|
|
||||||
case '|':
|
|
||||||
// | || |=
|
|
||||||
advance();
|
|
||||||
if (m_char == '|')
|
|
||||||
token = selectToken(Token::Or);
|
|
||||||
else if (m_char == '=')
|
|
||||||
token = selectToken(Token::AssignBitOr);
|
|
||||||
else
|
|
||||||
token = Token::BitOr;
|
|
||||||
break;
|
|
||||||
case '^':
|
|
||||||
// ^ ^=
|
|
||||||
token = selectToken('=', Token::AssignBitXor, Token::BitXor);
|
|
||||||
break;
|
|
||||||
case '.':
|
|
||||||
// . Number
|
|
||||||
advance();
|
|
||||||
if (isDecimalDigit(m_char))
|
|
||||||
token = scanNumber('.');
|
|
||||||
else
|
|
||||||
token = Token::Period;
|
|
||||||
break;
|
|
||||||
case ':':
|
|
||||||
token = selectToken(Token::Colon);
|
|
||||||
break;
|
|
||||||
case ';':
|
|
||||||
token = selectToken(Token::Semicolon);
|
|
||||||
break;
|
|
||||||
case ',':
|
|
||||||
token = selectToken(Token::Comma);
|
|
||||||
break;
|
|
||||||
case '(':
|
|
||||||
token = selectToken(Token::LParen);
|
|
||||||
break;
|
|
||||||
case ')':
|
|
||||||
token = selectToken(Token::RParen);
|
|
||||||
break;
|
|
||||||
case '[':
|
|
||||||
token = selectToken(Token::LBrack);
|
|
||||||
break;
|
|
||||||
case ']':
|
|
||||||
token = selectToken(Token::RBrack);
|
|
||||||
break;
|
|
||||||
case '{':
|
|
||||||
token = selectToken(Token::LBrace);
|
|
||||||
break;
|
|
||||||
case '}':
|
|
||||||
token = selectToken(Token::RBrace);
|
|
||||||
break;
|
|
||||||
case '?':
|
|
||||||
token = selectToken(Token::Conditional);
|
|
||||||
break;
|
|
||||||
case '~':
|
|
||||||
token = selectToken(Token::BitNot);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (isIdentifierStart(m_char))
|
|
||||||
token = scanIdentifierOrKeyword();
|
|
||||||
else if (isDecimalDigit(m_char))
|
|
||||||
token = scanNumber();
|
|
||||||
else if (skipWhitespace())
|
|
||||||
token = Token::Whitespace;
|
|
||||||
else if (isSourcePastEndOfInput())
|
|
||||||
token = Token::EOS;
|
|
||||||
else
|
|
||||||
token = selectToken(Token::Illegal);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Continue scanning for tokens as long as we're just skipping
|
|
||||||
// whitespace.
|
|
||||||
}
|
|
||||||
while (token == Token::Whitespace);
|
|
||||||
m_nextToken.location.end = getSourcePos();
|
|
||||||
m_nextToken.token = token;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Scanner::scanEscape()
|
|
||||||
{
|
|
||||||
char c = m_char;
|
|
||||||
advance();
|
|
||||||
// Skip escaped newlines.
|
|
||||||
if (isLineTerminator(c))
|
|
||||||
return true;
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case '\'': // fall through
|
|
||||||
case '"': // fall through
|
|
||||||
case '\\':
|
|
||||||
break;
|
|
||||||
case 'b':
|
|
||||||
c = '\b';
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
c = '\f';
|
|
||||||
break;
|
|
||||||
case 'n':
|
|
||||||
c = '\n';
|
|
||||||
break;
|
|
||||||
case 'r':
|
|
||||||
c = '\r';
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
c = '\t';
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
c = '\v';
|
|
||||||
break;
|
|
||||||
case 'x':
|
|
||||||
if (!scanHexByte(c))
|
|
||||||
return false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
addLiteralChar(c);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Token::Value Scanner::scanString()
|
|
||||||
{
|
|
||||||
char const quote = m_char;
|
|
||||||
advance(); // consume quote
|
|
||||||
LiteralScope literal(this, LITERAL_TYPE_STRING);
|
|
||||||
while (m_char != quote && !isSourcePastEndOfInput() && !isLineTerminator(m_char))
|
|
||||||
{
|
|
||||||
char c = m_char;
|
|
||||||
advance();
|
|
||||||
if (c == '\\')
|
|
||||||
{
|
|
||||||
if (isSourcePastEndOfInput() || !scanEscape())
|
|
||||||
return Token::Illegal;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
addLiteralChar(c);
|
|
||||||
}
|
|
||||||
if (m_char != quote)
|
|
||||||
return Token::Illegal;
|
|
||||||
literal.complete();
|
|
||||||
advance(); // consume quote
|
|
||||||
return Token::StringLiteral;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Scanner::scanDecimalDigits()
|
|
||||||
{
|
|
||||||
while (isDecimalDigit(m_char))
|
|
||||||
addLiteralCharAndAdvance();
|
|
||||||
}
|
|
||||||
|
|
||||||
Token::Value Scanner::scanNumber(char _charSeen)
|
|
||||||
{
|
|
||||||
enum { DECIMAL, HEX, BINARY } kind = DECIMAL;
|
|
||||||
LiteralScope literal(this, LITERAL_TYPE_NUMBER);
|
|
||||||
if (_charSeen == '.')
|
|
||||||
{
|
|
||||||
// we have already seen a decimal point of the float
|
|
||||||
addLiteralChar('.');
|
|
||||||
scanDecimalDigits(); // we know we have at least one digit
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
solAssert(_charSeen == 0, "");
|
|
||||||
// if the first character is '0' we must check for octals and hex
|
|
||||||
if (m_char == '0')
|
|
||||||
{
|
|
||||||
addLiteralCharAndAdvance();
|
|
||||||
// either 0, 0exxx, 0Exxx, 0.xxx or a hex number
|
|
||||||
if (m_char == 'x' || m_char == 'X')
|
|
||||||
{
|
|
||||||
// hex number
|
|
||||||
kind = HEX;
|
|
||||||
addLiteralCharAndAdvance();
|
|
||||||
if (!isHexDigit(m_char))
|
|
||||||
return Token::Illegal; // we must have at least one hex digit after 'x'/'X'
|
|
||||||
while (isHexDigit(m_char))
|
|
||||||
addLiteralCharAndAdvance();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Parse decimal digits and allow trailing fractional part.
|
|
||||||
if (kind == DECIMAL)
|
|
||||||
{
|
|
||||||
scanDecimalDigits(); // optional
|
|
||||||
if (m_char == '.')
|
|
||||||
{
|
|
||||||
addLiteralCharAndAdvance();
|
|
||||||
scanDecimalDigits(); // optional
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// scan exponent, if any
|
|
||||||
if (m_char == 'e' || m_char == 'E')
|
|
||||||
{
|
|
||||||
solAssert(kind != HEX, "'e'/'E' must be scanned as part of the hex number");
|
|
||||||
if (kind != DECIMAL)
|
|
||||||
return Token::Illegal;
|
|
||||||
// scan exponent
|
|
||||||
addLiteralCharAndAdvance();
|
|
||||||
if (m_char == '+' || m_char == '-')
|
|
||||||
addLiteralCharAndAdvance();
|
|
||||||
if (!isDecimalDigit(m_char))
|
|
||||||
return Token::Illegal; // we must have at least one decimal digit after 'e'/'E'
|
|
||||||
scanDecimalDigits();
|
|
||||||
}
|
|
||||||
// The source character immediately following a numeric literal must
|
|
||||||
// not be an identifier start or a decimal digit; see ECMA-262
|
|
||||||
// section 7.8.3, page 17 (note that we read only one decimal digit
|
|
||||||
// if the value is 0).
|
|
||||||
if (isDecimalDigit(m_char) || isIdentifierStart(m_char))
|
|
||||||
return Token::Illegal;
|
|
||||||
literal.complete();
|
|
||||||
return Token::Number;
|
|
||||||
}
|
|
||||||
|
|
||||||
Token::Value Scanner::scanIdentifierOrKeyword()
|
|
||||||
{
|
|
||||||
solAssert(isIdentifierStart(m_char), "");
|
|
||||||
LiteralScope literal(this, LITERAL_TYPE_STRING);
|
|
||||||
addLiteralCharAndAdvance();
|
|
||||||
// Scan the rest of the identifier characters.
|
|
||||||
while (isIdentifierPart(m_char))
|
|
||||||
addLiteralCharAndAdvance();
|
|
||||||
literal.complete();
|
|
||||||
return Token::fromIdentifierOrKeyword(m_nextToken.literal);
|
|
||||||
}
|
|
||||||
|
|
||||||
char CharStream::advanceAndGet(size_t _chars)
|
|
||||||
{
|
|
||||||
if (isPastEndOfInput())
|
|
||||||
return 0;
|
|
||||||
m_pos += _chars;
|
|
||||||
if (isPastEndOfInput())
|
|
||||||
return 0;
|
|
||||||
return m_source[m_pos];
|
|
||||||
}
|
|
||||||
|
|
||||||
char CharStream::rollback(size_t _amount)
|
|
||||||
{
|
|
||||||
solAssert(m_pos >= _amount, "");
|
|
||||||
m_pos -= _amount;
|
|
||||||
return get();
|
|
||||||
}
|
|
||||||
|
|
||||||
string CharStream::getLineAtPosition(int _position) const
|
|
||||||
{
|
|
||||||
// if _position points to \n, it returns the line before the \n
|
|
||||||
using size_type = string::size_type;
|
|
||||||
size_type searchStart = min<size_type>(m_source.size(), _position);
|
|
||||||
if (searchStart > 0)
|
|
||||||
searchStart--;
|
|
||||||
size_type lineStart = m_source.rfind('\n', searchStart);
|
|
||||||
if (lineStart == string::npos)
|
|
||||||
lineStart = 0;
|
|
||||||
else
|
|
||||||
lineStart++;
|
|
||||||
return m_source.substr(lineStart, min(m_source.find('\n', lineStart),
|
|
||||||
m_source.size()) - lineStart);
|
|
||||||
}
|
|
||||||
|
|
||||||
tuple<int, int> CharStream::translatePositionToLineColumn(int _position) const
|
|
||||||
{
|
|
||||||
using size_type = string::size_type;
|
|
||||||
size_type searchPosition = min<size_type>(m_source.size(), _position);
|
|
||||||
int lineNumber = count(m_source.begin(), m_source.begin() + searchPosition, '\n');
|
|
||||||
size_type lineStart;
|
|
||||||
if (searchPosition == 0)
|
|
||||||
lineStart = 0;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lineStart = m_source.rfind('\n', searchPosition - 1);
|
|
||||||
lineStart = lineStart == string::npos ? 0 : lineStart + 1;
|
|
||||||
}
|
|
||||||
return tuple<int, int>(lineNumber, searchPosition - lineStart);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
224
Scanner.h
224
Scanner.h
@ -1,224 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
This file is derived from the file "scanner.h", which was part of the
|
|
||||||
V8 project. The original copyright header follows:
|
|
||||||
|
|
||||||
Copyright 2006-2012, the V8 project authors. All rights reserved.
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following
|
|
||||||
disclaimer in the documentation and/or other materials provided
|
|
||||||
with the distribution.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Solidity scanner.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <libdevcore/Common.h>
|
|
||||||
#include <libdevcore/Log.h>
|
|
||||||
#include <libdevcore/CommonData.h>
|
|
||||||
#include <libsolidity/BaseTypes.h>
|
|
||||||
#include <libsolidity/Token.h>
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
class AstRawString;
|
|
||||||
class AstValueFactory;
|
|
||||||
class ParserRecorder;
|
|
||||||
|
|
||||||
class CharStream
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CharStream(): m_pos(0) {}
|
|
||||||
explicit CharStream(std::string const& _source): m_source(_source), m_pos(0) {}
|
|
||||||
int getPos() const { return m_pos; }
|
|
||||||
bool isPastEndOfInput(size_t _charsForward = 0) const { return (m_pos + _charsForward) >= m_source.size(); }
|
|
||||||
char get(size_t _charsForward = 0) const { return m_source[m_pos + _charsForward]; }
|
|
||||||
char advanceAndGet(size_t _chars=1);
|
|
||||||
char rollback(size_t _amount);
|
|
||||||
|
|
||||||
void reset() { m_pos = 0; }
|
|
||||||
|
|
||||||
///@{
|
|
||||||
///@name Error printing helper functions
|
|
||||||
/// Functions that help pretty-printing parse errors
|
|
||||||
/// Do only use in error cases, they are quite expensive.
|
|
||||||
std::string getLineAtPosition(int _position) const;
|
|
||||||
std::tuple<int, int> translatePositionToLineColumn(int _position) const;
|
|
||||||
///@}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string m_source;
|
|
||||||
size_t m_pos;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Scanner
|
|
||||||
{
|
|
||||||
friend class LiteralScope;
|
|
||||||
public:
|
|
||||||
|
|
||||||
explicit Scanner(CharStream const& _source = CharStream(), std::string const& _sourceName = "") { reset(_source, _sourceName); }
|
|
||||||
|
|
||||||
/// Resets the scanner as if newly constructed with _source and _sourceName as input.
|
|
||||||
void reset(CharStream const& _source, std::string const& _sourceName);
|
|
||||||
/// Resets scanner to the start of input.
|
|
||||||
void reset();
|
|
||||||
|
|
||||||
/// Returns the next token and advances input
|
|
||||||
Token::Value next();
|
|
||||||
|
|
||||||
///@{
|
|
||||||
///@name Information about the current token
|
|
||||||
|
|
||||||
/// Returns the current token
|
|
||||||
Token::Value getCurrentToken()
|
|
||||||
{
|
|
||||||
return m_currentToken.token;
|
|
||||||
}
|
|
||||||
|
|
||||||
Location getCurrentLocation() const { return m_currentToken.location; }
|
|
||||||
std::string const& getCurrentLiteral() const { return m_currentToken.literal; }
|
|
||||||
///@}
|
|
||||||
|
|
||||||
///@{
|
|
||||||
///@name Information about the current comment token
|
|
||||||
|
|
||||||
Location getCurrentCommentLocation() const { return m_skippedComment.location; }
|
|
||||||
std::string const& getCurrentCommentLiteral() const { return m_skippedComment.literal; }
|
|
||||||
/// Called by the parser during FunctionDefinition parsing to clear the current comment
|
|
||||||
void clearCurrentCommentLiteral() { m_skippedComment.literal.clear(); }
|
|
||||||
|
|
||||||
///@}
|
|
||||||
|
|
||||||
///@{
|
|
||||||
///@name Information about the next token
|
|
||||||
|
|
||||||
/// Returns the next token without advancing input.
|
|
||||||
Token::Value peekNextToken() const { return m_nextToken.token; }
|
|
||||||
Location peekLocation() const { return m_nextToken.location; }
|
|
||||||
std::string const& peekLiteral() const { return m_nextToken.literal; }
|
|
||||||
///@}
|
|
||||||
|
|
||||||
std::shared_ptr<std::string const> const& getSourceName() const { return m_sourceName; }
|
|
||||||
|
|
||||||
///@{
|
|
||||||
///@name Error printing helper functions
|
|
||||||
/// Functions that help pretty-printing parse errors
|
|
||||||
/// Do only use in error cases, they are quite expensive.
|
|
||||||
std::string getLineAtPosition(int _position) const { return m_source.getLineAtPosition(_position); }
|
|
||||||
std::tuple<int, int> translatePositionToLineColumn(int _position) const { return m_source.translatePositionToLineColumn(_position); }
|
|
||||||
///@}
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// Used for the current and look-ahead token and comments
|
|
||||||
struct TokenDesc
|
|
||||||
{
|
|
||||||
Token::Value token;
|
|
||||||
Location location;
|
|
||||||
std::string literal;
|
|
||||||
};
|
|
||||||
|
|
||||||
///@{
|
|
||||||
///@name Literal buffer support
|
|
||||||
inline void addLiteralChar(char c) { m_nextToken.literal.push_back(c); }
|
|
||||||
inline void addCommentLiteralChar(char c) { m_nextSkippedComment.literal.push_back(c); }
|
|
||||||
inline void addLiteralCharAndAdvance() { addLiteralChar(m_char); advance(); }
|
|
||||||
///@}
|
|
||||||
|
|
||||||
bool advance() { m_char = m_source.advanceAndGet(); return !m_source.isPastEndOfInput(); }
|
|
||||||
void rollback(int _amount) { m_char = m_source.rollback(_amount); }
|
|
||||||
|
|
||||||
inline Token::Value selectToken(Token::Value _tok) { advance(); return _tok; }
|
|
||||||
/// If the next character is _next, advance and return _then, otherwise return _else.
|
|
||||||
inline Token::Value selectToken(char _next, Token::Value _then, Token::Value _else);
|
|
||||||
|
|
||||||
bool scanHexByte(char& o_scannedByte);
|
|
||||||
|
|
||||||
/// Scans a single Solidity token.
|
|
||||||
void scanToken();
|
|
||||||
|
|
||||||
/// Skips all whitespace and @returns true if something was skipped.
|
|
||||||
bool skipWhitespace();
|
|
||||||
/// Skips all whitespace except Line feeds and returns true if something was skipped
|
|
||||||
bool skipWhitespaceExceptLF();
|
|
||||||
Token::Value skipSingleLineComment();
|
|
||||||
Token::Value skipMultiLineComment();
|
|
||||||
|
|
||||||
void scanDecimalDigits();
|
|
||||||
Token::Value scanNumber(char _charSeen = 0);
|
|
||||||
Token::Value scanIdentifierOrKeyword();
|
|
||||||
|
|
||||||
Token::Value scanString();
|
|
||||||
Token::Value scanSingleLineDocComment();
|
|
||||||
Token::Value scanMultiLineDocComment();
|
|
||||||
/// Scans a slash '/' and depending on the characters returns the appropriate token
|
|
||||||
Token::Value scanSlash();
|
|
||||||
|
|
||||||
/// Scans an escape-sequence which is part of a string and adds the
|
|
||||||
/// decoded character to the current literal. Returns true if a pattern
|
|
||||||
/// is scanned.
|
|
||||||
bool scanEscape();
|
|
||||||
|
|
||||||
/// Return the current source position.
|
|
||||||
int getSourcePos() { return m_source.getPos(); }
|
|
||||||
bool isSourcePastEndOfInput() { return m_source.isPastEndOfInput(); }
|
|
||||||
|
|
||||||
TokenDesc m_skippedComment; // desc for current skipped comment
|
|
||||||
TokenDesc m_nextSkippedComment; // desc for next skiped comment
|
|
||||||
|
|
||||||
TokenDesc m_currentToken; // desc for current token (as returned by Next())
|
|
||||||
TokenDesc m_nextToken; // desc for next token (one token look-ahead)
|
|
||||||
|
|
||||||
CharStream m_source;
|
|
||||||
std::shared_ptr<std::string const> m_sourceName;
|
|
||||||
|
|
||||||
/// one character look-ahead, equals 0 at end of input
|
|
||||||
char m_char;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,86 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Formatting functions for errors referencing positions and locations in the source.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <libsolidity/SourceReferenceFormatter.h>
|
|
||||||
#include <libsolidity/CompilerStack.h>
|
|
||||||
#include <libsolidity/Scanner.h>
|
|
||||||
#include <libsolidity/Exceptions.h>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
void SourceReferenceFormatter::printSourceLocation(ostream& _stream,
|
|
||||||
Location const& _location,
|
|
||||||
Scanner const& _scanner)
|
|
||||||
{
|
|
||||||
int startLine;
|
|
||||||
int startColumn;
|
|
||||||
tie(startLine, startColumn) = _scanner.translatePositionToLineColumn(_location.start);
|
|
||||||
int endLine;
|
|
||||||
int endColumn;
|
|
||||||
tie(endLine, endColumn) = _scanner.translatePositionToLineColumn(_location.end);
|
|
||||||
if (startLine == endLine)
|
|
||||||
{
|
|
||||||
_stream << _scanner.getLineAtPosition(_location.start) << endl
|
|
||||||
<< string(startColumn, ' ') << "^";
|
|
||||||
if (endColumn > startColumn + 2)
|
|
||||||
_stream << string(endColumn - startColumn - 2, '-');
|
|
||||||
if (endColumn > startColumn + 1)
|
|
||||||
_stream << "^";
|
|
||||||
_stream << endl;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
_stream << _scanner.getLineAtPosition(_location.start) << endl
|
|
||||||
<< string(startColumn, ' ') << "^\n"
|
|
||||||
<< "Spanning multiple lines.\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
void SourceReferenceFormatter::printExceptionInformation(ostream& _stream,
|
|
||||||
Exception const& _exception,
|
|
||||||
string const& _name,
|
|
||||||
CompilerStack const& _compiler)
|
|
||||||
{
|
|
||||||
Location const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);
|
|
||||||
Scanner const* scanner;
|
|
||||||
|
|
||||||
if (location)
|
|
||||||
{
|
|
||||||
scanner = &_compiler.getScanner(*location->sourceName);
|
|
||||||
int startLine;
|
|
||||||
int startColumn;
|
|
||||||
tie(startLine, startColumn) = scanner->translatePositionToLineColumn(location->start);
|
|
||||||
_stream << *location->sourceName << ":" << (startLine + 1) << ":" << (startColumn + 1) << ": ";
|
|
||||||
}
|
|
||||||
_stream << _name;
|
|
||||||
if (string const* description = boost::get_error_info<errinfo_comment>(_exception))
|
|
||||||
_stream << ": " << *description << endl;
|
|
||||||
|
|
||||||
if (location)
|
|
||||||
printSourceLocation(_stream, *location, *scanner);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Formatting functions for errors referencing positions and locations in the source.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <ostream>
|
|
||||||
#include <libsolidity/BaseTypes.h>
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
|
|
||||||
struct Exception; // forward
|
|
||||||
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
class Scanner; // forward
|
|
||||||
class CompilerStack; // forward
|
|
||||||
|
|
||||||
struct SourceReferenceFormatter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static void printSourceLocation(std::ostream& _stream, Location const& _location, Scanner const& _scanner);
|
|
||||||
static void printExceptionInformation(std::ostream& _stream, Exception const& _exception,
|
|
||||||
std::string const& _name, CompilerStack const& _compiler);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
100
Token.cpp
100
Token.cpp
@ -1,100 +0,0 @@
|
|||||||
// Copyright 2006-2012, the V8 project authors. All rights reserved.
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following
|
|
||||||
// disclaimer in the documentation and/or other materials provided
|
|
||||||
// with the distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived
|
|
||||||
// from this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Modifications as part of cpp-ethereum under the following license:
|
|
||||||
//
|
|
||||||
// cpp-ethereum 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.
|
|
||||||
//
|
|
||||||
// cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <libsolidity/Token.h>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
#define T(name, string, precedence) #name,
|
|
||||||
char const* const Token::m_name[NUM_TOKENS] =
|
|
||||||
{
|
|
||||||
TOKEN_LIST(T, T)
|
|
||||||
};
|
|
||||||
#undef T
|
|
||||||
|
|
||||||
|
|
||||||
#define T(name, string, precedence) string,
|
|
||||||
char const* const Token::m_string[NUM_TOKENS] =
|
|
||||||
{
|
|
||||||
TOKEN_LIST(T, T)
|
|
||||||
};
|
|
||||||
#undef T
|
|
||||||
|
|
||||||
|
|
||||||
#define T(name, string, precedence) precedence,
|
|
||||||
int8_t const Token::m_precedence[NUM_TOKENS] =
|
|
||||||
{
|
|
||||||
TOKEN_LIST(T, T)
|
|
||||||
};
|
|
||||||
#undef T
|
|
||||||
|
|
||||||
|
|
||||||
#define KT(a, b, c) 'T',
|
|
||||||
#define KK(a, b, c) 'K',
|
|
||||||
char const Token::m_tokenType[] =
|
|
||||||
{
|
|
||||||
TOKEN_LIST(KT, KK)
|
|
||||||
};
|
|
||||||
Token::Value Token::fromIdentifierOrKeyword(const std::string& _name)
|
|
||||||
{
|
|
||||||
// The following macros are used inside TOKEN_LIST and cause non-keyword tokens to be ignored
|
|
||||||
// and keywords to be put inside the keywords variable.
|
|
||||||
#define KEYWORD(name, string, precedence) {string, Token::name},
|
|
||||||
#define TOKEN(name, string, precedence)
|
|
||||||
static const map<string, Token::Value> keywords({TOKEN_LIST(TOKEN, KEYWORD)});
|
|
||||||
#undef KEYWORD
|
|
||||||
#undef TOKEN
|
|
||||||
auto it = keywords.find(_name);
|
|
||||||
return it == keywords.end() ? Token::Identifier : it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef KT
|
|
||||||
#undef KK
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
413
Token.h
413
Token.h
@ -1,413 +0,0 @@
|
|||||||
// Copyright 2006-2012, the V8 project authors. All rights reserved.
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following
|
|
||||||
// disclaimer in the documentation and/or other materials provided
|
|
||||||
// with the distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived
|
|
||||||
// from this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Modifications as part of cpp-ethereum under the following license:
|
|
||||||
//
|
|
||||||
// cpp-ethereum 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.
|
|
||||||
//
|
|
||||||
// cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <libdevcore/Common.h>
|
|
||||||
#include <libdevcore/Log.h>
|
|
||||||
#include <libsolidity/Utils.h>
|
|
||||||
#include <libsolidity/Exceptions.h>
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
// TOKEN_LIST takes a list of 3 macros M, all of which satisfy the
|
|
||||||
// same signature M(name, string, precedence), where name is the
|
|
||||||
// symbolic token name, string is the corresponding syntactic symbol
|
|
||||||
// (or NULL, for literals), and precedence is the precedence (or 0).
|
|
||||||
// The parameters are invoked for token categories as follows:
|
|
||||||
//
|
|
||||||
// T: Non-keyword tokens
|
|
||||||
// K: Keyword tokens
|
|
||||||
|
|
||||||
// IGNORE_TOKEN is a convenience macro that can be supplied as
|
|
||||||
// an argument (at any position) for a TOKEN_LIST call. It does
|
|
||||||
// nothing with tokens belonging to the respective category.
|
|
||||||
|
|
||||||
#define IGNORE_TOKEN(name, string, precedence)
|
|
||||||
|
|
||||||
#define TOKEN_LIST(T, K) \
|
|
||||||
/* End of source indicator. */ \
|
|
||||||
T(EOS, "EOS", 0) \
|
|
||||||
\
|
|
||||||
/* Punctuators (ECMA-262, section 7.7, page 15). */ \
|
|
||||||
T(LParen, "(", 0) \
|
|
||||||
T(RParen, ")", 0) \
|
|
||||||
T(LBrack, "[", 0) \
|
|
||||||
T(RBrack, "]", 0) \
|
|
||||||
T(LBrace, "{", 0) \
|
|
||||||
T(RBrace, "}", 0) \
|
|
||||||
T(Colon, ":", 0) \
|
|
||||||
T(Semicolon, ";", 0) \
|
|
||||||
T(Period, ".", 0) \
|
|
||||||
T(Conditional, "?", 3) \
|
|
||||||
T(Arrow, "=>", 0) \
|
|
||||||
\
|
|
||||||
/* Assignment operators. */ \
|
|
||||||
/* IsAssignmentOp() relies on this block of enum values being */ \
|
|
||||||
/* contiguous and sorted in the same order!*/ \
|
|
||||||
T(Assign, "=", 2) \
|
|
||||||
/* The following have to be in exactly the same order as the simple binary operators*/ \
|
|
||||||
T(AssignBitOr, "|=", 2) \
|
|
||||||
T(AssignBitXor, "^=", 2) \
|
|
||||||
T(AssignBitAnd, "&=", 2) \
|
|
||||||
T(AssignShl, "<<=", 2) \
|
|
||||||
T(AssignSar, ">>=", 2) \
|
|
||||||
T(AssignShr, ">>>=", 2) \
|
|
||||||
T(AssignAdd, "+=", 2) \
|
|
||||||
T(AssignSub, "-=", 2) \
|
|
||||||
T(AssignMul, "*=", 2) \
|
|
||||||
T(AssignDiv, "/=", 2) \
|
|
||||||
T(AssignMod, "%=", 2) \
|
|
||||||
\
|
|
||||||
/* Binary operators sorted by precedence. */ \
|
|
||||||
/* IsBinaryOp() relies on this block of enum values */ \
|
|
||||||
/* being contiguous and sorted in the same order! */ \
|
|
||||||
T(Comma, ",", 1) \
|
|
||||||
T(Or, "||", 4) \
|
|
||||||
T(And, "&&", 5) \
|
|
||||||
T(BitOr, "|", 8) \
|
|
||||||
T(BitXor, "^", 9) \
|
|
||||||
T(BitAnd, "&", 10) \
|
|
||||||
T(SHL, "<<", 11) \
|
|
||||||
T(SAR, ">>", 11) \
|
|
||||||
T(SHR, ">>>", 11) \
|
|
||||||
T(Add, "+", 12) \
|
|
||||||
T(Sub, "-", 12) \
|
|
||||||
T(Mul, "*", 13) \
|
|
||||||
T(Div, "/", 13) \
|
|
||||||
T(Mod, "%", 13) \
|
|
||||||
T(Exp, "**", 14) \
|
|
||||||
\
|
|
||||||
/* Compare operators sorted by precedence. */ \
|
|
||||||
/* IsCompareOp() relies on this block of enum values */ \
|
|
||||||
/* being contiguous and sorted in the same order! */ \
|
|
||||||
T(Equal, "==", 6) \
|
|
||||||
T(NotEqual, "!=", 6) \
|
|
||||||
T(LessThan, "<", 7) \
|
|
||||||
T(GreaterThan, ">", 7) \
|
|
||||||
T(LessThanOrEqual, "<=", 7) \
|
|
||||||
T(GreaterThanOrEqual, ">=", 7) \
|
|
||||||
K(In, "in", 7) \
|
|
||||||
\
|
|
||||||
/* Unary operators. */ \
|
|
||||||
/* IsUnaryOp() relies on this block of enum values */ \
|
|
||||||
/* being contiguous and sorted in the same order! */ \
|
|
||||||
T(Not, "!", 0) \
|
|
||||||
T(BitNot, "~", 0) \
|
|
||||||
T(Inc, "++", 0) \
|
|
||||||
T(Dec, "--", 0) \
|
|
||||||
K(Delete, "delete", 0) \
|
|
||||||
\
|
|
||||||
/* Keywords */ \
|
|
||||||
K(Break, "break", 0) \
|
|
||||||
K(Case, "case", 0) \
|
|
||||||
K(Const, "constant", 0) \
|
|
||||||
K(Continue, "continue", 0) \
|
|
||||||
K(Contract, "contract", 0) \
|
|
||||||
K(Default, "default", 0) \
|
|
||||||
K(Do, "do", 0) \
|
|
||||||
K(Else, "else", 0) \
|
|
||||||
K(Event, "event", 0) \
|
|
||||||
K(External, "external", 0) \
|
|
||||||
K(Is, "is", 0) \
|
|
||||||
K(Indexed, "indexed", 0) \
|
|
||||||
K(For, "for", 0) \
|
|
||||||
K(Function, "function", 0) \
|
|
||||||
K(If, "if", 0) \
|
|
||||||
K(Import, "import", 0) \
|
|
||||||
K(Mapping, "mapping", 0) \
|
|
||||||
K(Modifier, "modifier", 0) \
|
|
||||||
K(New, "new", 0) \
|
|
||||||
K(Public, "public", 0) \
|
|
||||||
K(Private, "private", 0) \
|
|
||||||
K(Inheritable, "inheritable", 0) \
|
|
||||||
K(Return, "return", 0) \
|
|
||||||
K(Returns, "returns", 0) \
|
|
||||||
K(Struct, "struct", 0) \
|
|
||||||
K(Switch, "switch", 0) \
|
|
||||||
K(Var, "var", 0) \
|
|
||||||
K(While, "while", 0) \
|
|
||||||
K(Enum, "enum", 0) \
|
|
||||||
\
|
|
||||||
/* Ether subdenominations */ \
|
|
||||||
K(SubWei, "wei", 0) \
|
|
||||||
K(SubSzabo, "szabo", 0) \
|
|
||||||
K(SubFinney, "finney", 0) \
|
|
||||||
K(SubEther, "ether", 0) \
|
|
||||||
/* type keywords, keep them in this order, keep int as first keyword
|
|
||||||
* the implementation in Types.cpp has to be synced to this here */\
|
|
||||||
K(Int, "int", 0) \
|
|
||||||
K(Int8, "int8", 0) \
|
|
||||||
K(Int16, "int16", 0) \
|
|
||||||
K(Int24, "int24", 0) \
|
|
||||||
K(Int32, "int32", 0) \
|
|
||||||
K(Int40, "int40", 0) \
|
|
||||||
K(Int48, "int48", 0) \
|
|
||||||
K(Int56, "int56", 0) \
|
|
||||||
K(Int64, "int64", 0) \
|
|
||||||
K(Int72, "int72", 0) \
|
|
||||||
K(Int80, "int80", 0) \
|
|
||||||
K(Int88, "int88", 0) \
|
|
||||||
K(Int96, "int96", 0) \
|
|
||||||
K(Int104, "int104", 0) \
|
|
||||||
K(Int112, "int112", 0) \
|
|
||||||
K(Int120, "int120", 0) \
|
|
||||||
K(Int128, "int128", 0) \
|
|
||||||
K(Int136, "int136", 0) \
|
|
||||||
K(Int144, "int144", 0) \
|
|
||||||
K(Int152, "int152", 0) \
|
|
||||||
K(Int160, "int160", 0) \
|
|
||||||
K(Int168, "int168", 0) \
|
|
||||||
K(Int176, "int178", 0) \
|
|
||||||
K(Int184, "int184", 0) \
|
|
||||||
K(Int192, "int192", 0) \
|
|
||||||
K(Int200, "int200", 0) \
|
|
||||||
K(Int208, "int208", 0) \
|
|
||||||
K(Int216, "int216", 0) \
|
|
||||||
K(Int224, "int224", 0) \
|
|
||||||
K(Int232, "int232", 0) \
|
|
||||||
K(Int240, "int240", 0) \
|
|
||||||
K(Int248, "int248", 0) \
|
|
||||||
K(Int256, "int256", 0) \
|
|
||||||
K(UInt, "uint", 0) \
|
|
||||||
K(UInt8, "uint8", 0) \
|
|
||||||
K(UInt16, "uint16", 0) \
|
|
||||||
K(UInt24, "uint24", 0) \
|
|
||||||
K(UInt32, "uint32", 0) \
|
|
||||||
K(UInt40, "uint40", 0) \
|
|
||||||
K(UInt48, "uint48", 0) \
|
|
||||||
K(UInt56, "uint56", 0) \
|
|
||||||
K(UInt64, "uint64", 0) \
|
|
||||||
K(UInt72, "uint72", 0) \
|
|
||||||
K(UInt80, "uint80", 0) \
|
|
||||||
K(UInt88, "uint88", 0) \
|
|
||||||
K(UInt96, "uint96", 0) \
|
|
||||||
K(UInt104, "uint104", 0) \
|
|
||||||
K(UInt112, "uint112", 0) \
|
|
||||||
K(UInt120, "uint120", 0) \
|
|
||||||
K(UInt128, "uint128", 0) \
|
|
||||||
K(UInt136, "uint136", 0) \
|
|
||||||
K(UInt144, "uint144", 0) \
|
|
||||||
K(UInt152, "uint152", 0) \
|
|
||||||
K(UInt160, "uint160", 0) \
|
|
||||||
K(UInt168, "uint168", 0) \
|
|
||||||
K(UInt176, "uint178", 0) \
|
|
||||||
K(UInt184, "uint184", 0) \
|
|
||||||
K(UInt192, "uint192", 0) \
|
|
||||||
K(UInt200, "uint200", 0) \
|
|
||||||
K(UInt208, "uint208", 0) \
|
|
||||||
K(UInt216, "uint216", 0) \
|
|
||||||
K(UInt224, "uint224", 0) \
|
|
||||||
K(UInt232, "uint232", 0) \
|
|
||||||
K(UInt240, "uint240", 0) \
|
|
||||||
K(UInt248, "uint248", 0) \
|
|
||||||
K(UInt256, "uint256", 0) \
|
|
||||||
K(Hash, "hash", 0) \
|
|
||||||
K(Hash8, "hash8", 0) \
|
|
||||||
K(Hash16, "hash16", 0) \
|
|
||||||
K(Hash24, "hash24", 0) \
|
|
||||||
K(Hash32, "hash32", 0) \
|
|
||||||
K(Hash40, "hash40", 0) \
|
|
||||||
K(Hash48, "hash48", 0) \
|
|
||||||
K(Hash56, "hash56", 0) \
|
|
||||||
K(Hash64, "hash64", 0) \
|
|
||||||
K(Hash72, "hash72", 0) \
|
|
||||||
K(Hash80, "hash80", 0) \
|
|
||||||
K(Hash88, "hash88", 0) \
|
|
||||||
K(Hash96, "hash96", 0) \
|
|
||||||
K(Hash104, "hash104", 0) \
|
|
||||||
K(Hash112, "hash112", 0) \
|
|
||||||
K(Hash120, "hash120", 0) \
|
|
||||||
K(Hash128, "hash128", 0) \
|
|
||||||
K(Hash136, "hash136", 0) \
|
|
||||||
K(Hash144, "hash144", 0) \
|
|
||||||
K(Hash152, "hash152", 0) \
|
|
||||||
K(Hash160, "hash160", 0) \
|
|
||||||
K(Hash168, "hash168", 0) \
|
|
||||||
K(Hash176, "hash178", 0) \
|
|
||||||
K(Hash184, "hash184", 0) \
|
|
||||||
K(Hash192, "hash192", 0) \
|
|
||||||
K(Hash200, "hash200", 0) \
|
|
||||||
K(Hash208, "hash208", 0) \
|
|
||||||
K(Hash216, "hash216", 0) \
|
|
||||||
K(Hash224, "hash224", 0) \
|
|
||||||
K(Hash232, "hash232", 0) \
|
|
||||||
K(Hash240, "hash240", 0) \
|
|
||||||
K(Hash248, "hash248", 0) \
|
|
||||||
K(Hash256, "hash256", 0) \
|
|
||||||
K(Address, "address", 0) \
|
|
||||||
K(Bool, "bool", 0) \
|
|
||||||
K(Bytes, "bytes", 0) \
|
|
||||||
K(StringType, "string", 0) \
|
|
||||||
K(String0, "string0", 0) \
|
|
||||||
K(String1, "string1", 0) \
|
|
||||||
K(String2, "string2", 0) \
|
|
||||||
K(String3, "string3", 0) \
|
|
||||||
K(String4, "string4", 0) \
|
|
||||||
K(String5, "string5", 0) \
|
|
||||||
K(String6, "string6", 0) \
|
|
||||||
K(String7, "string7", 0) \
|
|
||||||
K(String8, "string8", 0) \
|
|
||||||
K(String9, "string9", 0) \
|
|
||||||
K(String10, "string10", 0) \
|
|
||||||
K(String11, "string11", 0) \
|
|
||||||
K(String12, "string12", 0) \
|
|
||||||
K(String13, "string13", 0) \
|
|
||||||
K(String14, "string14", 0) \
|
|
||||||
K(String15, "string15", 0) \
|
|
||||||
K(String16, "string16", 0) \
|
|
||||||
K(String17, "string17", 0) \
|
|
||||||
K(String18, "string18", 0) \
|
|
||||||
K(String19, "string19", 0) \
|
|
||||||
K(String20, "string20", 0) \
|
|
||||||
K(String21, "string21", 0) \
|
|
||||||
K(String22, "string22", 0) \
|
|
||||||
K(String23, "string23", 0) \
|
|
||||||
K(String24, "string24", 0) \
|
|
||||||
K(String25, "string25", 0) \
|
|
||||||
K(String26, "string26", 0) \
|
|
||||||
K(String27, "string27", 0) \
|
|
||||||
K(String28, "string28", 0) \
|
|
||||||
K(String29, "string29", 0) \
|
|
||||||
K(String30, "string30", 0) \
|
|
||||||
K(String31, "string31", 0) \
|
|
||||||
K(String32, "string32", 0) \
|
|
||||||
K(Text, "text", 0) \
|
|
||||||
K(Real, "real", 0) \
|
|
||||||
K(UReal, "ureal", 0) \
|
|
||||||
T(TypesEnd, NULL, 0) /* used as type enum end marker */ \
|
|
||||||
\
|
|
||||||
/* Literals */ \
|
|
||||||
K(NullLiteral, "null", 0) \
|
|
||||||
K(TrueLiteral, "true", 0) \
|
|
||||||
K(FalseLiteral, "false", 0) \
|
|
||||||
T(Number, NULL, 0) \
|
|
||||||
T(StringLiteral, NULL, 0) \
|
|
||||||
T(CommentLiteral, NULL, 0) \
|
|
||||||
\
|
|
||||||
/* Identifiers (not keywords or future reserved words). */ \
|
|
||||||
T(Identifier, NULL, 0) \
|
|
||||||
\
|
|
||||||
/* Illegal token - not able to scan. */ \
|
|
||||||
T(Illegal, "ILLEGAL", 0) \
|
|
||||||
\
|
|
||||||
/* Scanner-internal use only. */ \
|
|
||||||
T(Whitespace, NULL, 0)
|
|
||||||
|
|
||||||
|
|
||||||
class Token
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// All token values.
|
|
||||||
// attention! msvc issue:
|
|
||||||
// http://stackoverflow.com/questions/9567868/compile-errors-after-adding-v8-to-my-project-c2143-c2059
|
|
||||||
// @todo: avoid TOKEN_LIST macro
|
|
||||||
#define T(name, string, precedence) name,
|
|
||||||
enum Value
|
|
||||||
{
|
|
||||||
TOKEN_LIST(T, T)
|
|
||||||
NUM_TOKENS
|
|
||||||
};
|
|
||||||
#undef T
|
|
||||||
|
|
||||||
// Returns a string corresponding to the C++ token name
|
|
||||||
// (e.g. "LT" for the token LT).
|
|
||||||
static char const* getName(Value tok)
|
|
||||||
{
|
|
||||||
solAssert(tok < NUM_TOKENS, "");
|
|
||||||
return m_name[tok];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Predicates
|
|
||||||
static bool isElementaryTypeName(Value tok) { return Int <= tok && tok < TypesEnd; }
|
|
||||||
static bool isAssignmentOp(Value tok) { return Assign <= tok && tok <= AssignMod; }
|
|
||||||
static bool isBinaryOp(Value op) { return Comma <= op && op <= Exp; }
|
|
||||||
static bool isCommutativeOp(Value op) { return op == BitOr || op == BitXor || op == BitAnd ||
|
|
||||||
op == Add || op == Mul || op == Equal || op == NotEqual; }
|
|
||||||
static bool isArithmeticOp(Value op) { return Add <= op && op <= Exp; }
|
|
||||||
static bool isCompareOp(Value op) { return Equal <= op && op <= In; }
|
|
||||||
|
|
||||||
static Value AssignmentToBinaryOp(Value op)
|
|
||||||
{
|
|
||||||
solAssert(isAssignmentOp(op) && op != Assign, "");
|
|
||||||
return Value(op + (BitOr - AssignBitOr));
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isBitOp(Value op) { return (BitOr <= op && op <= SHR) || op == BitNot; }
|
|
||||||
static bool isUnaryOp(Value op) { return (Not <= op && op <= Delete) || op == Add || op == Sub; }
|
|
||||||
static bool isCountOp(Value op) { return op == Inc || op == Dec; }
|
|
||||||
static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); }
|
|
||||||
static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; }
|
|
||||||
static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Inheritable; }
|
|
||||||
static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == Token::SubEther; }
|
|
||||||
|
|
||||||
// Returns a string corresponding to the JS token string
|
|
||||||
// (.e., "<" for the token LT) or NULL if the token doesn't
|
|
||||||
// have a (unique) string (e.g. an IDENTIFIER).
|
|
||||||
static char const* toString(Value tok)
|
|
||||||
{
|
|
||||||
solAssert(tok < NUM_TOKENS, "");
|
|
||||||
return m_string[tok];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the precedence > 0 for binary and compare
|
|
||||||
// operators; returns 0 otherwise.
|
|
||||||
static int precedence(Value tok)
|
|
||||||
{
|
|
||||||
solAssert(tok < NUM_TOKENS, "");
|
|
||||||
return m_precedence[tok];
|
|
||||||
}
|
|
||||||
|
|
||||||
static Token::Value fromIdentifierOrKeyword(std::string const& _name);
|
|
||||||
|
|
||||||
private:
|
|
||||||
static char const* const m_name[NUM_TOKENS];
|
|
||||||
static char const* const m_string[NUM_TOKENS];
|
|
||||||
static int8_t const m_precedence[NUM_TOKENS];
|
|
||||||
static char const m_tokenType[NUM_TOKENS];
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
631
Types.h
631
Types.h
@ -1,631 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Solidity data types
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <map>
|
|
||||||
#include <boost/noncopyable.hpp>
|
|
||||||
#include <libdevcore/Common.h>
|
|
||||||
#include <libsolidity/Exceptions.h>
|
|
||||||
#include <libsolidity/ASTForward.h>
|
|
||||||
#include <libsolidity/Token.h>
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
// @todo realMxN, dynamic strings, text, arrays
|
|
||||||
|
|
||||||
class Type; // forward
|
|
||||||
class FunctionType; // forward
|
|
||||||
using TypePointer = std::shared_ptr<Type const>;
|
|
||||||
using FunctionTypePointer = std::shared_ptr<FunctionType const>;
|
|
||||||
using TypePointers = std::vector<TypePointer>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List of members of a type.
|
|
||||||
*/
|
|
||||||
class MemberList
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using MemberMap = std::vector<std::pair<std::string, TypePointer>>;
|
|
||||||
|
|
||||||
MemberList() {}
|
|
||||||
explicit MemberList(MemberMap const& _members): m_memberTypes(_members) {}
|
|
||||||
TypePointer getMemberType(std::string const& _name) const
|
|
||||||
{
|
|
||||||
for (auto const& it: m_memberTypes)
|
|
||||||
if (it.first == _name)
|
|
||||||
return it.second;
|
|
||||||
return TypePointer();
|
|
||||||
}
|
|
||||||
|
|
||||||
MemberMap::const_iterator begin() const { return m_memberTypes.begin(); }
|
|
||||||
MemberMap::const_iterator end() const { return m_memberTypes.end(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
MemberMap m_memberTypes;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstract base class that forms the root of the type hierarchy.
|
|
||||||
*/
|
|
||||||
class Type: private boost::noncopyable, public std::enable_shared_from_this<Type>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum class Category
|
|
||||||
{
|
|
||||||
Integer, IntegerConstant, Bool, Real, ByteArray,
|
|
||||||
String, Contract, Struct, Function, Enum,
|
|
||||||
Mapping, Void, TypeType, Modifier, Magic
|
|
||||||
};
|
|
||||||
|
|
||||||
///@{
|
|
||||||
///@name Factory functions
|
|
||||||
/// Factory functions that convert an AST @ref TypeName to a Type.
|
|
||||||
static TypePointer fromElementaryTypeName(Token::Value _typeToken);
|
|
||||||
static TypePointer fromElementaryTypeName(std::string const& _name);
|
|
||||||
static TypePointer fromUserDefinedTypeName(UserDefinedTypeName const& _typeName);
|
|
||||||
static TypePointer fromMapping(Mapping const& _typeName);
|
|
||||||
static TypePointer fromFunction(FunctionDefinition const& _function);
|
|
||||||
/// @}
|
|
||||||
|
|
||||||
/// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does
|
|
||||||
/// not fit any type.
|
|
||||||
static TypePointer forLiteral(Literal const& _literal);
|
|
||||||
/// @returns a pointer to _a or _b if the other is implicitly convertible to it or nullptr otherwise
|
|
||||||
static TypePointer commonType(TypePointer const& _a, TypePointer const& _b);
|
|
||||||
|
|
||||||
virtual Category getCategory() const = 0;
|
|
||||||
virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; }
|
|
||||||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const
|
|
||||||
{
|
|
||||||
return isImplicitlyConvertibleTo(_convertTo);
|
|
||||||
}
|
|
||||||
/// @returns the resulting type of applying the given unary operator or an empty pointer if
|
|
||||||
/// this is not possible.
|
|
||||||
/// The default implementation does not allow any unary operator.
|
|
||||||
virtual TypePointer unaryOperatorResult(Token::Value) const { return TypePointer(); }
|
|
||||||
/// @returns the resulting type of applying the given binary operator or an empty pointer if
|
|
||||||
/// this is not possible.
|
|
||||||
/// The default implementation allows comparison operators if a common type exists
|
|
||||||
virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
|
|
||||||
{
|
|
||||||
return Token::isCompareOp(_operator) ? commonType(shared_from_this(), _other) : TypePointer();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool operator==(Type const& _other) const { return getCategory() == _other.getCategory(); }
|
|
||||||
virtual bool operator!=(Type const& _other) const { return !this->operator ==(_other); }
|
|
||||||
|
|
||||||
/// @returns number of bytes used by this type when encoded for CALL, or 0 if the encoding
|
|
||||||
/// is not a simple big-endian encoding or the type cannot be stored in calldata.
|
|
||||||
/// Note that irrespective of this size, each calldata element is padded to a multiple of 32 bytes.
|
|
||||||
virtual unsigned getCalldataEncodedSize() const { return 0; }
|
|
||||||
/// @returns true if the type is dynamically encoded in calldata
|
|
||||||
virtual bool isDynamicallySized() const { return false; }
|
|
||||||
/// @returns number of bytes required to hold this value in storage.
|
|
||||||
/// For dynamically "allocated" types, it returns the size of the statically allocated head,
|
|
||||||
virtual u256 getStorageSize() const { return 1; }
|
|
||||||
/// Returns true if the type can be stored in storage.
|
|
||||||
virtual bool canBeStored() const { return true; }
|
|
||||||
/// Returns false if the type cannot live outside the storage, i.e. if it includes some mapping.
|
|
||||||
virtual bool canLiveOutsideStorage() const { return true; }
|
|
||||||
/// Returns true if the type can be stored as a value (as opposed to a reference) on the stack,
|
|
||||||
/// i.e. it behaves differently in lvalue context and in value context.
|
|
||||||
virtual bool isValueType() const { return false; }
|
|
||||||
virtual unsigned getSizeOnStack() const { return 1; }
|
|
||||||
/// @returns the real type of some types, like e.g: IntegerConstant
|
|
||||||
virtual TypePointer getRealType() const { return shared_from_this(); }
|
|
||||||
|
|
||||||
/// Returns the list of all members of this type. Default implementation: no members.
|
|
||||||
virtual MemberList const& getMembers() const { return EmptyMemberList; }
|
|
||||||
/// Convenience method, returns the type of the given named member or an empty pointer if no such member exists.
|
|
||||||
TypePointer getMemberType(std::string const& _name) const { return getMembers().getMemberType(_name); }
|
|
||||||
|
|
||||||
virtual std::string toString() const = 0;
|
|
||||||
virtual u256 literalValue(Literal const*) const
|
|
||||||
{
|
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Literal value requested "
|
|
||||||
"for type without literals."));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/// Convenience object used when returning an empty member list.
|
|
||||||
static const MemberList EmptyMemberList;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Any kind of integer type including hash and address.
|
|
||||||
*/
|
|
||||||
class IntegerType: public Type
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum class Modifier
|
|
||||||
{
|
|
||||||
Unsigned, Signed, Hash, Address
|
|
||||||
};
|
|
||||||
virtual Category getCategory() const override { return Category::Integer; }
|
|
||||||
|
|
||||||
explicit IntegerType(int _bits, Modifier _modifier = Modifier::Unsigned);
|
|
||||||
|
|
||||||
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
|
||||||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
|
||||||
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
|
||||||
virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
|
|
||||||
|
|
||||||
virtual bool operator==(Type const& _other) const override;
|
|
||||||
|
|
||||||
virtual unsigned getCalldataEncodedSize() const override { return m_bits / 8; }
|
|
||||||
virtual bool isValueType() const override { return true; }
|
|
||||||
|
|
||||||
virtual MemberList const& getMembers() const { return isAddress() ? AddressMemberList : EmptyMemberList; }
|
|
||||||
|
|
||||||
virtual std::string toString() const override;
|
|
||||||
|
|
||||||
int getNumBits() const { return m_bits; }
|
|
||||||
bool isHash() const { return m_modifier == Modifier::Hash || m_modifier == Modifier::Address; }
|
|
||||||
bool isAddress() const { return m_modifier == Modifier::Address; }
|
|
||||||
bool isSigned() const { return m_modifier == Modifier::Signed; }
|
|
||||||
|
|
||||||
static const MemberList AddressMemberList;
|
|
||||||
|
|
||||||
private:
|
|
||||||
int m_bits;
|
|
||||||
Modifier m_modifier;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Integer constants either literals or computed. Example expressions: 2, 2+10, ~10.
|
|
||||||
* There is one distinct type per value.
|
|
||||||
*/
|
|
||||||
class IntegerConstantType: public Type
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual Category getCategory() const override { return Category::IntegerConstant; }
|
|
||||||
|
|
||||||
explicit IntegerConstantType(Literal const& _literal);
|
|
||||||
explicit IntegerConstantType(bigint _value): m_value(_value) {}
|
|
||||||
|
|
||||||
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
|
||||||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
|
||||||
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
|
||||||
virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
|
|
||||||
|
|
||||||
virtual bool operator==(Type const& _other) const override;
|
|
||||||
|
|
||||||
virtual bool canBeStored() const override { return false; }
|
|
||||||
virtual bool canLiveOutsideStorage() const override { return false; }
|
|
||||||
virtual unsigned getSizeOnStack() const override { return 1; }
|
|
||||||
|
|
||||||
virtual std::string toString() const override;
|
|
||||||
virtual u256 literalValue(Literal const* _literal) const override;
|
|
||||||
virtual TypePointer getRealType() const override;
|
|
||||||
|
|
||||||
/// @returns the smallest integer type that can hold the value or an empty pointer if not possible.
|
|
||||||
std::shared_ptr<IntegerType const> getIntegerType() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
bigint m_value;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* String type with fixed length, up to 32 bytes.
|
|
||||||
*/
|
|
||||||
class StaticStringType: public Type
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual Category getCategory() const override { return Category::String; }
|
|
||||||
|
|
||||||
/// @returns the smallest string type for the given literal or an empty pointer
|
|
||||||
/// if no type fits.
|
|
||||||
static std::shared_ptr<StaticStringType> smallestTypeForLiteral(std::string const& _literal);
|
|
||||||
|
|
||||||
explicit StaticStringType(int _bytes);
|
|
||||||
|
|
||||||
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
|
||||||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
|
||||||
virtual bool operator==(Type const& _other) const override;
|
|
||||||
|
|
||||||
virtual unsigned getCalldataEncodedSize() const override { return m_bytes; }
|
|
||||||
virtual bool isValueType() const override { return true; }
|
|
||||||
|
|
||||||
virtual std::string toString() const override { return "string" + dev::toString(m_bytes); }
|
|
||||||
virtual u256 literalValue(Literal const* _literal) const override;
|
|
||||||
|
|
||||||
int getNumBytes() const { return m_bytes; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
int m_bytes;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The boolean type.
|
|
||||||
*/
|
|
||||||
class BoolType: public Type
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
BoolType() {}
|
|
||||||
virtual Category getCategory() const override { return Category::Bool; }
|
|
||||||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
|
||||||
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
|
||||||
virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
|
|
||||||
|
|
||||||
virtual unsigned getCalldataEncodedSize() const { return 1; }
|
|
||||||
virtual bool isValueType() const override { return true; }
|
|
||||||
|
|
||||||
virtual std::string toString() const override { return "bool"; }
|
|
||||||
virtual u256 literalValue(Literal const* _literal) const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of a byte array, prototype for a general array.
|
|
||||||
*/
|
|
||||||
class ByteArrayType: public Type
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum class Location { Storage, CallData, Memory };
|
|
||||||
|
|
||||||
virtual Category getCategory() const override { return Category::ByteArray; }
|
|
||||||
explicit ByteArrayType(Location _location): m_location(_location) {}
|
|
||||||
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
|
||||||
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
|
||||||
virtual bool operator==(const Type& _other) const override;
|
|
||||||
virtual bool isDynamicallySized() const { return true; }
|
|
||||||
virtual unsigned getSizeOnStack() const override;
|
|
||||||
virtual std::string toString() const override { return "bytes"; }
|
|
||||||
virtual MemberList const& getMembers() const override { return s_byteArrayMemberList; }
|
|
||||||
|
|
||||||
Location getLocation() const { return m_location; }
|
|
||||||
|
|
||||||
/// @returns a copy of this type with location changed to @a _location
|
|
||||||
/// @todo this might move as far up as Type later
|
|
||||||
std::shared_ptr<ByteArrayType> copyForLocation(Location _location) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Location m_location;
|
|
||||||
static const MemberList s_byteArrayMemberList;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of a contract instance, there is one distinct type for each contract definition.
|
|
||||||
*/
|
|
||||||
class ContractType: public Type
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual Category getCategory() const override { return Category::Contract; }
|
|
||||||
explicit ContractType(ContractDefinition const& _contract, bool _super = false):
|
|
||||||
m_contract(_contract), m_super(_super) {}
|
|
||||||
/// Contracts can be implicitly converted to super classes and to addresses.
|
|
||||||
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
|
||||||
/// Contracts can be converted to themselves and to integers.
|
|
||||||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
|
||||||
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
|
||||||
virtual bool operator==(Type const& _other) const override;
|
|
||||||
virtual bool isValueType() const override { return true; }
|
|
||||||
virtual std::string toString() const override;
|
|
||||||
|
|
||||||
virtual MemberList const& getMembers() const override;
|
|
||||||
|
|
||||||
bool isSuper() const { return m_super; }
|
|
||||||
ContractDefinition const& getContractDefinition() const { return m_contract; }
|
|
||||||
|
|
||||||
/// Returns the function type of the constructor. Note that the location part of the function type
|
|
||||||
/// is not used, as this type cannot be the type of a variable or expression.
|
|
||||||
FunctionTypePointer const& getConstructorType() const;
|
|
||||||
|
|
||||||
/// @returns the identifier of the function with the given name or Invalid256 if such a name does
|
|
||||||
/// not exist.
|
|
||||||
u256 getFunctionIdentifier(std::string const& _functionName) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
ContractDefinition const& m_contract;
|
|
||||||
/// If true, it is the "super" type of the current contract, i.e. it contains only inherited
|
|
||||||
/// members.
|
|
||||||
bool m_super;
|
|
||||||
/// Type of the constructor, @see getConstructorType. Lazily initialized.
|
|
||||||
mutable FunctionTypePointer m_constructorType;
|
|
||||||
/// List of member types, will be lazy-initialized because of recursive references.
|
|
||||||
mutable std::unique_ptr<MemberList> m_members;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of a struct instance, there is one distinct type per struct definition.
|
|
||||||
*/
|
|
||||||
class StructType: public Type
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual Category getCategory() const override { return Category::Struct; }
|
|
||||||
explicit StructType(StructDefinition const& _struct): m_struct(_struct) {}
|
|
||||||
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
|
||||||
virtual bool operator==(Type const& _other) const override;
|
|
||||||
virtual u256 getStorageSize() const override;
|
|
||||||
virtual bool canLiveOutsideStorage() const override;
|
|
||||||
virtual unsigned getSizeOnStack() const override { return 1; /*@todo*/ }
|
|
||||||
virtual std::string toString() const override;
|
|
||||||
|
|
||||||
virtual MemberList const& getMembers() const override;
|
|
||||||
|
|
||||||
u256 getStorageOffsetOfMember(std::string const& _name) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
StructDefinition const& m_struct;
|
|
||||||
/// List of member types, will be lazy-initialized because of recursive references.
|
|
||||||
mutable std::unique_ptr<MemberList> m_members;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of an enum instance, there is one distinct type per enum definition.
|
|
||||||
*/
|
|
||||||
class EnumType: public Type
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual Category getCategory() const override { return Category::Enum; }
|
|
||||||
explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {}
|
|
||||||
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
|
||||||
virtual bool operator==(Type const& _other) const override;
|
|
||||||
virtual unsigned getSizeOnStack() const override { return 1; }
|
|
||||||
virtual std::string toString() const override;
|
|
||||||
virtual bool isValueType() const override { return true; }
|
|
||||||
|
|
||||||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
|
||||||
|
|
||||||
EnumDefinition const& getEnumDefinition() const { return m_enum; }
|
|
||||||
/// @returns the value that the string has in the Enum
|
|
||||||
unsigned int getMemberValue(ASTString const& _member) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
EnumDefinition const& m_enum;
|
|
||||||
/// List of member types, will be lazy-initialized because of recursive references.
|
|
||||||
mutable std::unique_ptr<MemberList> m_members;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of a function, identified by its (return) parameter types.
|
|
||||||
* @todo the return parameters should also have names, i.e. return parameters should be a struct
|
|
||||||
* type.
|
|
||||||
*/
|
|
||||||
class FunctionType: public Type
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/// The meaning of the value(s) on the stack referencing the function:
|
|
||||||
/// INTERNAL: jump tag, EXTERNAL: contract address + function identifier,
|
|
||||||
/// BARE: contract address (non-abi contract call)
|
|
||||||
/// OTHERS: special virtual function, nothing on the stack
|
|
||||||
/// @todo This documentation is outdated, and Location should rather be named "Type"
|
|
||||||
enum class Location { Internal, External, Creation, Send,
|
|
||||||
SHA3, Suicide,
|
|
||||||
ECRecover, SHA256, RIPEMD160,
|
|
||||||
Log0, Log1, Log2, Log3, Log4, Event,
|
|
||||||
SetGas, SetValue, BlockHash,
|
|
||||||
Bare };
|
|
||||||
|
|
||||||
virtual Category getCategory() const override { return Category::Function; }
|
|
||||||
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
|
|
||||||
explicit FunctionType(VariableDeclaration const& _varDecl);
|
|
||||||
explicit FunctionType(EventDefinition const& _event);
|
|
||||||
FunctionType(strings const& _parameterTypes, strings const& _returnParameterTypes,
|
|
||||||
Location _location = Location::Internal, bool _arbitraryParameters = false):
|
|
||||||
FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes),
|
|
||||||
_location, _arbitraryParameters) {}
|
|
||||||
FunctionType(
|
|
||||||
TypePointers const& _parameterTypes,
|
|
||||||
TypePointers const& _returnParameterTypes,
|
|
||||||
Location _location = Location::Internal,
|
|
||||||
bool _arbitraryParameters = false,
|
|
||||||
bool _gasSet = false,
|
|
||||||
bool _valueSet = false
|
|
||||||
):
|
|
||||||
m_parameterTypes (_parameterTypes),
|
|
||||||
m_returnParameterTypes (_returnParameterTypes),
|
|
||||||
m_location (_location),
|
|
||||||
m_arbitraryParameters (_arbitraryParameters),
|
|
||||||
m_gasSet (_gasSet),
|
|
||||||
m_valueSet (_valueSet)
|
|
||||||
{}
|
|
||||||
|
|
||||||
TypePointers const& getParameterTypes() const { return m_parameterTypes; }
|
|
||||||
std::vector<std::string> const& getParameterNames() const { return m_parameterNames; }
|
|
||||||
std::vector<std::string> const getParameterTypeNames() const;
|
|
||||||
TypePointers const& getReturnParameterTypes() const { return m_returnParameterTypes; }
|
|
||||||
std::vector<std::string> const& getReturnParameterNames() const { return m_returnParameterNames; }
|
|
||||||
std::vector<std::string> const getReturnParameterTypeNames() const;
|
|
||||||
|
|
||||||
virtual bool operator==(Type const& _other) const override;
|
|
||||||
virtual std::string toString() const override;
|
|
||||||
virtual bool canBeStored() const override { return false; }
|
|
||||||
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable function type requested.")); }
|
|
||||||
virtual bool canLiveOutsideStorage() const override { return false; }
|
|
||||||
virtual unsigned getSizeOnStack() const override;
|
|
||||||
virtual MemberList const& getMembers() const override;
|
|
||||||
|
|
||||||
Location const& getLocation() const { return m_location; }
|
|
||||||
/// @returns the canonical signature of this function type given the function name
|
|
||||||
/// If @a _name is not provided (empty string) then the @c m_declaration member of the
|
|
||||||
/// function type is used
|
|
||||||
std::string getCanonicalSignature(std::string const& _name = "") const;
|
|
||||||
Declaration const& getDeclaration() const
|
|
||||||
{
|
|
||||||
solAssert(m_declaration, "Requested declaration from a FunctionType that has none");
|
|
||||||
return *m_declaration;
|
|
||||||
}
|
|
||||||
bool hasDeclaration() const { return !!m_declaration; }
|
|
||||||
bool isConstant() const { return m_isConstant; }
|
|
||||||
/// @return A shared pointer of an ASTString.
|
|
||||||
/// Can contain a nullptr in which case indicates absence of documentation
|
|
||||||
ASTPointer<ASTString> getDocumentation() const;
|
|
||||||
|
|
||||||
/// true iff arguments are to be padded to multiples of 32 bytes for external calls
|
|
||||||
bool padArguments() const { return !(m_location == Location::SHA3 || m_location == Location::SHA256 || m_location == Location::RIPEMD160); }
|
|
||||||
bool takesArbitraryParameters() const { return m_arbitraryParameters; }
|
|
||||||
bool gasSet() const { return m_gasSet; }
|
|
||||||
bool valueSet() const { return m_valueSet; }
|
|
||||||
|
|
||||||
/// @returns a copy of this type, where gas or value are set manually. This will never set one
|
|
||||||
/// of the parameters to fals.
|
|
||||||
TypePointer copyAndSetGasOrValue(bool _setGas, bool _setValue) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
static TypePointers parseElementaryTypeVector(strings const& _types);
|
|
||||||
|
|
||||||
TypePointers m_parameterTypes;
|
|
||||||
TypePointers m_returnParameterTypes;
|
|
||||||
std::vector<std::string> m_parameterNames;
|
|
||||||
std::vector<std::string> m_returnParameterNames;
|
|
||||||
Location const m_location;
|
|
||||||
/// true iff the function takes an arbitrary number of arguments of arbitrary types
|
|
||||||
bool const m_arbitraryParameters = false;
|
|
||||||
bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack
|
|
||||||
bool const m_valueSet = false; ///< true iff the value to be sent is on the stack
|
|
||||||
bool m_isConstant = false;
|
|
||||||
mutable std::unique_ptr<MemberList> m_members;
|
|
||||||
Declaration const* m_declaration = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of a mapping, there is one distinct type per key/value type pair.
|
|
||||||
*/
|
|
||||||
class MappingType: public Type
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual Category getCategory() const override { return Category::Mapping; }
|
|
||||||
MappingType(TypePointer const& _keyType, TypePointer const& _valueType):
|
|
||||||
m_keyType(_keyType), m_valueType(_valueType) {}
|
|
||||||
|
|
||||||
virtual bool operator==(Type const& _other) const override;
|
|
||||||
virtual std::string toString() const override;
|
|
||||||
virtual bool canLiveOutsideStorage() const override { return false; }
|
|
||||||
|
|
||||||
TypePointer const& getKeyType() const { return m_keyType; }
|
|
||||||
TypePointer const& getValueType() const { return m_valueType; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
TypePointer m_keyType;
|
|
||||||
TypePointer m_valueType;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The void type, can only be implicitly used as the type that is returned by functions without
|
|
||||||
* return parameters.
|
|
||||||
*/
|
|
||||||
class VoidType: public Type
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual Category getCategory() const override { return Category::Void; }
|
|
||||||
VoidType() {}
|
|
||||||
|
|
||||||
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
|
|
||||||
virtual std::string toString() const override { return "void"; }
|
|
||||||
virtual bool canBeStored() const override { return false; }
|
|
||||||
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable void type requested.")); }
|
|
||||||
virtual bool canLiveOutsideStorage() const override { return false; }
|
|
||||||
virtual unsigned getSizeOnStack() const override { return 0; }
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of a type reference. The type of "uint32" when used in "a = uint32(2)" is an example
|
|
||||||
* of a TypeType.
|
|
||||||
*/
|
|
||||||
class TypeType: public Type
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual Category getCategory() const override { return Category::TypeType; }
|
|
||||||
explicit TypeType(TypePointer const& _actualType, ContractDefinition const* _currentContract = nullptr):
|
|
||||||
m_actualType(_actualType), m_currentContract(_currentContract) {}
|
|
||||||
TypePointer const& getActualType() const { return m_actualType; }
|
|
||||||
|
|
||||||
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
|
|
||||||
virtual bool operator==(Type const& _other) const override;
|
|
||||||
virtual bool canBeStored() const override { return false; }
|
|
||||||
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); }
|
|
||||||
virtual bool canLiveOutsideStorage() const override { return false; }
|
|
||||||
virtual unsigned getSizeOnStack() const override { return 0; }
|
|
||||||
virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; }
|
|
||||||
virtual MemberList const& getMembers() const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
TypePointer m_actualType;
|
|
||||||
/// Context in which this type is used (influences visibility etc.), can be nullptr.
|
|
||||||
ContractDefinition const* m_currentContract;
|
|
||||||
/// List of member types, will be lazy-initialized because of recursive references.
|
|
||||||
mutable std::unique_ptr<MemberList> m_members;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of a function modifier. Not used for anything for now.
|
|
||||||
*/
|
|
||||||
class ModifierType: public Type
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual Category getCategory() const override { return Category::Modifier; }
|
|
||||||
explicit ModifierType(ModifierDefinition const& _modifier);
|
|
||||||
|
|
||||||
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
|
|
||||||
virtual bool canBeStored() const override { return false; }
|
|
||||||
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); }
|
|
||||||
virtual bool canLiveOutsideStorage() const override { return false; }
|
|
||||||
virtual unsigned getSizeOnStack() const override { return 0; }
|
|
||||||
virtual bool operator==(Type const& _other) const override;
|
|
||||||
virtual std::string toString() const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
TypePointers m_parameterTypes;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Special type for magic variables (block, msg, tx), similar to a struct but without any reference
|
|
||||||
* (it always references a global singleton by name).
|
|
||||||
*/
|
|
||||||
class MagicType: public Type
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum class Kind { Block, Message, Transaction };
|
|
||||||
virtual Category getCategory() const override { return Category::Magic; }
|
|
||||||
|
|
||||||
explicit MagicType(Kind _kind);
|
|
||||||
|
|
||||||
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override
|
|
||||||
{
|
|
||||||
return TypePointer();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool operator==(Type const& _other) const;
|
|
||||||
virtual bool canBeStored() const override { return false; }
|
|
||||||
virtual bool canLiveOutsideStorage() const override { return true; }
|
|
||||||
virtual unsigned getSizeOnStack() const override { return 0; }
|
|
||||||
virtual MemberList const& getMembers() const override { return m_members; }
|
|
||||||
|
|
||||||
virtual std::string toString() const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Kind m_kind;
|
|
||||||
|
|
||||||
MemberList m_members;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
55
Utils.h
55
Utils.h
@ -1,55 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of cpp-ethereum.
|
|
||||||
|
|
||||||
cpp-ethereum 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.
|
|
||||||
|
|
||||||
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @author Christian <c@ethdev.com>
|
|
||||||
* @date 2014
|
|
||||||
* Solidity Utilities.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <libsolidity/Exceptions.h>
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
/// Assertion that throws an InternalCompilerError containing the given description if it is not met.
|
|
||||||
#define solAssert(CONDITION, DESCRIPTION) \
|
|
||||||
::dev::solidity::solAssertAux(CONDITION, DESCRIPTION, __LINE__, __FILE__, ETH_FUNC)
|
|
||||||
|
|
||||||
inline void solAssertAux(bool _condition, std::string const& _errorDescription, unsigned _line,
|
|
||||||
char const* _file, char const* _function)
|
|
||||||
{
|
|
||||||
if (!_condition)
|
|
||||||
::boost::throw_exception( InternalCompilerError()
|
|
||||||
<< errinfo_comment(_errorDescription)
|
|
||||||
<< ::boost::throw_function(_function)
|
|
||||||
<< ::boost::throw_file(_file)
|
|
||||||
<< ::boost::throw_line(_line));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void solAssertAux(void const* _pointer, std::string const& _errorDescription, unsigned _line,
|
|
||||||
char const* _file, char const* _function)
|
|
||||||
{
|
|
||||||
solAssertAux(_pointer != nullptr, _errorDescription, _line, _file, _function);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
46
grammar.txt
46
grammar.txt
@ -1,46 +0,0 @@
|
|||||||
ContractDefinition = 'contract' Identifier
|
|
||||||
( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )?
|
|
||||||
'{' ContractPart* '}'
|
|
||||||
ContractPart = StateVariableDeclaration | StructDefinition | ModifierDefinition | FunctionDefinition | EnumDefinition
|
|
||||||
|
|
||||||
InheritanceSpecifier = Identifier ( '(' Expression ( ',' Expression )* ')' )?
|
|
||||||
StructDefinition = 'struct' Identifier '{'
|
|
||||||
( VariableDeclaration (';' VariableDeclaration)* )? '}
|
|
||||||
StateVariableDeclaration = TypeName ( 'public' | 'inheritable' | 'private' )? Identifier ';'
|
|
||||||
ModifierDefinition = 'modifier' Identifier ParameterList? Block
|
|
||||||
FunctionDefinition = 'function' Identifier ParameterList
|
|
||||||
( Identifier | 'constant' | 'external' | 'public' | 'inheritable' | 'private' )*
|
|
||||||
( 'returns' ParameterList )? Block
|
|
||||||
|
|
||||||
EnumValue = Identifier
|
|
||||||
EnumDefinition = 'enum' '{' EnumValue (',' EnumValue)* '}'
|
|
||||||
ParameterList = '(' ( VariableDeclaration (',' VariableDeclaration)* )? ')'
|
|
||||||
// semantic restriction: mappings and structs (recursively) containing mappings
|
|
||||||
// are not allowed in argument lists
|
|
||||||
VariableDeclaration = TypeName Identifier
|
|
||||||
TypeName = ElementaryTypeName | Identifier | Mapping
|
|
||||||
Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')'
|
|
||||||
|
|
||||||
Block = '{' Statement* '}'
|
|
||||||
Statement = IfStatement | WhileStatement | Block |
|
|
||||||
( Continue | Break | Return | VariableDefinition | ExpressionStatement ) ';'
|
|
||||||
|
|
||||||
ExpressionStatement = Expression
|
|
||||||
IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )?
|
|
||||||
WhileStatement = 'while' '(' Expression ')' Statement
|
|
||||||
VardefOrExprStmt = Variabledefinition | ExpressionStatement
|
|
||||||
ForStatement = 'for' '(' (VardefOrExprStmt)? ';' (Expression)? ';' (ExpressionStatement)? ')' Statement
|
|
||||||
Continue = 'continue' ';'
|
|
||||||
Break = 'break' ';'
|
|
||||||
Return = 'return' Expression? ';'
|
|
||||||
VariableDefinition = VariableDeclaration ( = Expression )? ';'
|
|
||||||
|
|
||||||
Expression = Assignment | UnaryOperation | BinaryOperation | FunctionCall | NewExpression | IndexAccess |
|
|
||||||
MemberAccess | PrimaryExpression
|
|
||||||
// The expression syntax is actually much more complicated
|
|
||||||
Assignment = Expression (AssignmentOp Expression)
|
|
||||||
FunctionCall = Expression '(' Expression ( ',' Expression )* ')'
|
|
||||||
NewExpression = 'new' Identifier
|
|
||||||
MemberAccess = Expression '.' Identifier
|
|
||||||
IndexAccess = Expression '[' Expresison ']'
|
|
||||||
PrimaryExpression = Identifier | NumberLiteral | StringLiteral | ElementaryTypeName | '(' Expression ')'
|
|
Loading…
Reference in New Issue
Block a user