mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge branch 'develop'
Conflicts: README.md evmjit libdevcrypto/CryptoPP.cpp libethereum/State.cpp neth/main.cpp
This commit is contained in:
commit
89d84edb16
456
AST.cpp
456
AST.cpp
@ -27,6 +27,8 @@
|
||||
#include <libsolidity/Exceptions.h>
|
||||
#include <libsolidity/AST_accept.h>
|
||||
|
||||
#include <libdevcrypto/SHA3.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace dev
|
||||
@ -39,40 +41,199 @@ 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."));
|
||||
"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);
|
||||
}
|
||||
}
|
||||
|
||||
vector<FunctionDefinition const*> ContractDefinition::getInterfaceFunctions() const
|
||||
map<FixedHash<4>, FunctionTypePointer> ContractDefinition::getInterfaceFunctions() const
|
||||
{
|
||||
vector<FunctionDefinition const*> exportedFunctions;
|
||||
for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions)
|
||||
if (f->isPublic() && f->getName() != getName())
|
||||
exportedFunctions.push_back(f.get());
|
||||
auto compareNames = [](FunctionDefinition const* _a, FunctionDefinition const* _b)
|
||||
{
|
||||
return _a->getName().compare(_b->getName()) < 0;
|
||||
};
|
||||
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");
|
||||
|
||||
sort(exportedFunctions.begin(), exportedFunctions.end(), compareNames);
|
||||
return exportedFunctions;
|
||||
}
|
||||
|
||||
FunctionDefinition const* ContractDefinition::getConstructor() const
|
||||
{
|
||||
for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions)
|
||||
if (f->getName() == getName())
|
||||
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())
|
||||
@ -93,7 +254,7 @@ void StructDefinition::checkRecursion() const
|
||||
<< errinfo_comment("Recursive struct definition."));
|
||||
definitionsSeen.insert(def);
|
||||
for (ASTPointer<VariableDeclaration> const& member: def->getMembers())
|
||||
if (member->getType()->getCategory() == Type::Category::STRUCT)
|
||||
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()));
|
||||
@ -101,15 +262,89 @@ void StructDefinition::checkRecursion() const
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
@ -145,7 +380,8 @@ void Return::checkTypeRequirements()
|
||||
{
|
||||
if (!m_expression)
|
||||
return;
|
||||
solAssert(m_returnParameters, "Return parameters not assigned.");
|
||||
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."));
|
||||
@ -168,7 +404,17 @@ void VariableDefinition::checkTypeRequirements()
|
||||
{
|
||||
// no type declared and no previous assignment, infer the type
|
||||
m_value->checkTypeRequirements();
|
||||
m_variable->setType(m_value->getType());
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -177,20 +423,31 @@ void Assignment::checkTypeRequirements()
|
||||
{
|
||||
m_leftHandSide->checkTypeRequirements();
|
||||
m_leftHandSide->requireLValue();
|
||||
//@todo later, assignments to structs might be possible, but not to mappings
|
||||
if (!m_leftHandSide->getType()->isValueType() && !m_leftHandSide->isLocalLValue())
|
||||
BOOST_THROW_EXCEPTION(createTypeError("Assignment to non-local non-value lvalue."));
|
||||
m_rightHandSide->expectType(*m_leftHandSide->getType());
|
||||
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)
|
||||
if (m_assigmentOperator == Token::Assign)
|
||||
m_rightHandSide->expectType(*m_type);
|
||||
else
|
||||
{
|
||||
// compound assignment
|
||||
if (!m_type->acceptsBinaryOperator(Token::AssignmentToBinaryOp(m_assigmentOperator)))
|
||||
BOOST_THROW_EXCEPTION(createTypeError("Operator not compatible with type."));
|
||||
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)
|
||||
@ -212,12 +469,12 @@ void Expression::requireLValue()
|
||||
|
||||
void UnaryOperation::checkTypeRequirements()
|
||||
{
|
||||
// INC, DEC, ADD, SUB, NOT, BIT_NOT, DELETE
|
||||
// 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)
|
||||
if (m_operator == Token::Value::Inc || m_operator == Token::Value::Dec || m_operator == Token::Value::Delete)
|
||||
m_subExpression->requireLValue();
|
||||
m_type = m_subExpression->getType();
|
||||
if (!m_type->acceptsUnaryOperator(m_operator))
|
||||
m_type = m_subExpression->getType()->unaryOperatorResult(m_operator);
|
||||
if (!m_type)
|
||||
BOOST_THROW_EXCEPTION(createTypeError("Unary operator not compatible with type."));
|
||||
}
|
||||
|
||||
@ -225,24 +482,13 @@ void BinaryOperation::checkTypeRequirements()
|
||||
{
|
||||
m_left->checkTypeRequirements();
|
||||
m_right->checkTypeRequirements();
|
||||
if (m_right->getType()->isImplicitlyConvertibleTo(*m_left->getType()))
|
||||
m_commonType = m_left->getType();
|
||||
else if (m_left->getType()->isImplicitlyConvertibleTo(*m_right->getType()))
|
||||
m_commonType = m_right->getType();
|
||||
else
|
||||
BOOST_THROW_EXCEPTION(createTypeError("No common type found in binary operation: " +
|
||||
m_left->getType()->toString() + " vs. " +
|
||||
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()));
|
||||
if (Token::isCompareOp(m_operator))
|
||||
m_type = make_shared<BoolType>();
|
||||
else
|
||||
{
|
||||
m_type = m_commonType;
|
||||
if (!m_commonType->acceptsBinaryOperator(m_operator))
|
||||
BOOST_THROW_EXCEPTION(createTypeError("Operator " + string(Token::toString(m_operator)) +
|
||||
" not compatible with type " +
|
||||
m_commonType->toString()));
|
||||
}
|
||||
m_type = Token::isCompareOp(m_operator) ? make_shared<BoolType>() : m_commonType;
|
||||
}
|
||||
|
||||
void FunctionCall::checkTypeRequirements()
|
||||
@ -258,8 +504,9 @@ void FunctionCall::checkTypeRequirements()
|
||||
//@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 conersion."));
|
||||
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();
|
||||
@ -270,11 +517,48 @@ void FunctionCall::checkTypeRequirements()
|
||||
// and then ask if that is implicitly convertible to the struct represented by the
|
||||
// function parameters
|
||||
TypePointers const& parameterTypes = functionType->getParameterTypes();
|
||||
if (parameterTypes.size() != m_arguments.size())
|
||||
if (!functionType->takesArbitraryParameters() && parameterTypes.size() != m_arguments.size())
|
||||
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function 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 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())
|
||||
@ -288,26 +572,19 @@ void FunctionCall::checkTypeRequirements()
|
||||
|
||||
bool FunctionCall::isTypeConversion() const
|
||||
{
|
||||
return m_expression->getType()->getCategory() == Type::Category::TYPE;
|
||||
return m_expression->getType()->getCategory() == Type::Category::TypeType;
|
||||
}
|
||||
|
||||
void NewExpression::checkTypeRequirements()
|
||||
{
|
||||
m_contractName->checkTypeRequirements();
|
||||
for (ASTPointer<Expression> const& argument: m_arguments)
|
||||
argument->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> type = make_shared<ContractType const>(*m_contract);
|
||||
m_type = type;
|
||||
TypePointers const& parameterTypes = type->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 constructor call."));
|
||||
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()
|
||||
@ -316,78 +593,43 @@ void MemberAccess::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 in " + type.toString()));
|
||||
//@todo later, this will not always be STORAGE
|
||||
m_lvalue = type.getCategory() == Type::Category::STRUCT ? LValueType::STORAGE : LValueType::NONE;
|
||||
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)
|
||||
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_lvalue = LValueType::STORAGE;
|
||||
m_isLValue = true;
|
||||
}
|
||||
|
||||
void Identifier::checkTypeRequirements()
|
||||
{
|
||||
solAssert(m_referencedDeclaration, "Identifier not resolved.");
|
||||
|
||||
VariableDeclaration const* variable = dynamic_cast<VariableDeclaration const*>(m_referencedDeclaration);
|
||||
if (variable)
|
||||
{
|
||||
if (!variable->getType())
|
||||
BOOST_THROW_EXCEPTION(createTypeError("Variable referenced before type could be determined."));
|
||||
m_type = variable->getType();
|
||||
m_lvalue = variable->isLocalVariable() ? LValueType::LOCAL : LValueType::STORAGE;
|
||||
return;
|
||||
}
|
||||
//@todo can we unify these with TypeName::toType()?
|
||||
StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(m_referencedDeclaration);
|
||||
if (structDef)
|
||||
{
|
||||
// note that we do not have a struct type here
|
||||
m_type = make_shared<TypeType const>(make_shared<StructType const>(*structDef));
|
||||
return;
|
||||
}
|
||||
FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(m_referencedDeclaration);
|
||||
if (functionDef)
|
||||
{
|
||||
// a function reference is not a TypeType, because calling a TypeType converts to the type.
|
||||
// Calling a function (e.g. function(12), otherContract.function(34)) does not do a type
|
||||
// conversion.
|
||||
m_type = make_shared<FunctionType const>(*functionDef);
|
||||
return;
|
||||
}
|
||||
ContractDefinition const* contractDef = dynamic_cast<ContractDefinition const*>(m_referencedDeclaration);
|
||||
if (contractDef)
|
||||
{
|
||||
m_type = make_shared<TypeType const>(make_shared<ContractType>(*contractDef));
|
||||
return;
|
||||
}
|
||||
MagicVariableDeclaration const* magicVariable = dynamic_cast<MagicVariableDeclaration const*>(m_referencedDeclaration);
|
||||
if (magicVariable)
|
||||
{
|
||||
m_type = magicVariable->getType();
|
||||
return;
|
||||
}
|
||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Declaration reference of unknown/forbidden type."));
|
||||
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 const>(Type::fromElementaryTypeName(m_typeToken));
|
||||
m_type = make_shared<TypeType>(Type::fromElementaryTypeName(m_typeToken));
|
||||
}
|
||||
|
||||
void Literal::checkTypeRequirements()
|
||||
{
|
||||
m_type = Type::forLiteral(*this);
|
||||
if (!m_type)
|
||||
BOOST_THROW_EXCEPTION(createTypeError("Literal value too large."));
|
||||
BOOST_THROW_EXCEPTION(createTypeError("Invalid literal value."));
|
||||
}
|
||||
|
||||
}
|
||||
|
393
AST.h
393
AST.h
@ -132,68 +132,167 @@ private:
|
||||
class Declaration: public ASTNode
|
||||
{
|
||||
public:
|
||||
Declaration(Location const& _location, ASTPointer<ASTString> const& _name):
|
||||
ASTNode(_location), m_name(_name), m_scope(nullptr) {}
|
||||
/// Visibility ordered from restricted to unrestricted.
|
||||
enum class Visibility { Default, Private, Inheritable, Public, External };
|
||||
|
||||
Declaration(Location const& _location, ASTPointer<ASTString> const& _name,
|
||||
Visibility _visibility = Visibility::Default):
|
||||
ASTNode(_location), m_name(_name), m_visibility(_visibility), m_scope(nullptr) {}
|
||||
|
||||
/// @returns the declared name.
|
||||
ASTString const& getName() const { return *m_name; }
|
||||
Visibility getVisibility() const { return m_visibility == Visibility::Default ? getDefaultVisibility() : m_visibility; }
|
||||
bool isPublic() const { return getVisibility() >= Visibility::Public; }
|
||||
bool isVisibleInContract() const { return getVisibility() != Visibility::External; }
|
||||
bool isVisibleInDerivedContracts() const { return isVisibleInContract() && getVisibility() >= Visibility::Inheritable; }
|
||||
|
||||
/// @returns the scope this declaration resides in. Can be nullptr if it is the global scope.
|
||||
/// Available only after name and type resolution step.
|
||||
Declaration const* getScope() const { return m_scope; }
|
||||
void setScope(Declaration const* _scope) { m_scope = _scope; }
|
||||
|
||||
/// @returns the type of expressions referencing this declaration.
|
||||
/// The current contract has to be given since this context can change the type, especially of
|
||||
/// contract types.
|
||||
virtual TypePointer getType(ContractDefinition const* m_currentContract = nullptr) const = 0;
|
||||
virtual bool isLValue() const { return false; }
|
||||
|
||||
protected:
|
||||
virtual Visibility getDefaultVisibility() const { return Visibility::Public; }
|
||||
|
||||
private:
|
||||
ASTPointer<ASTString> m_name;
|
||||
Visibility m_visibility;
|
||||
Declaration const* m_scope;
|
||||
};
|
||||
|
||||
/**
|
||||
* Abstract class that is added to each AST node that can store local variables.
|
||||
*/
|
||||
class VariableScope
|
||||
{
|
||||
public:
|
||||
void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); }
|
||||
std::vector<VariableDeclaration const*> const& getLocalVariables() const { return m_localVariables; }
|
||||
|
||||
private:
|
||||
std::vector<VariableDeclaration const*> m_localVariables;
|
||||
};
|
||||
|
||||
/**
|
||||
* Abstract class that is added to each AST node that can receive documentation.
|
||||
*/
|
||||
class Documented
|
||||
{
|
||||
public:
|
||||
explicit Documented(ASTPointer<ASTString> const& _documentation): m_documentation(_documentation) {}
|
||||
|
||||
/// @return A shared pointer of an ASTString.
|
||||
/// Can contain a nullptr in which case indicates absence of documentation
|
||||
ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
|
||||
|
||||
protected:
|
||||
ASTPointer<ASTString> m_documentation;
|
||||
};
|
||||
|
||||
/// @}
|
||||
|
||||
/**
|
||||
* Definition of a contract. This is the only AST nodes where child nodes are not visited in
|
||||
* document order. It first visits all struct declarations, then all variable declarations and
|
||||
* finally all function declarations.
|
||||
*/
|
||||
class ContractDefinition: public Declaration
|
||||
class ContractDefinition: public Declaration, public Documented
|
||||
{
|
||||
public:
|
||||
ContractDefinition(Location const& _location,
|
||||
ASTPointer<ASTString> const& _name,
|
||||
ASTPointer<ASTString> const& _documentation,
|
||||
std::vector<ASTPointer<InheritanceSpecifier>> const& _baseContracts,
|
||||
std::vector<ASTPointer<StructDefinition>> const& _definedStructs,
|
||||
std::vector<ASTPointer<EnumDefinition>> const& _definedEnums,
|
||||
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables,
|
||||
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions):
|
||||
Declaration(_location, _name),
|
||||
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions,
|
||||
std::vector<ASTPointer<ModifierDefinition>> const& _functionModifiers,
|
||||
std::vector<ASTPointer<EventDefinition>> const& _events):
|
||||
Declaration(_location, _name), Documented(_documentation),
|
||||
m_baseContracts(_baseContracts),
|
||||
m_definedStructs(_definedStructs),
|
||||
m_definedEnums(_definedEnums),
|
||||
m_stateVariables(_stateVariables),
|
||||
m_definedFunctions(_definedFunctions),
|
||||
m_documentation(_documentation)
|
||||
m_functionModifiers(_functionModifiers),
|
||||
m_events(_events)
|
||||
{}
|
||||
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
|
||||
std::vector<ASTPointer<InheritanceSpecifier>> const& getBaseContracts() const { return m_baseContracts; }
|
||||
std::vector<ASTPointer<StructDefinition>> const& getDefinedStructs() const { return m_definedStructs; }
|
||||
std::vector<ASTPointer<EnumDefinition>> const& getDefinedEnums() const { return m_definedEnums; }
|
||||
std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() const { return m_stateVariables; }
|
||||
std::vector<ASTPointer<ModifierDefinition>> const& getFunctionModifiers() const { return m_functionModifiers; }
|
||||
std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() const { return m_definedFunctions; }
|
||||
std::vector<ASTPointer<EventDefinition>> const& getEvents() const { return m_events; }
|
||||
std::vector<ASTPointer<EventDefinition>> const& getInterfaceEvents() const;
|
||||
|
||||
/// Checks that the constructor does not have a "returns" statement and calls
|
||||
/// checkTypeRequirements on all its functions.
|
||||
virtual TypePointer getType(ContractDefinition const* m_currentContract) const override;
|
||||
|
||||
/// Checks that there are no illegal overrides, that the constructor does not have a "returns"
|
||||
/// and calls checkTypeRequirements on all its functions.
|
||||
void checkTypeRequirements();
|
||||
|
||||
/// @return A shared pointer of an ASTString.
|
||||
/// Can contain a nullptr in which case indicates absence of documentation
|
||||
ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
|
||||
/// @returns a map of canonical function signatures to FunctionDefinitions
|
||||
/// as intended for use by the ABI.
|
||||
std::map<FixedHash<4>, FunctionTypePointer> getInterfaceFunctions() const;
|
||||
|
||||
/// Returns the functions that make up the calling interface in the intended order.
|
||||
std::vector<FunctionDefinition const*> getInterfaceFunctions() const;
|
||||
/// List of all (direct and indirect) base contracts in order from derived to base, including
|
||||
/// the contract itself. Available after name resolution
|
||||
std::vector<ContractDefinition const*> const& getLinearizedBaseContracts() const { return m_linearizedBaseContracts; }
|
||||
void setLinearizedBaseContracts(std::vector<ContractDefinition const*> const& _bases) { m_linearizedBaseContracts = _bases; }
|
||||
|
||||
/// Returns the constructor or nullptr if no constructor was specified
|
||||
/// Returns the constructor or nullptr if no constructor was specified.
|
||||
FunctionDefinition const* getConstructor() const;
|
||||
/// Returns the fallback function or nullptr if no fallback function was specified.
|
||||
FunctionDefinition const* getFallbackFunction() const;
|
||||
|
||||
private:
|
||||
void checkIllegalOverrides() const;
|
||||
|
||||
std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& getInterfaceFunctionList() const;
|
||||
|
||||
std::vector<ASTPointer<InheritanceSpecifier>> m_baseContracts;
|
||||
std::vector<ASTPointer<StructDefinition>> m_definedStructs;
|
||||
std::vector<ASTPointer<EnumDefinition>> m_definedEnums;
|
||||
std::vector<ASTPointer<VariableDeclaration>> m_stateVariables;
|
||||
std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions;
|
||||
ASTPointer<ASTString> m_documentation;
|
||||
std::vector<ASTPointer<ModifierDefinition>> m_functionModifiers;
|
||||
std::vector<ASTPointer<EventDefinition>> m_events;
|
||||
|
||||
std::vector<ContractDefinition const*> m_linearizedBaseContracts;
|
||||
mutable std::unique_ptr<std::vector<std::pair<FixedHash<4>, FunctionTypePointer>>> m_interfaceFunctionList;
|
||||
mutable std::unique_ptr<std::vector<ASTPointer<EventDefinition>>> m_interfaceEvents;
|
||||
};
|
||||
|
||||
class InheritanceSpecifier: public ASTNode
|
||||
{
|
||||
public:
|
||||
InheritanceSpecifier(Location const& _location, ASTPointer<Identifier> const& _baseName,
|
||||
std::vector<ASTPointer<Expression>> _arguments):
|
||||
ASTNode(_location), m_baseName(_baseName), m_arguments(_arguments) {}
|
||||
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
|
||||
ASTPointer<Identifier> const& getName() const { return m_baseName; }
|
||||
std::vector<ASTPointer<Expression>> const& getArguments() const { return m_arguments; }
|
||||
|
||||
void checkTypeRequirements();
|
||||
|
||||
private:
|
||||
ASTPointer<Identifier> m_baseName;
|
||||
std::vector<ASTPointer<Expression>> m_arguments;
|
||||
};
|
||||
|
||||
class StructDefinition: public Declaration
|
||||
@ -208,6 +307,8 @@ public:
|
||||
|
||||
std::vector<ASTPointer<VariableDeclaration>> const& getMembers() const { return m_members; }
|
||||
|
||||
virtual TypePointer getType(ContractDefinition const*) const override;
|
||||
|
||||
/// Checks that the members do not include any recursive structs and have valid types
|
||||
/// (e.g. no functions).
|
||||
void checkValidityOfMembers() const;
|
||||
@ -219,6 +320,39 @@ private:
|
||||
std::vector<ASTPointer<VariableDeclaration>> m_members;
|
||||
};
|
||||
|
||||
class EnumDefinition: public Declaration
|
||||
{
|
||||
public:
|
||||
EnumDefinition(Location const& _location,
|
||||
ASTPointer<ASTString> const& _name,
|
||||
std::vector<ASTPointer<EnumValue>> const& _members):
|
||||
Declaration(_location, _name), m_members(_members) {}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
|
||||
std::vector<ASTPointer<EnumValue>> const& getMembers() const { return m_members; }
|
||||
|
||||
virtual TypePointer getType(ContractDefinition const*) const override;
|
||||
|
||||
private:
|
||||
std::vector<ASTPointer<EnumValue>> m_members;
|
||||
};
|
||||
|
||||
/**
|
||||
* Declaration of an Enum Value
|
||||
*/
|
||||
class EnumValue: public Declaration
|
||||
{
|
||||
public:
|
||||
EnumValue(Location const& _location,
|
||||
ASTPointer<ASTString> const& _name):
|
||||
Declaration(_location, _name) {}
|
||||
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
TypePointer getType(ContractDefinition const* = nullptr) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parameter list, used as function parameter list and return list.
|
||||
* None of the parameters is allowed to contain mappings (not even recursively
|
||||
@ -239,53 +373,55 @@ private:
|
||||
std::vector<ASTPointer<VariableDeclaration>> m_parameters;
|
||||
};
|
||||
|
||||
class FunctionDefinition: public Declaration
|
||||
class FunctionDefinition: public Declaration, public VariableScope, public Documented
|
||||
{
|
||||
public:
|
||||
FunctionDefinition(Location const& _location, ASTPointer<ASTString> const& _name,
|
||||
bool _isPublic,
|
||||
Declaration::Visibility _visibility, bool _isConstructor,
|
||||
ASTPointer<ASTString> const& _documentation,
|
||||
ASTPointer<ParameterList> const& _parameters,
|
||||
bool _isDeclaredConst,
|
||||
std::vector<ASTPointer<ModifierInvocation>> const& _modifiers,
|
||||
ASTPointer<ParameterList> const& _returnParameters,
|
||||
ASTPointer<Block> const& _body):
|
||||
Declaration(_location, _name), m_isPublic(_isPublic),
|
||||
Declaration(_location, _name, _visibility), Documented(_documentation),
|
||||
m_isConstructor(_isConstructor),
|
||||
m_parameters(_parameters),
|
||||
m_isDeclaredConst(_isDeclaredConst),
|
||||
m_functionModifiers(_modifiers),
|
||||
m_returnParameters(_returnParameters),
|
||||
m_body(_body),
|
||||
m_documentation(_documentation)
|
||||
m_body(_body)
|
||||
{}
|
||||
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
|
||||
bool isPublic() const { return m_isPublic; }
|
||||
bool isConstructor() const { return m_isConstructor; }
|
||||
bool isDeclaredConst() const { return m_isDeclaredConst; }
|
||||
std::vector<ASTPointer<ModifierInvocation>> const& getModifiers() const { return m_functionModifiers; }
|
||||
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); }
|
||||
ParameterList const& getParameterList() const { return *m_parameters; }
|
||||
std::vector<ASTPointer<VariableDeclaration>> const& getReturnParameters() const { return m_returnParameters->getParameters(); }
|
||||
ASTPointer<ParameterList> const& getReturnParameterList() const { return m_returnParameters; }
|
||||
Block const& getBody() const { return *m_body; }
|
||||
/// @return A shared pointer of an ASTString.
|
||||
/// Can contain a nullptr in which case indicates absence of documentation
|
||||
ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
|
||||
|
||||
void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); }
|
||||
std::vector<VariableDeclaration const*> const& getLocalVariables() const { return m_localVariables; }
|
||||
virtual TypePointer getType(ContractDefinition const*) const override;
|
||||
|
||||
/// Checks that all parameters have allowed types and calls checkTypeRequirements on the body.
|
||||
void checkTypeRequirements();
|
||||
|
||||
/// @returns the canonical signature of the function
|
||||
/// That consists of the name of the function followed by the types of the
|
||||
/// arguments separated by commas all enclosed in parentheses without any spaces.
|
||||
std::string getCanonicalSignature() const;
|
||||
|
||||
private:
|
||||
bool m_isPublic;
|
||||
bool m_isConstructor;
|
||||
ASTPointer<ParameterList> m_parameters;
|
||||
bool m_isDeclaredConst;
|
||||
std::vector<ASTPointer<ModifierInvocation>> m_functionModifiers;
|
||||
ASTPointer<ParameterList> m_returnParameters;
|
||||
ASTPointer<Block> m_body;
|
||||
ASTPointer<ASTString> m_documentation;
|
||||
|
||||
std::vector<VariableDeclaration const*> m_localVariables;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -296,8 +432,10 @@ class VariableDeclaration: public Declaration
|
||||
{
|
||||
public:
|
||||
VariableDeclaration(Location const& _location, ASTPointer<TypeName> const& _type,
|
||||
ASTPointer<ASTString> const& _name):
|
||||
Declaration(_location, _name), m_typeName(_type) {}
|
||||
ASTPointer<ASTString> const& _name, Visibility _visibility,
|
||||
bool _isStateVar = false, bool _isIndexed = false):
|
||||
Declaration(_location, _name, _visibility), m_typeName(_type),
|
||||
m_isStateVariable(_isStateVar), m_isIndexed(_isIndexed) {}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
|
||||
@ -305,17 +443,108 @@ public:
|
||||
|
||||
/// Returns the declared or inferred type. Can be an empty pointer if no type was explicitly
|
||||
/// declared and there is no assignment to the variable that fixes the type.
|
||||
std::shared_ptr<Type const> const& getType() const { return m_type; }
|
||||
TypePointer getType(ContractDefinition const* = nullptr) const { return m_type; }
|
||||
void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; }
|
||||
|
||||
virtual bool isLValue() const override;
|
||||
bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); }
|
||||
bool isExternalFunctionParameter() const;
|
||||
bool isStateVariable() const { return m_isStateVariable; }
|
||||
bool isIndexed() const { return m_isIndexed; }
|
||||
|
||||
protected:
|
||||
Visibility getDefaultVisibility() const override { return Visibility::Inheritable; }
|
||||
|
||||
private:
|
||||
ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
|
||||
ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
|
||||
bool m_isStateVariable; ///< Whether or not this is a contract state variable
|
||||
bool m_isIndexed; ///< Whether this is an indexed variable (used by events).
|
||||
|
||||
std::shared_ptr<Type const> m_type; ///< derived type, initially empty
|
||||
};
|
||||
|
||||
/**
|
||||
* Definition of a function modifier.
|
||||
*/
|
||||
class ModifierDefinition: public Declaration, public VariableScope, public Documented
|
||||
{
|
||||
public:
|
||||
ModifierDefinition(Location const& _location,
|
||||
ASTPointer<ASTString> const& _name,
|
||||
ASTPointer<ASTString> const& _documentation,
|
||||
ASTPointer<ParameterList> const& _parameters,
|
||||
ASTPointer<Block> const& _body):
|
||||
Declaration(_location, _name), Documented(_documentation),
|
||||
m_parameters(_parameters), m_body(_body) {}
|
||||
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
|
||||
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); }
|
||||
ParameterList const& getParameterList() const { return *m_parameters; }
|
||||
Block const& getBody() const { return *m_body; }
|
||||
|
||||
virtual TypePointer getType(ContractDefinition const* = nullptr) const override;
|
||||
|
||||
void checkTypeRequirements();
|
||||
|
||||
private:
|
||||
ASTPointer<ParameterList> m_parameters;
|
||||
ASTPointer<Block> m_body;
|
||||
};
|
||||
|
||||
/**
|
||||
* Invocation/usage of a modifier in a function header.
|
||||
*/
|
||||
class ModifierInvocation: public ASTNode
|
||||
{
|
||||
public:
|
||||
ModifierInvocation(Location const& _location, ASTPointer<Identifier> const& _name,
|
||||
std::vector<ASTPointer<Expression>> _arguments):
|
||||
ASTNode(_location), m_modifierName(_name), m_arguments(_arguments) {}
|
||||
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
|
||||
ASTPointer<Identifier> const& getName() const { return m_modifierName; }
|
||||
std::vector<ASTPointer<Expression>> const& getArguments() const { return m_arguments; }
|
||||
|
||||
void checkTypeRequirements();
|
||||
|
||||
private:
|
||||
ASTPointer<Identifier> m_modifierName;
|
||||
std::vector<ASTPointer<Expression>> m_arguments;
|
||||
};
|
||||
|
||||
/**
|
||||
* Definition of a (loggable) event.
|
||||
*/
|
||||
class EventDefinition: public Declaration, public VariableScope, public Documented
|
||||
{
|
||||
public:
|
||||
EventDefinition(Location const& _location,
|
||||
ASTPointer<ASTString> const& _name,
|
||||
ASTPointer<ASTString> const& _documentation,
|
||||
ASTPointer<ParameterList> const& _parameters):
|
||||
Declaration(_location, _name), Documented(_documentation), m_parameters(_parameters) {}
|
||||
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
|
||||
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); }
|
||||
ParameterList const& getParameterList() const { return *m_parameters; }
|
||||
|
||||
virtual TypePointer getType(ContractDefinition const* = nullptr) const override
|
||||
{
|
||||
return std::make_shared<FunctionType>(*this);
|
||||
}
|
||||
|
||||
void checkTypeRequirements();
|
||||
|
||||
private:
|
||||
ASTPointer<ParameterList> m_parameters;
|
||||
};
|
||||
|
||||
/**
|
||||
* Pseudo AST node that is used as declaration for "this", "msg", "tx", "block" and the global
|
||||
* functions when such an identifier is encountered. Will never have a valid location in the source code.
|
||||
@ -330,7 +559,7 @@ public:
|
||||
virtual void accept(ASTConstVisitor&) const override { BOOST_THROW_EXCEPTION(InternalCompilerError()
|
||||
<< errinfo_comment("MagicVariableDeclaration used inside real AST.")); }
|
||||
|
||||
std::shared_ptr<Type const> const& getType() const { return m_type; }
|
||||
virtual TypePointer getType(ContractDefinition const* = nullptr) const override { return m_type; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<Type const> m_type;
|
||||
@ -457,6 +686,21 @@ private:
|
||||
std::vector<ASTPointer<Statement>> m_statements;
|
||||
};
|
||||
|
||||
/**
|
||||
* Special placeholder statement denoted by "_" used in function modifiers. This is replaced by
|
||||
* the original function when the modifier is applied.
|
||||
*/
|
||||
class PlaceholderStatement: public Statement
|
||||
{
|
||||
public:
|
||||
PlaceholderStatement(Location const& _location): Statement(_location) {}
|
||||
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
|
||||
virtual void checkTypeRequirements() override { }
|
||||
};
|
||||
|
||||
/**
|
||||
* If-statement with an optional "else" part. Note that "else if" is modeled by having a new
|
||||
* if-statement as the false (else) body.
|
||||
@ -474,7 +718,7 @@ public:
|
||||
|
||||
Expression const& getCondition() const { return *m_condition; }
|
||||
Statement const& getTrueStatement() const { return *m_trueBody; }
|
||||
/// @returns the "else" part of the if statement or nullptr if there is no "else" part.
|
||||
/// @returns the "else" part of the if statement or nullptr if there is no "else" part.
|
||||
Statement const* getFalseStatement() const { return m_falseBody.get(); }
|
||||
|
||||
private:
|
||||
@ -573,19 +817,15 @@ public:
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
virtual void checkTypeRequirements() override;
|
||||
|
||||
void setFunctionReturnParameters(ParameterList& _parameters) { m_returnParameters = &_parameters; }
|
||||
ParameterList const& getFunctionReturnParameters() const
|
||||
{
|
||||
solAssert(m_returnParameters, "");
|
||||
return *m_returnParameters;
|
||||
}
|
||||
void setFunctionReturnParameters(ParameterList const* _parameters) { m_returnParameters = _parameters; }
|
||||
ParameterList const* getFunctionReturnParameters() const { return m_returnParameters; }
|
||||
Expression const* getExpression() const { return m_expression.get(); }
|
||||
|
||||
private:
|
||||
ASTPointer<Expression> m_expression; ///< value to return, optional
|
||||
|
||||
/// Pointer to the parameter list of the function, filled by the @ref NameAndTypeResolver.
|
||||
ParameterList* m_returnParameters;
|
||||
ParameterList const* m_returnParameters;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -641,16 +881,12 @@ private:
|
||||
*/
|
||||
class Expression: public ASTNode
|
||||
{
|
||||
protected:
|
||||
enum class LValueType { NONE, LOCAL, STORAGE };
|
||||
|
||||
public:
|
||||
Expression(Location const& _location): ASTNode(_location), m_lvalue(LValueType::NONE), m_lvalueRequested(false) {}
|
||||
Expression(Location const& _location): ASTNode(_location) {}
|
||||
virtual void checkTypeRequirements() = 0;
|
||||
|
||||
std::shared_ptr<Type const> const& getType() const { return m_type; }
|
||||
bool isLValue() const { return m_lvalue != LValueType::NONE; }
|
||||
bool isLocalLValue() const { return m_lvalue == LValueType::LOCAL; }
|
||||
bool isLValue() const { return m_isLValue; }
|
||||
|
||||
/// Helper function, infer the type via @ref checkTypeRequirements and then check that it
|
||||
/// is implicitly convertible to @a _expectedType. If not, throw exception.
|
||||
@ -665,11 +901,11 @@ public:
|
||||
protected:
|
||||
//! Inferred type of the expression, only filled after a call to checkTypeRequirements().
|
||||
std::shared_ptr<Type const> m_type;
|
||||
//! If this expression is an lvalue (i.e. something that can be assigned to) and is stored
|
||||
//! locally or in storage. This is set during calls to @a checkTypeRequirements()
|
||||
LValueType m_lvalue;
|
||||
//! If this expression is an lvalue (i.e. something that can be assigned to).
|
||||
//! This is set during calls to @a checkTypeRequirements()
|
||||
bool m_isLValue = false;
|
||||
//! Whether the outer expression requested the address (true) or the value (false) of this expression.
|
||||
bool m_lvalueRequested;
|
||||
bool m_lvalueRequested = false;
|
||||
};
|
||||
|
||||
/// Assignment, can also be a compound assignment.
|
||||
@ -765,14 +1001,15 @@ class FunctionCall: public Expression
|
||||
{
|
||||
public:
|
||||
FunctionCall(Location const& _location, ASTPointer<Expression> const& _expression,
|
||||
std::vector<ASTPointer<Expression>> const& _arguments):
|
||||
Expression(_location), m_expression(_expression), m_arguments(_arguments) {}
|
||||
std::vector<ASTPointer<Expression>> const& _arguments, std::vector<ASTPointer<ASTString>> const& _names):
|
||||
Expression(_location), m_expression(_expression), m_arguments(_arguments), m_names(_names) {}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
virtual void checkTypeRequirements() override;
|
||||
|
||||
Expression const& getExpression() const { return *m_expression; }
|
||||
std::vector<ASTPointer<Expression const>> getArguments() const { return {m_arguments.begin(), m_arguments.end()}; }
|
||||
std::vector<ASTPointer<ASTString>> const& getNames() const { return m_names; }
|
||||
|
||||
/// Returns true if this is not an actual function call, but an explicit type conversion
|
||||
/// or constructor call.
|
||||
@ -781,29 +1018,26 @@ public:
|
||||
private:
|
||||
ASTPointer<Expression> m_expression;
|
||||
std::vector<ASTPointer<Expression>> m_arguments;
|
||||
std::vector<ASTPointer<ASTString>> m_names;
|
||||
};
|
||||
|
||||
/**
|
||||
* Expression that creates a new contract, e.g. "new SomeContract(1, 2)".
|
||||
* Expression that creates a new contract, e.g. the "new SomeContract" part in "new SomeContract(1, 2)".
|
||||
*/
|
||||
class NewExpression: public Expression
|
||||
{
|
||||
public:
|
||||
NewExpression(Location const& _location, ASTPointer<Identifier> const& _contractName,
|
||||
std::vector<ASTPointer<Expression>> const& _arguments):
|
||||
Expression(_location), m_contractName(_contractName), m_arguments(_arguments) {}
|
||||
NewExpression(Location const& _location, ASTPointer<Identifier> const& _contractName):
|
||||
Expression(_location), m_contractName(_contractName) {}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
virtual void checkTypeRequirements() override;
|
||||
|
||||
std::vector<ASTPointer<Expression const>> getArguments() const { return {m_arguments.begin(), m_arguments.end()}; }
|
||||
|
||||
/// Returns the referenced contract. Can only be called after type checking.
|
||||
ContractDefinition const* getContract() const { solAssert(m_contract, ""); return m_contract; }
|
||||
|
||||
private:
|
||||
ASTPointer<Identifier> m_contractName;
|
||||
std::vector<ASTPointer<Expression>> m_arguments;
|
||||
|
||||
ContractDefinition const* m_contract = nullptr;
|
||||
};
|
||||
@ -866,21 +1100,30 @@ class Identifier: public PrimaryExpression
|
||||
{
|
||||
public:
|
||||
Identifier(Location const& _location, ASTPointer<ASTString> const& _name):
|
||||
PrimaryExpression(_location), m_name(_name), m_referencedDeclaration(nullptr) {}
|
||||
PrimaryExpression(_location), m_name(_name) {}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
virtual void checkTypeRequirements() override;
|
||||
|
||||
ASTString const& getName() const { return *m_name; }
|
||||
|
||||
void setReferencedDeclaration(Declaration const& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; }
|
||||
void setReferencedDeclaration(Declaration const& _referencedDeclaration,
|
||||
ContractDefinition const* _currentContract = nullptr)
|
||||
{
|
||||
m_referencedDeclaration = &_referencedDeclaration;
|
||||
m_currentContract = _currentContract;
|
||||
}
|
||||
Declaration const* getReferencedDeclaration() const { return m_referencedDeclaration; }
|
||||
ContractDefinition const* getCurrentContract() const { return m_currentContract; }
|
||||
|
||||
private:
|
||||
ASTPointer<ASTString> m_name;
|
||||
|
||||
/// Declaration the name refers to.
|
||||
Declaration const* m_referencedDeclaration;
|
||||
Declaration const* m_referencedDeclaration = nullptr;
|
||||
/// Stores a reference to the current contract. This is needed because types of base contracts
|
||||
/// change depending on the context.
|
||||
ContractDefinition const* m_currentContract = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -907,13 +1150,23 @@ private:
|
||||
};
|
||||
|
||||
/**
|
||||
* A literal string or number. @see Type::literalToBigEndian is used to actually parse its value.
|
||||
* A literal string or number. @see ExpressionCompiler::endVisit() is used to actually parse its value.
|
||||
*/
|
||||
class Literal: public PrimaryExpression
|
||||
{
|
||||
public:
|
||||
Literal(Location const& _location, Token::Value _token, ASTPointer<ASTString> const& _value):
|
||||
PrimaryExpression(_location), m_token(_token), m_value(_value) {}
|
||||
enum class SubDenomination
|
||||
{
|
||||
None = Token::Illegal,
|
||||
Wei = Token::SubWei,
|
||||
Szabo = Token::SubSzabo,
|
||||
Finney = Token::SubFinney,
|
||||
Ether = Token::SubEther
|
||||
};
|
||||
Literal(Location const& _location, Token::Value _token,
|
||||
ASTPointer<ASTString> const& _value,
|
||||
SubDenomination _sub = SubDenomination::None):
|
||||
PrimaryExpression(_location), m_token(_token), m_value(_value), m_subDenomination(_sub) {}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
virtual void checkTypeRequirements() override;
|
||||
@ -922,12 +1175,16 @@ public:
|
||||
/// @returns the non-parsed value of the literal
|
||||
ASTString const& getValue() const { return *m_value; }
|
||||
|
||||
SubDenomination getSubDenomination() const { return m_subDenomination; }
|
||||
|
||||
private:
|
||||
Token::Value m_token;
|
||||
ASTPointer<ASTString> m_value;
|
||||
SubDenomination m_subDenomination;
|
||||
};
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -38,10 +38,16 @@ 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;
|
||||
@ -49,6 +55,7 @@ class UserDefinedTypeName;
|
||||
class Mapping;
|
||||
class Statement;
|
||||
class Block;
|
||||
class PlaceholderStatement;
|
||||
class IfStatement;
|
||||
class BreakableStatement;
|
||||
class WhileStatement;
|
||||
@ -71,6 +78,8 @@ 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.
|
||||
|
469
ASTJsonConverter.cpp
Normal file
469
ASTJsonConverter.cpp
Normal file
@ -0,0 +1,469 @@
|
||||
/*
|
||||
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";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
135
ASTJsonConverter.h
Normal file
135
ASTJsonConverter.h
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -57,6 +57,13 @@ bool ASTPrinter::visit(ContractDefinition const& _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() + "\"");
|
||||
@ -64,6 +71,18 @@ bool ASTPrinter::visit(StructDefinition const& _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");
|
||||
@ -87,6 +106,27 @@ bool ASTPrinter::visit(VariableDeclaration const& _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");
|
||||
@ -129,6 +169,13 @@ bool ASTPrinter::visit(Block const& _node)
|
||||
return goDeeper();
|
||||
}
|
||||
|
||||
bool ASTPrinter::visit(PlaceholderStatement const& _node)
|
||||
{
|
||||
writeLine("PlaceholderStatement");
|
||||
printSourcePart(_node);
|
||||
return goDeeper();
|
||||
}
|
||||
|
||||
bool ASTPrinter::visit(IfStatement const& _node)
|
||||
{
|
||||
writeLine("IfStatement");
|
||||
@ -302,11 +349,26 @@ 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--;
|
||||
@ -322,6 +384,21 @@ 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--;
|
||||
@ -352,6 +429,11 @@ void ASTPrinter::endVisit(Block const&)
|
||||
m_indentation--;
|
||||
}
|
||||
|
||||
void ASTPrinter::endVisit(PlaceholderStatement const&)
|
||||
{
|
||||
m_indentation--;
|
||||
}
|
||||
|
||||
void ASTPrinter::endVisit(IfStatement const&)
|
||||
{
|
||||
m_indentation--;
|
||||
|
14
ASTPrinter.h
14
ASTPrinter.h
@ -44,16 +44,23 @@ public:
|
||||
|
||||
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;
|
||||
@ -78,16 +85,23 @@ public:
|
||||
|
||||
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;
|
||||
|
28
ASTVisitor.h
28
ASTVisitor.h
@ -45,16 +45,23 @@ public:
|
||||
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; }
|
||||
@ -81,16 +88,23 @@ public:
|
||||
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&) { }
|
||||
@ -121,16 +135,23 @@ public:
|
||||
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; }
|
||||
@ -157,16 +178,23 @@ public:
|
||||
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&) { }
|
||||
|
128
AST_accept.h
128
AST_accept.h
@ -61,8 +61,12 @@ 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);
|
||||
@ -72,13 +76,63 @@ 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))
|
||||
@ -120,6 +174,7 @@ void FunctionDefinition::accept(ASTVisitor& _visitor)
|
||||
m_parameters->accept(_visitor);
|
||||
if (m_returnParameters)
|
||||
m_returnParameters->accept(_visitor);
|
||||
listAccept(m_functionModifiers, _visitor);
|
||||
m_body->accept(_visitor);
|
||||
}
|
||||
_visitor.endVisit(*this);
|
||||
@ -132,6 +187,7 @@ void FunctionDefinition::accept(ASTConstVisitor& _visitor) const
|
||||
m_parameters->accept(_visitor);
|
||||
if (m_returnParameters)
|
||||
m_returnParameters->accept(_visitor);
|
||||
listAccept(m_functionModifiers, _visitor);
|
||||
m_body->accept(_visitor);
|
||||
}
|
||||
_visitor.endVisit(*this);
|
||||
@ -153,6 +209,60 @@ void VariableDeclaration::accept(ASTConstVisitor& _visitor) const
|
||||
_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);
|
||||
@ -223,6 +333,18 @@ void Block::accept(ASTConstVisitor& _visitor) const
|
||||
_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))
|
||||
@ -452,20 +574,14 @@ void FunctionCall::accept(ASTConstVisitor& _visitor) const
|
||||
void NewExpression::accept(ASTVisitor& _visitor)
|
||||
{
|
||||
if (_visitor.visit(*this))
|
||||
{
|
||||
m_contractName->accept(_visitor);
|
||||
listAccept(m_arguments, _visitor);
|
||||
}
|
||||
_visitor.endVisit(*this);
|
||||
}
|
||||
|
||||
void NewExpression::accept(ASTConstVisitor& _visitor) const
|
||||
{
|
||||
if (_visitor.visit(*this))
|
||||
{
|
||||
m_contractName->accept(_visitor);
|
||||
listAccept(m_arguments, _visitor);
|
||||
}
|
||||
_visitor.endVisit(*this);
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,8 @@ struct Location
|
||||
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;
|
||||
@ -49,6 +51,8 @@ struct Location
|
||||
/// 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 << ")";
|
||||
}
|
||||
|
||||
|
@ -11,9 +11,9 @@ 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})
|
||||
include_directories(${JSONCPP_INCLUDE_DIRS})
|
||||
include_directories(..)
|
||||
|
||||
set(EXECUTABLE solidity)
|
||||
|
||||
@ -28,6 +28,7 @@ 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} )
|
||||
|
@ -1,67 +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
|
||||
* Callgraph of functions inside a contract.
|
||||
*/
|
||||
|
||||
#include <libsolidity/AST.h>
|
||||
#include <libsolidity/CallGraph.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
|
||||
void CallGraph::addFunction(FunctionDefinition const& _function)
|
||||
{
|
||||
if (!m_functionsSeen.count(&_function))
|
||||
{
|
||||
m_functionsSeen.insert(&_function);
|
||||
m_workQueue.push(&_function);
|
||||
}
|
||||
}
|
||||
|
||||
set<FunctionDefinition const*> const& CallGraph::getCalls()
|
||||
{
|
||||
return m_functionsSeen;
|
||||
}
|
||||
|
||||
void CallGraph::computeCallGraph()
|
||||
{
|
||||
while (!m_workQueue.empty())
|
||||
{
|
||||
FunctionDefinition const* fun = m_workQueue.front();
|
||||
fun->accept(*this);
|
||||
m_workQueue.pop();
|
||||
}
|
||||
}
|
||||
|
||||
bool CallGraph::visit(Identifier const& _identifier)
|
||||
{
|
||||
FunctionDefinition const* fun = dynamic_cast<FunctionDefinition const*>(_identifier.getReferencedDeclaration());
|
||||
if (fun)
|
||||
addFunction(*fun);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
55
CallGraph.h
55
CallGraph.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
|
||||
* Callgraph of functions inside a contract.
|
||||
*/
|
||||
|
||||
#include <set>
|
||||
#include <queue>
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
#include <libsolidity/ASTVisitor.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
|
||||
/**
|
||||
* Can be used to compute the graph of calls (or rather references) between functions of the same
|
||||
* contract. Current functionality is limited to computing all functions that are directly
|
||||
* or indirectly called by some functions.
|
||||
*/
|
||||
class CallGraph: private ASTConstVisitor
|
||||
{
|
||||
public:
|
||||
void addFunction(FunctionDefinition const& _function);
|
||||
void computeCallGraph();
|
||||
|
||||
std::set<FunctionDefinition const*> const& getCalls();
|
||||
|
||||
private:
|
||||
void addFunctionToQueue(FunctionDefinition const& _function);
|
||||
virtual bool visit(Identifier const& _identifier) override;
|
||||
|
||||
std::set<FunctionDefinition const*> m_functionsSeen;
|
||||
std::queue<FunctionDefinition const*> m_workQueue;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
345
Compiler.cpp
345
Compiler.cpp
@ -21,72 +21,103 @@
|
||||
*/
|
||||
|
||||
#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>
|
||||
#include <libsolidity/CallGraph.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace dev {
|
||||
namespace solidity {
|
||||
|
||||
void Compiler::compileContract(ContractDefinition const& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals,
|
||||
void Compiler::compileContract(ContractDefinition const& _contract,
|
||||
map<ContractDefinition const*, bytes const*> const& _contracts)
|
||||
{
|
||||
m_context = CompilerContext(); // clear it just in case
|
||||
initializeContext(_contract, _magicGlobals, _contracts);
|
||||
|
||||
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
|
||||
if (function->getName() != _contract.getName()) // don't add the constructor here
|
||||
m_context.addFunction(*function);
|
||||
|
||||
initializeContext(_contract, _contracts);
|
||||
appendFunctionSelector(_contract);
|
||||
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
|
||||
if (function->getName() != _contract.getName()) // don't add the constructor here
|
||||
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
|
||||
CompilerContext runtimeContext;
|
||||
swap(m_context, runtimeContext);
|
||||
initializeContext(_contract, _magicGlobals, _contracts);
|
||||
packIntoContractCreator(_contract, runtimeContext);
|
||||
swap(m_context, m_runtimeContext);
|
||||
initializeContext(_contract, _contracts);
|
||||
packIntoContractCreator(_contract, m_runtimeContext);
|
||||
}
|
||||
|
||||
void Compiler::initializeContext(ContractDefinition const& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals,
|
||||
void Compiler::initializeContext(ContractDefinition const& _contract,
|
||||
map<ContractDefinition const*, bytes const*> const& _contracts)
|
||||
{
|
||||
m_context.setCompiledContracts(_contracts);
|
||||
for (MagicVariableDeclaration const* variable: _magicGlobals)
|
||||
m_context.addMagicGlobal(*variable);
|
||||
m_context.setInheritanceHierarchy(_contract.getLinearizedBaseContracts());
|
||||
registerStateVariables(_contract);
|
||||
}
|
||||
|
||||
void Compiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext)
|
||||
{
|
||||
set<FunctionDefinition const*> neededFunctions;
|
||||
FunctionDefinition const* constructor = _contract.getConstructor();
|
||||
if (constructor)
|
||||
neededFunctions = getFunctionsNeededByConstructor(*constructor);
|
||||
// arguments for base constructors, filled in derived-to-base order
|
||||
map<ContractDefinition const*, vector<ASTPointer<Expression>> const*> baseArguments;
|
||||
|
||||
for (FunctionDefinition const* fun: neededFunctions)
|
||||
m_context.addFunction(*fun);
|
||||
// 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();
|
||||
}
|
||||
|
||||
if (constructor)
|
||||
appendConstructorCall(*constructor);
|
||||
// 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 explicitly include all used functions because of absolute jump
|
||||
// labels
|
||||
for (FunctionDefinition const* fun: neededFunctions)
|
||||
fun->accept(*this);
|
||||
// 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)
|
||||
@ -95,104 +126,115 @@ void Compiler::appendConstructorCall(FunctionDefinition const& _constructor)
|
||||
// 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 += var->getType()->getCalldataEncodedSize();
|
||||
argumentSize += CompilerUtils::getPaddedSize(var->getType()->getCalldataEncodedSize());
|
||||
|
||||
if (argumentSize > 0)
|
||||
{
|
||||
m_context << u256(argumentSize);
|
||||
m_context.appendProgramSize();
|
||||
m_context << u256(1); // copy it to byte one as expected for ABI calls
|
||||
m_context << u256(CompilerUtils::dataStartOffset); // copy it to byte four as expected for ABI calls
|
||||
m_context << eth::Instruction::CODECOPY;
|
||||
appendCalldataUnpacker(_constructor, true);
|
||||
appendCalldataUnpacker(FunctionType(_constructor).getParameterTypes(), true);
|
||||
}
|
||||
m_context.appendJumpTo(m_context.getFunctionEntryLabel(_constructor));
|
||||
m_context << returnTag;
|
||||
}
|
||||
|
||||
set<FunctionDefinition const*> Compiler::getFunctionsNeededByConstructor(FunctionDefinition const& _constructor)
|
||||
{
|
||||
CallGraph callgraph;
|
||||
callgraph.addFunction(_constructor);
|
||||
callgraph.computeCallGraph();
|
||||
return callgraph.getCalls();
|
||||
}
|
||||
|
||||
void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
|
||||
{
|
||||
vector<FunctionDefinition const*> interfaceFunctions = _contract.getInterfaceFunctions();
|
||||
vector<eth::AssemblyItem> callDataUnpackerEntryPoints;
|
||||
map<FixedHash<4>, FunctionTypePointer> interfaceFunctions = _contract.getInterfaceFunctions();
|
||||
map<FixedHash<4>, const eth::AssemblyItem> callDataUnpackerEntryPoints;
|
||||
|
||||
if (interfaceFunctions.size() > 255)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("More than 255 public functions for contract."));
|
||||
// retrieve the function signature hash from the calldata
|
||||
if (!interfaceFunctions.empty())
|
||||
CompilerUtils(m_context).loadFromMemory(0, IntegerType(CompilerUtils::dataStartOffset * 8), true);
|
||||
|
||||
// retrieve the first byte of the call data, which determines the called function
|
||||
// @todo This code had a jump table in a previous version which was more efficient but also
|
||||
// error prone (due to the optimizer and variable length tag addresses)
|
||||
m_context << u256(1) << u256(0) // some constants
|
||||
<< eth::dupInstruction(1) << eth::Instruction::CALLDATALOAD
|
||||
<< eth::dupInstruction(2) << eth::Instruction::BYTE
|
||||
<< eth::dupInstruction(2);
|
||||
|
||||
// stack here: 1 0 <funid> 0, stack top will be counted up until it matches funid
|
||||
for (unsigned funid = 0; funid < interfaceFunctions.size(); ++funid)
|
||||
// stack now is: 1 0 <funhash>
|
||||
for (auto const& it: interfaceFunctions)
|
||||
{
|
||||
callDataUnpackerEntryPoints.push_back(m_context.newTag());
|
||||
m_context << eth::dupInstruction(2) << eth::dupInstruction(2) << eth::Instruction::EQ;
|
||||
m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.back());
|
||||
if (funid < interfaceFunctions.size() - 1)
|
||||
m_context << eth::dupInstruction(4) << eth::Instruction::ADD;
|
||||
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));
|
||||
}
|
||||
m_context << eth::Instruction::STOP; // function not found
|
||||
|
||||
for (unsigned funid = 0; funid < interfaceFunctions.size(); ++funid)
|
||||
if (FunctionDefinition const* fallback = _contract.getFallbackFunction())
|
||||
{
|
||||
FunctionDefinition const& function = *interfaceFunctions[funid];
|
||||
m_context << callDataUnpackerEntryPoints[funid];
|
||||
eth::AssemblyItem returnTag = m_context.pushNewTag();
|
||||
appendCalldataUnpacker(function);
|
||||
m_context.appendJumpTo(m_context.getFunctionEntryLabel(function));
|
||||
fallback->accept(*this);
|
||||
m_context << returnTag;
|
||||
appendReturnValuePacker(function);
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, bool _fromMemory)
|
||||
void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory)
|
||||
{
|
||||
// We do not check the calldata size, everything is zero-padded.
|
||||
unsigned dataOffset = 1;
|
||||
//@todo this can be done more efficiently, saving some CALLDATALOAD calls
|
||||
for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
|
||||
{
|
||||
unsigned const numBytes = var->getType()->getCalldataEncodedSize();
|
||||
if (numBytes > 32)
|
||||
BOOST_THROW_EXCEPTION(CompilerError()
|
||||
<< errinfo_sourceLocation(var->getLocation())
|
||||
<< errinfo_comment("Type " + var->getType()->toString() + " not yet supported."));
|
||||
bool leftAligned = var->getType()->getCategory() == Type::Category::STRING;
|
||||
CompilerUtils(m_context).loadFromMemory(dataOffset, numBytes, leftAligned, !_fromMemory);
|
||||
dataOffset += numBytes;
|
||||
}
|
||||
return dataOffset;
|
||||
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(FunctionDefinition const& _function)
|
||||
void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters)
|
||||
{
|
||||
//@todo this can be also done more efficiently
|
||||
unsigned dataOffset = 0;
|
||||
vector<ASTPointer<VariableDeclaration>> const& parameters = _function.getReturnParameters();
|
||||
unsigned stackDepth = CompilerUtils(m_context).getSizeOnStack(parameters);
|
||||
for (unsigned i = 0; i < parameters.size(); ++i)
|
||||
unsigned stackDepth = 0;
|
||||
for (TypePointer const& type: _typeParameters)
|
||||
stackDepth += type->getSizeOnStack();
|
||||
|
||||
for (TypePointer const& type: _typeParameters)
|
||||
{
|
||||
Type const& paramType = *parameters[i]->getType();
|
||||
unsigned numBytes = paramType.getCalldataEncodedSize();
|
||||
if (numBytes > 32)
|
||||
BOOST_THROW_EXCEPTION(CompilerError()
|
||||
<< errinfo_sourceLocation(parameters[i]->getLocation())
|
||||
<< errinfo_comment("Type " + paramType.toString() + " not yet supported."));
|
||||
CompilerUtils(m_context).copyToStackTop(stackDepth, paramType);
|
||||
bool const leftAligned = paramType.getCategory() == Type::Category::STRING;
|
||||
CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned);
|
||||
stackDepth -= paramType.getSizeOnStack();
|
||||
dataOffset += numBytes;
|
||||
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;
|
||||
@ -200,9 +242,23 @@ void Compiler::appendReturnValuePacker(FunctionDefinition const& _function)
|
||||
|
||||
void Compiler::registerStateVariables(ContractDefinition const& _contract)
|
||||
{
|
||||
//@todo sort them?
|
||||
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables())
|
||||
m_context.addStateVariable(*variable);
|
||||
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)
|
||||
@ -211,24 +267,30 @@ bool Compiler::visit(FunctionDefinition const& _function)
|
||||
// caller puts: [retarg0] ... [retargm] [return address] [arg0] ... [argn]
|
||||
// although note that this reduces the size of the visible stack
|
||||
|
||||
m_context.startNewFunction();
|
||||
m_context.startFunction(_function);
|
||||
m_returnTag = m_context.newTag();
|
||||
m_breakTags.clear();
|
||||
m_continueTags.clear();
|
||||
|
||||
m_context << m_context.getFunctionEntryLabel(_function);
|
||||
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);
|
||||
{
|
||||
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);
|
||||
|
||||
_function.getBody().accept(*this);
|
||||
appendModifierOrFunctionCode();
|
||||
|
||||
m_context << m_returnTag;
|
||||
|
||||
@ -238,16 +300,16 @@ bool Compiler::visit(FunctionDefinition const& _function)
|
||||
// Note that the fact that the return arguments are of increasing index is vital for this
|
||||
// algorithm to work.
|
||||
|
||||
unsigned const argumentsSize = CompilerUtils::getSizeOnStack(_function.getParameters());
|
||||
unsigned const returnValuesSize = CompilerUtils::getSizeOnStack(_function.getReturnParameters());
|
||||
unsigned const localVariablesSize = CompilerUtils::getSizeOnStack(_function.getLocalVariables());
|
||||
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(returnValuesSize); // target of return address
|
||||
stackLayout += vector<int>(argumentsSize, -1); // discard all arguments
|
||||
for (unsigned i = 0; i < returnValuesSize; ++i)
|
||||
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>(localVariablesSize, -1);
|
||||
stackLayout += vector<int>(c_localVariablesSize, -1);
|
||||
|
||||
while (stackLayout.back() != int(stackLayout.size() - 1))
|
||||
if (stackLayout.back() < 0)
|
||||
@ -355,13 +417,15 @@ 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())
|
||||
{
|
||||
compileExpression(*expression);
|
||||
VariableDeclaration const& firstVariable = *_return.getFunctionReturnParameters().getParameters().front();
|
||||
ExpressionCompiler::appendTypeConversion(m_context, *expression->getType(), *firstVariable.getType());
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -369,10 +433,7 @@ bool Compiler::visit(VariableDefinition const& _variableDefinition)
|
||||
{
|
||||
if (Expression const* expression = _variableDefinition.getExpression())
|
||||
{
|
||||
compileExpression(*expression);
|
||||
ExpressionCompiler::appendTypeConversion(m_context,
|
||||
*expression->getType(),
|
||||
*_variableDefinition.getDeclaration().getType());
|
||||
compileExpression(*expression, _variableDefinition.getDeclaration().getType());
|
||||
CompilerUtils(m_context).moveToStackVariable(_variableDefinition.getDeclaration());
|
||||
}
|
||||
return false;
|
||||
@ -386,9 +447,51 @@ bool Compiler::visit(ExpressionStatement const& _expressionStatement)
|
||||
return false;
|
||||
}
|
||||
|
||||
void Compiler::compileExpression(Expression const& _expression)
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
40
Compiler.h
40
Compiler.h
@ -20,7 +20,10 @@
|
||||
* Solidity AST to EVM bytecode compiler.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ostream>
|
||||
#include <functional>
|
||||
#include <libsolidity/ASTVisitor.h>
|
||||
#include <libsolidity/CompilerContext.h>
|
||||
|
||||
@ -30,32 +33,34 @@ namespace solidity {
|
||||
class Compiler: private ASTConstVisitor
|
||||
{
|
||||
public:
|
||||
explicit Compiler(bool _optimize = false): m_optimize(_optimize), m_returnTag(m_context.newTag()) {}
|
||||
explicit Compiler(bool _optimize = false): m_optimize(_optimize), m_context(),
|
||||
m_returnTag(m_context.newTag()) {}
|
||||
|
||||
void compileContract(ContractDefinition const& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals,
|
||||
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 global objects and the non-function objects inside the contract with the context.
|
||||
void initializeContext(ContractDefinition const& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals,
|
||||
/// 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.
|
||||
/// 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);
|
||||
/// Recursively searches the call graph and returns all functions needed by the constructor (including itself).
|
||||
std::set<FunctionDefinition const*> getFunctionsNeededByConstructor(FunctionDefinition const& _constructor);
|
||||
void appendFunctionSelector(ContractDefinition const& _contract);
|
||||
/// Creates code that unpacks the arguments for the given function, from memory if
|
||||
/// @a _fromMemory is true, otherwise from call data. @returns the size of the data in bytes.
|
||||
unsigned appendCalldataUnpacker(FunctionDefinition const& _function, bool _fromMemory = false);
|
||||
void appendReturnValuePacker(FunctionDefinition const& _function);
|
||||
/// 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;
|
||||
@ -65,14 +70,23 @@ private:
|
||||
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;
|
||||
|
||||
void compileExpression(Expression const& _expression);
|
||||
/// 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
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -43,25 +43,28 @@ void CompilerContext::addStateVariable(VariableDeclaration const& _declaration)
|
||||
m_stateVariablesSize += _declaration.getType()->getStorageSize();
|
||||
}
|
||||
|
||||
void CompilerContext::addVariable(VariableDeclaration const& _declaration)
|
||||
void CompilerContext::startFunction(Declaration const& _function)
|
||||
{
|
||||
m_localVariables[&_declaration] = m_localVariablesSize;
|
||||
m_localVariablesSize += _declaration.getType()->getSizeOnStack();
|
||||
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);
|
||||
|
||||
unsigned const size = _declaration.getType()->getSizeOnStack();
|
||||
for (unsigned i = 0; i < size; ++i)
|
||||
int const size = _declaration.getType()->getSizeOnStack();
|
||||
for (int i = 0; i < size; ++i)
|
||||
*this << u256(0);
|
||||
m_asm.adjustDeposit(-size);
|
||||
}
|
||||
|
||||
void CompilerContext::addFunction(FunctionDefinition const& _function)
|
||||
{
|
||||
m_functionEntryLabels.insert(std::make_pair(&_function, m_asm.newTag()));
|
||||
}
|
||||
|
||||
bytes const& CompilerContext::getCompiledContract(const ContractDefinition& _contract) const
|
||||
@ -73,26 +76,82 @@ bytes const& CompilerContext::getCompiledContract(const ContractDefinition& _con
|
||||
|
||||
bool CompilerContext::isLocalVariable(Declaration const* _declaration) const
|
||||
{
|
||||
return m_localVariables.count(_declaration) > 0;
|
||||
return !!m_localVariables.count(_declaration);
|
||||
}
|
||||
|
||||
eth::AssemblyItem CompilerContext::getFunctionEntryLabel(FunctionDefinition const& _function) const
|
||||
eth::AssemblyItem CompilerContext::getFunctionEntryLabel(Declaration const& _declaration)
|
||||
{
|
||||
auto res = m_functionEntryLabels.find(&_function);
|
||||
solAssert(res != m_functionEntryLabels.end(), "Function entry label not found.");
|
||||
return res->second.tag();
|
||||
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 m_localVariablesSize - res->second - 1;
|
||||
return res->second;
|
||||
}
|
||||
|
||||
unsigned CompilerContext::baseToCurrentStackOffset(unsigned _baseOffset) const
|
||||
{
|
||||
return _baseOffset + m_asm.deposit();
|
||||
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
|
||||
|
@ -41,27 +41,41 @@ class CompilerContext
|
||||
public:
|
||||
void addMagicGlobal(MagicVariableDeclaration const& _declaration);
|
||||
void addStateVariable(VariableDeclaration const& _declaration);
|
||||
void startNewFunction() { m_localVariables.clear(); m_asm.setDeposit(0); }
|
||||
void addVariable(VariableDeclaration const& _declaration);
|
||||
void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0);
|
||||
void addAndInitializeVariable(VariableDeclaration const& _declaration);
|
||||
void addFunction(FunctionDefinition const& _function);
|
||||
|
||||
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); }
|
||||
bool isFunctionDefinition(Declaration const* _declaration) const { return m_functionEntryLabels.count(_declaration); }
|
||||
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); }
|
||||
bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration) != 0; }
|
||||
|
||||
eth::AssemblyItem getFunctionEntryLabel(FunctionDefinition const& _function) const;
|
||||
/// Returns the distance of the given local variable from the top of the local variable stack.
|
||||
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
|
||||
@ -109,10 +123,12 @@ private:
|
||||
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;
|
||||
/// Sum of stack sizes of local variables
|
||||
unsigned m_localVariablesSize;
|
||||
/// Labels pointing to the entry points of funcitons.
|
||||
/// 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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -16,10 +16,12 @@
|
||||
*/
|
||||
/**
|
||||
* @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>
|
||||
@ -29,6 +31,8 @@
|
||||
#include <libsolidity/CompilerStack.h>
|
||||
#include <libsolidity/InterfaceHandler.h>
|
||||
|
||||
#include <libdevcrypto/SHA3.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace dev
|
||||
@ -36,18 +40,39 @@ 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);
|
||||
bool existed = m_sources.count(_name) != 0;
|
||||
reset(true);
|
||||
m_sources[_name].scanner = make_shared<Scanner>(CharStream(_content), _name);
|
||||
m_sources[_name].scanner = make_shared<Scanner>(CharStream(expanded(_content)), _name);
|
||||
return existed;
|
||||
}
|
||||
|
||||
void CompilerStack::setSource(string const& _sourceCode)
|
||||
{
|
||||
reset();
|
||||
addSource("", _sourceCode);
|
||||
addSource("", expanded(_sourceCode));
|
||||
}
|
||||
|
||||
void CompilerStack::parse()
|
||||
@ -69,6 +94,7 @@ void CompilerStack::parse()
|
||||
{
|
||||
m_globalContext->setCurrentContract(*contract);
|
||||
resolver.updateDeclaration(*m_globalContext->getCurrentThis());
|
||||
resolver.updateDeclaration(*m_globalContext->getCurrentSuper());
|
||||
resolver.resolveNamesAndTypes(*contract);
|
||||
m_contracts[contract->getName()].contract = contract;
|
||||
}
|
||||
@ -100,6 +126,58 @@ vector<string> CompilerStack::getContractNames() const
|
||||
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)
|
||||
@ -110,12 +188,11 @@ void CompilerStack::compile(bool _optimize)
|
||||
for (ASTPointer<ASTNode> const& node: source->ast->getNodes())
|
||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
||||
{
|
||||
m_globalContext->setCurrentContract(*contract);
|
||||
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize);
|
||||
compiler->compileContract(*contract, m_globalContext->getMagicVariables(),
|
||||
contractBytecode);
|
||||
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;
|
||||
}
|
||||
@ -133,6 +210,16 @@ 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);
|
||||
@ -140,10 +227,15 @@ void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractN
|
||||
|
||||
string const& CompilerStack::getInterface(string const& _contractName) const
|
||||
{
|
||||
return getJsonDocumentation(_contractName, DocumentationType::ABI_INTERFACE);
|
||||
return getMetadata(_contractName, DocumentationType::ABIInterface);
|
||||
}
|
||||
|
||||
string const& CompilerStack::getJsonDocumentation(string const& _contractName, DocumentationType _type) const
|
||||
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."));
|
||||
@ -153,15 +245,18 @@ string const& CompilerStack::getJsonDocumentation(string const& _contractName, D
|
||||
std::unique_ptr<string const>* doc;
|
||||
switch (_type)
|
||||
{
|
||||
case DocumentationType::NATSPEC_USER:
|
||||
case DocumentationType::NatspecUser:
|
||||
doc = &contract.userDocumentation;
|
||||
break;
|
||||
case DocumentationType::NATSPEC_DEV:
|
||||
case DocumentationType::NatspecDev:
|
||||
doc = &contract.devDocumentation;
|
||||
break;
|
||||
case DocumentationType::ABI_INTERFACE:
|
||||
case DocumentationType::ABIInterface:
|
||||
doc = &contract.interface;
|
||||
break;
|
||||
case DocumentationType::ABISolidityInterface:
|
||||
doc = &contract.solidityInterface;
|
||||
break;
|
||||
default:
|
||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type."));
|
||||
}
|
||||
@ -198,7 +293,11 @@ void CompilerStack::reset(bool _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();
|
||||
@ -234,16 +333,23 @@ void CompilerStack::resolveImports()
|
||||
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 the "last contract"
|
||||
for (ASTPointer<ASTNode> const& node: m_sourceOrder.back()->ast->getNodes())
|
||||
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
|
||||
contractName = contract->getName();
|
||||
// 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."));
|
||||
|
@ -16,6 +16,7 @@
|
||||
*/
|
||||
/**
|
||||
* @author Christian <c@ethdev.com>
|
||||
* @author Gav Wood <g@ethdev.com>
|
||||
* @date 2014
|
||||
* Full-stack compiler that converts a source code string to bytecode.
|
||||
*/
|
||||
@ -27,6 +28,7 @@
|
||||
#include <memory>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <libdevcore/Common.h>
|
||||
#include <libdevcore/FixedHash.h>
|
||||
|
||||
namespace dev {
|
||||
namespace solidity {
|
||||
@ -41,11 +43,14 @@ class InterfaceHandler;
|
||||
|
||||
enum class DocumentationType: uint8_t
|
||||
{
|
||||
NATSPEC_USER = 1,
|
||||
NATSPEC_DEV,
|
||||
ABI_INTERFACE
|
||||
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.
|
||||
@ -54,18 +59,21 @@ enum class DocumentationType: uint8_t
|
||||
class CompilerStack: boost::noncopyable
|
||||
{
|
||||
public:
|
||||
CompilerStack(): m_parseSuccessful(false) {}
|
||||
/// 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 and parses it.
|
||||
/// 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);
|
||||
@ -73,7 +81,13 @@ public:
|
||||
/// @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;
|
||||
@ -81,11 +95,14 @@ public:
|
||||
/// 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 3 types defined at @c DocumentationType
|
||||
std::string const& getJsonDocumentation(std::string const& _contractName, DocumentationType _type) const;
|
||||
/// 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;
|
||||
@ -113,23 +130,30 @@ private:
|
||||
|
||||
struct Contract
|
||||
{
|
||||
ContractDefinition const* 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;
|
||||
|
@ -31,42 +31,94 @@ namespace dev
|
||||
namespace solidity
|
||||
{
|
||||
|
||||
void CompilerUtils::loadFromMemory(unsigned _offset, unsigned _bytes, bool _leftAligned, bool _fromCalldata)
|
||||
const unsigned int CompilerUtils::dataStartOffset = 4;
|
||||
|
||||
unsigned CompilerUtils::loadFromMemory(unsigned _offset, Type const& _type,
|
||||
bool _fromCalldata, bool _padToWordBoundaries)
|
||||
{
|
||||
if (_bytes == 0)
|
||||
{
|
||||
m_context << u256(0);
|
||||
return;
|
||||
}
|
||||
eth::Instruction load = _fromCalldata ? eth::Instruction::CALLDATALOAD : eth::Instruction::MLOAD;
|
||||
solAssert(_bytes <= 32, "Memory load of more than 32 bytes requested.");
|
||||
if (_bytes == 32)
|
||||
m_context << u256(_offset) << load;
|
||||
else
|
||||
{
|
||||
// load data and add leading or trailing zeros by dividing/multiplying depending on alignment
|
||||
u256 shiftFactor = u256(1) << ((32 - _bytes) * 8);
|
||||
m_context << shiftFactor;
|
||||
if (_leftAligned)
|
||||
m_context << eth::Instruction::DUP1;
|
||||
m_context << u256(_offset) << load << eth::Instruction::DIV;
|
||||
if (_leftAligned)
|
||||
m_context << eth::Instruction::MUL;
|
||||
}
|
||||
solAssert(_type.getCategory() != Type::Category::ByteArray, "Unable to statically load dynamic type.");
|
||||
m_context << u256(_offset);
|
||||
return loadFromMemoryHelper(_type, _fromCalldata, _padToWordBoundaries);
|
||||
}
|
||||
|
||||
void CompilerUtils::storeInMemory(unsigned _offset, unsigned _bytes, bool _leftAligned)
|
||||
void CompilerUtils::loadFromMemoryDynamic(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries)
|
||||
{
|
||||
if (_bytes == 0)
|
||||
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)
|
||||
{
|
||||
m_context << eth::Instruction::POP;
|
||||
return;
|
||||
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;
|
||||
}
|
||||
}
|
||||
solAssert(_bytes <= 32, "Memory store of more than 32 bytes requested.");
|
||||
if (_bytes != 32 && !_leftAligned)
|
||||
// shift the value accordingly before storing
|
||||
m_context << (u256(1) << ((32 - _bytes) * 8)) << eth::Instruction::MUL;
|
||||
m_context << u256(_offset) << eth::Instruction::MSTORE;
|
||||
}
|
||||
|
||||
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
|
||||
@ -105,5 +157,220 @@ unsigned CompilerUtils::getSizeOnStack(vector<shared_ptr<Type const>> const& _va
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -37,15 +37,31 @@ public:
|
||||
|
||||
/// Loads data from memory to the stack.
|
||||
/// @param _offset offset in memory (or calldata)
|
||||
/// @param _bytes number of bytes to load
|
||||
/// @param _leftAligned if true, store left aligned on stack (otherwise right aligned)
|
||||
/// @param _type data type to load
|
||||
/// @param _fromCalldata if true, load from calldata, not from memory
|
||||
void loadFromMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false, bool _fromCalldata = false);
|
||||
/// @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 _bytes number of bytes to store
|
||||
/// @param _leftAligned if true, data is left aligned on stack (otherwise right aligned)
|
||||
void storeInMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false);
|
||||
/// @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);
|
||||
@ -58,10 +74,38 @@ public:
|
||||
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)
|
||||
{
|
||||
|
@ -28,16 +28,25 @@ namespace dev
|
||||
namespace solidity
|
||||
{
|
||||
|
||||
bool DeclarationContainer::registerDeclaration(Declaration const& _declaration, bool _update)
|
||||
bool DeclarationContainer::registerDeclaration(Declaration const& _declaration, bool _invisible, bool _update)
|
||||
{
|
||||
if (!_update && m_declarations.find(_declaration.getName()) != m_declarations.end())
|
||||
ASTString const& name(_declaration.getName());
|
||||
if (name.empty())
|
||||
return true;
|
||||
|
||||
if (!_update && (m_declarations.count(name) || m_invisibleDeclarations.count(name)))
|
||||
return false;
|
||||
m_declarations[_declaration.getName()] = &_declaration;
|
||||
|
||||
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;
|
||||
|
@ -23,6 +23,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
#include <libsolidity/ASTForward.h>
|
||||
@ -42,16 +43,20 @@ 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. Returns true iff
|
||||
/// it was not yet declared.
|
||||
bool registerDeclaration(Declaration const& _declaration, bool _update = false);
|
||||
/// 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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ struct CompilerError: virtual Exception {};
|
||||
struct InternalCompilerError: virtual Exception {};
|
||||
struct DocstringParsingError: virtual Exception {};
|
||||
|
||||
typedef boost::error_info<struct tag_sourceLocation, Location> errinfo_sourceLocation;
|
||||
using errinfo_sourceLocation = boost::error_info<struct tag_sourceLocation, Location>;
|
||||
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -16,13 +16,16 @@
|
||||
*/
|
||||
/**
|
||||
* @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 {
|
||||
@ -36,6 +39,7 @@ namespace solidity {
|
||||
class CompilerContext;
|
||||
class Type;
|
||||
class IntegerType;
|
||||
class ByteArrayType;
|
||||
class StaticStringType;
|
||||
|
||||
/**
|
||||
@ -50,14 +54,17 @@ public:
|
||||
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);
|
||||
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 void endVisit(UnaryOperation const& _unaryOperation) 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;
|
||||
@ -85,24 +92,24 @@ private:
|
||||
//// Appends code that cleans higher-order bits for integer types.
|
||||
void appendHighBitsCleanup(IntegerType const& _typeOnStack);
|
||||
|
||||
/// Additional options used in appendExternalFunctionCall.
|
||||
struct FunctionCallOptions
|
||||
{
|
||||
FunctionCallOptions() {}
|
||||
/// Invoked to copy the address to the stack
|
||||
std::function<void()> obtainAddress;
|
||||
/// Invoked to copy the ethe value to the stack (if not specified, value is 0).
|
||||
std::function<void()> obtainValue;
|
||||
/// If true, do not prepend function index to call data
|
||||
bool bare = false;
|
||||
/// If false, use calling convention that all arguments and return values are packed as
|
||||
/// 32 byte values with padding.
|
||||
bool packDensely = true;
|
||||
};
|
||||
|
||||
/// 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,
|
||||
FunctionCallOptions const& _options = FunctionCallOptions());
|
||||
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.
|
||||
@ -112,46 +119,55 @@ private:
|
||||
class LValue
|
||||
{
|
||||
public:
|
||||
enum LValueType { NONE, STACK, MEMORY, STORAGE };
|
||||
enum class LValueType { None, Stack, Memory, Storage };
|
||||
|
||||
explicit LValue(CompilerContext& _compilerContext): m_context(&_compilerContext) { reset(); }
|
||||
LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType, unsigned _baseStackOffset = 0);
|
||||
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 = NONE; m_baseStackOffset = 0; }
|
||||
void reset() { m_type = LValueType::None; m_dataType.reset(); m_baseStackOffset = 0; m_size = 0; }
|
||||
|
||||
bool isValid() const { return m_type != NONE; }
|
||||
bool isInOnStack() const { return m_type == STACK; }
|
||||
bool isInMemory() const { return m_type == MEMORY; }
|
||||
bool isInStorage() const { return m_type == STORAGE; }
|
||||
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 == STORAGE || m_type == MEMORY; }
|
||||
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 _expression is the current expression, used for error reporting.
|
||||
void retrieveValue(Expression const& _expression, bool _remove = false) const;
|
||||
/// Stores a value (from the stack directly beneath the reference, which is assumed to
|
||||
/// be on the top of the stack, if any) in the lvalue and removes the reference.
|
||||
/// Also removes the stored value from the stack if @a _move is
|
||||
/// true. @a _expression is the current expression, used for error reporting.
|
||||
void storeValue(Expression const& _expression, bool _move = false) const;
|
||||
|
||||
/// @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 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;
|
||||
/// Size of the value of this lvalue on the stack.
|
||||
unsigned m_stackSize;
|
||||
unsigned m_baseStackOffset = 0;
|
||||
/// Size of the value of this lvalue on the stack or the storage.
|
||||
unsigned m_size = 0;
|
||||
};
|
||||
|
||||
bool m_optimize;
|
||||
|
@ -16,6 +16,7 @@
|
||||
*/
|
||||
/**
|
||||
* @author Christian <c@ethdev.com>
|
||||
* @author Gav Wood <g@ethdev.com>
|
||||
* @date 2014
|
||||
* Container of the (implicit and explicit) global objects.
|
||||
*/
|
||||
@ -33,33 +34,29 @@ 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::MSG)),
|
||||
make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::TX)),
|
||||
make_shared<MagicVariableDeclaration>("suicide",
|
||||
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(0,
|
||||
IntegerType::Modifier::ADDRESS)}),
|
||||
TypePointers(),
|
||||
FunctionType::Location::SUICIDE)),
|
||||
make_shared<MagicVariableDeclaration>("sha3",
|
||||
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
|
||||
TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
|
||||
FunctionType::Location::SHA3)),
|
||||
make_shared<MagicVariableDeclaration>("sha256",
|
||||
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
|
||||
TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
|
||||
FunctionType::Location::SHA256)),
|
||||
make_shared<MagicVariableDeclaration>("ecrecover",
|
||||
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH),
|
||||
std::make_shared<IntegerType>(8, IntegerType::Modifier::HASH),
|
||||
std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH),
|
||||
std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
|
||||
TypePointers({std::make_shared<IntegerType>(0, IntegerType::Modifier::ADDRESS)}),
|
||||
FunctionType::Location::ECRECOVER)),
|
||||
make_shared<MagicVariableDeclaration>("ripemd160",
|
||||
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
|
||||
TypePointers({std::make_shared<IntegerType>(160, IntegerType::Modifier::HASH)}),
|
||||
FunctionType::Location::RIPEMD160))})
|
||||
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))})
|
||||
{
|
||||
}
|
||||
|
||||
@ -71,7 +68,7 @@ void GlobalContext::setCurrentContract(ContractDefinition const& _contract)
|
||||
vector<Declaration const*> GlobalContext::getDeclarations() const
|
||||
{
|
||||
vector<Declaration const*> declarations;
|
||||
declarations.reserve(m_magicVariables.size() + 1);
|
||||
declarations.reserve(m_magicVariables.size());
|
||||
for (ASTPointer<Declaration const> const& variable: m_magicVariables)
|
||||
declarations.push_back(variable.get());
|
||||
return declarations;
|
||||
@ -86,14 +83,12 @@ MagicVariableDeclaration const* GlobalContext::getCurrentThis() const
|
||||
|
||||
}
|
||||
|
||||
vector<MagicVariableDeclaration const*> GlobalContext::getMagicVariables() const
|
||||
MagicVariableDeclaration const* GlobalContext::getCurrentSuper() const
|
||||
{
|
||||
vector<MagicVariableDeclaration const*> declarations;
|
||||
declarations.reserve(m_magicVariables.size() + 1);
|
||||
for (ASTPointer<MagicVariableDeclaration const> const& variable: m_magicVariables)
|
||||
declarations.push_back(variable.get());
|
||||
declarations.push_back(getCurrentThis());
|
||||
return declarations;
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -48,16 +48,16 @@ public:
|
||||
GlobalContext();
|
||||
void setCurrentContract(ContractDefinition const& _contract);
|
||||
MagicVariableDeclaration const* getCurrentThis() const;
|
||||
MagicVariableDeclaration const* getCurrentSuper() const;
|
||||
|
||||
/// @returns all magic variables.
|
||||
std::vector<MagicVariableDeclaration const*> getMagicVariables() 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;
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include <libsolidity/InterfaceHandler.h>
|
||||
#include <libsolidity/AST.h>
|
||||
#include <libsolidity/CompilerStack.h>
|
||||
using namespace std;
|
||||
|
||||
namespace dev
|
||||
{
|
||||
@ -12,7 +13,7 @@ namespace solidity
|
||||
|
||||
InterfaceHandler::InterfaceHandler()
|
||||
{
|
||||
m_lastTag = DocTagType::NONE;
|
||||
m_lastTag = DocTagType::None;
|
||||
}
|
||||
|
||||
std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefinition const& _contractDef,
|
||||
@ -20,12 +21,14 @@ std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefiniti
|
||||
{
|
||||
switch(_type)
|
||||
{
|
||||
case DocumentationType::NATSPEC_USER:
|
||||
case DocumentationType::NatspecUser:
|
||||
return getUserDocumentation(_contractDef);
|
||||
case DocumentationType::NATSPEC_DEV:
|
||||
case DocumentationType::NatspecDev:
|
||||
return getDevDocumentation(_contractDef);
|
||||
case DocumentationType::ABI_INTERFACE:
|
||||
case DocumentationType::ABIInterface:
|
||||
return getABIInterface(_contractDef);
|
||||
case DocumentationType::ABISolidityInterface:
|
||||
return getABISolidityInterface(_contractDef);
|
||||
}
|
||||
|
||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type"));
|
||||
@ -34,34 +37,79 @@ std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefiniti
|
||||
|
||||
std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinition const& _contractDef)
|
||||
{
|
||||
Json::Value methods(Json::arrayValue);
|
||||
|
||||
for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions())
|
||||
Json::Value abi(Json::arrayValue);
|
||||
for (auto const& it: _contractDef.getInterfaceFunctions())
|
||||
{
|
||||
Json::Value method;
|
||||
Json::Value inputs(Json::arrayValue);
|
||||
Json::Value outputs(Json::arrayValue);
|
||||
|
||||
auto populateParameters = [](std::vector<ASTPointer<VariableDeclaration>> const& _vars)
|
||||
auto populateParameters = [](vector<string> const& _paramNames, vector<string> const& _paramTypes)
|
||||
{
|
||||
Json::Value params(Json::arrayValue);
|
||||
for (ASTPointer<VariableDeclaration> const& var: _vars)
|
||||
solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match");
|
||||
for (unsigned i = 0; i < _paramNames.size(); ++i)
|
||||
{
|
||||
Json::Value input;
|
||||
input["name"] = var->getName();
|
||||
input["type"] = var->getType()->toString();
|
||||
params.append(input);
|
||||
Json::Value param;
|
||||
param["name"] = _paramNames[i];
|
||||
param["type"] = _paramTypes[i];
|
||||
params.append(param);
|
||||
}
|
||||
return params;
|
||||
};
|
||||
|
||||
method["name"] = f->getName();
|
||||
method["constant"] = f->isDeclaredConst();
|
||||
method["inputs"] = populateParameters(f->getParameters());
|
||||
method["outputs"] = populateParameters(f->getReturnParameters());
|
||||
methods.append(method);
|
||||
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);
|
||||
}
|
||||
return std::unique_ptr<std::string>(new std::string(m_writer.write(methods)));
|
||||
|
||||
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)
|
||||
@ -69,18 +117,18 @@ std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefi
|
||||
Json::Value doc;
|
||||
Json::Value methods(Json::objectValue);
|
||||
|
||||
for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions())
|
||||
for (auto const& it: _contractDef.getInterfaceFunctions())
|
||||
{
|
||||
Json::Value user;
|
||||
auto strPtr = f->getDocumentation();
|
||||
auto strPtr = it.second->getDocumentation();
|
||||
if (strPtr)
|
||||
{
|
||||
resetUser();
|
||||
parseDocString(*strPtr, CommentOwner::FUNCTION);
|
||||
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[f->getName()] = user;
|
||||
methods[it.second->getCanonicalSignature()] = user;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -101,7 +149,7 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
|
||||
{
|
||||
m_contractAuthor.clear();
|
||||
m_title.clear();
|
||||
parseDocString(*contractDoc, CommentOwner::CONTRACT);
|
||||
parseDocString(*contractDoc, CommentOwner::Contract);
|
||||
|
||||
if (!m_contractAuthor.empty())
|
||||
doc["author"] = m_contractAuthor;
|
||||
@ -110,14 +158,14 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
|
||||
doc["title"] = m_title;
|
||||
}
|
||||
|
||||
for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions())
|
||||
for (auto const& it: _contractDef.getInterfaceFunctions())
|
||||
{
|
||||
Json::Value method;
|
||||
auto strPtr = f->getDocumentation();
|
||||
auto strPtr = it.second->getDocumentation();
|
||||
if (strPtr)
|
||||
{
|
||||
resetDev();
|
||||
parseDocString(*strPtr, CommentOwner::FUNCTION);
|
||||
parseDocString(*strPtr, CommentOwner::Function);
|
||||
|
||||
if (!m_dev.empty())
|
||||
method["details"] = Json::Value(m_dev);
|
||||
@ -136,7 +184,7 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
|
||||
method["return"] = m_return;
|
||||
|
||||
if (!method.empty()) // add the function, only if we have any documentation to add
|
||||
methods[f->getName()] = method;
|
||||
methods[it.second->getCanonicalSignature()] = method;
|
||||
}
|
||||
}
|
||||
doc["methods"] = methods;
|
||||
@ -194,7 +242,7 @@ std::string::const_iterator InterfaceHandler::parseDocTagParam(std::string::cons
|
||||
auto paramDesc = std::string(currPos, nlPos);
|
||||
m_params.push_back(std::make_pair(paramName, paramDesc));
|
||||
|
||||
m_lastTag = DocTagType::PARAM;
|
||||
m_lastTag = DocTagType::Param;
|
||||
return skipLineOrEOS(nlPos, _end);
|
||||
}
|
||||
|
||||
@ -223,28 +271,28 @@ std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_ite
|
||||
// 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 (m_lastTag == DocTagType::None || _tag != "")
|
||||
{
|
||||
if (_tag == "dev")
|
||||
return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV, false);
|
||||
return parseDocTagLine(_pos, _end, m_dev, DocTagType::Dev, false);
|
||||
else if (_tag == "notice")
|
||||
return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE, false);
|
||||
return parseDocTagLine(_pos, _end, m_notice, DocTagType::Notice, false);
|
||||
else if (_tag == "return")
|
||||
return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN, false);
|
||||
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);
|
||||
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);
|
||||
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"));
|
||||
@ -265,27 +313,27 @@ std::string::const_iterator InterfaceHandler::appendDocTag(std::string::const_it
|
||||
{
|
||||
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);
|
||||
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);
|
||||
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:
|
||||
case DocTagType::Param:
|
||||
return appendDocTagParam(_pos, _end);
|
||||
default:
|
||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Internal: Illegal documentation tag type"));
|
||||
@ -321,10 +369,21 @@ void InterfaceHandler::parseDocString(std::string const& _string, CommentOwner _
|
||||
|
||||
currPos = parseDocTag(tagNameEndPos + 1, end, std::string(tagPos + 1, tagNameEndPos), _owner);
|
||||
}
|
||||
else if (m_lastTag != DocTagType::NONE) // continuation of the previous tag
|
||||
else if (m_lastTag != DocTagType::None) // continuation of the previous tag
|
||||
currPos = appendDocTag(currPos, end, _owner);
|
||||
else if (currPos != end) // skip the line if a newline was found
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <jsoncpp/json/json.h>
|
||||
#include <json/json.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
@ -41,19 +41,19 @@ enum class DocumentationType: uint8_t;
|
||||
|
||||
enum class DocTagType: uint8_t
|
||||
{
|
||||
NONE = 0,
|
||||
DEV,
|
||||
NOTICE,
|
||||
PARAM,
|
||||
RETURN,
|
||||
AUTHOR,
|
||||
TITLE
|
||||
None = 0,
|
||||
Dev,
|
||||
Notice,
|
||||
Param,
|
||||
Return,
|
||||
Author,
|
||||
Title
|
||||
};
|
||||
|
||||
enum class CommentOwner
|
||||
{
|
||||
CONTRACT,
|
||||
FUNCTION
|
||||
Contract,
|
||||
Function
|
||||
};
|
||||
|
||||
class InterfaceHandler
|
||||
@ -74,6 +74,7 @@ public:
|
||||
/// @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
|
||||
|
@ -31,7 +31,6 @@ namespace dev
|
||||
namespace solidity
|
||||
{
|
||||
|
||||
|
||||
NameAndTypeResolver::NameAndTypeResolver(std::vector<Declaration const*> const& _globals)
|
||||
{
|
||||
for (Declaration const* declaration: _globals)
|
||||
@ -46,18 +45,36 @@ void NameAndTypeResolver::registerDeclarations(SourceUnit& _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, nullptr);
|
||||
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, nullptr);
|
||||
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,
|
||||
ReferencesResolver referencesResolver(*function, *this, &_contract,
|
||||
function->getReturnParameterList().get());
|
||||
}
|
||||
m_currentScope = &m_scopes[nullptr];
|
||||
}
|
||||
|
||||
void NameAndTypeResolver::checkTypeRequirements(ContractDefinition& _contract)
|
||||
@ -69,7 +86,7 @@ void NameAndTypeResolver::checkTypeRequirements(ContractDefinition& _contract)
|
||||
|
||||
void NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
|
||||
{
|
||||
m_scopes[nullptr].registerDeclaration(_declaration, true);
|
||||
m_scopes[nullptr].registerDeclaration(_declaration, false, true);
|
||||
solAssert(_declaration.getScope() == nullptr, "Updated declaration outside global scope.");
|
||||
}
|
||||
|
||||
@ -86,6 +103,98 @@ Declaration const* NameAndTypeResolver::getNameFromCurrentScope(ASTString const&
|
||||
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)
|
||||
@ -115,6 +224,23 @@ 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);
|
||||
@ -128,6 +254,19 @@ void DeclarationRegistrationHelper::endVisit(FunctionDefinition&)
|
||||
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
|
||||
@ -142,6 +281,17 @@ bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration)
|
||||
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;
|
||||
@ -159,7 +309,7 @@ void DeclarationRegistrationHelper::closeCurrentScope()
|
||||
|
||||
void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope)
|
||||
{
|
||||
if (!m_scopes[m_currentScope].registerDeclaration(_declaration))
|
||||
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
|
||||
@ -169,8 +319,10 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio
|
||||
}
|
||||
|
||||
ReferencesResolver::ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver,
|
||||
ParameterList* _returnParameters, bool _allowLazyTypes):
|
||||
m_resolver(_resolver), m_returnParameters(_returnParameters), m_allowLazyTypes(_allowLazyTypes)
|
||||
ContractDefinition const* _currentContract,
|
||||
ParameterList const* _returnParameters, bool _allowLazyTypes):
|
||||
m_resolver(_resolver), m_currentContract(_currentContract),
|
||||
m_returnParameters(_returnParameters), m_allowLazyTypes(_allowLazyTypes)
|
||||
{
|
||||
_root.accept(*this);
|
||||
}
|
||||
@ -181,7 +333,13 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable)
|
||||
// or mapping
|
||||
if (_variable.getTypeName())
|
||||
{
|
||||
_variable.setType(_variable.getTypeName()->toType());
|
||||
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"));
|
||||
}
|
||||
@ -192,8 +350,7 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable)
|
||||
|
||||
bool ReferencesResolver::visit(Return& _return)
|
||||
{
|
||||
solAssert(m_returnParameters, "Return parameters not set.");
|
||||
_return.setFunctionReturnParameters(*m_returnParameters);
|
||||
_return.setFunctionReturnParameters(m_returnParameters);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -218,7 +375,7 @@ bool ReferencesResolver::visit(Identifier& _identifier)
|
||||
if (!declaration)
|
||||
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_identifier.getLocation())
|
||||
<< errinfo_comment("Undeclared identifier."));
|
||||
_identifier.setReferencedDeclaration(*declaration);
|
||||
_identifier.setReferencedDeclaration(*declaration, m_currentContract);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
#include <libsolidity/DeclarationContainer.h>
|
||||
@ -64,12 +65,23 @@ public:
|
||||
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;
|
||||
DeclarationContainer* m_currentScope = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -82,14 +94,21 @@ public:
|
||||
DeclarationRegistrationHelper(std::map<ASTNode const*, DeclarationContainer>& _scopes, ASTNode& _astRoot);
|
||||
|
||||
private:
|
||||
bool visit(ContractDefinition& _contract);
|
||||
void endVisit(ContractDefinition& _contract);
|
||||
bool visit(StructDefinition& _struct);
|
||||
void endVisit(StructDefinition& _struct);
|
||||
bool visit(FunctionDefinition& _function);
|
||||
void endVisit(FunctionDefinition& _function);
|
||||
void endVisit(VariableDefinition& _variableDefinition);
|
||||
bool visit(VariableDeclaration& _declaration);
|
||||
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();
|
||||
@ -97,7 +116,7 @@ private:
|
||||
|
||||
std::map<ASTNode const*, DeclarationContainer>& m_scopes;
|
||||
Declaration const* m_currentScope;
|
||||
FunctionDefinition* m_currentFunction;
|
||||
VariableScope* m_currentFunction;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -108,7 +127,9 @@ class ReferencesResolver: private ASTVisitor
|
||||
{
|
||||
public:
|
||||
ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver,
|
||||
ParameterList* _returnParameters, bool _allowLazyTypes = true);
|
||||
ContractDefinition const* _currentContract,
|
||||
ParameterList const* _returnParameters,
|
||||
bool _allowLazyTypes = true);
|
||||
|
||||
private:
|
||||
virtual void endVisit(VariableDeclaration& _variable) override;
|
||||
@ -118,7 +139,8 @@ private:
|
||||
virtual bool visit(Return& _return) override;
|
||||
|
||||
NameAndTypeResolver& m_resolver;
|
||||
ParameterList* m_returnParameters;
|
||||
ContractDefinition const* m_currentContract;
|
||||
ParameterList const* m_returnParameters;
|
||||
bool m_allowLazyTypes;
|
||||
};
|
||||
|
||||
|
507
Parser.cpp
507
Parser.cpp
@ -69,10 +69,10 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
|
||||
{
|
||||
switch (m_scanner->getCurrentToken())
|
||||
{
|
||||
case Token::IMPORT:
|
||||
case Token::Import:
|
||||
nodes.push_back(parseImportDirective());
|
||||
break;
|
||||
case Token::CONTRACT:
|
||||
case Token::Contract:
|
||||
nodes.push_back(parseContractDefinition());
|
||||
break;
|
||||
default:
|
||||
@ -100,119 +100,298 @@ int Parser::getEndPosition() const
|
||||
ASTPointer<ImportDirective> Parser::parseImportDirective()
|
||||
{
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
expectToken(Token::IMPORT);
|
||||
if (m_scanner->getCurrentToken() != Token::STRING_LITERAL)
|
||||
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);
|
||||
expectToken(Token::Semicolon);
|
||||
return nodeFactory.createNode<ImportDirective>(url);
|
||||
}
|
||||
|
||||
ASTPointer<ContractDefinition> Parser::parseContractDefinition()
|
||||
{
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
ASTPointer<ASTString> docstring;
|
||||
ASTPointer<ASTString> docString;
|
||||
if (m_scanner->getCurrentCommentLiteral() != "")
|
||||
docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
|
||||
expectToken(Token::CONTRACT);
|
||||
docString = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
|
||||
expectToken(Token::Contract);
|
||||
ASTPointer<ASTString> name = expectIdentifierToken();
|
||||
expectToken(Token::LBRACE);
|
||||
vector<ASTPointer<InheritanceSpecifier>> baseContracts;
|
||||
vector<ASTPointer<StructDefinition>> structs;
|
||||
vector<ASTPointer<EnumDefinition>> enums;
|
||||
vector<ASTPointer<VariableDeclaration>> stateVariables;
|
||||
vector<ASTPointer<FunctionDefinition>> functions;
|
||||
bool visibilityIsPublic = true;
|
||||
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)
|
||||
if (currentToken == Token::RBrace)
|
||||
break;
|
||||
else if (currentToken == Token::PUBLIC || currentToken == Token::PRIVATE)
|
||||
{
|
||||
visibilityIsPublic = (m_scanner->getCurrentToken() == Token::PUBLIC);
|
||||
m_scanner->next();
|
||||
expectToken(Token::COLON);
|
||||
}
|
||||
else if (currentToken == Token::FUNCTION)
|
||||
functions.push_back(parseFunctionDefinition(visibilityIsPublic));
|
||||
else if (currentToken == Token::STRUCT)
|
||||
else if (currentToken == Token::Function)
|
||||
functions.push_back(parseFunctionDefinition(name.get()));
|
||||
else if (currentToken == Token::Struct)
|
||||
structs.push_back(parseStructDefinition());
|
||||
else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING ||
|
||||
else if (currentToken == Token::Enum)
|
||||
enums.push_back(parseEnumDefinition());
|
||||
else if (currentToken == Token::Identifier || currentToken == Token::Mapping ||
|
||||
Token::isElementaryTypeName(currentToken))
|
||||
{
|
||||
bool const allowVar = false;
|
||||
stateVariables.push_back(parseVariableDeclaration(allowVar));
|
||||
expectToken(Token::SEMICOLON);
|
||||
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 or struct declaration expected."));
|
||||
BOOST_THROW_EXCEPTION(createParserError("Function, variable, struct or modifier declaration expected."));
|
||||
}
|
||||
nodeFactory.markEndPosition();
|
||||
expectToken(Token::RBRACE);
|
||||
return nodeFactory.createNode<ContractDefinition>(name, docstring, structs, stateVariables, functions);
|
||||
expectToken(Token::RBrace);
|
||||
return nodeFactory.createNode<ContractDefinition>(name, docString, baseContracts, structs, enums,
|
||||
stateVariables, functions, modifiers, events);
|
||||
}
|
||||
|
||||
ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic)
|
||||
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(expectIdentifierToken());
|
||||
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;
|
||||
if (m_scanner->getCurrentToken() == Token::CONST)
|
||||
Declaration::Visibility visibility(Declaration::Visibility::Default);
|
||||
vector<ASTPointer<ModifierInvocation>> modifiers;
|
||||
while (true)
|
||||
{
|
||||
isDeclaredConst = true;
|
||||
m_scanner->next();
|
||||
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)
|
||||
if (m_scanner->getCurrentToken() == Token::Returns)
|
||||
{
|
||||
bool const permitEmptyParameterList = false;
|
||||
m_scanner->next();
|
||||
returnParameters = parseParameterList(permitEmptyParameterList);
|
||||
}
|
||||
else
|
||||
{
|
||||
// create an empty parameter list at a zero-length location
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
nodeFactory.setLocationEmpty();
|
||||
returnParameters = nodeFactory.createNode<ParameterList>(vector<ASTPointer<VariableDeclaration>>());
|
||||
}
|
||||
returnParameters = createEmptyParameterList();
|
||||
ASTPointer<Block> block = parseBlock();
|
||||
nodeFactory.setEndPositionFromNode(block);
|
||||
return nodeFactory.createNode<FunctionDefinition>(name, _isPublic, docstring,
|
||||
parameters,
|
||||
isDeclaredConst, returnParameters, 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);
|
||||
expectToken(Token::Struct);
|
||||
ASTPointer<ASTString> name = expectIdentifierToken();
|
||||
vector<ASTPointer<VariableDeclaration>> members;
|
||||
expectToken(Token::LBRACE);
|
||||
while (m_scanner->getCurrentToken() != Token::RBRACE)
|
||||
expectToken(Token::LBrace);
|
||||
while (m_scanner->getCurrentToken() != Token::RBrace)
|
||||
{
|
||||
bool const allowVar = false;
|
||||
members.push_back(parseVariableDeclaration(allowVar));
|
||||
expectToken(Token::SEMICOLON);
|
||||
members.push_back(parseVariableDeclaration());
|
||||
expectToken(Token::Semicolon);
|
||||
}
|
||||
nodeFactory.markEndPosition();
|
||||
expectToken(Token::RBRACE);
|
||||
expectToken(Token::RBrace);
|
||||
return nodeFactory.createNode<StructDefinition>(name, members);
|
||||
}
|
||||
|
||||
ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(bool _allowVar)
|
||||
ASTPointer<EnumValue> Parser::parseEnumValue()
|
||||
{
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
ASTPointer<TypeName> type = parseTypeName(_allowVar);
|
||||
nodeFactory.markEndPosition();
|
||||
return nodeFactory.createNode<VariableDeclaration>(type, expectIdentifierToken());
|
||||
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)
|
||||
@ -224,17 +403,17 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
|
||||
type = ASTNodeFactory(*this).createNode<ElementaryTypeName>(token);
|
||||
m_scanner->next();
|
||||
}
|
||||
else if (token == Token::VAR)
|
||||
else if (token == Token::Var)
|
||||
{
|
||||
if (!_allowVar)
|
||||
BOOST_THROW_EXCEPTION(createParserError("Expected explicit type name."));
|
||||
m_scanner->next();
|
||||
}
|
||||
else if (token == Token::MAPPING)
|
||||
else if (token == Token::Mapping)
|
||||
{
|
||||
type = parseMapping();
|
||||
}
|
||||
else if (token == Token::IDENTIFIER)
|
||||
else if (token == Token::Identifier)
|
||||
{
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
nodeFactory.markEndPosition();
|
||||
@ -248,34 +427,36 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
|
||||
ASTPointer<Mapping> Parser::parseMapping()
|
||||
{
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
expectToken(Token::MAPPING);
|
||||
expectToken(Token::LPAREN);
|
||||
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);
|
||||
expectToken(Token::Arrow);
|
||||
bool const allowVar = false;
|
||||
ASTPointer<TypeName> valueType = parseTypeName(allowVar);
|
||||
nodeFactory.markEndPosition();
|
||||
expectToken(Token::RPAREN);
|
||||
expectToken(Token::RParen);
|
||||
return nodeFactory.createNode<Mapping>(keyType, valueType);
|
||||
}
|
||||
|
||||
ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty)
|
||||
ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty, bool _allowIndexed)
|
||||
{
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
vector<ASTPointer<VariableDeclaration>> parameters;
|
||||
expectToken(Token::LPAREN);
|
||||
if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN)
|
||||
VarDeclParserOptions options;
|
||||
options.allowIndexed = _allowIndexed;
|
||||
options.allowEmptyName = true;
|
||||
expectToken(Token::LParen);
|
||||
if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RParen)
|
||||
{
|
||||
bool const allowVar = false;
|
||||
parameters.push_back(parseVariableDeclaration(allowVar));
|
||||
while (m_scanner->getCurrentToken() != Token::RPAREN)
|
||||
parameters.push_back(parseVariableDeclaration(options));
|
||||
while (m_scanner->getCurrentToken() != Token::RParen)
|
||||
{
|
||||
expectToken(Token::COMMA);
|
||||
parameters.push_back(parseVariableDeclaration(allowVar));
|
||||
expectToken(Token::Comma);
|
||||
parameters.push_back(parseVariableDeclaration(options));
|
||||
}
|
||||
}
|
||||
nodeFactory.markEndPosition();
|
||||
@ -286,12 +467,12 @@ ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty)
|
||||
ASTPointer<Block> Parser::parseBlock()
|
||||
{
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
expectToken(Token::LBRACE);
|
||||
expectToken(Token::LBrace);
|
||||
vector<ASTPointer<Statement>> statements;
|
||||
while (m_scanner->getCurrentToken() != Token::RBRACE)
|
||||
while (m_scanner->getCurrentToken() != Token::RBrace)
|
||||
statements.push_back(parseStatement());
|
||||
nodeFactory.markEndPosition();
|
||||
expectToken(Token::RBRACE);
|
||||
expectToken(Token::RBrace);
|
||||
return nodeFactory.createNode<Block>(statements);
|
||||
}
|
||||
|
||||
@ -300,52 +481,60 @@ ASTPointer<Statement> Parser::parseStatement()
|
||||
ASTPointer<Statement> statement;
|
||||
switch (m_scanner->getCurrentToken())
|
||||
{
|
||||
case Token::IF:
|
||||
case Token::If:
|
||||
return parseIfStatement();
|
||||
case Token::WHILE:
|
||||
case Token::While:
|
||||
return parseWhileStatement();
|
||||
case Token::FOR:
|
||||
case Token::For:
|
||||
return parseForStatement();
|
||||
case Token::LBRACE:
|
||||
case Token::LBrace:
|
||||
return parseBlock();
|
||||
// starting from here, all statements must be terminated by a semicolon
|
||||
case Token::CONTINUE:
|
||||
case Token::Continue:
|
||||
statement = ASTNodeFactory(*this).createNode<Continue>();
|
||||
m_scanner->next();
|
||||
break;
|
||||
case Token::BREAK:
|
||||
case Token::Break:
|
||||
statement = ASTNodeFactory(*this).createNode<Break>();
|
||||
m_scanner->next();
|
||||
break;
|
||||
case Token::RETURN:
|
||||
case Token::Return:
|
||||
{
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
ASTPointer<Expression> expression;
|
||||
if (m_scanner->next() != Token::SEMICOLON)
|
||||
if (m_scanner->next() != Token::Semicolon)
|
||||
{
|
||||
expression = parseExpression();
|
||||
nodeFactory.setEndPositionFromNode(expression);
|
||||
}
|
||||
statement = nodeFactory.createNode<Return>(expression);
|
||||
break;
|
||||
}
|
||||
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);
|
||||
expectToken(Token::Semicolon);
|
||||
return statement;
|
||||
}
|
||||
|
||||
ASTPointer<IfStatement> Parser::parseIfStatement()
|
||||
{
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
expectToken(Token::IF);
|
||||
expectToken(Token::LPAREN);
|
||||
expectToken(Token::If);
|
||||
expectToken(Token::LParen);
|
||||
ASTPointer<Expression> condition = parseExpression();
|
||||
expectToken(Token::RPAREN);
|
||||
expectToken(Token::RParen);
|
||||
ASTPointer<Statement> trueBody = parseStatement();
|
||||
ASTPointer<Statement> falseBody;
|
||||
if (m_scanner->getCurrentToken() == Token::ELSE)
|
||||
if (m_scanner->getCurrentToken() == Token::Else)
|
||||
{
|
||||
m_scanner->next();
|
||||
falseBody = parseStatement();
|
||||
@ -359,10 +548,10 @@ ASTPointer<IfStatement> Parser::parseIfStatement()
|
||||
ASTPointer<WhileStatement> Parser::parseWhileStatement()
|
||||
{
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
expectToken(Token::WHILE);
|
||||
expectToken(Token::LPAREN);
|
||||
expectToken(Token::While);
|
||||
expectToken(Token::LParen);
|
||||
ASTPointer<Expression> condition = parseExpression();
|
||||
expectToken(Token::RPAREN);
|
||||
expectToken(Token::RParen);
|
||||
ASTPointer<Statement> body = parseStatement();
|
||||
nodeFactory.setEndPositionFromNode(body);
|
||||
return nodeFactory.createNode<WhileStatement>(condition, body);
|
||||
@ -374,21 +563,21 @@ ASTPointer<ForStatement> Parser::parseForStatement()
|
||||
ASTPointer<Statement> initExpression;
|
||||
ASTPointer<Expression> conditionExpression;
|
||||
ASTPointer<ExpressionStatement> loopExpression;
|
||||
expectToken(Token::FOR);
|
||||
expectToken(Token::LPAREN);
|
||||
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)
|
||||
// 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);
|
||||
expectToken(Token::Semicolon);
|
||||
|
||||
if (m_scanner->getCurrentToken() != Token::SEMICOLON)
|
||||
if (m_scanner->getCurrentToken() != Token::Semicolon)
|
||||
conditionExpression = parseExpression();
|
||||
expectToken(Token::SEMICOLON);
|
||||
expectToken(Token::Semicolon);
|
||||
|
||||
if (m_scanner->getCurrentToken() != Token::RPAREN)
|
||||
if (m_scanner->getCurrentToken() != Token::RParen)
|
||||
loopExpression = parseExpressionStatement();
|
||||
expectToken(Token::RPAREN);
|
||||
expectToken(Token::RParen);
|
||||
|
||||
ASTPointer<Statement> body = parseStatement();
|
||||
nodeFactory.setEndPositionFromNode(body);
|
||||
@ -409,10 +598,11 @@ ASTPointer<Statement> Parser::parseVarDefOrExprStmt()
|
||||
ASTPointer<VariableDefinition> Parser::parseVariableDefinition()
|
||||
{
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
bool const allowVar = true;
|
||||
ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(allowVar);
|
||||
VarDeclParserOptions options;
|
||||
options.allowVar = true;
|
||||
ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(options);
|
||||
ASTPointer<Expression> value;
|
||||
if (m_scanner->getCurrentToken() == Token::ASSIGN)
|
||||
if (m_scanner->getCurrentToken() == Token::Assign)
|
||||
{
|
||||
m_scanner->next();
|
||||
value = parseExpression();
|
||||
@ -449,7 +639,6 @@ ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence)
|
||||
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();
|
||||
@ -458,7 +647,6 @@ ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence)
|
||||
nodeFactory.setEndPositionFromNode(right);
|
||||
expression = nodeFactory.createNode<BinaryOperation>(expression, op, right);
|
||||
}
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
|
||||
@ -466,17 +654,7 @@ ASTPointer<Expression> Parser::parseUnaryExpression()
|
||||
{
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
Token::Value token = m_scanner->getCurrentToken();
|
||||
if (token == Token::NEW)
|
||||
{
|
||||
expectToken(Token::NEW);
|
||||
ASTPointer<Identifier> contractName = ASTNodeFactory(*this).createNode<Identifier>(expectIdentifierToken());
|
||||
expectToken(Token::LPAREN);
|
||||
vector<ASTPointer<Expression>> arguments(parseFunctionCallArguments());
|
||||
expectToken(Token::RPAREN);
|
||||
nodeFactory.markEndPosition();
|
||||
return nodeFactory.createNode<NewExpression>(contractName, arguments);
|
||||
}
|
||||
else if (Token::isUnaryOp(token) || Token::isCountOp(token))
|
||||
if (Token::isUnaryOp(token) || Token::isCountOp(token))
|
||||
{
|
||||
// prefix expression
|
||||
m_scanner->next();
|
||||
@ -500,34 +678,46 @@ ASTPointer<Expression> Parser::parseUnaryExpression()
|
||||
ASTPointer<Expression> Parser::parseLeftHandSideExpression()
|
||||
{
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
ASTPointer<Expression> expression = parsePrimaryExpression();
|
||||
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:
|
||||
case Token::LBrack:
|
||||
{
|
||||
m_scanner->next();
|
||||
ASTPointer<Expression> index = parseExpression();
|
||||
nodeFactory.markEndPosition();
|
||||
expectToken(Token::RBRACK);
|
||||
expectToken(Token::RBrack);
|
||||
expression = nodeFactory.createNode<IndexAccess>(expression, index);
|
||||
}
|
||||
break;
|
||||
case Token::PERIOD:
|
||||
case Token::Period:
|
||||
{
|
||||
m_scanner->next();
|
||||
nodeFactory.markEndPosition();
|
||||
expression = nodeFactory.createNode<MemberAccess>(expression, expectIdentifierToken());
|
||||
}
|
||||
break;
|
||||
case Token::LPAREN:
|
||||
case Token::LParen:
|
||||
{
|
||||
m_scanner->next();
|
||||
vector<ASTPointer<Expression>> arguments = parseFunctionCallArguments();
|
||||
vector<ASTPointer<Expression>> arguments;
|
||||
vector<ASTPointer<ASTString>> names;
|
||||
std::tie(arguments, names) = parseFunctionCallArguments();
|
||||
nodeFactory.markEndPosition();
|
||||
expectToken(Token::RPAREN);
|
||||
expression = nodeFactory.createNode<FunctionCall>(expression, arguments);
|
||||
expectToken(Token::RParen);
|
||||
expression = nodeFactory.createNode<FunctionCall>(expression, arguments, names);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -543,24 +733,34 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
|
||||
ASTPointer<Expression> expression;
|
||||
switch (token)
|
||||
{
|
||||
case Token::TRUE_LITERAL:
|
||||
case Token::FALSE_LITERAL:
|
||||
case Token::TrueLiteral:
|
||||
case Token::FalseLiteral:
|
||||
expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance());
|
||||
break;
|
||||
case Token::NUMBER:
|
||||
case Token::STRING_LITERAL:
|
||||
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:
|
||||
case Token::Identifier:
|
||||
nodeFactory.markEndPosition();
|
||||
expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance());
|
||||
break;
|
||||
case Token::LPAREN:
|
||||
case Token::LParen:
|
||||
{
|
||||
m_scanner->next();
|
||||
ASTPointer<Expression> expression = parseExpression();
|
||||
expectToken(Token::RPAREN);
|
||||
expectToken(Token::RParen);
|
||||
return expression;
|
||||
}
|
||||
default:
|
||||
@ -580,21 +780,47 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
|
||||
return expression;
|
||||
}
|
||||
|
||||
vector<ASTPointer<Expression>> Parser::parseFunctionCallArguments()
|
||||
vector<ASTPointer<Expression>> Parser::parseFunctionCallListArguments()
|
||||
{
|
||||
vector<ASTPointer<Expression>> arguments;
|
||||
if (m_scanner->getCurrentToken() != Token::RPAREN)
|
||||
if (m_scanner->getCurrentToken() != Token::RParen)
|
||||
{
|
||||
arguments.push_back(parseExpression());
|
||||
while (m_scanner->getCurrentToken() != Token::RPAREN)
|
||||
while (m_scanner->getCurrentToken() != Token::RParen)
|
||||
{
|
||||
expectToken(Token::COMMA);
|
||||
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()
|
||||
{
|
||||
@ -602,11 +828,11 @@ bool Parser::peekVariableDefinition()
|
||||
// (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 ||
|
||||
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));
|
||||
m_scanner->getCurrentToken() == Token::Identifier) &&
|
||||
m_scanner->peekNextToken() == Token::Identifier));
|
||||
}
|
||||
|
||||
void Parser::expectToken(Token::Value _value)
|
||||
@ -627,7 +853,7 @@ Token::Value Parser::expectAssignmentOperator()
|
||||
|
||||
ASTPointer<ASTString> Parser::expectIdentifierToken()
|
||||
{
|
||||
if (m_scanner->getCurrentToken() != Token::IDENTIFIER)
|
||||
if (m_scanner->getCurrentToken() != Token::Identifier)
|
||||
BOOST_THROW_EXCEPTION(createParserError("Expected identifier"));
|
||||
return getLiteralAndAdvance();
|
||||
}
|
||||
@ -639,6 +865,13 @@ ASTPointer<ASTString> Parser::getLiteralAndAdvance()
|
||||
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()))
|
||||
|
30
Parser.h
30
Parser.h
@ -45,16 +45,32 @@ private:
|
||||
/// 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<FunctionDefinition> parseFunctionDefinition(bool _isPublic);
|
||||
ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier();
|
||||
Declaration::Visibility parseVisibilitySpecifier(Token::Value _token);
|
||||
ASTPointer<FunctionDefinition> parseFunctionDefinition(ASTString const* _contractName);
|
||||
ASTPointer<StructDefinition> parseStructDefinition();
|
||||
ASTPointer<VariableDeclaration> parseVariableDeclaration(bool _allowVar);
|
||||
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);
|
||||
ASTPointer<ParameterList> parseParameterList(bool _allowEmpty = true, bool _allowIndexed = false);
|
||||
ASTPointer<Block> parseBlock();
|
||||
ASTPointer<Statement> parseStatement();
|
||||
ASTPointer<IfStatement> parseIfStatement();
|
||||
@ -68,7 +84,8 @@ private:
|
||||
ASTPointer<Expression> parseUnaryExpression();
|
||||
ASTPointer<Expression> parseLeftHandSideExpression();
|
||||
ASTPointer<Expression> parsePrimaryExpression();
|
||||
std::vector<ASTPointer<Expression>> parseFunctionCallArguments();
|
||||
std::vector<ASTPointer<Expression>> parseFunctionCallListArguments();
|
||||
std::pair<std::vector<ASTPointer<Expression>>, std::vector<ASTPointer<ASTString>>> parseFunctionCallArguments();
|
||||
///@}
|
||||
|
||||
///@{
|
||||
@ -84,11 +101,16 @@ private:
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
||||
|
185
Scanner.cpp
185
Scanner.cpp
@ -225,7 +225,7 @@ Token::Value Scanner::skipSingleLineComment()
|
||||
// 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;
|
||||
return Token::Whitespace;
|
||||
}
|
||||
|
||||
Token::Value Scanner::scanSingleLineDocComment()
|
||||
@ -255,7 +255,7 @@ Token::Value Scanner::scanSingleLineDocComment()
|
||||
advance();
|
||||
}
|
||||
literal.complete();
|
||||
return Token::COMMENT_LITERAL;
|
||||
return Token::CommentLiteral;
|
||||
}
|
||||
|
||||
Token::Value Scanner::skipMultiLineComment()
|
||||
@ -272,11 +272,11 @@ Token::Value Scanner::skipMultiLineComment()
|
||||
if (ch == '*' && m_char == '/')
|
||||
{
|
||||
m_char = ' ';
|
||||
return Token::WHITESPACE;
|
||||
return Token::Whitespace;
|
||||
}
|
||||
}
|
||||
// Unterminated multi-line comment.
|
||||
return Token::ILLEGAL;
|
||||
return Token::Illegal;
|
||||
}
|
||||
|
||||
Token::Value Scanner::scanMultiLineDocComment()
|
||||
@ -285,8 +285,6 @@ Token::Value Scanner::scanMultiLineDocComment()
|
||||
bool endFound = false;
|
||||
bool charsAdded = false;
|
||||
|
||||
advance(); //consume the last '*' at /**
|
||||
skipWhitespaceExceptLF();
|
||||
while (!isSourcePastEndOfInput())
|
||||
{
|
||||
//handle newlines in multline comments
|
||||
@ -321,9 +319,9 @@ Token::Value Scanner::scanMultiLineDocComment()
|
||||
}
|
||||
literal.complete();
|
||||
if (!endFound)
|
||||
return Token::ILLEGAL;
|
||||
return Token::Illegal;
|
||||
else
|
||||
return Token::COMMENT_LITERAL;
|
||||
return Token::CommentLiteral;
|
||||
}
|
||||
|
||||
Token::Value Scanner::scanSlash()
|
||||
@ -333,7 +331,7 @@ Token::Value Scanner::scanSlash()
|
||||
if (m_char == '/')
|
||||
{
|
||||
if (!advance()) /* double slash comment directly before EOS */
|
||||
return Token::WHITESPACE;
|
||||
return Token::Whitespace;
|
||||
else if (m_char == '/')
|
||||
{
|
||||
// doxygen style /// comment
|
||||
@ -342,7 +340,7 @@ Token::Value Scanner::scanSlash()
|
||||
comment = scanSingleLineDocComment();
|
||||
m_nextSkippedComment.location.end = getSourcePos();
|
||||
m_nextSkippedComment.token = comment;
|
||||
return Token::WHITESPACE;
|
||||
return Token::Whitespace;
|
||||
}
|
||||
else
|
||||
return skipSingleLineComment();
|
||||
@ -351,23 +349,32 @@ Token::Value Scanner::scanSlash()
|
||||
{
|
||||
// doxygen style /** natspec comment
|
||||
if (!advance()) /* slash star comment before EOS */
|
||||
return Token::WHITESPACE;
|
||||
return Token::Whitespace;
|
||||
else if (m_char == '*')
|
||||
{
|
||||
Token::Value comment;
|
||||
m_nextSkippedComment.location.start = firstSlashPosition;
|
||||
comment = scanMultiLineDocComment();
|
||||
m_nextSkippedComment.location.end = getSourcePos();
|
||||
m_nextSkippedComment.token = comment;
|
||||
return Token::WHITESPACE;
|
||||
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::ASSIGN_DIV);
|
||||
return selectToken(Token::AssignDiv);
|
||||
else
|
||||
return Token::DIV;
|
||||
return Token::Div;
|
||||
}
|
||||
|
||||
void Scanner::scanToken()
|
||||
@ -384,7 +391,7 @@ void Scanner::scanToken()
|
||||
case '\n': // fall-through
|
||||
case ' ':
|
||||
case '\t':
|
||||
token = selectToken(Token::WHITESPACE);
|
||||
token = selectToken(Token::Whitespace);
|
||||
break;
|
||||
case '"':
|
||||
case '\'':
|
||||
@ -394,81 +401,82 @@ void Scanner::scanToken()
|
||||
// < <= << <<=
|
||||
advance();
|
||||
if (m_char == '=')
|
||||
token = selectToken(Token::LTE);
|
||||
token = selectToken(Token::LessThanOrEqual);
|
||||
else if (m_char == '<')
|
||||
token = selectToken('=', Token::ASSIGN_SHL, Token::SHL);
|
||||
token = selectToken('=', Token::AssignShl, Token::SHL);
|
||||
else
|
||||
token = Token::LT;
|
||||
token = Token::LessThan;
|
||||
break;
|
||||
case '>':
|
||||
// > >= >> >>= >>> >>>=
|
||||
advance();
|
||||
if (m_char == '=')
|
||||
token = selectToken(Token::GTE);
|
||||
token = selectToken(Token::GreaterThanOrEqual);
|
||||
else if (m_char == '>')
|
||||
{
|
||||
// >> >>= >>> >>>=
|
||||
advance();
|
||||
if (m_char == '=')
|
||||
token = selectToken(Token::ASSIGN_SAR);
|
||||
token = selectToken(Token::AssignSar);
|
||||
else if (m_char == '>')
|
||||
token = selectToken('=', Token::ASSIGN_SHR, Token::SHR);
|
||||
token = selectToken('=', Token::AssignShr, Token::SHR);
|
||||
else
|
||||
token = Token::SAR;
|
||||
}
|
||||
else
|
||||
token = Token::GT;
|
||||
token = Token::GreaterThan;
|
||||
break;
|
||||
case '=':
|
||||
// = == =>
|
||||
advance();
|
||||
if (m_char == '=')
|
||||
token = selectToken(Token::EQ);
|
||||
token = selectToken(Token::Equal);
|
||||
else if (m_char == '>')
|
||||
token = selectToken(Token::ARROW);
|
||||
token = selectToken(Token::Arrow);
|
||||
else
|
||||
token = Token::ASSIGN;
|
||||
token = Token::Assign;
|
||||
break;
|
||||
case '!':
|
||||
// ! !=
|
||||
advance();
|
||||
if (m_char == '=')
|
||||
token = selectToken(Token::NE);
|
||||
token = selectToken(Token::NotEqual);
|
||||
else
|
||||
token = Token::NOT;
|
||||
token = Token::Not;
|
||||
break;
|
||||
case '+':
|
||||
// + ++ +=
|
||||
advance();
|
||||
if (m_char == '+')
|
||||
token = selectToken(Token::INC);
|
||||
token = selectToken(Token::Inc);
|
||||
else if (m_char == '=')
|
||||
token = selectToken(Token::ASSIGN_ADD);
|
||||
token = selectToken(Token::AssignAdd);
|
||||
else
|
||||
token = Token::ADD;
|
||||
token = Token::Add;
|
||||
break;
|
||||
case '-':
|
||||
// - -- -= Number
|
||||
// - -- -=
|
||||
advance();
|
||||
if (m_char == '-')
|
||||
{
|
||||
advance();
|
||||
token = Token::DEC;
|
||||
}
|
||||
token = selectToken(Token::Dec);
|
||||
else if (m_char == '=')
|
||||
token = selectToken(Token::ASSIGN_SUB);
|
||||
else if (m_char == '.' || isDecimalDigit(m_char))
|
||||
token = scanNumber('-');
|
||||
token = selectToken(Token::AssignSub);
|
||||
else
|
||||
token = Token::SUB;
|
||||
token = Token::Sub;
|
||||
break;
|
||||
case '*':
|
||||
// * *=
|
||||
token = selectToken('=', Token::ASSIGN_MUL, Token::MUL);
|
||||
// * ** *=
|
||||
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::ASSIGN_MOD, Token::MOD);
|
||||
token = selectToken('=', Token::AssignMod, Token::Mod);
|
||||
break;
|
||||
case '/':
|
||||
// / // /* /=
|
||||
@ -478,25 +486,25 @@ void Scanner::scanToken()
|
||||
// & && &=
|
||||
advance();
|
||||
if (m_char == '&')
|
||||
token = selectToken(Token::AND);
|
||||
token = selectToken(Token::And);
|
||||
else if (m_char == '=')
|
||||
token = selectToken(Token::ASSIGN_BIT_AND);
|
||||
token = selectToken(Token::AssignBitAnd);
|
||||
else
|
||||
token = Token::BIT_AND;
|
||||
token = Token::BitAnd;
|
||||
break;
|
||||
case '|':
|
||||
// | || |=
|
||||
advance();
|
||||
if (m_char == '|')
|
||||
token = selectToken(Token::OR);
|
||||
token = selectToken(Token::Or);
|
||||
else if (m_char == '=')
|
||||
token = selectToken(Token::ASSIGN_BIT_OR);
|
||||
token = selectToken(Token::AssignBitOr);
|
||||
else
|
||||
token = Token::BIT_OR;
|
||||
token = Token::BitOr;
|
||||
break;
|
||||
case '^':
|
||||
// ^ ^=
|
||||
token = selectToken('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR);
|
||||
token = selectToken('=', Token::AssignBitXor, Token::BitXor);
|
||||
break;
|
||||
case '.':
|
||||
// . Number
|
||||
@ -504,40 +512,40 @@ void Scanner::scanToken()
|
||||
if (isDecimalDigit(m_char))
|
||||
token = scanNumber('.');
|
||||
else
|
||||
token = Token::PERIOD;
|
||||
token = Token::Period;
|
||||
break;
|
||||
case ':':
|
||||
token = selectToken(Token::COLON);
|
||||
token = selectToken(Token::Colon);
|
||||
break;
|
||||
case ';':
|
||||
token = selectToken(Token::SEMICOLON);
|
||||
token = selectToken(Token::Semicolon);
|
||||
break;
|
||||
case ',':
|
||||
token = selectToken(Token::COMMA);
|
||||
token = selectToken(Token::Comma);
|
||||
break;
|
||||
case '(':
|
||||
token = selectToken(Token::LPAREN);
|
||||
token = selectToken(Token::LParen);
|
||||
break;
|
||||
case ')':
|
||||
token = selectToken(Token::RPAREN);
|
||||
token = selectToken(Token::RParen);
|
||||
break;
|
||||
case '[':
|
||||
token = selectToken(Token::LBRACK);
|
||||
token = selectToken(Token::LBrack);
|
||||
break;
|
||||
case ']':
|
||||
token = selectToken(Token::RBRACK);
|
||||
token = selectToken(Token::RBrack);
|
||||
break;
|
||||
case '{':
|
||||
token = selectToken(Token::LBRACE);
|
||||
token = selectToken(Token::LBrace);
|
||||
break;
|
||||
case '}':
|
||||
token = selectToken(Token::RBRACE);
|
||||
token = selectToken(Token::RBrace);
|
||||
break;
|
||||
case '?':
|
||||
token = selectToken(Token::CONDITIONAL);
|
||||
token = selectToken(Token::Conditional);
|
||||
break;
|
||||
case '~':
|
||||
token = selectToken(Token::BIT_NOT);
|
||||
token = selectToken(Token::BitNot);
|
||||
break;
|
||||
default:
|
||||
if (isIdentifierStart(m_char))
|
||||
@ -545,17 +553,17 @@ void Scanner::scanToken()
|
||||
else if (isDecimalDigit(m_char))
|
||||
token = scanNumber();
|
||||
else if (skipWhitespace())
|
||||
token = Token::WHITESPACE;
|
||||
token = Token::Whitespace;
|
||||
else if (isSourcePastEndOfInput())
|
||||
token = Token::EOS;
|
||||
else
|
||||
token = selectToken(Token::ILLEGAL);
|
||||
token = selectToken(Token::Illegal);
|
||||
break;
|
||||
}
|
||||
// Continue scanning for tokens as long as we're just skipping
|
||||
// whitespace.
|
||||
}
|
||||
while (token == Token::WHITESPACE);
|
||||
while (token == Token::Whitespace);
|
||||
m_nextToken.location.end = getSourcePos();
|
||||
m_nextToken.token = token;
|
||||
}
|
||||
@ -613,16 +621,16 @@ Token::Value Scanner::scanString()
|
||||
if (c == '\\')
|
||||
{
|
||||
if (isSourcePastEndOfInput() || !scanEscape())
|
||||
return Token::ILLEGAL;
|
||||
return Token::Illegal;
|
||||
}
|
||||
else
|
||||
addLiteralChar(c);
|
||||
}
|
||||
if (m_char != quote)
|
||||
return Token::ILLEGAL;
|
||||
return Token::Illegal;
|
||||
literal.complete();
|
||||
advance(); // consume quote
|
||||
return Token::STRING_LITERAL;
|
||||
return Token::StringLiteral;
|
||||
}
|
||||
|
||||
void Scanner::scanDecimalDigits()
|
||||
@ -643,8 +651,7 @@ Token::Value Scanner::scanNumber(char _charSeen)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_charSeen == '-')
|
||||
addLiteralChar('-');
|
||||
solAssert(_charSeen == 0, "");
|
||||
// if the first character is '0' we must check for octals and hex
|
||||
if (m_char == '0')
|
||||
{
|
||||
@ -656,7 +663,7 @@ Token::Value Scanner::scanNumber(char _charSeen)
|
||||
kind = HEX;
|
||||
addLiteralCharAndAdvance();
|
||||
if (!isHexDigit(m_char))
|
||||
return Token::ILLEGAL; // we must have at least one hex digit after 'x'/'X'
|
||||
return Token::Illegal; // we must have at least one hex digit after 'x'/'X'
|
||||
while (isHexDigit(m_char))
|
||||
addLiteralCharAndAdvance();
|
||||
}
|
||||
@ -677,13 +684,13 @@ Token::Value Scanner::scanNumber(char _charSeen)
|
||||
{
|
||||
solAssert(kind != HEX, "'e'/'E' must be scanned as part of the hex number");
|
||||
if (kind != DECIMAL)
|
||||
return Token::ILLEGAL;
|
||||
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'
|
||||
return Token::Illegal; // we must have at least one decimal digit after 'e'/'E'
|
||||
scanDecimalDigits();
|
||||
}
|
||||
// The source character immediately following a numeric literal must
|
||||
@ -691,27 +698,9 @@ Token::Value Scanner::scanNumber(char _charSeen)
|
||||
// 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;
|
||||
return Token::Illegal;
|
||||
literal.complete();
|
||||
return Token::NUMBER;
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Keyword Matcher
|
||||
|
||||
|
||||
static Token::Value keywordOrIdentifierToken(string const& _input)
|
||||
{
|
||||
// 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(_input);
|
||||
return it == keywords.end() ? Token::IDENTIFIER : it->second;
|
||||
return Token::Number;
|
||||
}
|
||||
|
||||
Token::Value Scanner::scanIdentifierOrKeyword()
|
||||
@ -723,7 +712,7 @@ Token::Value Scanner::scanIdentifierOrKeyword()
|
||||
while (isIdentifierPart(m_char))
|
||||
addLiteralCharAndAdvance();
|
||||
literal.complete();
|
||||
return keywordOrIdentifierToken(m_nextToken.literal);
|
||||
return Token::fromIdentifierOrKeyword(m_nextToken.literal);
|
||||
}
|
||||
|
||||
char CharStream::advanceAndGet(size_t _chars)
|
||||
|
@ -119,6 +119,7 @@ public:
|
||||
{
|
||||
return m_currentToken.token;
|
||||
}
|
||||
|
||||
Location getCurrentLocation() const { return m_currentToken.location; }
|
||||
std::string const& getCurrentLiteral() const { return m_currentToken.literal; }
|
||||
///@}
|
||||
|
16
Token.cpp
16
Token.cpp
@ -40,8 +40,11 @@
|
||||
// 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
|
||||
@ -77,6 +80,19 @@ 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
|
||||
|
||||
|
496
Token.h
496
Token.h
@ -67,263 +67,274 @@ namespace solidity
|
||||
|
||||
#define IGNORE_TOKEN(name, string, precedence)
|
||||
|
||||
#define TOKEN_LIST(T, K) \
|
||||
/* End of source indicator. */ \
|
||||
T(EOS, "EOS", 0) \
|
||||
#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) \
|
||||
\
|
||||
/* 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) \
|
||||
/* 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(ASSIGN_BIT_OR, "|=", 2) \
|
||||
T(ASSIGN_BIT_XOR, "^=", 2) \
|
||||
T(ASSIGN_BIT_AND, "&=", 2) \
|
||||
T(ASSIGN_SHL, "<<=", 2) \
|
||||
T(ASSIGN_SAR, ">>=", 2) \
|
||||
T(ASSIGN_SHR, ">>>=", 2) \
|
||||
T(ASSIGN_ADD, "+=", 2) \
|
||||
T(ASSIGN_SUB, "-=", 2) \
|
||||
T(ASSIGN_MUL, "*=", 2) \
|
||||
T(ASSIGN_DIV, "/=", 2) \
|
||||
T(ASSIGN_MOD, "%=", 2) \
|
||||
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(BIT_OR, "|", 8) \
|
||||
T(BIT_XOR, "^", 9) \
|
||||
T(BIT_AND, "&", 10) \
|
||||
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(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(EQ, "==", 6) \
|
||||
T(NE, "!=", 6) \
|
||||
T(LT, "<", 7) \
|
||||
T(GT, ">", 7) \
|
||||
T(LTE, "<=", 7) \
|
||||
T(GTE, ">=", 7) \
|
||||
K(IN, "in", 7) \
|
||||
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(BIT_NOT, "~", 0) \
|
||||
T(INC, "++", 0) \
|
||||
T(DEC, "--", 0) \
|
||||
K(DELETE, "delete", 0) \
|
||||
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(EXTENDS, "extends", 0) \
|
||||
K(FOR, "for", 0) \
|
||||
K(FUNCTION, "function", 0) \
|
||||
K(IF, "if", 0) \
|
||||
K(IMPORT, "import", 0) \
|
||||
K(MAPPING, "mapping", 0) \
|
||||
K(NEW, "new", 0) \
|
||||
K(PUBLIC, "public", 0) \
|
||||
K(PRIVATE, "private", 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(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
|
||||
* TODO more to be added */ \
|
||||
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(STRING_TYPE, "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(TYPES_END, NULL, 0) /* used as type enum end marker */ \
|
||||
* 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(NULL_LITERAL, "null", 0) \
|
||||
K(TRUE_LITERAL, "true", 0) \
|
||||
K(FALSE_LITERAL, "false", 0) \
|
||||
T(NUMBER, NULL, 0) \
|
||||
T(STRING_LITERAL, NULL, 0) \
|
||||
T(COMMENT_LITERAL, NULL, 0) \
|
||||
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) \
|
||||
T(Identifier, NULL, 0) \
|
||||
\
|
||||
/* Illegal token - not able to scan. */ \
|
||||
T(ILLEGAL, "ILLEGAL", 0) \
|
||||
T(Illegal, "ILLEGAL", 0) \
|
||||
\
|
||||
/* Scanner-internal use only. */ \
|
||||
T(WHITESPACE, NULL, 0)
|
||||
T(Whitespace, NULL, 0)
|
||||
|
||||
|
||||
class Token
|
||||
@ -350,24 +361,27 @@ public:
|
||||
}
|
||||
|
||||
// Predicates
|
||||
static bool isElementaryTypeName(Value tok) { return INT <= tok && tok < TYPES_END; }
|
||||
static bool isAssignmentOp(Value tok) { return ASSIGN <= tok && tok <= ASSIGN_MOD; }
|
||||
static bool isBinaryOp(Value op) { return COMMA <= op && op <= MOD; }
|
||||
static bool isCommutativeOp(Value op) { return op == BIT_OR || op == BIT_XOR || op == BIT_AND ||
|
||||
op == ADD || op == MUL || op == EQ || op == NE; }
|
||||
static bool isArithmeticOp(Value op) { return ADD <= op && op <= MOD; }
|
||||
static bool isCompareOp(Value op) { return EQ <= op && op <= IN; }
|
||||
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 Token::Value(op + (BIT_OR - ASSIGN_BIT_OR));
|
||||
solAssert(isAssignmentOp(op) && op != Assign, "");
|
||||
return Value(op + (BitOr - AssignBitOr));
|
||||
}
|
||||
|
||||
static bool isBitOp(Value op) { return (BIT_OR <= op && op <= SHR) || op == BIT_NOT; }
|
||||
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 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
|
||||
@ -386,6 +400,8 @@ public:
|
||||
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];
|
||||
|
352
Types.h
352
Types.h
@ -41,6 +41,7 @@ namespace solidity
|
||||
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>;
|
||||
|
||||
/**
|
||||
@ -49,14 +50,16 @@ using TypePointers = std::vector<TypePointer>;
|
||||
class MemberList
|
||||
{
|
||||
public:
|
||||
using MemberMap = std::map<std::string, TypePointer>;
|
||||
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
|
||||
{
|
||||
auto it = m_memberTypes.find(_name);
|
||||
return it != m_memberTypes.end() ? it->second : TypePointer();
|
||||
for (auto const& it: m_memberTypes)
|
||||
if (it.first == _name)
|
||||
return it.second;
|
||||
return TypePointer();
|
||||
}
|
||||
|
||||
MemberMap::const_iterator begin() const { return m_memberTypes.begin(); }
|
||||
@ -70,26 +73,31 @@ private:
|
||||
/**
|
||||
* Abstract base class that forms the root of the type hierarchy.
|
||||
*/
|
||||
class Type: private boost::noncopyable
|
||||
class Type: private boost::noncopyable, public std::enable_shared_from_this<Type>
|
||||
{
|
||||
public:
|
||||
enum class Category
|
||||
{
|
||||
INTEGER, BOOL, REAL, STRING, CONTRACT, STRUCT, FUNCTION, MAPPING, VOID, TYPE, MAGIC
|
||||
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 std::shared_ptr<Type const> fromElementaryTypeName(Token::Value _typeToken);
|
||||
static std::shared_ptr<Type const> fromUserDefinedTypeName(UserDefinedTypeName const& _typeName);
|
||||
static std::shared_ptr<Type const> fromMapping(Mapping const& _typeName);
|
||||
static std::shared_ptr<Type const> fromFunction(FunctionDefinition const& _function);
|
||||
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 std::shared_ptr<Type const> forLiteral(Literal const& _literal);
|
||||
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; }
|
||||
@ -97,15 +105,27 @@ public:
|
||||
{
|
||||
return isImplicitlyConvertibleTo(_convertTo);
|
||||
}
|
||||
virtual bool acceptsBinaryOperator(Token::Value) const { return false; }
|
||||
virtual bool acceptsUnaryOperator(Token::Value) const { return false; }
|
||||
/// @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 on the stack.
|
||||
/// 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; }
|
||||
@ -117,6 +137,8 @@ public:
|
||||
/// 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; }
|
||||
@ -124,10 +146,10 @@ public:
|
||||
TypePointer getMemberType(std::string const& _name) const { return getMembers().getMemberType(_name); }
|
||||
|
||||
virtual std::string toString() const = 0;
|
||||
virtual u256 literalValue(Literal const&) const
|
||||
virtual u256 literalValue(Literal const*) const
|
||||
{
|
||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Literal value requested "
|
||||
"for type without literals."));
|
||||
"for type without literals."));
|
||||
}
|
||||
|
||||
protected:
|
||||
@ -143,20 +165,16 @@ class IntegerType: public Type
|
||||
public:
|
||||
enum class Modifier
|
||||
{
|
||||
UNSIGNED, SIGNED, HASH, ADDRESS
|
||||
Unsigned, Signed, Hash, Address
|
||||
};
|
||||
virtual Category getCategory() const override { return Category::INTEGER; }
|
||||
virtual Category getCategory() const override { return Category::Integer; }
|
||||
|
||||
/// @returns the smallest integer type for the given literal or an empty pointer
|
||||
/// if no type fits.
|
||||
static std::shared_ptr<IntegerType const> smallestTypeForLiteral(std::string const& _literal);
|
||||
|
||||
explicit IntegerType(int _bits, Modifier _modifier = Modifier::UNSIGNED);
|
||||
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 bool acceptsBinaryOperator(Token::Value _operator) const override;
|
||||
virtual bool acceptsUnaryOperator(Token::Value _operator) 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;
|
||||
|
||||
@ -166,17 +184,51 @@ public:
|
||||
virtual MemberList const& getMembers() const { return isAddress() ? AddressMemberList : EmptyMemberList; }
|
||||
|
||||
virtual std::string toString() const override;
|
||||
virtual u256 literalValue(Literal const& _literal) 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; }
|
||||
int isSigned() const { return m_modifier == Modifier::SIGNED; }
|
||||
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;
|
||||
static const MemberList AddressMemberList;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -185,22 +237,23 @@ private:
|
||||
class StaticStringType: public Type
|
||||
{
|
||||
public:
|
||||
virtual Category getCategory() const override { return Category::STRING; }
|
||||
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);
|
||||
|
||||
StaticStringType(int _bytes);
|
||||
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;
|
||||
virtual u256 literalValue(Literal const* _literal) const override;
|
||||
|
||||
int getNumBytes() const { return m_bytes; }
|
||||
|
||||
@ -215,22 +268,45 @@ class BoolType: public Type
|
||||
{
|
||||
public:
|
||||
BoolType() {}
|
||||
virtual Category getCategory() const { return Category::BOOL; }
|
||||
virtual Category getCategory() const override { return Category::Bool; }
|
||||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
virtual bool acceptsBinaryOperator(Token::Value _operator) const override
|
||||
{
|
||||
return _operator == Token::AND || _operator == Token::OR;
|
||||
}
|
||||
virtual bool acceptsUnaryOperator(Token::Value _operator) const override
|
||||
{
|
||||
return _operator == Token::NOT || _operator == Token::DELETE;
|
||||
}
|
||||
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;
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -239,27 +315,38 @@ public:
|
||||
class ContractType: public Type
|
||||
{
|
||||
public:
|
||||
virtual Category getCategory() const override { return Category::CONTRACT; }
|
||||
ContractType(ContractDefinition const& _contract): m_contract(_contract) {}
|
||||
/// Contracts can be converted to themselves and to addresses.
|
||||
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 u256 getStorageSize() 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.
|
||||
std::shared_ptr<FunctionType const> const& getConstructorType() const;
|
||||
FunctionTypePointer const& getConstructorType() const;
|
||||
|
||||
unsigned getFunctionIndex(std::string const& _functionName) 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 std::shared_ptr<FunctionType const> m_constructorType;
|
||||
mutable FunctionTypePointer m_constructorType;
|
||||
/// List of member types, will be lazy-initialized because of recursive references.
|
||||
mutable std::unique_ptr<MemberList> m_members;
|
||||
};
|
||||
@ -270,13 +357,9 @@ private:
|
||||
class StructType: public Type
|
||||
{
|
||||
public:
|
||||
virtual Category getCategory() const override { return Category::STRUCT; }
|
||||
StructType(StructDefinition const& _struct): m_struct(_struct) {}
|
||||
virtual bool acceptsUnaryOperator(Token::Value _operator) const override
|
||||
{
|
||||
return _operator == Token::DELETE;
|
||||
}
|
||||
|
||||
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;
|
||||
@ -293,6 +376,32 @@ private:
|
||||
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
|
||||
@ -302,20 +411,47 @@ class FunctionType: public Type
|
||||
{
|
||||
public:
|
||||
/// The meaning of the value(s) on the stack referencing the function:
|
||||
/// INTERNAL: jump tag, EXTERNAL: contract address + function index,
|
||||
/// INTERNAL: jump tag, EXTERNAL: contract address + function identifier,
|
||||
/// BARE: contract address (non-abi contract call)
|
||||
/// OTHERS: special virtual function, nothing on the stack
|
||||
enum class Location { INTERNAL, EXTERNAL, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160, BARE };
|
||||
/// @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; }
|
||||
virtual Category getCategory() const override { return Category::Function; }
|
||||
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
|
||||
FunctionType(TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes,
|
||||
Location _location = Location::INTERNAL):
|
||||
m_parameterTypes(_parameterTypes), m_returnParameterTypes(_returnParameterTypes),
|
||||
m_location(_location) {}
|
||||
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;
|
||||
@ -323,13 +459,49 @@ public:
|
||||
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;
|
||||
Location m_location;
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -338,7 +510,7 @@ private:
|
||||
class MappingType: public Type
|
||||
{
|
||||
public:
|
||||
virtual Category getCategory() const override { return Category::MAPPING; }
|
||||
virtual Category getCategory() const override { return Category::Mapping; }
|
||||
MappingType(TypePointer const& _keyType, TypePointer const& _valueType):
|
||||
m_keyType(_keyType), m_valueType(_valueType) {}
|
||||
|
||||
@ -361,9 +533,10 @@ private:
|
||||
class VoidType: public Type
|
||||
{
|
||||
public:
|
||||
virtual Category getCategory() const override { return Category::VOID; }
|
||||
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.")); }
|
||||
@ -378,19 +551,48 @@ public:
|
||||
class TypeType: public Type
|
||||
{
|
||||
public:
|
||||
virtual Category getCategory() const override { return Category::TYPE; }
|
||||
TypeType(TypePointer const& _actualType): m_actualType(_actualType) {}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
@ -401,10 +603,16 @@ private:
|
||||
class MagicType: public Type
|
||||
{
|
||||
public:
|
||||
enum class Kind { BLOCK, MSG, TX };
|
||||
virtual Category getCategory() const override { return Category::MAGIC; }
|
||||
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();
|
||||
}
|
||||
|
||||
MagicType(Kind _kind);
|
||||
virtual bool operator==(Type const& _other) const;
|
||||
virtual bool canBeStored() const override { return false; }
|
||||
virtual bool canLiveOutsideStorage() const override { return true; }
|
||||
|
6
Utils.h
6
Utils.h
@ -45,5 +45,11 @@ inline void solAssertAux(bool _condition, std::string const& _errorDescription,
|
||||
<< ::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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
19
grammar.txt
19
grammar.txt
@ -1,12 +1,19 @@
|
||||
ContractDefinition = 'contract' Identifier '{' ContractPart* '}'
|
||||
ContractPart = VariableDeclaration ';' | StructDefinition |
|
||||
FunctionDefinition | 'public:' | 'private:'
|
||||
ContractDefinition = 'contract' Identifier
|
||||
( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )?
|
||||
'{' ContractPart* '}'
|
||||
ContractPart = StateVariableDeclaration | StructDefinition | ModifierDefinition | FunctionDefinition | EnumDefinition
|
||||
|
||||
InheritanceSpecifier = Identifier ( '(' Expression ( ',' Expression )* ')' )?
|
||||
StructDefinition = 'struct' Identifier '{'
|
||||
( VariableDeclaration (';' VariableDeclaration)* )? '}
|
||||
|
||||
FunctionDefinition = 'function' Identifier ParameterList 'const'?
|
||||
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
|
||||
@ -33,7 +40,7 @@ Expression = Assignment | UnaryOperation | BinaryOperation | FunctionCall | NewE
|
||||
// The expression syntax is actually much more complicated
|
||||
Assignment = Expression (AssignmentOp Expression)
|
||||
FunctionCall = Expression '(' Expression ( ',' Expression )* ')'
|
||||
NewExpression = 'new' Identifier '(' ( 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