Merge branch 'develop'

Conflicts:
	README.md
	evmjit
	libdevcrypto/CryptoPP.cpp
	libethereum/State.cpp
	neth/main.cpp
This commit is contained in:
Gav Wood 2015-02-20 21:56:37 +01:00
commit 89d84edb16
42 changed files with 5342 additions and 1687 deletions

456
AST.cpp
View File

@ -27,6 +27,8 @@
#include <libsolidity/Exceptions.h> #include <libsolidity/Exceptions.h>
#include <libsolidity/AST_accept.h> #include <libsolidity/AST_accept.h>
#include <libdevcrypto/SHA3.h>
using namespace std; using namespace std;
namespace dev namespace dev
@ -39,40 +41,199 @@ TypeError ASTNode::createTypeError(string const& _description) const
return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description); 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() void ContractDefinition::checkTypeRequirements()
{ {
for (ASTPointer<InheritanceSpecifier> const& baseSpecifier: getBaseContracts())
baseSpecifier->checkTypeRequirements();
checkIllegalOverrides();
FunctionDefinition const* constructor = getConstructor(); FunctionDefinition const* constructor = getConstructor();
if (constructor && !constructor->getReturnParameters().empty()) if (constructor && !constructor->getReturnParameters().empty())
BOOST_THROW_EXCEPTION(constructor->getReturnParameterList()->createTypeError( 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()) for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions())
function->checkTypeRequirements(); 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; auto exportedFunctionList = getInterfaceFunctionList();
for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions)
if (f->isPublic() && f->getName() != getName()) map<FixedHash<4>, FunctionTypePointer> exportedFunctions;
exportedFunctions.push_back(f.get()); for (auto const& it: exportedFunctionList)
auto compareNames = [](FunctionDefinition const* _a, FunctionDefinition const* _b) exportedFunctions.insert(it);
{
return _a->getName().compare(_b->getName()) < 0; solAssert(exportedFunctionList.size() == exportedFunctions.size(),
}; "Hash collision at Function Definition Hash calculation");
sort(exportedFunctions.begin(), exportedFunctions.end(), compareNames);
return exportedFunctions; return exportedFunctions;
} }
FunctionDefinition const* ContractDefinition::getConstructor() const FunctionDefinition const* ContractDefinition::getConstructor() const
{ {
for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions) for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions)
if (f->getName() == getName()) if (f->isConstructor())
return f.get(); return f.get();
return nullptr; 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 void StructDefinition::checkMemberTypes() const
{ {
for (ASTPointer<VariableDeclaration> const& member: getMembers()) for (ASTPointer<VariableDeclaration> const& member: getMembers())
@ -93,7 +254,7 @@ void StructDefinition::checkRecursion() const
<< errinfo_comment("Recursive struct definition.")); << errinfo_comment("Recursive struct definition."));
definitionsSeen.insert(def); definitionsSeen.insert(def);
for (ASTPointer<VariableDeclaration> const& member: def->getMembers()) 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()); UserDefinedTypeName const& typeName = dynamic_cast<UserDefinedTypeName const&>(*member->getTypeName());
queue.push_back(&dynamic_cast<StructDefinition const&>(*typeName.getReferencedDeclaration())); 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() void FunctionDefinition::checkTypeRequirements()
{ {
for (ASTPointer<VariableDeclaration> const& var: getParameters() + getReturnParameters()) for (ASTPointer<VariableDeclaration> const& var: getParameters() + getReturnParameters())
if (!var->getType()->canLiveOutsideStorage()) if (!var->getType()->canLiveOutsideStorage())
BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage."));
for (ASTPointer<ModifierInvocation> const& modifier: m_functionModifiers)
modifier->checkTypeRequirements();
m_body->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() void Block::checkTypeRequirements()
{ {
for (shared_ptr<Statement> const& statement: m_statements) for (shared_ptr<Statement> const& statement: m_statements)
@ -145,7 +380,8 @@ void Return::checkTypeRequirements()
{ {
if (!m_expression) if (!m_expression)
return; 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) if (m_returnParameters->getParameters().size() != 1)
BOOST_THROW_EXCEPTION(createTypeError("Different number of arguments in return statement " BOOST_THROW_EXCEPTION(createTypeError("Different number of arguments in return statement "
"than in returns declaration.")); "than in returns declaration."));
@ -168,7 +404,17 @@ void VariableDefinition::checkTypeRequirements()
{ {
// no type declared and no previous assignment, infer the type // no type declared and no previous assignment, infer the type
m_value->checkTypeRequirements(); 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->checkTypeRequirements();
m_leftHandSide->requireLValue(); m_leftHandSide->requireLValue();
//@todo later, assignments to structs might be possible, but not to mappings if (m_leftHandSide->getType()->getCategory() == Type::Category::Mapping)
if (!m_leftHandSide->getType()->isValueType() && !m_leftHandSide->isLocalLValue()) BOOST_THROW_EXCEPTION(createTypeError("Mappings cannot be assigned to."));
BOOST_THROW_EXCEPTION(createTypeError("Assignment to non-local non-value lvalue."));
m_rightHandSide->expectType(*m_leftHandSide->getType());
m_type = m_leftHandSide->getType(); m_type = m_leftHandSide->getType();
if (m_assigmentOperator != Token::ASSIGN) if (m_assigmentOperator == Token::Assign)
m_rightHandSide->expectType(*m_type);
else
{
// compound assignment // compound assignment
if (!m_type->acceptsBinaryOperator(Token::AssignmentToBinaryOp(m_assigmentOperator))) m_rightHandSide->checkTypeRequirements();
BOOST_THROW_EXCEPTION(createTypeError("Operator not compatible with type.")); 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() void ExpressionStatement::checkTypeRequirements()
{ {
m_expression->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) void Expression::expectType(Type const& _expectedType)
@ -212,12 +469,12 @@ void Expression::requireLValue()
void UnaryOperation::checkTypeRequirements() void UnaryOperation::checkTypeRequirements()
{ {
// INC, DEC, ADD, SUB, NOT, BIT_NOT, DELETE // Inc, Dec, Add, Sub, Not, BitNot, Delete
m_subExpression->checkTypeRequirements(); 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_subExpression->requireLValue();
m_type = m_subExpression->getType(); m_type = m_subExpression->getType()->unaryOperatorResult(m_operator);
if (!m_type->acceptsUnaryOperator(m_operator)) if (!m_type)
BOOST_THROW_EXCEPTION(createTypeError("Unary operator not compatible with type.")); BOOST_THROW_EXCEPTION(createTypeError("Unary operator not compatible with type."));
} }
@ -225,24 +482,13 @@ void BinaryOperation::checkTypeRequirements()
{ {
m_left->checkTypeRequirements(); m_left->checkTypeRequirements();
m_right->checkTypeRequirements(); m_right->checkTypeRequirements();
if (m_right->getType()->isImplicitlyConvertibleTo(*m_left->getType())) m_commonType = m_left->getType()->binaryOperatorResult(m_operator, m_right->getType());
m_commonType = m_left->getType(); if (!m_commonType)
else if (m_left->getType()->isImplicitlyConvertibleTo(*m_right->getType())) BOOST_THROW_EXCEPTION(createTypeError("Operator " + string(Token::toString(m_operator)) +
m_commonType = m_right->getType(); " not compatible with types " +
else m_left->getType()->toString() + " and " +
BOOST_THROW_EXCEPTION(createTypeError("No common type found in binary operation: " +
m_left->getType()->toString() + " vs. " +
m_right->getType()->toString())); m_right->getType()->toString()));
if (Token::isCompareOp(m_operator)) m_type = Token::isCompareOp(m_operator) ? make_shared<BoolType>() : m_commonType;
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()));
}
} }
void FunctionCall::checkTypeRequirements() 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 //@todo for structs, we have to check the number of arguments to be equal to the
// number of non-mapping members // number of non-mapping members
if (m_arguments.size() != 1) if (m_arguments.size() != 1)
BOOST_THROW_EXCEPTION(createTypeError("More than one argument for " BOOST_THROW_EXCEPTION(createTypeError("More than one argument for explicit type conversion."));
"explicit type conersion.")); if (!m_names.empty())
BOOST_THROW_EXCEPTION(createTypeError("Type conversion cannot allow named arguments."));
if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type.getActualType())) if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type.getActualType()))
BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed.")); BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed."));
m_type = type.getActualType(); 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 // and then ask if that is implicitly convertible to the struct represented by the
// function parameters // function parameters
TypePointers const& parameterTypes = functionType->getParameterTypes(); 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.")); 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])) if (m_names.empty())
BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in function call.")); {
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, // @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 // but we change it to the type of the first return value until we have structs
if (functionType->getReturnParameterTypes().empty()) if (functionType->getReturnParameterTypes().empty())
@ -288,26 +572,19 @@ void FunctionCall::checkTypeRequirements()
bool FunctionCall::isTypeConversion() const bool FunctionCall::isTypeConversion() const
{ {
return m_expression->getType()->getCategory() == Type::Category::TYPE; return m_expression->getType()->getCategory() == Type::Category::TypeType;
} }
void NewExpression::checkTypeRequirements() void NewExpression::checkTypeRequirements()
{ {
m_contractName->checkTypeRequirements(); m_contractName->checkTypeRequirements();
for (ASTPointer<Expression> const& argument: m_arguments)
argument->checkTypeRequirements();
m_contract = dynamic_cast<ContractDefinition const*>(m_contractName->getReferencedDeclaration()); m_contract = dynamic_cast<ContractDefinition const*>(m_contractName->getReferencedDeclaration());
if (!m_contract) if (!m_contract)
BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract.")); BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract."));
shared_ptr<ContractType const> type = make_shared<ContractType const>(*m_contract); shared_ptr<ContractType const> contractType = make_shared<ContractType>(*m_contract);
m_type = type; TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes();
TypePointers const& parameterTypes = type->getConstructorType()->getParameterTypes(); m_type = make_shared<FunctionType>(parameterTypes, TypePointers{contractType},
if (parameterTypes.size() != m_arguments.size()) FunctionType::Location::Creation);
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."));
} }
void MemberAccess::checkTypeRequirements() void MemberAccess::checkTypeRequirements()
@ -316,78 +593,43 @@ void MemberAccess::checkTypeRequirements()
Type const& type = *m_expression->getType(); Type const& type = *m_expression->getType();
m_type = type.getMemberType(*m_memberName); m_type = type.getMemberType(*m_memberName);
if (!m_type) if (!m_type)
BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found in " + type.toString())); BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found or not "
//@todo later, this will not always be STORAGE "visible in " + type.toString()));
m_lvalue = type.getCategory() == Type::Category::STRUCT ? LValueType::STORAGE : LValueType::NONE; m_isLValue = (type.getCategory() == Type::Category::Struct);
} }
void IndexAccess::checkTypeRequirements() void IndexAccess::checkTypeRequirements()
{ {
m_base->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 " + BOOST_THROW_EXCEPTION(m_base->createTypeError("Indexed expression has to be a mapping (is " +
m_base->getType()->toString() + ")")); m_base->getType()->toString() + ")"));
MappingType const& type = dynamic_cast<MappingType const&>(*m_base->getType()); MappingType const& type = dynamic_cast<MappingType const&>(*m_base->getType());
m_index->expectType(*type.getKeyType()); m_index->expectType(*type.getKeyType());
m_type = type.getValueType(); m_type = type.getValueType();
m_lvalue = LValueType::STORAGE; m_isLValue = true;
} }
void Identifier::checkTypeRequirements() void Identifier::checkTypeRequirements()
{ {
solAssert(m_referencedDeclaration, "Identifier not resolved."); solAssert(m_referencedDeclaration, "Identifier not resolved.");
VariableDeclaration const* variable = dynamic_cast<VariableDeclaration const*>(m_referencedDeclaration); m_isLValue = m_referencedDeclaration->isLValue();
if (variable) m_type = m_referencedDeclaration->getType(m_currentContract);
{ if (!m_type)
if (!variable->getType()) BOOST_THROW_EXCEPTION(createTypeError("Declaration referenced before type could be determined."));
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."));
} }
void ElementaryTypeNameExpression::checkTypeRequirements() 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() void Literal::checkTypeRequirements()
{ {
m_type = Type::forLiteral(*this); m_type = Type::forLiteral(*this);
if (!m_type) if (!m_type)
BOOST_THROW_EXCEPTION(createTypeError("Literal value too large.")); BOOST_THROW_EXCEPTION(createTypeError("Invalid literal value."));
} }
} }

391
AST.h
View File

@ -132,68 +132,167 @@ private:
class Declaration: public ASTNode class Declaration: public ASTNode
{ {
public: public:
Declaration(Location const& _location, ASTPointer<ASTString> const& _name): /// Visibility ordered from restricted to unrestricted.
ASTNode(_location), m_name(_name), m_scope(nullptr) {} 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. /// @returns the declared name.
ASTString const& getName() const { return *m_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. /// @returns the scope this declaration resides in. Can be nullptr if it is the global scope.
/// Available only after name and type resolution step. /// Available only after name and type resolution step.
Declaration const* getScope() const { return m_scope; } Declaration const* getScope() const { return m_scope; }
void setScope(Declaration const* _scope) { m_scope = _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: private:
ASTPointer<ASTString> m_name; ASTPointer<ASTString> m_name;
Visibility m_visibility;
Declaration const* m_scope; 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 * 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 * document order. It first visits all struct declarations, then all variable declarations and
* finally all function declarations. * finally all function declarations.
*/ */
class ContractDefinition: public Declaration class ContractDefinition: public Declaration, public Documented
{ {
public: public:
ContractDefinition(Location const& _location, ContractDefinition(Location const& _location,
ASTPointer<ASTString> const& _name, ASTPointer<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation, ASTPointer<ASTString> const& _documentation,
std::vector<ASTPointer<InheritanceSpecifier>> const& _baseContracts,
std::vector<ASTPointer<StructDefinition>> const& _definedStructs, std::vector<ASTPointer<StructDefinition>> const& _definedStructs,
std::vector<ASTPointer<EnumDefinition>> const& _definedEnums,
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables, std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables,
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions): std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions,
Declaration(_location, _name), std::vector<ASTPointer<ModifierDefinition>> const& _functionModifiers,
std::vector<ASTPointer<EventDefinition>> const& _events):
Declaration(_location, _name), Documented(_documentation),
m_baseContracts(_baseContracts),
m_definedStructs(_definedStructs), m_definedStructs(_definedStructs),
m_definedEnums(_definedEnums),
m_stateVariables(_stateVariables), m_stateVariables(_stateVariables),
m_definedFunctions(_definedFunctions), m_definedFunctions(_definedFunctions),
m_documentation(_documentation) m_functionModifiers(_functionModifiers),
m_events(_events)
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const 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<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<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<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 virtual TypePointer getType(ContractDefinition const* m_currentContract) const override;
/// checkTypeRequirements on all its functions.
/// Checks that there are no illegal overrides, that the constructor does not have a "returns"
/// and calls checkTypeRequirements on all its functions.
void checkTypeRequirements(); void checkTypeRequirements();
/// @return A shared pointer of an ASTString. /// @returns a map of canonical function signatures to FunctionDefinitions
/// Can contain a nullptr in which case indicates absence of documentation /// as intended for use by the ABI.
ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; } std::map<FixedHash<4>, FunctionTypePointer> getInterfaceFunctions() const;
/// Returns the functions that make up the calling interface in the intended order. /// List of all (direct and indirect) base contracts in order from derived to base, including
std::vector<FunctionDefinition const*> getInterfaceFunctions() const; /// 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; FunctionDefinition const* getConstructor() const;
/// Returns the fallback function or nullptr if no fallback function was specified.
FunctionDefinition const* getFallbackFunction() const;
private: 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<StructDefinition>> m_definedStructs;
std::vector<ASTPointer<EnumDefinition>> m_definedEnums;
std::vector<ASTPointer<VariableDeclaration>> m_stateVariables; std::vector<ASTPointer<VariableDeclaration>> m_stateVariables;
std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions; 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 class StructDefinition: public Declaration
@ -208,6 +307,8 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& getMembers() const { return m_members; } 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 /// Checks that the members do not include any recursive structs and have valid types
/// (e.g. no functions). /// (e.g. no functions).
void checkValidityOfMembers() const; void checkValidityOfMembers() const;
@ -219,6 +320,39 @@ private:
std::vector<ASTPointer<VariableDeclaration>> m_members; 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. * Parameter list, used as function parameter list and return list.
* None of the parameters is allowed to contain mappings (not even recursively * None of the parameters is allowed to contain mappings (not even recursively
@ -239,53 +373,55 @@ private:
std::vector<ASTPointer<VariableDeclaration>> m_parameters; std::vector<ASTPointer<VariableDeclaration>> m_parameters;
}; };
class FunctionDefinition: public Declaration class FunctionDefinition: public Declaration, public VariableScope, public Documented
{ {
public: public:
FunctionDefinition(Location const& _location, ASTPointer<ASTString> const& _name, FunctionDefinition(Location const& _location, ASTPointer<ASTString> const& _name,
bool _isPublic, Declaration::Visibility _visibility, bool _isConstructor,
ASTPointer<ASTString> const& _documentation, ASTPointer<ASTString> const& _documentation,
ASTPointer<ParameterList> const& _parameters, ASTPointer<ParameterList> const& _parameters,
bool _isDeclaredConst, bool _isDeclaredConst,
std::vector<ASTPointer<ModifierInvocation>> const& _modifiers,
ASTPointer<ParameterList> const& _returnParameters, ASTPointer<ParameterList> const& _returnParameters,
ASTPointer<Block> const& _body): ASTPointer<Block> const& _body):
Declaration(_location, _name), m_isPublic(_isPublic), Declaration(_location, _name, _visibility), Documented(_documentation),
m_isConstructor(_isConstructor),
m_parameters(_parameters), m_parameters(_parameters),
m_isDeclaredConst(_isDeclaredConst), m_isDeclaredConst(_isDeclaredConst),
m_functionModifiers(_modifiers),
m_returnParameters(_returnParameters), m_returnParameters(_returnParameters),
m_body(_body), m_body(_body)
m_documentation(_documentation)
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const 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; } 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(); } std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); }
ParameterList const& getParameterList() const { return *m_parameters; } ParameterList const& getParameterList() const { return *m_parameters; }
std::vector<ASTPointer<VariableDeclaration>> const& getReturnParameters() const { return m_returnParameters->getParameters(); } std::vector<ASTPointer<VariableDeclaration>> const& getReturnParameters() const { return m_returnParameters->getParameters(); }
ASTPointer<ParameterList> const& getReturnParameterList() const { return m_returnParameters; } ASTPointer<ParameterList> const& getReturnParameterList() const { return m_returnParameters; }
Block const& getBody() const { return *m_body; } 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); } virtual TypePointer getType(ContractDefinition const*) const override;
std::vector<VariableDeclaration const*> const& getLocalVariables() const { return m_localVariables; }
/// Checks that all parameters have allowed types and calls checkTypeRequirements on the body. /// Checks that all parameters have allowed types and calls checkTypeRequirements on the body.
void checkTypeRequirements(); 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: private:
bool m_isPublic; bool m_isConstructor;
ASTPointer<ParameterList> m_parameters; ASTPointer<ParameterList> m_parameters;
bool m_isDeclaredConst; bool m_isDeclaredConst;
std::vector<ASTPointer<ModifierInvocation>> m_functionModifiers;
ASTPointer<ParameterList> m_returnParameters; ASTPointer<ParameterList> m_returnParameters;
ASTPointer<Block> m_body; ASTPointer<Block> m_body;
ASTPointer<ASTString> m_documentation;
std::vector<VariableDeclaration const*> m_localVariables;
}; };
/** /**
@ -296,8 +432,10 @@ class VariableDeclaration: public Declaration
{ {
public: public:
VariableDeclaration(Location const& _location, ASTPointer<TypeName> const& _type, VariableDeclaration(Location const& _location, ASTPointer<TypeName> const& _type,
ASTPointer<ASTString> const& _name): ASTPointer<ASTString> const& _name, Visibility _visibility,
Declaration(_location, _name), m_typeName(_type) {} 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(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const 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 /// 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. /// 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; } 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 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: 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 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 * Pseudo AST node that is used as declaration for "this", "msg", "tx", "block" and the global
* functions when such an identifier is encountered. Will never have a valid location in the source code. * functions when such an identifier is encountered. Will never have a valid location in the source code.
@ -330,7 +559,7 @@ public:
virtual void accept(ASTConstVisitor&) const override { BOOST_THROW_EXCEPTION(InternalCompilerError() virtual void accept(ASTConstVisitor&) const override { BOOST_THROW_EXCEPTION(InternalCompilerError()
<< errinfo_comment("MagicVariableDeclaration used inside real AST.")); } << 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: private:
std::shared_ptr<Type const> m_type; std::shared_ptr<Type const> m_type;
@ -457,6 +686,21 @@ private:
std::vector<ASTPointer<Statement>> m_statements; 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 with an optional "else" part. Note that "else if" is modeled by having a new
* if-statement as the false (else) body. * if-statement as the false (else) body.
@ -573,19 +817,15 @@ public:
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
void setFunctionReturnParameters(ParameterList& _parameters) { m_returnParameters = &_parameters; } void setFunctionReturnParameters(ParameterList const* _parameters) { m_returnParameters = _parameters; }
ParameterList const& getFunctionReturnParameters() const ParameterList const* getFunctionReturnParameters() const { return m_returnParameters; }
{
solAssert(m_returnParameters, "");
return *m_returnParameters;
}
Expression const* getExpression() const { return m_expression.get(); } Expression const* getExpression() const { return m_expression.get(); }
private: private:
ASTPointer<Expression> m_expression; ///< value to return, optional ASTPointer<Expression> m_expression; ///< value to return, optional
/// Pointer to the parameter list of the function, filled by the @ref NameAndTypeResolver. /// 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 class Expression: public ASTNode
{ {
protected:
enum class LValueType { NONE, LOCAL, STORAGE };
public: public:
Expression(Location const& _location): ASTNode(_location), m_lvalue(LValueType::NONE), m_lvalueRequested(false) {} Expression(Location const& _location): ASTNode(_location) {}
virtual void checkTypeRequirements() = 0; virtual void checkTypeRequirements() = 0;
std::shared_ptr<Type const> const& getType() const { return m_type; } std::shared_ptr<Type const> const& getType() const { return m_type; }
bool isLValue() const { return m_lvalue != LValueType::NONE; } bool isLValue() const { return m_isLValue; }
bool isLocalLValue() const { return m_lvalue == LValueType::LOCAL; }
/// Helper function, infer the type via @ref checkTypeRequirements and then check that it /// Helper function, infer the type via @ref checkTypeRequirements and then check that it
/// is implicitly convertible to @a _expectedType. If not, throw exception. /// is implicitly convertible to @a _expectedType. If not, throw exception.
@ -665,11 +901,11 @@ public:
protected: protected:
//! Inferred type of the expression, only filled after a call to checkTypeRequirements(). //! Inferred type of the expression, only filled after a call to checkTypeRequirements().
std::shared_ptr<Type const> m_type; std::shared_ptr<Type const> m_type;
//! If this expression is an lvalue (i.e. something that can be assigned to) and is stored //! If this expression is an lvalue (i.e. something that can be assigned to).
//! locally or in storage. This is set during calls to @a checkTypeRequirements() //! This is set during calls to @a checkTypeRequirements()
LValueType m_lvalue; bool m_isLValue = false;
//! Whether the outer expression requested the address (true) or the value (false) of this expression. //! 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. /// Assignment, can also be a compound assignment.
@ -765,14 +1001,15 @@ class FunctionCall: public Expression
{ {
public: public:
FunctionCall(Location const& _location, ASTPointer<Expression> const& _expression, FunctionCall(Location const& _location, ASTPointer<Expression> const& _expression,
std::vector<ASTPointer<Expression>> const& _arguments): std::vector<ASTPointer<Expression>> const& _arguments, std::vector<ASTPointer<ASTString>> const& _names):
Expression(_location), m_expression(_expression), m_arguments(_arguments) {} Expression(_location), m_expression(_expression), m_arguments(_arguments), m_names(_names) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
Expression const& getExpression() const { return *m_expression; } Expression const& getExpression() const { return *m_expression; }
std::vector<ASTPointer<Expression const>> getArguments() const { return {m_arguments.begin(), m_arguments.end()}; } 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 /// Returns true if this is not an actual function call, but an explicit type conversion
/// or constructor call. /// or constructor call.
@ -781,29 +1018,26 @@ public:
private: private:
ASTPointer<Expression> m_expression; ASTPointer<Expression> m_expression;
std::vector<ASTPointer<Expression>> m_arguments; 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 class NewExpression: public Expression
{ {
public: public:
NewExpression(Location const& _location, ASTPointer<Identifier> const& _contractName, NewExpression(Location const& _location, ASTPointer<Identifier> const& _contractName):
std::vector<ASTPointer<Expression>> const& _arguments): Expression(_location), m_contractName(_contractName) {}
Expression(_location), m_contractName(_contractName), m_arguments(_arguments) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() 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. /// Returns the referenced contract. Can only be called after type checking.
ContractDefinition const* getContract() const { solAssert(m_contract, ""); return m_contract; } ContractDefinition const* getContract() const { solAssert(m_contract, ""); return m_contract; }
private: private:
ASTPointer<Identifier> m_contractName; ASTPointer<Identifier> m_contractName;
std::vector<ASTPointer<Expression>> m_arguments;
ContractDefinition const* m_contract = nullptr; ContractDefinition const* m_contract = nullptr;
}; };
@ -866,21 +1100,30 @@ class Identifier: public PrimaryExpression
{ {
public: public:
Identifier(Location const& _location, ASTPointer<ASTString> const& _name): 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(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
ASTString const& getName() const { return *m_name; } 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; } Declaration const* getReferencedDeclaration() const { return m_referencedDeclaration; }
ContractDefinition const* getCurrentContract() const { return m_currentContract; }
private: private:
ASTPointer<ASTString> m_name; ASTPointer<ASTString> m_name;
/// Declaration the name refers to. /// 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 class Literal: public PrimaryExpression
{ {
public: public:
Literal(Location const& _location, Token::Value _token, ASTPointer<ASTString> const& _value): enum class SubDenomination
PrimaryExpression(_location), m_token(_token), m_value(_value) {} {
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(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
@ -922,12 +1175,16 @@ public:
/// @returns the non-parsed value of the literal /// @returns the non-parsed value of the literal
ASTString const& getValue() const { return *m_value; } ASTString const& getValue() const { return *m_value; }
SubDenomination getSubDenomination() const { return m_subDenomination; }
private: private:
Token::Value m_token; Token::Value m_token;
ASTPointer<ASTString> m_value; ASTPointer<ASTString> m_value;
SubDenomination m_subDenomination;
}; };
/// @} /// @}
} }
} }

View File

@ -38,10 +38,16 @@ class SourceUnit;
class ImportDirective; class ImportDirective;
class Declaration; class Declaration;
class ContractDefinition; class ContractDefinition;
class InheritanceSpecifier;
class StructDefinition; class StructDefinition;
class EnumDefinition;
class EnumValue;
class ParameterList; class ParameterList;
class FunctionDefinition; class FunctionDefinition;
class VariableDeclaration; class VariableDeclaration;
class ModifierDefinition;
class ModifierInvocation;
class EventDefinition;
class MagicVariableDeclaration; class MagicVariableDeclaration;
class TypeName; class TypeName;
class ElementaryTypeName; class ElementaryTypeName;
@ -49,6 +55,7 @@ class UserDefinedTypeName;
class Mapping; class Mapping;
class Statement; class Statement;
class Block; class Block;
class PlaceholderStatement;
class IfStatement; class IfStatement;
class BreakableStatement; class BreakableStatement;
class WhileStatement; class WhileStatement;
@ -71,6 +78,8 @@ class Identifier;
class ElementaryTypeNameExpression; class ElementaryTypeNameExpression;
class Literal; class Literal;
class VariableScope;
// Used as pointers to AST nodes, to be replaced by more clever pointers, e.g. pointers which do // 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 // not do reference counting but point to a special memory area that is completely released
// explicitly. // explicitly.

469
ASTJsonConverter.cpp Normal file
View 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
View 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;
};
}
}

View File

@ -57,6 +57,13 @@ bool ASTPrinter::visit(ContractDefinition const& _node)
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(InheritanceSpecifier const& _node)
{
writeLine("InheritanceSpecifier \"" + _node.getName()->getName() + "\"");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(StructDefinition const& _node) bool ASTPrinter::visit(StructDefinition const& _node)
{ {
writeLine("StructDefinition \"" + _node.getName() + "\""); writeLine("StructDefinition \"" + _node.getName() + "\"");
@ -64,6 +71,18 @@ bool ASTPrinter::visit(StructDefinition const& _node)
return goDeeper(); 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) bool ASTPrinter::visit(ParameterList const& _node)
{ {
writeLine("ParameterList"); writeLine("ParameterList");
@ -87,6 +106,27 @@ bool ASTPrinter::visit(VariableDeclaration const& _node)
return goDeeper(); 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) bool ASTPrinter::visit(TypeName const& _node)
{ {
writeLine("TypeName"); writeLine("TypeName");
@ -129,6 +169,13 @@ bool ASTPrinter::visit(Block const& _node)
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(PlaceholderStatement const& _node)
{
writeLine("PlaceholderStatement");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(IfStatement const& _node) bool ASTPrinter::visit(IfStatement const& _node)
{ {
writeLine("IfStatement"); writeLine("IfStatement");
@ -302,11 +349,26 @@ void ASTPrinter::endVisit(ContractDefinition const&)
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(InheritanceSpecifier const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(StructDefinition const&) void ASTPrinter::endVisit(StructDefinition const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(EnumDefinition const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(EnumValue const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(ParameterList const&) void ASTPrinter::endVisit(ParameterList const&)
{ {
m_indentation--; m_indentation--;
@ -322,6 +384,21 @@ void ASTPrinter::endVisit(VariableDeclaration const&)
m_indentation--; 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&) void ASTPrinter::endVisit(TypeName const&)
{ {
m_indentation--; m_indentation--;
@ -352,6 +429,11 @@ void ASTPrinter::endVisit(Block const&)
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(PlaceholderStatement const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(IfStatement const&) void ASTPrinter::endVisit(IfStatement const&)
{ {
m_indentation--; m_indentation--;

View File

@ -44,16 +44,23 @@ public:
bool visit(ImportDirective const& _node) override; bool visit(ImportDirective const& _node) override;
bool visit(ContractDefinition const& _node) override; bool visit(ContractDefinition const& _node) override;
bool visit(InheritanceSpecifier const& _node) override;
bool visit(StructDefinition 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(ParameterList const& _node) override;
bool visit(FunctionDefinition const& _node) override; bool visit(FunctionDefinition const& _node) override;
bool visit(VariableDeclaration 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(TypeName const& _node) override;
bool visit(ElementaryTypeName const& _node) override; bool visit(ElementaryTypeName const& _node) override;
bool visit(UserDefinedTypeName const& _node) override; bool visit(UserDefinedTypeName const& _node) override;
bool visit(Mapping const& _node) override; bool visit(Mapping const& _node) override;
bool visit(Statement const& _node) override; bool visit(Statement const& _node) override;
bool visit(Block const& _node) override; bool visit(Block const& _node) override;
bool visit(PlaceholderStatement const& _node) override;
bool visit(IfStatement const& _node) override; bool visit(IfStatement const& _node) override;
bool visit(BreakableStatement const& _node) override; bool visit(BreakableStatement const& _node) override;
bool visit(WhileStatement const& _node) override; bool visit(WhileStatement const& _node) override;
@ -78,16 +85,23 @@ public:
void endVisit(ImportDirective const&) override; void endVisit(ImportDirective const&) override;
void endVisit(ContractDefinition const&) override; void endVisit(ContractDefinition const&) override;
void endVisit(InheritanceSpecifier const&) override;
void endVisit(StructDefinition const&) override; void endVisit(StructDefinition const&) override;
void endVisit(EnumDefinition const&) override;
void endVisit(EnumValue const&) override;
void endVisit(ParameterList const&) override; void endVisit(ParameterList const&) override;
void endVisit(FunctionDefinition const&) override; void endVisit(FunctionDefinition const&) override;
void endVisit(VariableDeclaration 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(TypeName const&) override;
void endVisit(ElementaryTypeName const&) override; void endVisit(ElementaryTypeName const&) override;
void endVisit(UserDefinedTypeName const&) override; void endVisit(UserDefinedTypeName const&) override;
void endVisit(Mapping const&) override; void endVisit(Mapping const&) override;
void endVisit(Statement const&) override; void endVisit(Statement const&) override;
void endVisit(Block const&) override; void endVisit(Block const&) override;
void endVisit(PlaceholderStatement const&) override;
void endVisit(IfStatement const&) override; void endVisit(IfStatement const&) override;
void endVisit(BreakableStatement const&) override; void endVisit(BreakableStatement const&) override;
void endVisit(WhileStatement const&) override; void endVisit(WhileStatement const&) override;

View File

@ -45,16 +45,23 @@ public:
virtual bool visit(SourceUnit&) { return true; } virtual bool visit(SourceUnit&) { return true; }
virtual bool visit(ImportDirective&) { return true; } virtual bool visit(ImportDirective&) { return true; }
virtual bool visit(ContractDefinition&) { return true; } virtual bool visit(ContractDefinition&) { return true; }
virtual bool visit(InheritanceSpecifier&) { return true; }
virtual bool visit(StructDefinition&) { 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(ParameterList&) { return true; }
virtual bool visit(FunctionDefinition&) { return true; } virtual bool visit(FunctionDefinition&) { return true; }
virtual bool visit(VariableDeclaration&) { 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(TypeName&) { return true; }
virtual bool visit(ElementaryTypeName&) { return true; } virtual bool visit(ElementaryTypeName&) { return true; }
virtual bool visit(UserDefinedTypeName&) { return true; } virtual bool visit(UserDefinedTypeName&) { return true; }
virtual bool visit(Mapping&) { return true; } virtual bool visit(Mapping&) { return true; }
virtual bool visit(Statement&) { return true; } virtual bool visit(Statement&) { return true; }
virtual bool visit(Block&) { return true; } virtual bool visit(Block&) { return true; }
virtual bool visit(PlaceholderStatement&) { return true; }
virtual bool visit(IfStatement&) { return true; } virtual bool visit(IfStatement&) { return true; }
virtual bool visit(BreakableStatement&) { return true; } virtual bool visit(BreakableStatement&) { return true; }
virtual bool visit(WhileStatement&) { return true; } virtual bool visit(WhileStatement&) { return true; }
@ -81,16 +88,23 @@ public:
virtual void endVisit(SourceUnit&) { } virtual void endVisit(SourceUnit&) { }
virtual void endVisit(ImportDirective&) { } virtual void endVisit(ImportDirective&) { }
virtual void endVisit(ContractDefinition&) { } virtual void endVisit(ContractDefinition&) { }
virtual void endVisit(InheritanceSpecifier&) { }
virtual void endVisit(StructDefinition&) { } virtual void endVisit(StructDefinition&) { }
virtual void endVisit(EnumDefinition&) { }
virtual void endVisit(EnumValue&) { }
virtual void endVisit(ParameterList&) { } virtual void endVisit(ParameterList&) { }
virtual void endVisit(FunctionDefinition&) { } virtual void endVisit(FunctionDefinition&) { }
virtual void endVisit(VariableDeclaration&) { } virtual void endVisit(VariableDeclaration&) { }
virtual void endVisit(ModifierDefinition&) { }
virtual void endVisit(ModifierInvocation&) { }
virtual void endVisit(EventDefinition&) { }
virtual void endVisit(TypeName&) { } virtual void endVisit(TypeName&) { }
virtual void endVisit(ElementaryTypeName&) { } virtual void endVisit(ElementaryTypeName&) { }
virtual void endVisit(UserDefinedTypeName&) { } virtual void endVisit(UserDefinedTypeName&) { }
virtual void endVisit(Mapping&) { } virtual void endVisit(Mapping&) { }
virtual void endVisit(Statement&) { } virtual void endVisit(Statement&) { }
virtual void endVisit(Block&) { } virtual void endVisit(Block&) { }
virtual void endVisit(PlaceholderStatement&) { }
virtual void endVisit(IfStatement&) { } virtual void endVisit(IfStatement&) { }
virtual void endVisit(BreakableStatement&) { } virtual void endVisit(BreakableStatement&) { }
virtual void endVisit(WhileStatement&) { } virtual void endVisit(WhileStatement&) { }
@ -121,16 +135,23 @@ public:
virtual bool visit(SourceUnit const&) { return true; } virtual bool visit(SourceUnit const&) { return true; }
virtual bool visit(ImportDirective const&) { return true; } virtual bool visit(ImportDirective const&) { return true; }
virtual bool visit(ContractDefinition 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(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(ParameterList const&) { return true; }
virtual bool visit(FunctionDefinition const&) { return true; } virtual bool visit(FunctionDefinition const&) { return true; }
virtual bool visit(VariableDeclaration 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(TypeName const&) { return true; }
virtual bool visit(ElementaryTypeName const&) { return true; } virtual bool visit(ElementaryTypeName const&) { return true; }
virtual bool visit(UserDefinedTypeName const&) { return true; } virtual bool visit(UserDefinedTypeName const&) { return true; }
virtual bool visit(Mapping const&) { return true; } virtual bool visit(Mapping const&) { return true; }
virtual bool visit(Statement const&) { return true; } virtual bool visit(Statement const&) { return true; }
virtual bool visit(Block 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(IfStatement const&) { return true; }
virtual bool visit(BreakableStatement const&) { return true; } virtual bool visit(BreakableStatement const&) { return true; }
virtual bool visit(WhileStatement const&) { return true; } virtual bool visit(WhileStatement const&) { return true; }
@ -157,16 +178,23 @@ public:
virtual void endVisit(SourceUnit const&) { } virtual void endVisit(SourceUnit const&) { }
virtual void endVisit(ImportDirective const&) { } virtual void endVisit(ImportDirective const&) { }
virtual void endVisit(ContractDefinition const&) { } virtual void endVisit(ContractDefinition const&) { }
virtual void endVisit(InheritanceSpecifier const&) { }
virtual void endVisit(StructDefinition const&) { } virtual void endVisit(StructDefinition const&) { }
virtual void endVisit(EnumDefinition const&) { }
virtual void endVisit(EnumValue const&) { }
virtual void endVisit(ParameterList const&) { } virtual void endVisit(ParameterList const&) { }
virtual void endVisit(FunctionDefinition const&) { } virtual void endVisit(FunctionDefinition const&) { }
virtual void endVisit(VariableDeclaration 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(TypeName const&) { }
virtual void endVisit(ElementaryTypeName const&) { } virtual void endVisit(ElementaryTypeName const&) { }
virtual void endVisit(UserDefinedTypeName const&) { } virtual void endVisit(UserDefinedTypeName const&) { }
virtual void endVisit(Mapping const&) { } virtual void endVisit(Mapping const&) { }
virtual void endVisit(Statement const&) { } virtual void endVisit(Statement const&) { }
virtual void endVisit(Block const&) { } virtual void endVisit(Block const&) { }
virtual void endVisit(PlaceholderStatement const&) { }
virtual void endVisit(IfStatement const&) { } virtual void endVisit(IfStatement const&) { }
virtual void endVisit(BreakableStatement const&) { } virtual void endVisit(BreakableStatement const&) { }
virtual void endVisit(WhileStatement const&) { } virtual void endVisit(WhileStatement const&) { }

View File

@ -61,8 +61,12 @@ void ContractDefinition::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{ {
listAccept(m_baseContracts, _visitor);
listAccept(m_definedStructs, _visitor); listAccept(m_definedStructs, _visitor);
listAccept(m_definedEnums, _visitor);
listAccept(m_stateVariables, _visitor); listAccept(m_stateVariables, _visitor);
listAccept(m_events, _visitor);
listAccept(m_functionModifiers, _visitor);
listAccept(m_definedFunctions, _visitor); listAccept(m_definedFunctions, _visitor);
} }
_visitor.endVisit(*this); _visitor.endVisit(*this);
@ -72,13 +76,63 @@ void ContractDefinition::accept(ASTConstVisitor& _visitor) const
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{ {
listAccept(m_baseContracts, _visitor);
listAccept(m_definedStructs, _visitor); listAccept(m_definedStructs, _visitor);
listAccept(m_definedEnums, _visitor);
listAccept(m_stateVariables, _visitor); listAccept(m_stateVariables, _visitor);
listAccept(m_events, _visitor);
listAccept(m_functionModifiers, _visitor);
listAccept(m_definedFunctions, _visitor); listAccept(m_definedFunctions, _visitor);
} }
_visitor.endVisit(*this); _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) void StructDefinition::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
@ -120,6 +174,7 @@ void FunctionDefinition::accept(ASTVisitor& _visitor)
m_parameters->accept(_visitor); m_parameters->accept(_visitor);
if (m_returnParameters) if (m_returnParameters)
m_returnParameters->accept(_visitor); m_returnParameters->accept(_visitor);
listAccept(m_functionModifiers, _visitor);
m_body->accept(_visitor); m_body->accept(_visitor);
} }
_visitor.endVisit(*this); _visitor.endVisit(*this);
@ -132,6 +187,7 @@ void FunctionDefinition::accept(ASTConstVisitor& _visitor) const
m_parameters->accept(_visitor); m_parameters->accept(_visitor);
if (m_returnParameters) if (m_returnParameters)
m_returnParameters->accept(_visitor); m_returnParameters->accept(_visitor);
listAccept(m_functionModifiers, _visitor);
m_body->accept(_visitor); m_body->accept(_visitor);
} }
_visitor.endVisit(*this); _visitor.endVisit(*this);
@ -153,6 +209,60 @@ void VariableDeclaration::accept(ASTConstVisitor& _visitor) const
_visitor.endVisit(*this); _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) void TypeName::accept(ASTVisitor& _visitor)
{ {
_visitor.visit(*this); _visitor.visit(*this);
@ -223,6 +333,18 @@ void Block::accept(ASTConstVisitor& _visitor) const
_visitor.endVisit(*this); _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) void IfStatement::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
@ -452,20 +574,14 @@ void FunctionCall::accept(ASTConstVisitor& _visitor) const
void NewExpression::accept(ASTVisitor& _visitor) void NewExpression::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{
m_contractName->accept(_visitor); m_contractName->accept(_visitor);
listAccept(m_arguments, _visitor);
}
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }
void NewExpression::accept(ASTConstVisitor& _visitor) const void NewExpression::accept(ASTConstVisitor& _visitor) const
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{
m_contractName->accept(_visitor); m_contractName->accept(_visitor);
listAccept(m_arguments, _visitor);
}
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }

View File

@ -41,6 +41,8 @@ struct Location
start(_start), end(_end), sourceName(_sourceName) { } start(_start), end(_end), sourceName(_sourceName) { }
Location(): start(-1), end(-1) { } Location(): start(-1), end(-1) { }
bool isEmpty() const { return start == -1 && end == -1; }
int start; int start;
int end; int end;
std::shared_ptr<std::string const> sourceName; std::shared_ptr<std::string const> sourceName;
@ -49,6 +51,8 @@ struct Location
/// Stream output for Location (used e.g. in boost exceptions). /// Stream output for Location (used e.g. in boost exceptions).
inline std::ostream& operator<<(std::ostream& _out, Location const& _location) 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 << ")"; return _out << *_location.sourceName << "[" << _location.start << "," << _location.end << ")";
} }

View File

@ -11,9 +11,9 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB")
aux_source_directory(. SRC_LIST) aux_source_directory(. SRC_LIST)
include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
include_directories(BEFORE ..)
include_directories(${Boost_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIRS})
include_directories(${JSONCPP_INCLUDE_DIRS})
include_directories(..)
set(EXECUTABLE solidity) set(EXECUTABLE solidity)
@ -28,6 +28,7 @@ endif()
target_link_libraries(${EXECUTABLE} ${JSONCPP_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${JSONCPP_LIBRARIES})
target_link_libraries(${EXECUTABLE} evmcore) target_link_libraries(${EXECUTABLE} evmcore)
target_link_libraries(${EXECUTABLE} devcore) target_link_libraries(${EXECUTABLE} devcore)
target_link_libraries(${EXECUTABLE} devcrypto)
install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )

View File

@ -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;
}
}
}

View File

@ -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;
};
}
}

View File

@ -21,72 +21,103 @@
*/ */
#include <algorithm> #include <algorithm>
#include <boost/range/adaptor/reversed.hpp>
#include <libevmcore/Instruction.h> #include <libevmcore/Instruction.h>
#include <libevmcore/Assembly.h> #include <libevmcore/Assembly.h>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <libsolidity/Compiler.h> #include <libsolidity/Compiler.h>
#include <libsolidity/ExpressionCompiler.h> #include <libsolidity/ExpressionCompiler.h>
#include <libsolidity/CompilerUtils.h> #include <libsolidity/CompilerUtils.h>
#include <libsolidity/CallGraph.h>
using namespace std; using namespace std;
namespace dev { namespace dev {
namespace solidity { 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) map<ContractDefinition const*, bytes const*> const& _contracts)
{ {
m_context = CompilerContext(); // clear it just in case m_context = CompilerContext(); // clear it just in case
initializeContext(_contract, _magicGlobals, _contracts); initializeContext(_contract, _contracts);
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
if (function->getName() != _contract.getName()) // don't add the constructor here
m_context.addFunction(*function);
appendFunctionSelector(_contract); appendFunctionSelector(_contract);
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions()) set<Declaration const*> functions = m_context.getFunctionsWithoutCode();
if (function->getName() != _contract.getName()) // don't add the constructor here while (!functions.empty())
{
for (Declaration const* function: functions)
function->accept(*this); function->accept(*this);
functions = m_context.getFunctionsWithoutCode();
}
// Swap the runtime context with the creation-time context // Swap the runtime context with the creation-time context
CompilerContext runtimeContext; swap(m_context, m_runtimeContext);
swap(m_context, runtimeContext); initializeContext(_contract, _contracts);
initializeContext(_contract, _magicGlobals, _contracts); packIntoContractCreator(_contract, m_runtimeContext);
packIntoContractCreator(_contract, 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) map<ContractDefinition const*, bytes const*> const& _contracts)
{ {
m_context.setCompiledContracts(_contracts); m_context.setCompiledContracts(_contracts);
for (MagicVariableDeclaration const* variable: _magicGlobals) m_context.setInheritanceHierarchy(_contract.getLinearizedBaseContracts());
m_context.addMagicGlobal(*variable);
registerStateVariables(_contract); registerStateVariables(_contract);
} }
void Compiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext) void Compiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext)
{ {
set<FunctionDefinition const*> neededFunctions; // arguments for base constructors, filled in derived-to-base order
FunctionDefinition const* constructor = _contract.getConstructor(); map<ContractDefinition const*, vector<ASTPointer<Expression>> const*> baseArguments;
if (constructor)
neededFunctions = getFunctionsNeededByConstructor(*constructor);
for (FunctionDefinition const* fun: neededFunctions) // Determine the arguments that are used for the base constructors.
m_context.addFunction(*fun); 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) // Call constructors in base-to-derived order.
appendConstructorCall(*constructor); // 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()); eth::AssemblyItem sub = m_context.addSubroutine(_runtimeContext.getAssembly());
// stack contains sub size // stack contains sub size
m_context << eth::Instruction::DUP1 << sub << u256(0) << eth::Instruction::CODECOPY; m_context << eth::Instruction::DUP1 << sub << u256(0) << eth::Instruction::CODECOPY;
m_context << u256(0) << eth::Instruction::RETURN; m_context << u256(0) << eth::Instruction::RETURN;
// note that we have to explicitly include all used functions because of absolute jump // note that we have to include the functions again because of absolute jump labels
// labels set<Declaration const*> functions = m_context.getFunctionsWithoutCode();
for (FunctionDefinition const* fun: neededFunctions) while (!functions.empty())
fun->accept(*this); {
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) 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 // copy constructor arguments from code to memory and then to stack, they are supplied after the actual program
unsigned argumentSize = 0; unsigned argumentSize = 0;
for (ASTPointer<VariableDeclaration> const& var: _constructor.getParameters()) for (ASTPointer<VariableDeclaration> const& var: _constructor.getParameters())
argumentSize += var->getType()->getCalldataEncodedSize(); argumentSize += CompilerUtils::getPaddedSize(var->getType()->getCalldataEncodedSize());
if (argumentSize > 0) if (argumentSize > 0)
{ {
m_context << u256(argumentSize); m_context << u256(argumentSize);
m_context.appendProgramSize(); 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; m_context << eth::Instruction::CODECOPY;
appendCalldataUnpacker(_constructor, true); appendCalldataUnpacker(FunctionType(_constructor).getParameterTypes(), true);
} }
m_context.appendJumpTo(m_context.getFunctionEntryLabel(_constructor)); m_context.appendJumpTo(m_context.getFunctionEntryLabel(_constructor));
m_context << returnTag; 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) void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
{ {
vector<FunctionDefinition const*> interfaceFunctions = _contract.getInterfaceFunctions(); map<FixedHash<4>, FunctionTypePointer> interfaceFunctions = _contract.getInterfaceFunctions();
vector<eth::AssemblyItem> callDataUnpackerEntryPoints; map<FixedHash<4>, const eth::AssemblyItem> callDataUnpackerEntryPoints;
if (interfaceFunctions.size() > 255) // retrieve the function signature hash from the calldata
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("More than 255 public functions for contract.")); 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 // stack now is: 1 0 <funhash>
// @todo This code had a jump table in a previous version which was more efficient but also for (auto const& it: interfaceFunctions)
// 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)
{ {
callDataUnpackerEntryPoints.push_back(m_context.newTag()); callDataUnpackerEntryPoints.insert(std::make_pair(it.first, m_context.newTag()));
m_context << eth::dupInstruction(2) << eth::dupInstruction(2) << eth::Instruction::EQ; m_context << eth::dupInstruction(1) << u256(FixedHash<4>::Arith(it.first)) << eth::Instruction::EQ;
m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.back()); m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.at(it.first));
if (funid < interfaceFunctions.size() - 1)
m_context << eth::dupInstruction(4) << eth::Instruction::ADD;
} }
m_context << eth::Instruction::STOP; // function not found if (FunctionDefinition const* fallback = _contract.getFallbackFunction())
for (unsigned funid = 0; funid < interfaceFunctions.size(); ++funid)
{ {
FunctionDefinition const& function = *interfaceFunctions[funid];
m_context << callDataUnpackerEntryPoints[funid];
eth::AssemblyItem returnTag = m_context.pushNewTag(); eth::AssemblyItem returnTag = m_context.pushNewTag();
appendCalldataUnpacker(function); fallback->accept(*this);
m_context.appendJumpTo(m_context.getFunctionEntryLabel(function));
m_context << returnTag; 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. // We do not check the calldata size, everything is zero-padded.
unsigned dataOffset = 1; unsigned offset(CompilerUtils::dataStartOffset);
//@todo this can be done more efficiently, saving some CALLDATALOAD calls bool const c_padToWords = true;
for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
{ unsigned dynamicParameterCount = 0;
unsigned const numBytes = var->getType()->getCalldataEncodedSize(); for (TypePointer const& type: _typeParameters)
if (numBytes > 32) if (type->isDynamicallySized())
BOOST_THROW_EXCEPTION(CompilerError() dynamicParameterCount++;
<< errinfo_sourceLocation(var->getLocation()) offset += dynamicParameterCount * 32;
<< errinfo_comment("Type " + var->getType()->toString() + " not yet supported.")); unsigned currentDynamicParameter = 0;
bool leftAligned = var->getType()->getCategory() == Type::Category::STRING; for (TypePointer const& type: _typeParameters)
CompilerUtils(m_context).loadFromMemory(dataOffset, numBytes, leftAligned, !_fromMemory); if (type->isDynamicallySized())
dataOffset += numBytes; {
} // value on stack: [calldata_offset] (only if we are already in dynamic mode)
return dataOffset; 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 //@todo this can be also done more efficiently
unsigned dataOffset = 0; unsigned dataOffset = 0;
vector<ASTPointer<VariableDeclaration>> const& parameters = _function.getReturnParameters(); unsigned stackDepth = 0;
unsigned stackDepth = CompilerUtils(m_context).getSizeOnStack(parameters); for (TypePointer const& type: _typeParameters)
for (unsigned i = 0; i < parameters.size(); ++i) stackDepth += type->getSizeOnStack();
for (TypePointer const& type: _typeParameters)
{ {
Type const& paramType = *parameters[i]->getType(); CompilerUtils(m_context).copyToStackTop(stackDepth, *type);
unsigned numBytes = paramType.getCalldataEncodedSize(); ExpressionCompiler::appendTypeConversion(m_context, *type, *type, true);
if (numBytes > 32) bool const c_padToWords = true;
BOOST_THROW_EXCEPTION(CompilerError() dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, *type, c_padToWords);
<< errinfo_sourceLocation(parameters[i]->getLocation()) stackDepth -= type->getSizeOnStack();
<< 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;
} }
// note that the stack is not cleaned up here // note that the stack is not cleaned up here
m_context << u256(dataOffset) << u256(0) << eth::Instruction::RETURN; 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) void Compiler::registerStateVariables(ContractDefinition const& _contract)
{ {
//@todo sort them? for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.getLinearizedBaseContracts()))
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables()) for (ASTPointer<VariableDeclaration> const& variable: contract->getStateVariables())
m_context.addStateVariable(*variable); 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) bool Compiler::visit(FunctionDefinition const& _function)
@ -211,24 +267,30 @@ bool Compiler::visit(FunctionDefinition const& _function)
// caller puts: [retarg0] ... [retargm] [return address] [arg0] ... [argn] // caller puts: [retarg0] ... [retargm] [return address] [arg0] ... [argn]
// although note that this reduces the size of the visible stack // although note that this reduces the size of the visible stack
m_context.startNewFunction(); m_context.startFunction(_function);
m_returnTag = m_context.newTag(); m_returnTag = m_context.newTag();
m_breakTags.clear(); m_breakTags.clear();
m_continueTags.clear(); m_continueTags.clear();
m_stackCleanupForReturn = 0;
m_context << m_context.getFunctionEntryLabel(_function); m_currentFunction = &_function;
m_modifierDepth = 0;
// stack upon entry: [return address] [arg0] [arg1] ... [argn] // stack upon entry: [return address] [arg0] [arg1] ... [argn]
// reserve additional slots: [retarg0] ... [retargm] [localvar0] ... [localvarp] // 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()) 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()) for (ASTPointer<VariableDeclaration const> const& variable: _function.getReturnParameters())
m_context.addAndInitializeVariable(*variable); m_context.addAndInitializeVariable(*variable);
for (VariableDeclaration const* localVariable: _function.getLocalVariables()) for (VariableDeclaration const* localVariable: _function.getLocalVariables())
m_context.addAndInitializeVariable(*localVariable); m_context.addAndInitializeVariable(*localVariable);
_function.getBody().accept(*this); appendModifierOrFunctionCode();
m_context << m_returnTag; 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 // Note that the fact that the return arguments are of increasing index is vital for this
// algorithm to work. // algorithm to work.
unsigned const argumentsSize = CompilerUtils::getSizeOnStack(_function.getParameters()); unsigned const c_argumentsSize = CompilerUtils::getSizeOnStack(_function.getParameters());
unsigned const returnValuesSize = CompilerUtils::getSizeOnStack(_function.getReturnParameters()); unsigned const c_returnValuesSize = CompilerUtils::getSizeOnStack(_function.getReturnParameters());
unsigned const localVariablesSize = CompilerUtils::getSizeOnStack(_function.getLocalVariables()); unsigned const c_localVariablesSize = CompilerUtils::getSizeOnStack(_function.getLocalVariables());
vector<int> stackLayout; vector<int> stackLayout;
stackLayout.push_back(returnValuesSize); // target of return address stackLayout.push_back(c_returnValuesSize); // target of return address
stackLayout += vector<int>(argumentsSize, -1); // discard all arguments stackLayout += vector<int>(c_argumentsSize, -1); // discard all arguments
for (unsigned i = 0; i < returnValuesSize; ++i) for (unsigned i = 0; i < c_returnValuesSize; ++i)
stackLayout.push_back(i); stackLayout.push_back(i);
stackLayout += vector<int>(localVariablesSize, -1); stackLayout += vector<int>(c_localVariablesSize, -1);
while (stackLayout.back() != int(stackLayout.size() - 1)) while (stackLayout.back() != int(stackLayout.size() - 1))
if (stackLayout.back() < 0) 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 //@todo modifications are needed to make this work with functions returning multiple values
if (Expression const* expression = _return.getExpression()) if (Expression const* expression = _return.getExpression())
{ {
compileExpression(*expression); solAssert(_return.getFunctionReturnParameters(), "Invalid return parameters pointer.");
VariableDeclaration const& firstVariable = *_return.getFunctionReturnParameters().getParameters().front(); VariableDeclaration const& firstVariable = *_return.getFunctionReturnParameters()->getParameters().front();
ExpressionCompiler::appendTypeConversion(m_context, *expression->getType(), *firstVariable.getType()); compileExpression(*expression, firstVariable.getType());
CompilerUtils(m_context).moveToStackVariable(firstVariable); 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.appendJumpTo(m_returnTag);
m_context.adjustStackOffset(m_stackCleanupForReturn);
return false; return false;
} }
@ -369,10 +433,7 @@ bool Compiler::visit(VariableDefinition const& _variableDefinition)
{ {
if (Expression const* expression = _variableDefinition.getExpression()) if (Expression const* expression = _variableDefinition.getExpression())
{ {
compileExpression(*expression); compileExpression(*expression, _variableDefinition.getDeclaration().getType());
ExpressionCompiler::appendTypeConversion(m_context,
*expression->getType(),
*_variableDefinition.getDeclaration().getType());
CompilerUtils(m_context).moveToStackVariable(_variableDefinition.getDeclaration()); CompilerUtils(m_context).moveToStackVariable(_variableDefinition.getDeclaration());
} }
return false; return false;
@ -386,9 +447,51 @@ bool Compiler::visit(ExpressionStatement const& _expressionStatement)
return false; 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); ExpressionCompiler::compileExpression(m_context, _expression, m_optimize);
if (_targetType)
ExpressionCompiler::appendTypeConversion(m_context, *_expression.getType(), *_targetType);
} }
} }

View File

@ -20,7 +20,10 @@
* Solidity AST to EVM bytecode compiler. * Solidity AST to EVM bytecode compiler.
*/ */
#pragma once
#include <ostream> #include <ostream>
#include <functional>
#include <libsolidity/ASTVisitor.h> #include <libsolidity/ASTVisitor.h>
#include <libsolidity/CompilerContext.h> #include <libsolidity/CompilerContext.h>
@ -30,32 +33,34 @@ namespace solidity {
class Compiler: private ASTConstVisitor class Compiler: private ASTConstVisitor
{ {
public: 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); std::map<ContractDefinition const*, bytes const*> const& _contracts);
bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); } 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); } void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); }
private: private:
/// Registers the global objects and the non-function objects inside the contract with the context. /// Registers the non-function objects inside the contract with the context.
void initializeContext(ContractDefinition const& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals, void initializeContext(ContractDefinition const& _contract,
std::map<ContractDefinition const*, bytes const*> const& _contracts); 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 /// Adds the code that is run at creation time. Should be run after exchanging the run-time context
/// with a new and initialized context. /// with a new and initialized context. Adds the constructor code.
/// adds the constructor code.
void packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext); void packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext);
void appendBaseConstructorCall(FunctionDefinition const& _constructor,
std::vector<ASTPointer<Expression>> const& _arguments);
void appendConstructorCall(FunctionDefinition const& _constructor); void 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); void appendFunctionSelector(ContractDefinition const& _contract);
/// Creates code that unpacks the arguments for the given function, from memory if /// Creates code that unpacks the arguments for the given function represented by a vector of TypePointers.
/// @a _fromMemory is true, otherwise from call data. @returns the size of the data in bytes. /// From memory if @a _fromMemory is true, otherwise from call data.
unsigned appendCalldataUnpacker(FunctionDefinition const& _function, bool _fromMemory = false); void appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory = false);
void appendReturnValuePacker(FunctionDefinition const& _function); void appendReturnValuePacker(TypePointers const& _typeParameters);
void registerStateVariables(ContractDefinition const& _contract); void registerStateVariables(ContractDefinition const& _contract);
virtual bool visit(VariableDeclaration const& _variableDeclaration) override;
virtual bool visit(FunctionDefinition const& _function) override; virtual bool visit(FunctionDefinition const& _function) override;
virtual bool visit(IfStatement const& _ifStatement) override; virtual bool visit(IfStatement const& _ifStatement) override;
virtual bool visit(WhileStatement const& _whileStatement) override; virtual bool visit(WhileStatement const& _whileStatement) override;
@ -65,14 +70,23 @@ private:
virtual bool visit(Return const& _return) override; virtual bool visit(Return const& _return) override;
virtual bool visit(VariableDefinition const& _variableDefinition) override; virtual bool visit(VariableDefinition const& _variableDefinition) override;
virtual bool visit(ExpressionStatement const& _expressionStatement) 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; bool const m_optimize;
CompilerContext m_context; 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_breakTags; ///< tag to jump to for a "break" statement
std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" 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 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
}; };
} }

View File

@ -43,25 +43,28 @@ void CompilerContext::addStateVariable(VariableDeclaration const& _declaration)
m_stateVariablesSize += _declaration.getType()->getStorageSize(); m_stateVariablesSize += _declaration.getType()->getStorageSize();
} }
void CompilerContext::addVariable(VariableDeclaration const& _declaration) void CompilerContext::startFunction(Declaration const& _function)
{ {
m_localVariables[&_declaration] = m_localVariablesSize; m_functionsWithCode.insert(&_function);
m_localVariablesSize += _declaration.getType()->getSizeOnStack(); 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) void CompilerContext::addAndInitializeVariable(VariableDeclaration const& _declaration)
{ {
addVariable(_declaration); addVariable(_declaration);
unsigned const size = _declaration.getType()->getSizeOnStack(); int const size = _declaration.getType()->getSizeOnStack();
for (unsigned i = 0; i < size; ++i) for (int i = 0; i < size; ++i)
*this << u256(0); *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 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 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); auto res = m_functionEntryLabels.find(&_declaration);
solAssert(res != m_functionEntryLabels.end(), "Function entry label not found."); if (res == m_functionEntryLabels.end())
return res->second.tag(); {
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 unsigned CompilerContext::getBaseStackOffsetOfVariable(Declaration const& _declaration) const
{ {
auto res = m_localVariables.find(&_declaration); auto res = m_localVariables.find(&_declaration);
solAssert(res != m_localVariables.end(), "Variable not found on stack."); 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 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 u256 CompilerContext::getStorageLocationOfVariable(const Declaration& _declaration) const

View File

@ -41,27 +41,41 @@ class CompilerContext
public: public:
void addMagicGlobal(MagicVariableDeclaration const& _declaration); void addMagicGlobal(MagicVariableDeclaration const& _declaration);
void addStateVariable(VariableDeclaration const& _declaration); void addStateVariable(VariableDeclaration const& _declaration);
void startNewFunction() { m_localVariables.clear(); m_asm.setDeposit(0); } void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0);
void addVariable(VariableDeclaration const& _declaration);
void addAndInitializeVariable(VariableDeclaration const& _declaration); void addAndInitializeVariable(VariableDeclaration const& _declaration);
void addFunction(FunctionDefinition const& _function);
void setCompiledContracts(std::map<ContractDefinition const*, bytes const*> const& _contracts) { m_compiledContracts = _contracts; } void setCompiledContracts(std::map<ContractDefinition const*, bytes const*> const& _contracts) { m_compiledContracts = _contracts; }
bytes const& getCompiledContract(ContractDefinition const& _contract) const; bytes const& getCompiledContract(ContractDefinition const& _contract) const;
void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); } 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 isMagicGlobal(Declaration const* _declaration) const { return m_magicGlobals.count(_declaration) != 0; }
bool isFunctionDefinition(Declaration const* _declaration) const { return m_functionEntryLabels.count(_declaration); }
bool isLocalVariable(Declaration const* _declaration) const; 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; eth::AssemblyItem getFunctionEntryLabel(Declaration const& _declaration);
/// Returns the distance of the given local variable from the top of the local variable stack. 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; unsigned getBaseStackOffsetOfVariable(Declaration const& _declaration) const;
/// If supplied by a value returned by @ref getBaseStackOffsetOfVariable(variable), returns /// If supplied by a value returned by @ref getBaseStackOffsetOfVariable(variable), returns
/// the distance of that variable from the current top of the stack. /// the distance of that variable from the current top of the stack.
unsigned baseToCurrentStackOffset(unsigned _baseOffset) const; 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; u256 getStorageLocationOfVariable(Declaration const& _declaration) const;
/// Appends a JUMPI instruction to a new tag and @returns the tag /// Appends a JUMPI instruction to a new tag and @returns the tag
@ -109,10 +123,12 @@ private:
std::map<Declaration const*, u256> m_stateVariables; std::map<Declaration const*, u256> m_stateVariables;
/// Offsets of local variables on the stack (relative to stack base). /// Offsets of local variables on the stack (relative to stack base).
std::map<Declaration const*, unsigned> m_localVariables; std::map<Declaration const*, unsigned> m_localVariables;
/// Sum of stack sizes of local variables /// Labels pointing to the entry points of functions.
unsigned m_localVariablesSize;
/// Labels pointing to the entry points of funcitons.
std::map<Declaration const*, eth::AssemblyItem> m_functionEntryLabels; 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;
}; };
} }

View File

@ -16,10 +16,12 @@
*/ */
/** /**
* @author Christian <c@ethdev.com> * @author Christian <c@ethdev.com>
* @author Gav Wood <g@ethdev.com>
* @date 2014 * @date 2014
* Full-stack compiler that converts a source code string to bytecode. * Full-stack compiler that converts a source code string to bytecode.
*/ */
#include <boost/algorithm/string.hpp>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <libsolidity/Scanner.h> #include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h> #include <libsolidity/Parser.h>
@ -29,6 +31,8 @@
#include <libsolidity/CompilerStack.h> #include <libsolidity/CompilerStack.h>
#include <libsolidity/InterfaceHandler.h> #include <libsolidity/InterfaceHandler.h>
#include <libdevcrypto/SHA3.h>
using namespace std; using namespace std;
namespace dev namespace dev
@ -36,18 +40,39 @@ namespace dev
namespace solidity 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 CompilerStack::addSource(string const& _name, string const& _content)
{ {
bool existed = m_sources.count(_name); bool existed = m_sources.count(_name) != 0;
reset(true); 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; return existed;
} }
void CompilerStack::setSource(string const& _sourceCode) void CompilerStack::setSource(string const& _sourceCode)
{ {
reset(); reset();
addSource("", _sourceCode); addSource("", expanded(_sourceCode));
} }
void CompilerStack::parse() void CompilerStack::parse()
@ -69,6 +94,7 @@ void CompilerStack::parse()
{ {
m_globalContext->setCurrentContract(*contract); m_globalContext->setCurrentContract(*contract);
resolver.updateDeclaration(*m_globalContext->getCurrentThis()); resolver.updateDeclaration(*m_globalContext->getCurrentThis());
resolver.updateDeclaration(*m_globalContext->getCurrentSuper());
resolver.resolveNamesAndTypes(*contract); resolver.resolveNamesAndTypes(*contract);
m_contracts[contract->getName()].contract = contract; m_contracts[contract->getName()].contract = contract;
} }
@ -100,6 +126,58 @@ vector<string> CompilerStack::getContractNames() const
return contractNames; 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) void CompilerStack::compile(bool _optimize)
{ {
if (!m_parseSuccessful) if (!m_parseSuccessful)
@ -110,12 +188,11 @@ void CompilerStack::compile(bool _optimize)
for (ASTPointer<ASTNode> const& node: source->ast->getNodes()) for (ASTPointer<ASTNode> const& node: source->ast->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{ {
m_globalContext->setCurrentContract(*contract);
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize); shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize);
compiler->compileContract(*contract, m_globalContext->getMagicVariables(), compiler->compileContract(*contract, contractBytecode);
contractBytecode);
Contract& compiledContract = m_contracts[contract->getName()]; Contract& compiledContract = m_contracts[contract->getName()];
compiledContract.bytecode = compiler->getAssembledBytecode(); compiledContract.bytecode = compiler->getAssembledBytecode();
compiledContract.runtimeBytecode = compiler->getRuntimeBytecode();
compiledContract.compiler = move(compiler); compiledContract.compiler = move(compiler);
contractBytecode[compiledContract.contract] = &compiledContract.bytecode; contractBytecode[compiledContract.contract] = &compiledContract.bytecode;
} }
@ -133,6 +210,16 @@ bytes const& CompilerStack::getBytecode(string const& _contractName) const
return getContract(_contractName).bytecode; 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 void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName) const
{ {
getContract(_contractName).compiler->streamAssembly(_outStream); 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 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) if (!m_parseSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); 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; std::unique_ptr<string const>* doc;
switch (_type) switch (_type)
{ {
case DocumentationType::NATSPEC_USER: case DocumentationType::NatspecUser:
doc = &contract.userDocumentation; doc = &contract.userDocumentation;
break; break;
case DocumentationType::NATSPEC_DEV: case DocumentationType::NatspecDev:
doc = &contract.devDocumentation; doc = &contract.devDocumentation;
break; break;
case DocumentationType::ABI_INTERFACE: case DocumentationType::ABIInterface:
doc = &contract.interface; doc = &contract.interface;
break; break;
case DocumentationType::ABISolidityInterface:
doc = &contract.solidityInterface;
break;
default: default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type."));
} }
@ -198,7 +293,11 @@ void CompilerStack::reset(bool _keepSources)
for (auto sourcePair: m_sources) for (auto sourcePair: m_sources)
sourcePair.second.reset(); sourcePair.second.reset();
else else
{
m_sources.clear(); m_sources.clear();
if (m_addStandardSources)
addSources(StandardSources);
}
m_globalContext.reset(); m_globalContext.reset();
m_sourceOrder.clear(); m_sourceOrder.clear();
m_contracts.clear(); m_contracts.clear();
@ -234,16 +333,23 @@ void CompilerStack::resolveImports()
swap(m_sourceOrder, sourceOrder); swap(m_sourceOrder, sourceOrder);
} }
std::string CompilerStack::defaultContractName() const
{
return getContract("").contract->getName();
}
CompilerStack::Contract const& CompilerStack::getContract(string const& _contractName) const CompilerStack::Contract const& CompilerStack::getContract(string const& _contractName) const
{ {
if (m_contracts.empty()) if (m_contracts.empty())
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No compiled contracts found.")); BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No compiled contracts found."));
string contractName = _contractName; string contractName = _contractName;
if (_contractName.empty()) if (_contractName.empty())
// try to find the "last contract" // try to find some user-supplied contract
for (ASTPointer<ASTNode> const& node: m_sourceOrder.back()->ast->getNodes()) for (auto const& it: m_sources)
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get())) if (!StandardSources.count(it.first))
contractName = contract->getName(); 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); auto it = m_contracts.find(contractName);
if (it == m_contracts.end()) if (it == m_contracts.end())
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Contract " + _contractName + " not found.")); BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Contract " + _contractName + " not found."));

View File

@ -16,6 +16,7 @@
*/ */
/** /**
* @author Christian <c@ethdev.com> * @author Christian <c@ethdev.com>
* @author Gav Wood <g@ethdev.com>
* @date 2014 * @date 2014
* Full-stack compiler that converts a source code string to bytecode. * Full-stack compiler that converts a source code string to bytecode.
*/ */
@ -27,6 +28,7 @@
#include <memory> #include <memory>
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/FixedHash.h>
namespace dev { namespace dev {
namespace solidity { namespace solidity {
@ -41,11 +43,14 @@ class InterfaceHandler;
enum class DocumentationType: uint8_t enum class DocumentationType: uint8_t
{ {
NATSPEC_USER = 1, NatspecUser = 1,
NATSPEC_DEV, NatspecDev,
ABI_INTERFACE 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. * 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. * 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 class CompilerStack: boost::noncopyable
{ {
public: 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. /// 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. /// @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); bool addSource(std::string const& _name, std::string const& _content);
void setSource(std::string const& _sourceCode); void setSource(std::string const& _sourceCode);
/// Parses all source units that were added /// Parses all source units that were added
void parse(); 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); void parse(std::string const& _sourceCode);
/// Returns a list of the contract names in the sources. /// Returns a list of the contract names in the sources.
std::vector<std::string> getContractNames() const; std::vector<std::string> getContractNames() const;
std::string defaultContractName() const;
/// Compiles the source units that were previously added and parsed. /// Compiles the source units that were previously added and parsed.
void compile(bool _optimize = false); void compile(bool _optimize = false);
@ -73,7 +81,13 @@ public:
/// @returns the compiled bytecode /// @returns the compiled bytecode
bytes const& compile(std::string const& _sourceCode, bool _optimize = false); 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; 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. /// Streams a verbose version of the assembly to @a _outStream.
/// Prerequisite: Successful compilation. /// Prerequisite: Successful compilation.
void streamAssembly(std::ostream& _outStream, std::string const& _contractName = "") const; void streamAssembly(std::ostream& _outStream, std::string const& _contractName = "") const;
@ -81,11 +95,14 @@ public:
/// Returns a string representing the contract interface in JSON. /// Returns a string representing the contract interface in JSON.
/// Prerequisite: Successful call to parse or compile. /// Prerequisite: Successful call to parse or compile.
std::string const& getInterface(std::string const& _contractName = "") const; 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. /// Returns a string representing the contract's documentation in JSON.
/// Prerequisite: Successful call to parse or compile. /// Prerequisite: Successful call to parse or compile.
/// @param type The type of the documentation to get. /// @param type The type of the documentation to get.
/// Can be one of 3 types defined at @c DocumentationType /// Can be one of 4 types defined at @c DocumentationType
std::string const& getJsonDocumentation(std::string const& _contractName, DocumentationType _type) const; std::string const& getMetadata(std::string const& _contractName, DocumentationType _type) const;
/// @returns the previously used scanner, useful for counting lines during error reporting. /// @returns the previously used scanner, useful for counting lines during error reporting.
Scanner const& getScanner(std::string const& _sourceName = "") const; Scanner const& getScanner(std::string const& _sourceName = "") const;
@ -113,23 +130,30 @@ private:
struct Contract struct Contract
{ {
ContractDefinition const* contract; ContractDefinition const* contract = nullptr;
std::shared_ptr<Compiler> compiler; std::shared_ptr<Compiler> compiler;
bytes bytecode; bytes bytecode;
bytes runtimeBytecode;
std::shared_ptr<InterfaceHandler> interfaceHandler; std::shared_ptr<InterfaceHandler> interfaceHandler;
mutable std::unique_ptr<std::string const> interface; 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> userDocumentation;
mutable std::unique_ptr<std::string const> devDocumentation; mutable std::unique_ptr<std::string const> devDocumentation;
Contract(); 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 reset(bool _keepSources = false);
void resolveImports(); void resolveImports();
Contract const& getContract(std::string const& _contractName = "") const; Contract const& getContract(std::string const& _contractName = "") const;
Source const& getSource(std::string const& _sourceName = "") const; Source const& getSource(std::string const& _sourceName = "") const;
bool m_addStandardSources; ///< If true, standard sources are added.
bool m_parseSuccessful; bool m_parseSuccessful;
std::map<std::string const, Source> m_sources; std::map<std::string const, Source> m_sources;
std::shared_ptr<GlobalContext> m_globalContext; std::shared_ptr<GlobalContext> m_globalContext;

View File

@ -31,42 +31,94 @@ namespace dev
namespace solidity 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) solAssert(_type.getCategory() != Type::Category::ByteArray, "Unable to statically load dynamic type.");
{ m_context << u256(_offset);
m_context << u256(0); return loadFromMemoryHelper(_type, _fromCalldata, _padToWordBoundaries);
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;
}
} }
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; auto const& type = dynamic_cast<ByteArrayType const&>(_type);
return;
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) void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
@ -105,5 +157,220 @@ unsigned CompilerUtils::getSizeOnStack(vector<shared_ptr<Type const>> const& _va
return size; 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;
}
} }
} }

View File

@ -37,15 +37,31 @@ public:
/// Loads data from memory to the stack. /// Loads data from memory to the stack.
/// @param _offset offset in memory (or calldata) /// @param _offset offset in memory (or calldata)
/// @param _bytes number of bytes to load /// @param _type data type to load
/// @param _leftAligned if true, store left aligned on stack (otherwise right aligned)
/// @param _fromCalldata if true, load from calldata, not from memory /// @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. /// Stores data from stack in memory.
/// @param _offset offset in memory /// @param _offset offset in memory
/// @param _bytes number of bytes to store /// @param _type type of the data on the stack
/// @param _leftAligned if true, data is left aligned on stack (otherwise right aligned) /// @param _padToWordBoundaries if true, pad the data to word (32 byte) boundaries
void storeInMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false); /// @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. /// Moves the value that is at the top of the stack to a stack variable.
void moveToStackVariable(VariableDeclaration const& _variable); void moveToStackVariable(VariableDeclaration const& _variable);
@ -58,10 +74,38 @@ public:
static unsigned getSizeOnStack(std::vector<T> const& _variables); static unsigned getSizeOnStack(std::vector<T> const& _variables);
static unsigned getSizeOnStack(std::vector<std::shared_ptr<Type const>> const& _variableTypes); 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: 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; CompilerContext& m_context;
}; };
template <class T> template <class T>
unsigned CompilerUtils::getSizeOnStack(std::vector<T> const& _variables) unsigned CompilerUtils::getSizeOnStack(std::vector<T> const& _variables)
{ {

View File

@ -28,16 +28,25 @@ namespace dev
namespace solidity 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; return false;
m_declarations[_declaration.getName()] = &_declaration;
if (_invisible)
m_invisibleDeclarations.insert(name);
else
m_declarations[name] = &_declaration;
return true; return true;
} }
Declaration const* DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const Declaration const* DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const
{ {
solAssert(!_name.empty(), "Attempt to resolve empty name.");
auto result = m_declarations.find(_name); auto result = m_declarations.find(_name);
if (result != m_declarations.end()) if (result != m_declarations.end())
return result->second; return result->second;

View File

@ -23,6 +23,7 @@
#pragma once #pragma once
#include <map> #include <map>
#include <set>
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
#include <libsolidity/ASTForward.h> #include <libsolidity/ASTForward.h>
@ -42,16 +43,20 @@ public:
explicit DeclarationContainer(Declaration const* _enclosingDeclaration = nullptr, explicit DeclarationContainer(Declaration const* _enclosingDeclaration = nullptr,
DeclarationContainer const* _enclosingContainer = nullptr): DeclarationContainer const* _enclosingContainer = nullptr):
m_enclosingDeclaration(_enclosingDeclaration), m_enclosingContainer(_enclosingContainer) {} m_enclosingDeclaration(_enclosingDeclaration), m_enclosingContainer(_enclosingContainer) {}
/// Registers the declaration in the scope unless its name is already declared. Returns true iff /// Registers the declaration in the scope unless its name is already declared or the name is empty.
/// it was not yet declared. /// @param _invisible if true, registers the declaration, reports name clashes but does not return it in @a resolveName
bool registerDeclaration(Declaration const& _declaration, bool _update = false); /// @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* resolveName(ASTString const& _name, bool _recursive = false) const;
Declaration const* getEnclosingDeclaration() const { return m_enclosingDeclaration; } Declaration const* getEnclosingDeclaration() const { return m_enclosingDeclaration; }
std::map<ASTString, Declaration const*> const& getDeclarations() const { return m_declarations; }
private: private:
Declaration const* m_enclosingDeclaration; Declaration const* m_enclosingDeclaration;
DeclarationContainer const* m_enclosingContainer; DeclarationContainer const* m_enclosingContainer;
std::map<ASTString, Declaration const*> m_declarations; std::map<ASTString, Declaration const*> m_declarations;
std::set<ASTString> m_invisibleDeclarations;
}; };
} }

View File

@ -38,7 +38,7 @@ struct CompilerError: virtual Exception {};
struct InternalCompilerError: virtual Exception {}; struct InternalCompilerError: virtual Exception {};
struct DocstringParsingError: 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

View File

@ -16,13 +16,16 @@
*/ */
/** /**
* @author Christian <c@ethdev.com> * @author Christian <c@ethdev.com>
* @author Gav Wood <g@ethdev.com>
* @date 2014 * @date 2014
* Solidity AST to EVM bytecode compiler for expressions. * Solidity AST to EVM bytecode compiler for expressions.
*/ */
#include <functional> #include <functional>
#include <memory>
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libsolidity/BaseTypes.h>
#include <libsolidity/ASTVisitor.h> #include <libsolidity/ASTVisitor.h>
namespace dev { namespace dev {
@ -36,6 +39,7 @@ namespace solidity {
class CompilerContext; class CompilerContext;
class Type; class Type;
class IntegerType; class IntegerType;
class ByteArrayType;
class StaticStringType; class StaticStringType;
/** /**
@ -50,14 +54,17 @@ public:
static void compileExpression(CompilerContext& _context, Expression const& _expression, bool _optimize = false); 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. /// 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: private:
explicit ExpressionCompiler(CompilerContext& _compilerContext, bool _optimize = false): explicit ExpressionCompiler(CompilerContext& _compilerContext, bool _optimize = false):
m_optimize(_optimize), m_context(_compilerContext), m_currentLValue(m_context) {} m_optimize(_optimize), m_context(_compilerContext), m_currentLValue(m_context) {}
virtual bool visit(Assignment const& _assignment) override; 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(BinaryOperation const& _binaryOperation) override;
virtual bool visit(FunctionCall const& _functionCall) override; virtual bool visit(FunctionCall const& _functionCall) override;
virtual bool visit(NewExpression const& _newExpression) override; virtual bool visit(NewExpression const& _newExpression) override;
@ -85,24 +92,24 @@ private:
//// Appends code that cleans higher-order bits for integer types. //// Appends code that cleans higher-order bits for integer types.
void appendHighBitsCleanup(IntegerType const& _typeOnStack); 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. /// 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, 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. * Helper class to store and retrieve lvalues to and from various locations.
@ -112,46 +119,55 @@ private:
class LValue class LValue
{ {
public: public:
enum LValueType { NONE, STACK, MEMORY, STORAGE }; enum class LValueType { None, Stack, Memory, Storage };
explicit LValue(CompilerContext& _compilerContext): m_context(&_compilerContext) { reset(); } 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. /// Set type according to the declaration and retrieve the reference.
/// @a _expression is the current expression /// @a _expression is the current expression
void fromIdentifier(Identifier const& _identifier, Declaration const& _declaration); 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 isValid() const { return m_type != LValueType::None; }
bool isInOnStack() const { return m_type == STACK; } bool isInOnStack() const { return m_type == LValueType::Stack; }
bool isInMemory() const { return m_type == MEMORY; } bool isInMemory() const { return m_type == LValueType::Memory; }
bool isInStorage() const { return m_type == STORAGE; } bool isInStorage() const { return m_type == LValueType::Storage; }
/// @returns true if this lvalue reference type occupies a slot on the stack. /// @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, /// 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). /// 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. /// @a _location source location of the current expression, used for error reporting.
void retrieveValue(Expression const& _expression, bool _remove = false) const; void retrieveValue(Location const& _location, bool _remove = false) const;
/// Stores a value (from the stack directly beneath the reference, which is assumed to /// Moves a value from the stack to the lvalue. Removes the value if @a _move is true.
/// be on the top of the stack, if any) in the lvalue and removes the reference. /// @a _location is the source location of the expression that caused this operation.
/// Also removes the stored value from the stack if @a _move is /// Stack pre: value [lvalue_ref]
/// true. @a _expression is the current expression, used for error reporting. /// Stack post if !_move: value_of(lvalue_ref)
void storeValue(Expression const& _expression, bool _move = false) const; 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 /// Convenience function to convert the stored reference to a value and reset type to NONE if
/// the reference was not requested by @a _expression. /// the reference was not requested by @a _expression.
void retrieveValueIfLValueNotRequested(Expression const& _expression); void retrieveValueIfLValueNotRequested(Expression const& _expression);
private: 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; 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 /// If m_type is STACK, this is base stack offset (@see
/// CompilerContext::getBaseStackOffsetOfVariable) of a local variable. /// CompilerContext::getBaseStackOffsetOfVariable) of a local variable.
unsigned m_baseStackOffset; unsigned m_baseStackOffset = 0;
/// Size of the value of this lvalue on the stack. /// Size of the value of this lvalue on the stack or the storage.
unsigned m_stackSize; unsigned m_size = 0;
}; };
bool m_optimize; bool m_optimize;

View File

@ -16,6 +16,7 @@
*/ */
/** /**
* @author Christian <c@ethdev.com> * @author Christian <c@ethdev.com>
* @author Gav Wood <g@ethdev.com>
* @date 2014 * @date 2014
* Container of the (implicit and explicit) global objects. * Container of the (implicit and explicit) global objects.
*/ */
@ -33,33 +34,29 @@ namespace solidity
{ {
GlobalContext::GlobalContext(): GlobalContext::GlobalContext():
m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{make_shared<MagicVariableDeclaration>("block", make_shared<MagicType>(MagicType::Kind::BLOCK)), 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>("msg", make_shared<MagicType>(MagicType::Kind::Message)),
make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::TX)), make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::Transaction)),
make_shared<MagicVariableDeclaration>("suicide", make_shared<MagicVariableDeclaration>("suicide",
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(0, make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Location::Suicide)),
IntegerType::Modifier::ADDRESS)}), make_shared<MagicVariableDeclaration>("sha3",
TypePointers(), make_shared<FunctionType>(strings(), strings{"hash"}, FunctionType::Location::SHA3, true)),
FunctionType::Location::SUICIDE)), make_shared<MagicVariableDeclaration>("log0",
make_shared<MagicVariableDeclaration>("sha3", make_shared<FunctionType>(strings{"hash"},strings{}, FunctionType::Location::Log0)),
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}), make_shared<MagicVariableDeclaration>("log1",
TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}), make_shared<FunctionType>(strings{"hash", "hash"},strings{}, FunctionType::Location::Log1)),
FunctionType::Location::SHA3)), make_shared<MagicVariableDeclaration>("log2",
make_shared<MagicVariableDeclaration>("sha256", make_shared<FunctionType>(strings{"hash", "hash", "hash"},strings{}, FunctionType::Location::Log2)),
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}), make_shared<MagicVariableDeclaration>("log3",
TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}), make_shared<FunctionType>(strings{"hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::Log3)),
FunctionType::Location::SHA256)), make_shared<MagicVariableDeclaration>("log4",
make_shared<MagicVariableDeclaration>("ecrecover", make_shared<FunctionType>(strings{"hash", "hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::Log4)),
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH), make_shared<MagicVariableDeclaration>("sha256",
std::make_shared<IntegerType>(8, IntegerType::Modifier::HASH), make_shared<FunctionType>(strings(), strings{"hash"}, FunctionType::Location::SHA256, true)),
std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH), make_shared<MagicVariableDeclaration>("ecrecover",
std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}), make_shared<FunctionType>(strings{"hash", "hash8", "hash", "hash"}, strings{"address"}, FunctionType::Location::ECRecover)),
TypePointers({std::make_shared<IntegerType>(0, IntegerType::Modifier::ADDRESS)}), make_shared<MagicVariableDeclaration>("ripemd160",
FunctionType::Location::ECRECOVER)), make_shared<FunctionType>(strings(), strings{"hash160"}, FunctionType::Location::RIPEMD160, true))})
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))})
{ {
} }
@ -71,7 +68,7 @@ void GlobalContext::setCurrentContract(ContractDefinition const& _contract)
vector<Declaration const*> GlobalContext::getDeclarations() const vector<Declaration const*> GlobalContext::getDeclarations() const
{ {
vector<Declaration const*> declarations; vector<Declaration const*> declarations;
declarations.reserve(m_magicVariables.size() + 1); declarations.reserve(m_magicVariables.size());
for (ASTPointer<Declaration const> const& variable: m_magicVariables) for (ASTPointer<Declaration const> const& variable: m_magicVariables)
declarations.push_back(variable.get()); declarations.push_back(variable.get());
return declarations; 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; if (!m_superPointer[m_currentContract])
declarations.reserve(m_magicVariables.size() + 1); m_superPointer[m_currentContract] = make_shared<MagicVariableDeclaration>(
for (ASTPointer<MagicVariableDeclaration const> const& variable: m_magicVariables) "super", make_shared<ContractType>(*m_currentContract, true));
declarations.push_back(variable.get()); return m_superPointer[m_currentContract].get();
declarations.push_back(getCurrentThis());
return declarations;
} }
} }

View File

@ -48,16 +48,16 @@ public:
GlobalContext(); GlobalContext();
void setCurrentContract(ContractDefinition const& _contract); void setCurrentContract(ContractDefinition const& _contract);
MagicVariableDeclaration const* getCurrentThis() const; 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". /// @returns a vector of all implicit global declarations excluding "this".
std::vector<Declaration const*> getDeclarations() const; std::vector<Declaration const*> getDeclarations() const;
private: private:
std::vector<std::shared_ptr<MagicVariableDeclaration const>> m_magicVariables; 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_thisPointer;
std::map<ContractDefinition const*, std::shared_ptr<MagicVariableDeclaration const>> mutable m_superPointer;
}; };
} }

View File

@ -2,6 +2,7 @@
#include <libsolidity/InterfaceHandler.h> #include <libsolidity/InterfaceHandler.h>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <libsolidity/CompilerStack.h> #include <libsolidity/CompilerStack.h>
using namespace std;
namespace dev namespace dev
{ {
@ -12,7 +13,7 @@ namespace solidity
InterfaceHandler::InterfaceHandler() InterfaceHandler::InterfaceHandler()
{ {
m_lastTag = DocTagType::NONE; m_lastTag = DocTagType::None;
} }
std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefinition const& _contractDef, std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefinition const& _contractDef,
@ -20,12 +21,14 @@ std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefiniti
{ {
switch(_type) switch(_type)
{ {
case DocumentationType::NATSPEC_USER: case DocumentationType::NatspecUser:
return getUserDocumentation(_contractDef); return getUserDocumentation(_contractDef);
case DocumentationType::NATSPEC_DEV: case DocumentationType::NatspecDev:
return getDevDocumentation(_contractDef); return getDevDocumentation(_contractDef);
case DocumentationType::ABI_INTERFACE: case DocumentationType::ABIInterface:
return getABIInterface(_contractDef); return getABIInterface(_contractDef);
case DocumentationType::ABISolidityInterface:
return getABISolidityInterface(_contractDef);
} }
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type")); 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) std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinition const& _contractDef)
{ {
Json::Value methods(Json::arrayValue); Json::Value abi(Json::arrayValue);
for (auto const& it: _contractDef.getInterfaceFunctions())
for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions())
{ {
Json::Value method; auto populateParameters = [](vector<string> const& _paramNames, vector<string> const& _paramTypes)
Json::Value inputs(Json::arrayValue);
Json::Value outputs(Json::arrayValue);
auto populateParameters = [](std::vector<ASTPointer<VariableDeclaration>> const& _vars)
{ {
Json::Value params(Json::arrayValue); 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; Json::Value param;
input["name"] = var->getName(); param["name"] = _paramNames[i];
input["type"] = var->getType()->toString(); param["type"] = _paramTypes[i];
params.append(input); params.append(param);
} }
return params; return params;
}; };
method["name"] = f->getName(); Json::Value method;
method["constant"] = f->isDeclaredConst(); method["type"] = "function";
method["inputs"] = populateParameters(f->getParameters()); method["name"] = it.second->getDeclaration().getName();
method["outputs"] = populateParameters(f->getReturnParameters()); method["constant"] = it.second->isConstant();
methods.append(method); 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) 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 doc;
Json::Value methods(Json::objectValue); Json::Value methods(Json::objectValue);
for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions()) for (auto const& it: _contractDef.getInterfaceFunctions())
{ {
Json::Value user; Json::Value user;
auto strPtr = f->getDocumentation(); auto strPtr = it.second->getDocumentation();
if (strPtr) if (strPtr)
{ {
resetUser(); resetUser();
parseDocString(*strPtr, CommentOwner::FUNCTION); parseDocString(*strPtr, CommentOwner::Function);
if (!m_notice.empty()) if (!m_notice.empty())
{// since @notice is the only user tag if missing function should not appear {// since @notice is the only user tag if missing function should not appear
user["notice"] = Json::Value(m_notice); 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_contractAuthor.clear();
m_title.clear(); m_title.clear();
parseDocString(*contractDoc, CommentOwner::CONTRACT); parseDocString(*contractDoc, CommentOwner::Contract);
if (!m_contractAuthor.empty()) if (!m_contractAuthor.empty())
doc["author"] = m_contractAuthor; doc["author"] = m_contractAuthor;
@ -110,14 +158,14 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
doc["title"] = m_title; doc["title"] = m_title;
} }
for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions()) for (auto const& it: _contractDef.getInterfaceFunctions())
{ {
Json::Value method; Json::Value method;
auto strPtr = f->getDocumentation(); auto strPtr = it.second->getDocumentation();
if (strPtr) if (strPtr)
{ {
resetDev(); resetDev();
parseDocString(*strPtr, CommentOwner::FUNCTION); parseDocString(*strPtr, CommentOwner::Function);
if (!m_dev.empty()) if (!m_dev.empty())
method["details"] = Json::Value(m_dev); method["details"] = Json::Value(m_dev);
@ -136,7 +184,7 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
method["return"] = m_return; method["return"] = m_return;
if (!method.empty()) // add the function, only if we have any documentation to add 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; doc["methods"] = methods;
@ -194,7 +242,7 @@ std::string::const_iterator InterfaceHandler::parseDocTagParam(std::string::cons
auto paramDesc = std::string(currPos, nlPos); auto paramDesc = std::string(currPos, nlPos);
m_params.push_back(std::make_pair(paramName, paramDesc)); m_params.push_back(std::make_pair(paramName, paramDesc));
m_lastTag = DocTagType::PARAM; m_lastTag = DocTagType::Param;
return skipLineOrEOS(nlPos, _end); 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 // 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 // for all cases. Also somehow automate list of acceptable tags for each
// language construct since current way does not scale well. // language construct since current way does not scale well.
if (m_lastTag == DocTagType::NONE || _tag != "") if (m_lastTag == DocTagType::None || _tag != "")
{ {
if (_tag == "dev") 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") 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") 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") else if (_tag == "author")
{ {
if (_owner == CommentOwner::CONTRACT) if (_owner == CommentOwner::Contract)
return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::AUTHOR, false); return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::Author, false);
else if (_owner == CommentOwner::FUNCTION) else if (_owner == CommentOwner::Function)
return parseDocTagLine(_pos, _end, m_author, DocTagType::AUTHOR, false); return parseDocTagLine(_pos, _end, m_author, DocTagType::Author, false);
else else
// LTODO: for now this else makes no sense but later comments will go to more language constructs // 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")); BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag is legal only for contracts"));
} }
else if (_tag == "title") else if (_tag == "title")
{ {
if (_owner == CommentOwner::CONTRACT) if (_owner == CommentOwner::Contract)
return parseDocTagLine(_pos, _end, m_title, DocTagType::TITLE, false); return parseDocTagLine(_pos, _end, m_title, DocTagType::Title, false);
else else
// LTODO: Unknown tag, throw some form of warning and not just an exception // 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")); 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) switch (m_lastTag)
{ {
case DocTagType::DEV: case DocTagType::Dev:
return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV, true); return parseDocTagLine(_pos, _end, m_dev, DocTagType::Dev, true);
case DocTagType::NOTICE: case DocTagType::Notice:
return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE, true); return parseDocTagLine(_pos, _end, m_notice, DocTagType::Notice, true);
case DocTagType::RETURN: case DocTagType::Return:
return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN, true); return parseDocTagLine(_pos, _end, m_return, DocTagType::Return, true);
case DocTagType::AUTHOR: case DocTagType::Author:
if (_owner == CommentOwner::CONTRACT) if (_owner == CommentOwner::Contract)
return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::AUTHOR, true); return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::Author, true);
else if (_owner == CommentOwner::FUNCTION) else if (_owner == CommentOwner::Function)
return parseDocTagLine(_pos, _end, m_author, DocTagType::AUTHOR, true); return parseDocTagLine(_pos, _end, m_author, DocTagType::Author, true);
else else
// LTODO: Unknown tag, throw some form of warning and not just an exception // LTODO: Unknown tag, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag in illegal comment")); BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag in illegal comment"));
case DocTagType::TITLE: case DocTagType::Title:
if (_owner == CommentOwner::CONTRACT) if (_owner == CommentOwner::Contract)
return parseDocTagLine(_pos, _end, m_title, DocTagType::TITLE, true); return parseDocTagLine(_pos, _end, m_title, DocTagType::Title, true);
else else
// LTODO: Unknown tag, throw some form of warning and not just an exception // LTODO: Unknown tag, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag in illegal comment")); BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag in illegal comment"));
case DocTagType::PARAM: case DocTagType::Param:
return appendDocTagParam(_pos, _end); return appendDocTagParam(_pos, _end);
default: default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Internal: Illegal documentation tag type")); 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); 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); 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; currPos = nlPos + 1;
}
} }
} }

View File

@ -28,7 +28,7 @@
#include <string> #include <string>
#include <memory> #include <memory>
#include <jsoncpp/json/json.h> #include <json/json.h>
namespace dev namespace dev
{ {
@ -41,19 +41,19 @@ enum class DocumentationType: uint8_t;
enum class DocTagType: uint8_t enum class DocTagType: uint8_t
{ {
NONE = 0, None = 0,
DEV, Dev,
NOTICE, Notice,
PARAM, Param,
RETURN, Return,
AUTHOR, Author,
TITLE Title
}; };
enum class CommentOwner enum class CommentOwner
{ {
CONTRACT, Contract,
FUNCTION Function
}; };
class InterfaceHandler class InterfaceHandler
@ -74,6 +74,7 @@ public:
/// @return A unique pointer contained string with the json /// @return A unique pointer contained string with the json
/// representation of the contract's ABI Interface /// representation of the contract's ABI Interface
std::unique_ptr<std::string> getABIInterface(ContractDefinition const& _contractDef); 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 /// Get the User documentation of the contract
/// @param _contractDef The contract definition /// @param _contractDef The contract definition
/// @return A unique pointer contained string with the json /// @return A unique pointer contained string with the json

View File

@ -31,7 +31,6 @@ namespace dev
namespace solidity namespace solidity
{ {
NameAndTypeResolver::NameAndTypeResolver(std::vector<Declaration const*> const& _globals) NameAndTypeResolver::NameAndTypeResolver(std::vector<Declaration const*> const& _globals)
{ {
for (Declaration const* declaration: _globals) for (Declaration const* declaration: _globals)
@ -46,18 +45,36 @@ void NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit)
void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) 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]; m_currentScope = &m_scopes[&_contract];
linearizeBaseContracts(_contract);
for (ContractDefinition const* base: _contract.getLinearizedBaseContracts())
importInheritedScope(*base);
for (ASTPointer<StructDefinition> const& structDef: _contract.getDefinedStructs()) 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()) 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()) for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
{ {
m_currentScope = &m_scopes[function.get()]; m_currentScope = &m_scopes[function.get()];
ReferencesResolver referencesResolver(*function, *this, ReferencesResolver referencesResolver(*function, *this, &_contract,
function->getReturnParameterList().get()); function->getReturnParameterList().get());
} }
m_currentScope = &m_scopes[nullptr];
} }
void NameAndTypeResolver::checkTypeRequirements(ContractDefinition& _contract) void NameAndTypeResolver::checkTypeRequirements(ContractDefinition& _contract)
@ -69,7 +86,7 @@ void NameAndTypeResolver::checkTypeRequirements(ContractDefinition& _contract)
void NameAndTypeResolver::updateDeclaration(Declaration const& _declaration) 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."); 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); 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, DeclarationRegistrationHelper::DeclarationRegistrationHelper(map<ASTNode const*, DeclarationContainer>& _scopes,
ASTNode& _astRoot): ASTNode& _astRoot):
m_scopes(_scopes), m_currentScope(nullptr) m_scopes(_scopes), m_currentScope(nullptr)
@ -115,6 +224,23 @@ void DeclarationRegistrationHelper::endVisit(StructDefinition&)
closeCurrentScope(); 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) bool DeclarationRegistrationHelper::visit(FunctionDefinition& _function)
{ {
registerDeclaration(_function, true); registerDeclaration(_function, true);
@ -128,6 +254,19 @@ void DeclarationRegistrationHelper::endVisit(FunctionDefinition&)
closeCurrentScope(); 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) void DeclarationRegistrationHelper::endVisit(VariableDefinition& _variableDefinition)
{ {
// Register the local variables with the function // Register the local variables with the function
@ -142,6 +281,17 @@ bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration)
return true; return true;
} }
bool DeclarationRegistrationHelper::visit(EventDefinition& _event)
{
registerDeclaration(_event, true);
return true;
}
void DeclarationRegistrationHelper::endVisit(EventDefinition&)
{
closeCurrentScope();
}
void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declaration) void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declaration)
{ {
map<ASTNode const*, DeclarationContainer>::iterator iter; map<ASTNode const*, DeclarationContainer>::iterator iter;
@ -159,7 +309,7 @@ void DeclarationRegistrationHelper::closeCurrentScope()
void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope) 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()) BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_declaration.getLocation())
<< errinfo_comment("Identifier already declared.")); << errinfo_comment("Identifier already declared."));
//@todo the exception should also contain the location of the first declaration //@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, ReferencesResolver::ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver,
ParameterList* _returnParameters, bool _allowLazyTypes): ContractDefinition const* _currentContract,
m_resolver(_resolver), m_returnParameters(_returnParameters), m_allowLazyTypes(_allowLazyTypes) ParameterList const* _returnParameters, bool _allowLazyTypes):
m_resolver(_resolver), m_currentContract(_currentContract),
m_returnParameters(_returnParameters), m_allowLazyTypes(_allowLazyTypes)
{ {
_root.accept(*this); _root.accept(*this);
} }
@ -181,7 +333,13 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable)
// or mapping // or mapping
if (_variable.getTypeName()) 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()) if (!_variable.getType())
BOOST_THROW_EXCEPTION(_variable.getTypeName()->createTypeError("Invalid type name")); BOOST_THROW_EXCEPTION(_variable.getTypeName()->createTypeError("Invalid type name"));
} }
@ -192,8 +350,7 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable)
bool ReferencesResolver::visit(Return& _return) bool ReferencesResolver::visit(Return& _return)
{ {
solAssert(m_returnParameters, "Return parameters not set."); _return.setFunctionReturnParameters(m_returnParameters);
_return.setFunctionReturnParameters(*m_returnParameters);
return true; return true;
} }
@ -218,7 +375,7 @@ bool ReferencesResolver::visit(Identifier& _identifier)
if (!declaration) if (!declaration)
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_identifier.getLocation()) BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_identifier.getLocation())
<< errinfo_comment("Undeclared identifier.")); << errinfo_comment("Undeclared identifier."));
_identifier.setReferencedDeclaration(*declaration); _identifier.setReferencedDeclaration(*declaration, m_currentContract);
return false; return false;
} }

View File

@ -23,6 +23,7 @@
#pragma once #pragma once
#include <map> #include <map>
#include <list>
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
#include <libsolidity/DeclarationContainer.h> #include <libsolidity/DeclarationContainer.h>
@ -64,12 +65,23 @@ public:
private: private:
void reset(); 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, /// 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 /// where nullptr denotes the global scope. Note that structs are not scope since they do
/// not contain code. /// not contain code.
std::map<ASTNode const*, DeclarationContainer> m_scopes; 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); DeclarationRegistrationHelper(std::map<ASTNode const*, DeclarationContainer>& _scopes, ASTNode& _astRoot);
private: private:
bool visit(ContractDefinition& _contract); bool visit(ContractDefinition& _contract) override;
void endVisit(ContractDefinition& _contract); void endVisit(ContractDefinition& _contract) override;
bool visit(StructDefinition& _struct); bool visit(StructDefinition& _struct) override;
void endVisit(StructDefinition& _struct); void endVisit(StructDefinition& _struct) override;
bool visit(FunctionDefinition& _function); bool visit(EnumDefinition& _enum) override;
void endVisit(FunctionDefinition& _function); void endVisit(EnumDefinition& _enum) override;
void endVisit(VariableDefinition& _variableDefinition); bool visit(EnumValue& _value) override;
bool visit(VariableDeclaration& _declaration); 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 enterNewSubScope(Declaration const& _declaration);
void closeCurrentScope(); void closeCurrentScope();
@ -97,7 +116,7 @@ private:
std::map<ASTNode const*, DeclarationContainer>& m_scopes; std::map<ASTNode const*, DeclarationContainer>& m_scopes;
Declaration const* m_currentScope; Declaration const* m_currentScope;
FunctionDefinition* m_currentFunction; VariableScope* m_currentFunction;
}; };
/** /**
@ -108,7 +127,9 @@ class ReferencesResolver: private ASTVisitor
{ {
public: public:
ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver, ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver,
ParameterList* _returnParameters, bool _allowLazyTypes = true); ContractDefinition const* _currentContract,
ParameterList const* _returnParameters,
bool _allowLazyTypes = true);
private: private:
virtual void endVisit(VariableDeclaration& _variable) override; virtual void endVisit(VariableDeclaration& _variable) override;
@ -118,7 +139,8 @@ private:
virtual bool visit(Return& _return) override; virtual bool visit(Return& _return) override;
NameAndTypeResolver& m_resolver; NameAndTypeResolver& m_resolver;
ParameterList* m_returnParameters; ContractDefinition const* m_currentContract;
ParameterList const* m_returnParameters;
bool m_allowLazyTypes; bool m_allowLazyTypes;
}; };

View File

@ -69,10 +69,10 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
{ {
switch (m_scanner->getCurrentToken()) switch (m_scanner->getCurrentToken())
{ {
case Token::IMPORT: case Token::Import:
nodes.push_back(parseImportDirective()); nodes.push_back(parseImportDirective());
break; break;
case Token::CONTRACT: case Token::Contract:
nodes.push_back(parseContractDefinition()); nodes.push_back(parseContractDefinition());
break; break;
default: default:
@ -100,119 +100,298 @@ int Parser::getEndPosition() const
ASTPointer<ImportDirective> Parser::parseImportDirective() ASTPointer<ImportDirective> Parser::parseImportDirective()
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
expectToken(Token::IMPORT); expectToken(Token::Import);
if (m_scanner->getCurrentToken() != Token::STRING_LITERAL) if (m_scanner->getCurrentToken() != Token::StringLiteral)
BOOST_THROW_EXCEPTION(createParserError("Expected string literal (URL).")); BOOST_THROW_EXCEPTION(createParserError("Expected string literal (URL)."));
ASTPointer<ASTString> url = getLiteralAndAdvance(); ASTPointer<ASTString> url = getLiteralAndAdvance();
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::SEMICOLON); expectToken(Token::Semicolon);
return nodeFactory.createNode<ImportDirective>(url); return nodeFactory.createNode<ImportDirective>(url);
} }
ASTPointer<ContractDefinition> Parser::parseContractDefinition() ASTPointer<ContractDefinition> Parser::parseContractDefinition()
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docstring; ASTPointer<ASTString> docString;
if (m_scanner->getCurrentCommentLiteral() != "") if (m_scanner->getCurrentCommentLiteral() != "")
docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral()); docString = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
expectToken(Token::CONTRACT); expectToken(Token::Contract);
ASTPointer<ASTString> name = expectIdentifierToken(); ASTPointer<ASTString> name = expectIdentifierToken();
expectToken(Token::LBRACE); vector<ASTPointer<InheritanceSpecifier>> baseContracts;
vector<ASTPointer<StructDefinition>> structs; vector<ASTPointer<StructDefinition>> structs;
vector<ASTPointer<EnumDefinition>> enums;
vector<ASTPointer<VariableDeclaration>> stateVariables; vector<ASTPointer<VariableDeclaration>> stateVariables;
vector<ASTPointer<FunctionDefinition>> functions; 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) while (true)
{ {
Token::Value currentToken = m_scanner->getCurrentToken(); Token::Value currentToken = m_scanner->getCurrentToken();
if (currentToken == Token::RBRACE) if (currentToken == Token::RBrace)
break; break;
else if (currentToken == Token::PUBLIC || currentToken == Token::PRIVATE) else if (currentToken == Token::Function)
{ functions.push_back(parseFunctionDefinition(name.get()));
visibilityIsPublic = (m_scanner->getCurrentToken() == Token::PUBLIC); else if (currentToken == Token::Struct)
m_scanner->next();
expectToken(Token::COLON);
}
else if (currentToken == Token::FUNCTION)
functions.push_back(parseFunctionDefinition(visibilityIsPublic));
else if (currentToken == Token::STRUCT)
structs.push_back(parseStructDefinition()); 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)) Token::isElementaryTypeName(currentToken))
{ {
bool const allowVar = false; VarDeclParserOptions options;
stateVariables.push_back(parseVariableDeclaration(allowVar)); options.isStateVariable = true;
expectToken(Token::SEMICOLON); 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 else
BOOST_THROW_EXCEPTION(createParserError("Function, variable or struct declaration expected.")); BOOST_THROW_EXCEPTION(createParserError("Function, variable, struct or modifier declaration expected."));
} }
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::RBRACE); expectToken(Token::RBrace);
return nodeFactory.createNode<ContractDefinition>(name, docstring, structs, stateVariables, functions); 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); ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docstring; ASTPointer<ASTString> docstring;
if (m_scanner->getCurrentCommentLiteral() != "") if (m_scanner->getCurrentCommentLiteral() != "")
docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral()); docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
expectToken(Token::FUNCTION); expectToken(Token::Function);
ASTPointer<ASTString> name(expectIdentifierToken()); ASTPointer<ASTString> name;
if (m_scanner->getCurrentToken() == Token::LParen)
name = make_shared<ASTString>(); // anonymous function
else
name = expectIdentifierToken();
ASTPointer<ParameterList> parameters(parseParameterList()); ASTPointer<ParameterList> parameters(parseParameterList());
bool isDeclaredConst = false; bool isDeclaredConst = false;
if (m_scanner->getCurrentToken() == Token::CONST) Declaration::Visibility visibility(Declaration::Visibility::Default);
vector<ASTPointer<ModifierInvocation>> modifiers;
while (true)
{ {
isDeclaredConst = true; Token::Value token = m_scanner->getCurrentToken();
m_scanner->next(); 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; ASTPointer<ParameterList> returnParameters;
if (m_scanner->getCurrentToken() == Token::RETURNS) if (m_scanner->getCurrentToken() == Token::Returns)
{ {
bool const permitEmptyParameterList = false; bool const permitEmptyParameterList = false;
m_scanner->next(); m_scanner->next();
returnParameters = parseParameterList(permitEmptyParameterList); returnParameters = parseParameterList(permitEmptyParameterList);
} }
else else
{ returnParameters = createEmptyParameterList();
// create an empty parameter list at a zero-length location
ASTNodeFactory nodeFactory(*this);
nodeFactory.setLocationEmpty();
returnParameters = nodeFactory.createNode<ParameterList>(vector<ASTPointer<VariableDeclaration>>());
}
ASTPointer<Block> block = parseBlock(); ASTPointer<Block> block = parseBlock();
nodeFactory.setEndPositionFromNode(block); nodeFactory.setEndPositionFromNode(block);
return nodeFactory.createNode<FunctionDefinition>(name, _isPublic, docstring, bool const c_isConstructor = (_contractName && *name == *_contractName);
parameters, return nodeFactory.createNode<FunctionDefinition>(name, visibility, c_isConstructor, docstring,
isDeclaredConst, returnParameters, block); parameters, isDeclaredConst, modifiers,
returnParameters, block);
} }
ASTPointer<StructDefinition> Parser::parseStructDefinition() ASTPointer<StructDefinition> Parser::parseStructDefinition()
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
expectToken(Token::STRUCT); expectToken(Token::Struct);
ASTPointer<ASTString> name = expectIdentifierToken(); ASTPointer<ASTString> name = expectIdentifierToken();
vector<ASTPointer<VariableDeclaration>> members; vector<ASTPointer<VariableDeclaration>> members;
expectToken(Token::LBRACE); expectToken(Token::LBrace);
while (m_scanner->getCurrentToken() != Token::RBRACE) while (m_scanner->getCurrentToken() != Token::RBrace)
{ {
bool const allowVar = false; members.push_back(parseVariableDeclaration());
members.push_back(parseVariableDeclaration(allowVar)); expectToken(Token::Semicolon);
expectToken(Token::SEMICOLON);
} }
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::RBRACE); expectToken(Token::RBrace);
return nodeFactory.createNode<StructDefinition>(name, members); return nodeFactory.createNode<StructDefinition>(name, members);
} }
ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(bool _allowVar) ASTPointer<EnumValue> Parser::parseEnumValue()
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
ASTPointer<TypeName> type = parseTypeName(_allowVar);
nodeFactory.markEndPosition(); 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) ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
@ -224,17 +403,17 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
type = ASTNodeFactory(*this).createNode<ElementaryTypeName>(token); type = ASTNodeFactory(*this).createNode<ElementaryTypeName>(token);
m_scanner->next(); m_scanner->next();
} }
else if (token == Token::VAR) else if (token == Token::Var)
{ {
if (!_allowVar) if (!_allowVar)
BOOST_THROW_EXCEPTION(createParserError("Expected explicit type name.")); BOOST_THROW_EXCEPTION(createParserError("Expected explicit type name."));
m_scanner->next(); m_scanner->next();
} }
else if (token == Token::MAPPING) else if (token == Token::Mapping)
{ {
type = parseMapping(); type = parseMapping();
} }
else if (token == Token::IDENTIFIER) else if (token == Token::Identifier)
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
@ -248,34 +427,36 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
ASTPointer<Mapping> Parser::parseMapping() ASTPointer<Mapping> Parser::parseMapping()
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
expectToken(Token::MAPPING); expectToken(Token::Mapping);
expectToken(Token::LPAREN); expectToken(Token::LParen);
if (!Token::isElementaryTypeName(m_scanner->getCurrentToken())) if (!Token::isElementaryTypeName(m_scanner->getCurrentToken()))
BOOST_THROW_EXCEPTION(createParserError("Expected elementary type name for mapping key type")); BOOST_THROW_EXCEPTION(createParserError("Expected elementary type name for mapping key type"));
ASTPointer<ElementaryTypeName> keyType; ASTPointer<ElementaryTypeName> keyType;
keyType = ASTNodeFactory(*this).createNode<ElementaryTypeName>(m_scanner->getCurrentToken()); keyType = ASTNodeFactory(*this).createNode<ElementaryTypeName>(m_scanner->getCurrentToken());
m_scanner->next(); m_scanner->next();
expectToken(Token::ARROW); expectToken(Token::Arrow);
bool const allowVar = false; bool const allowVar = false;
ASTPointer<TypeName> valueType = parseTypeName(allowVar); ASTPointer<TypeName> valueType = parseTypeName(allowVar);
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::RPAREN); expectToken(Token::RParen);
return nodeFactory.createNode<Mapping>(keyType, valueType); return nodeFactory.createNode<Mapping>(keyType, valueType);
} }
ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty) ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty, bool _allowIndexed)
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
vector<ASTPointer<VariableDeclaration>> parameters; vector<ASTPointer<VariableDeclaration>> parameters;
expectToken(Token::LPAREN); VarDeclParserOptions options;
if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN) options.allowIndexed = _allowIndexed;
options.allowEmptyName = true;
expectToken(Token::LParen);
if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RParen)
{ {
bool const allowVar = false; parameters.push_back(parseVariableDeclaration(options));
parameters.push_back(parseVariableDeclaration(allowVar)); while (m_scanner->getCurrentToken() != Token::RParen)
while (m_scanner->getCurrentToken() != Token::RPAREN)
{ {
expectToken(Token::COMMA); expectToken(Token::Comma);
parameters.push_back(parseVariableDeclaration(allowVar)); parameters.push_back(parseVariableDeclaration(options));
} }
} }
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
@ -286,12 +467,12 @@ ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty)
ASTPointer<Block> Parser::parseBlock() ASTPointer<Block> Parser::parseBlock()
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
expectToken(Token::LBRACE); expectToken(Token::LBrace);
vector<ASTPointer<Statement>> statements; vector<ASTPointer<Statement>> statements;
while (m_scanner->getCurrentToken() != Token::RBRACE) while (m_scanner->getCurrentToken() != Token::RBrace)
statements.push_back(parseStatement()); statements.push_back(parseStatement());
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::RBRACE); expectToken(Token::RBrace);
return nodeFactory.createNode<Block>(statements); return nodeFactory.createNode<Block>(statements);
} }
@ -300,52 +481,60 @@ ASTPointer<Statement> Parser::parseStatement()
ASTPointer<Statement> statement; ASTPointer<Statement> statement;
switch (m_scanner->getCurrentToken()) switch (m_scanner->getCurrentToken())
{ {
case Token::IF: case Token::If:
return parseIfStatement(); return parseIfStatement();
case Token::WHILE: case Token::While:
return parseWhileStatement(); return parseWhileStatement();
case Token::FOR: case Token::For:
return parseForStatement(); return parseForStatement();
case Token::LBRACE: case Token::LBrace:
return parseBlock(); return parseBlock();
// starting from here, all statements must be terminated by a semicolon // starting from here, all statements must be terminated by a semicolon
case Token::CONTINUE: case Token::Continue:
statement = ASTNodeFactory(*this).createNode<Continue>(); statement = ASTNodeFactory(*this).createNode<Continue>();
m_scanner->next(); m_scanner->next();
break; break;
case Token::BREAK: case Token::Break:
statement = ASTNodeFactory(*this).createNode<Break>(); statement = ASTNodeFactory(*this).createNode<Break>();
m_scanner->next(); m_scanner->next();
break; break;
case Token::RETURN: case Token::Return:
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
ASTPointer<Expression> expression; ASTPointer<Expression> expression;
if (m_scanner->next() != Token::SEMICOLON) if (m_scanner->next() != Token::Semicolon)
{ {
expression = parseExpression(); expression = parseExpression();
nodeFactory.setEndPositionFromNode(expression); nodeFactory.setEndPositionFromNode(expression);
} }
statement = nodeFactory.createNode<Return>(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: default:
statement = parseVarDefOrExprStmt(); statement = parseVarDefOrExprStmt();
} }
expectToken(Token::SEMICOLON); expectToken(Token::Semicolon);
return statement; return statement;
} }
ASTPointer<IfStatement> Parser::parseIfStatement() ASTPointer<IfStatement> Parser::parseIfStatement()
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
expectToken(Token::IF); expectToken(Token::If);
expectToken(Token::LPAREN); expectToken(Token::LParen);
ASTPointer<Expression> condition = parseExpression(); ASTPointer<Expression> condition = parseExpression();
expectToken(Token::RPAREN); expectToken(Token::RParen);
ASTPointer<Statement> trueBody = parseStatement(); ASTPointer<Statement> trueBody = parseStatement();
ASTPointer<Statement> falseBody; ASTPointer<Statement> falseBody;
if (m_scanner->getCurrentToken() == Token::ELSE) if (m_scanner->getCurrentToken() == Token::Else)
{ {
m_scanner->next(); m_scanner->next();
falseBody = parseStatement(); falseBody = parseStatement();
@ -359,10 +548,10 @@ ASTPointer<IfStatement> Parser::parseIfStatement()
ASTPointer<WhileStatement> Parser::parseWhileStatement() ASTPointer<WhileStatement> Parser::parseWhileStatement()
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
expectToken(Token::WHILE); expectToken(Token::While);
expectToken(Token::LPAREN); expectToken(Token::LParen);
ASTPointer<Expression> condition = parseExpression(); ASTPointer<Expression> condition = parseExpression();
expectToken(Token::RPAREN); expectToken(Token::RParen);
ASTPointer<Statement> body = parseStatement(); ASTPointer<Statement> body = parseStatement();
nodeFactory.setEndPositionFromNode(body); nodeFactory.setEndPositionFromNode(body);
return nodeFactory.createNode<WhileStatement>(condition, body); return nodeFactory.createNode<WhileStatement>(condition, body);
@ -374,21 +563,21 @@ ASTPointer<ForStatement> Parser::parseForStatement()
ASTPointer<Statement> initExpression; ASTPointer<Statement> initExpression;
ASTPointer<Expression> conditionExpression; ASTPointer<Expression> conditionExpression;
ASTPointer<ExpressionStatement> loopExpression; ASTPointer<ExpressionStatement> loopExpression;
expectToken(Token::FOR); expectToken(Token::For);
expectToken(Token::LPAREN); expectToken(Token::LParen);
// LTODO: Maybe here have some predicate like peekExpression() instead of checking for semicolon and RPAREN? // LTODO: Maybe here have some predicate like peekExpression() instead of checking for semicolon and RParen?
if (m_scanner->getCurrentToken() != Token::SEMICOLON) if (m_scanner->getCurrentToken() != Token::Semicolon)
initExpression = parseVarDefOrExprStmt(); initExpression = parseVarDefOrExprStmt();
expectToken(Token::SEMICOLON); expectToken(Token::Semicolon);
if (m_scanner->getCurrentToken() != Token::SEMICOLON) if (m_scanner->getCurrentToken() != Token::Semicolon)
conditionExpression = parseExpression(); conditionExpression = parseExpression();
expectToken(Token::SEMICOLON); expectToken(Token::Semicolon);
if (m_scanner->getCurrentToken() != Token::RPAREN) if (m_scanner->getCurrentToken() != Token::RParen)
loopExpression = parseExpressionStatement(); loopExpression = parseExpressionStatement();
expectToken(Token::RPAREN); expectToken(Token::RParen);
ASTPointer<Statement> body = parseStatement(); ASTPointer<Statement> body = parseStatement();
nodeFactory.setEndPositionFromNode(body); nodeFactory.setEndPositionFromNode(body);
@ -409,10 +598,11 @@ ASTPointer<Statement> Parser::parseVarDefOrExprStmt()
ASTPointer<VariableDefinition> Parser::parseVariableDefinition() ASTPointer<VariableDefinition> Parser::parseVariableDefinition()
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
bool const allowVar = true; VarDeclParserOptions options;
ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(allowVar); options.allowVar = true;
ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(options);
ASTPointer<Expression> value; ASTPointer<Expression> value;
if (m_scanner->getCurrentToken() == Token::ASSIGN) if (m_scanner->getCurrentToken() == Token::Assign)
{ {
m_scanner->next(); m_scanner->next();
value = parseExpression(); value = parseExpression();
@ -449,7 +639,6 @@ ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence)
ASTPointer<Expression> expression = parseUnaryExpression(); ASTPointer<Expression> expression = parseUnaryExpression();
int precedence = Token::precedence(m_scanner->getCurrentToken()); int precedence = Token::precedence(m_scanner->getCurrentToken());
for (; precedence >= _minPrecedence; --precedence) for (; precedence >= _minPrecedence; --precedence)
{
while (Token::precedence(m_scanner->getCurrentToken()) == precedence) while (Token::precedence(m_scanner->getCurrentToken()) == precedence)
{ {
Token::Value op = m_scanner->getCurrentToken(); Token::Value op = m_scanner->getCurrentToken();
@ -458,7 +647,6 @@ ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence)
nodeFactory.setEndPositionFromNode(right); nodeFactory.setEndPositionFromNode(right);
expression = nodeFactory.createNode<BinaryOperation>(expression, op, right); expression = nodeFactory.createNode<BinaryOperation>(expression, op, right);
} }
}
return expression; return expression;
} }
@ -466,17 +654,7 @@ ASTPointer<Expression> Parser::parseUnaryExpression()
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
Token::Value token = m_scanner->getCurrentToken(); Token::Value token = m_scanner->getCurrentToken();
if (token == Token::NEW) if (Token::isUnaryOp(token) || Token::isCountOp(token))
{
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))
{ {
// prefix expression // prefix expression
m_scanner->next(); m_scanner->next();
@ -500,34 +678,46 @@ ASTPointer<Expression> Parser::parseUnaryExpression()
ASTPointer<Expression> Parser::parseLeftHandSideExpression() ASTPointer<Expression> Parser::parseLeftHandSideExpression()
{ {
ASTNodeFactory nodeFactory(*this); 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) while (true)
{ {
switch (m_scanner->getCurrentToken()) switch (m_scanner->getCurrentToken())
{ {
case Token::LBRACK: case Token::LBrack:
{ {
m_scanner->next(); m_scanner->next();
ASTPointer<Expression> index = parseExpression(); ASTPointer<Expression> index = parseExpression();
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::RBRACK); expectToken(Token::RBrack);
expression = nodeFactory.createNode<IndexAccess>(expression, index); expression = nodeFactory.createNode<IndexAccess>(expression, index);
} }
break; break;
case Token::PERIOD: case Token::Period:
{ {
m_scanner->next(); m_scanner->next();
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expression = nodeFactory.createNode<MemberAccess>(expression, expectIdentifierToken()); expression = nodeFactory.createNode<MemberAccess>(expression, expectIdentifierToken());
} }
break; break;
case Token::LPAREN: case Token::LParen:
{ {
m_scanner->next(); m_scanner->next();
vector<ASTPointer<Expression>> arguments = parseFunctionCallArguments(); vector<ASTPointer<Expression>> arguments;
vector<ASTPointer<ASTString>> names;
std::tie(arguments, names) = parseFunctionCallArguments();
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::RPAREN); expectToken(Token::RParen);
expression = nodeFactory.createNode<FunctionCall>(expression, arguments); expression = nodeFactory.createNode<FunctionCall>(expression, arguments, names);
} }
break; break;
default: default:
@ -543,24 +733,34 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
ASTPointer<Expression> expression; ASTPointer<Expression> expression;
switch (token) switch (token)
{ {
case Token::TRUE_LITERAL: case Token::TrueLiteral:
case Token::FALSE_LITERAL: case Token::FalseLiteral:
expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance()); expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance());
break; break;
case Token::NUMBER: case Token::Number:
case Token::STRING_LITERAL: 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(); nodeFactory.markEndPosition();
expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance()); expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance());
break; break;
case Token::IDENTIFIER: case Token::Identifier:
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance()); expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance());
break; break;
case Token::LPAREN: case Token::LParen:
{ {
m_scanner->next(); m_scanner->next();
ASTPointer<Expression> expression = parseExpression(); ASTPointer<Expression> expression = parseExpression();
expectToken(Token::RPAREN); expectToken(Token::RParen);
return expression; return expression;
} }
default: default:
@ -580,21 +780,47 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
return expression; return expression;
} }
vector<ASTPointer<Expression>> Parser::parseFunctionCallArguments() vector<ASTPointer<Expression>> Parser::parseFunctionCallListArguments()
{ {
vector<ASTPointer<Expression>> arguments; vector<ASTPointer<Expression>> arguments;
if (m_scanner->getCurrentToken() != Token::RPAREN) if (m_scanner->getCurrentToken() != Token::RParen)
{ {
arguments.push_back(parseExpression()); 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()); arguments.push_back(parseExpression());
} }
} }
return arguments; 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() bool Parser::peekVariableDefinition()
{ {
@ -602,11 +828,11 @@ bool Parser::peekVariableDefinition()
// (which include assignments to other expressions and pre-declared variables) // (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 // 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. // in the case of a user-defined type, we have two identifiers following each other.
return (m_scanner->getCurrentToken() == Token::MAPPING || return (m_scanner->getCurrentToken() == Token::Mapping ||
m_scanner->getCurrentToken() == Token::VAR || m_scanner->getCurrentToken() == Token::Var ||
((Token::isElementaryTypeName(m_scanner->getCurrentToken()) || ((Token::isElementaryTypeName(m_scanner->getCurrentToken()) ||
m_scanner->getCurrentToken() == Token::IDENTIFIER) && m_scanner->getCurrentToken() == Token::Identifier) &&
m_scanner->peekNextToken() == Token::IDENTIFIER)); m_scanner->peekNextToken() == Token::Identifier));
} }
void Parser::expectToken(Token::Value _value) void Parser::expectToken(Token::Value _value)
@ -627,7 +853,7 @@ Token::Value Parser::expectAssignmentOperator()
ASTPointer<ASTString> Parser::expectIdentifierToken() ASTPointer<ASTString> Parser::expectIdentifierToken()
{ {
if (m_scanner->getCurrentToken() != Token::IDENTIFIER) if (m_scanner->getCurrentToken() != Token::Identifier)
BOOST_THROW_EXCEPTION(createParserError("Expected identifier")); BOOST_THROW_EXCEPTION(createParserError("Expected identifier"));
return getLiteralAndAdvance(); return getLiteralAndAdvance();
} }
@ -639,6 +865,13 @@ ASTPointer<ASTString> Parser::getLiteralAndAdvance()
return identifier; 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 ParserError Parser::createParserError(string const& _description) const
{ {
return ParserError() << errinfo_sourceLocation(Location(getPosition(), getPosition(), getSourceName())) return ParserError() << errinfo_sourceLocation(Location(getPosition(), getPosition(), getSourceName()))

View File

@ -45,16 +45,32 @@ private:
/// End position of the current token /// End position of the current token
int getEndPosition() const; 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 ///@name Parsing functions for the AST nodes
ASTPointer<ImportDirective> parseImportDirective(); ASTPointer<ImportDirective> parseImportDirective();
ASTPointer<ContractDefinition> parseContractDefinition(); 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<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<TypeName> parseTypeName(bool _allowVar);
ASTPointer<Mapping> parseMapping(); ASTPointer<Mapping> parseMapping();
ASTPointer<ParameterList> parseParameterList(bool _allowEmpty = true); ASTPointer<ParameterList> parseParameterList(bool _allowEmpty = true, bool _allowIndexed = false);
ASTPointer<Block> parseBlock(); ASTPointer<Block> parseBlock();
ASTPointer<Statement> parseStatement(); ASTPointer<Statement> parseStatement();
ASTPointer<IfStatement> parseIfStatement(); ASTPointer<IfStatement> parseIfStatement();
@ -68,7 +84,8 @@ private:
ASTPointer<Expression> parseUnaryExpression(); ASTPointer<Expression> parseUnaryExpression();
ASTPointer<Expression> parseLeftHandSideExpression(); ASTPointer<Expression> parseLeftHandSideExpression();
ASTPointer<Expression> parsePrimaryExpression(); 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(); 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 /// Creates a @ref ParserError exception and annotates it with the current position and the
/// given @a _description. /// given @a _description.
ParserError createParserError(std::string const& _description) const; ParserError createParserError(std::string const& _description) const;
std::shared_ptr<Scanner> m_scanner; std::shared_ptr<Scanner> m_scanner;
/// Flag that signifies whether '_' is parsed as a PlaceholderStatement or a regular identifier.
bool m_insideModifier = false;
}; };
} }

View File

@ -225,7 +225,7 @@ Token::Value Scanner::skipSingleLineComment()
// separately by the lexical grammar and becomes part of the // separately by the lexical grammar and becomes part of the
// stream of input elements for the syntactic grammar // stream of input elements for the syntactic grammar
while (advance() && !isLineTerminator(m_char)) { }; while (advance() && !isLineTerminator(m_char)) { };
return Token::WHITESPACE; return Token::Whitespace;
} }
Token::Value Scanner::scanSingleLineDocComment() Token::Value Scanner::scanSingleLineDocComment()
@ -255,7 +255,7 @@ Token::Value Scanner::scanSingleLineDocComment()
advance(); advance();
} }
literal.complete(); literal.complete();
return Token::COMMENT_LITERAL; return Token::CommentLiteral;
} }
Token::Value Scanner::skipMultiLineComment() Token::Value Scanner::skipMultiLineComment()
@ -272,11 +272,11 @@ Token::Value Scanner::skipMultiLineComment()
if (ch == '*' && m_char == '/') if (ch == '*' && m_char == '/')
{ {
m_char = ' '; m_char = ' ';
return Token::WHITESPACE; return Token::Whitespace;
} }
} }
// Unterminated multi-line comment. // Unterminated multi-line comment.
return Token::ILLEGAL; return Token::Illegal;
} }
Token::Value Scanner::scanMultiLineDocComment() Token::Value Scanner::scanMultiLineDocComment()
@ -285,8 +285,6 @@ Token::Value Scanner::scanMultiLineDocComment()
bool endFound = false; bool endFound = false;
bool charsAdded = false; bool charsAdded = false;
advance(); //consume the last '*' at /**
skipWhitespaceExceptLF();
while (!isSourcePastEndOfInput()) while (!isSourcePastEndOfInput())
{ {
//handle newlines in multline comments //handle newlines in multline comments
@ -321,9 +319,9 @@ Token::Value Scanner::scanMultiLineDocComment()
} }
literal.complete(); literal.complete();
if (!endFound) if (!endFound)
return Token::ILLEGAL; return Token::Illegal;
else else
return Token::COMMENT_LITERAL; return Token::CommentLiteral;
} }
Token::Value Scanner::scanSlash() Token::Value Scanner::scanSlash()
@ -333,7 +331,7 @@ Token::Value Scanner::scanSlash()
if (m_char == '/') if (m_char == '/')
{ {
if (!advance()) /* double slash comment directly before EOS */ if (!advance()) /* double slash comment directly before EOS */
return Token::WHITESPACE; return Token::Whitespace;
else if (m_char == '/') else if (m_char == '/')
{ {
// doxygen style /// comment // doxygen style /// comment
@ -342,7 +340,7 @@ Token::Value Scanner::scanSlash()
comment = scanSingleLineDocComment(); comment = scanSingleLineDocComment();
m_nextSkippedComment.location.end = getSourcePos(); m_nextSkippedComment.location.end = getSourcePos();
m_nextSkippedComment.token = comment; m_nextSkippedComment.token = comment;
return Token::WHITESPACE; return Token::Whitespace;
} }
else else
return skipSingleLineComment(); return skipSingleLineComment();
@ -351,23 +349,32 @@ Token::Value Scanner::scanSlash()
{ {
// doxygen style /** natspec comment // doxygen style /** natspec comment
if (!advance()) /* slash star comment before EOS */ if (!advance()) /* slash star comment before EOS */
return Token::WHITESPACE; return Token::Whitespace;
else if (m_char == '*') else if (m_char == '*')
{ {
Token::Value comment; advance(); //consume the last '*' at /**
m_nextSkippedComment.location.start = firstSlashPosition; skipWhitespaceExceptLF();
comment = scanMultiLineDocComment();
m_nextSkippedComment.location.end = getSourcePos(); // special case of a closed normal multiline comment
m_nextSkippedComment.token = comment; if (!m_source.isPastEndOfInput() && m_source.get(0) == '/')
return Token::WHITESPACE; 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 else
return skipMultiLineComment(); return skipMultiLineComment();
} }
else if (m_char == '=') else if (m_char == '=')
return selectToken(Token::ASSIGN_DIV); return selectToken(Token::AssignDiv);
else else
return Token::DIV; return Token::Div;
} }
void Scanner::scanToken() void Scanner::scanToken()
@ -384,7 +391,7 @@ void Scanner::scanToken()
case '\n': // fall-through case '\n': // fall-through
case ' ': case ' ':
case '\t': case '\t':
token = selectToken(Token::WHITESPACE); token = selectToken(Token::Whitespace);
break; break;
case '"': case '"':
case '\'': case '\'':
@ -394,81 +401,82 @@ void Scanner::scanToken()
// < <= << <<= // < <= << <<=
advance(); advance();
if (m_char == '=') if (m_char == '=')
token = selectToken(Token::LTE); token = selectToken(Token::LessThanOrEqual);
else if (m_char == '<') else if (m_char == '<')
token = selectToken('=', Token::ASSIGN_SHL, Token::SHL); token = selectToken('=', Token::AssignShl, Token::SHL);
else else
token = Token::LT; token = Token::LessThan;
break; break;
case '>': case '>':
// > >= >> >>= >>> >>>= // > >= >> >>= >>> >>>=
advance(); advance();
if (m_char == '=') if (m_char == '=')
token = selectToken(Token::GTE); token = selectToken(Token::GreaterThanOrEqual);
else if (m_char == '>') else if (m_char == '>')
{ {
// >> >>= >>> >>>= // >> >>= >>> >>>=
advance(); advance();
if (m_char == '=') if (m_char == '=')
token = selectToken(Token::ASSIGN_SAR); token = selectToken(Token::AssignSar);
else if (m_char == '>') else if (m_char == '>')
token = selectToken('=', Token::ASSIGN_SHR, Token::SHR); token = selectToken('=', Token::AssignShr, Token::SHR);
else else
token = Token::SAR; token = Token::SAR;
} }
else else
token = Token::GT; token = Token::GreaterThan;
break; break;
case '=': case '=':
// = == => // = == =>
advance(); advance();
if (m_char == '=') if (m_char == '=')
token = selectToken(Token::EQ); token = selectToken(Token::Equal);
else if (m_char == '>') else if (m_char == '>')
token = selectToken(Token::ARROW); token = selectToken(Token::Arrow);
else else
token = Token::ASSIGN; token = Token::Assign;
break; break;
case '!': case '!':
// ! != // ! !=
advance(); advance();
if (m_char == '=') if (m_char == '=')
token = selectToken(Token::NE); token = selectToken(Token::NotEqual);
else else
token = Token::NOT; token = Token::Not;
break; break;
case '+': case '+':
// + ++ += // + ++ +=
advance(); advance();
if (m_char == '+') if (m_char == '+')
token = selectToken(Token::INC); token = selectToken(Token::Inc);
else if (m_char == '=') else if (m_char == '=')
token = selectToken(Token::ASSIGN_ADD); token = selectToken(Token::AssignAdd);
else else
token = Token::ADD; token = Token::Add;
break; break;
case '-': case '-':
// - -- -= Number // - -- -=
advance(); advance();
if (m_char == '-') if (m_char == '-')
{ token = selectToken(Token::Dec);
advance();
token = Token::DEC;
}
else if (m_char == '=') else if (m_char == '=')
token = selectToken(Token::ASSIGN_SUB); token = selectToken(Token::AssignSub);
else if (m_char == '.' || isDecimalDigit(m_char))
token = scanNumber('-');
else else
token = Token::SUB; token = Token::Sub;
break; break;
case '*': 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; break;
case '%': case '%':
// % %= // % %=
token = selectToken('=', Token::ASSIGN_MOD, Token::MOD); token = selectToken('=', Token::AssignMod, Token::Mod);
break; break;
case '/': case '/':
// / // /* /= // / // /* /=
@ -478,25 +486,25 @@ void Scanner::scanToken()
// & && &= // & && &=
advance(); advance();
if (m_char == '&') if (m_char == '&')
token = selectToken(Token::AND); token = selectToken(Token::And);
else if (m_char == '=') else if (m_char == '=')
token = selectToken(Token::ASSIGN_BIT_AND); token = selectToken(Token::AssignBitAnd);
else else
token = Token::BIT_AND; token = Token::BitAnd;
break; break;
case '|': case '|':
// | || |= // | || |=
advance(); advance();
if (m_char == '|') if (m_char == '|')
token = selectToken(Token::OR); token = selectToken(Token::Or);
else if (m_char == '=') else if (m_char == '=')
token = selectToken(Token::ASSIGN_BIT_OR); token = selectToken(Token::AssignBitOr);
else else
token = Token::BIT_OR; token = Token::BitOr;
break; break;
case '^': case '^':
// ^ ^= // ^ ^=
token = selectToken('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR); token = selectToken('=', Token::AssignBitXor, Token::BitXor);
break; break;
case '.': case '.':
// . Number // . Number
@ -504,40 +512,40 @@ void Scanner::scanToken()
if (isDecimalDigit(m_char)) if (isDecimalDigit(m_char))
token = scanNumber('.'); token = scanNumber('.');
else else
token = Token::PERIOD; token = Token::Period;
break; break;
case ':': case ':':
token = selectToken(Token::COLON); token = selectToken(Token::Colon);
break; break;
case ';': case ';':
token = selectToken(Token::SEMICOLON); token = selectToken(Token::Semicolon);
break; break;
case ',': case ',':
token = selectToken(Token::COMMA); token = selectToken(Token::Comma);
break; break;
case '(': case '(':
token = selectToken(Token::LPAREN); token = selectToken(Token::LParen);
break; break;
case ')': case ')':
token = selectToken(Token::RPAREN); token = selectToken(Token::RParen);
break; break;
case '[': case '[':
token = selectToken(Token::LBRACK); token = selectToken(Token::LBrack);
break; break;
case ']': case ']':
token = selectToken(Token::RBRACK); token = selectToken(Token::RBrack);
break; break;
case '{': case '{':
token = selectToken(Token::LBRACE); token = selectToken(Token::LBrace);
break; break;
case '}': case '}':
token = selectToken(Token::RBRACE); token = selectToken(Token::RBrace);
break; break;
case '?': case '?':
token = selectToken(Token::CONDITIONAL); token = selectToken(Token::Conditional);
break; break;
case '~': case '~':
token = selectToken(Token::BIT_NOT); token = selectToken(Token::BitNot);
break; break;
default: default:
if (isIdentifierStart(m_char)) if (isIdentifierStart(m_char))
@ -545,17 +553,17 @@ void Scanner::scanToken()
else if (isDecimalDigit(m_char)) else if (isDecimalDigit(m_char))
token = scanNumber(); token = scanNumber();
else if (skipWhitespace()) else if (skipWhitespace())
token = Token::WHITESPACE; token = Token::Whitespace;
else if (isSourcePastEndOfInput()) else if (isSourcePastEndOfInput())
token = Token::EOS; token = Token::EOS;
else else
token = selectToken(Token::ILLEGAL); token = selectToken(Token::Illegal);
break; break;
} }
// Continue scanning for tokens as long as we're just skipping // Continue scanning for tokens as long as we're just skipping
// whitespace. // whitespace.
} }
while (token == Token::WHITESPACE); while (token == Token::Whitespace);
m_nextToken.location.end = getSourcePos(); m_nextToken.location.end = getSourcePos();
m_nextToken.token = token; m_nextToken.token = token;
} }
@ -613,16 +621,16 @@ Token::Value Scanner::scanString()
if (c == '\\') if (c == '\\')
{ {
if (isSourcePastEndOfInput() || !scanEscape()) if (isSourcePastEndOfInput() || !scanEscape())
return Token::ILLEGAL; return Token::Illegal;
} }
else else
addLiteralChar(c); addLiteralChar(c);
} }
if (m_char != quote) if (m_char != quote)
return Token::ILLEGAL; return Token::Illegal;
literal.complete(); literal.complete();
advance(); // consume quote advance(); // consume quote
return Token::STRING_LITERAL; return Token::StringLiteral;
} }
void Scanner::scanDecimalDigits() void Scanner::scanDecimalDigits()
@ -643,8 +651,7 @@ Token::Value Scanner::scanNumber(char _charSeen)
} }
else else
{ {
if (_charSeen == '-') solAssert(_charSeen == 0, "");
addLiteralChar('-');
// if the first character is '0' we must check for octals and hex // if the first character is '0' we must check for octals and hex
if (m_char == '0') if (m_char == '0')
{ {
@ -656,7 +663,7 @@ Token::Value Scanner::scanNumber(char _charSeen)
kind = HEX; kind = HEX;
addLiteralCharAndAdvance(); addLiteralCharAndAdvance();
if (!isHexDigit(m_char)) 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)) while (isHexDigit(m_char))
addLiteralCharAndAdvance(); 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"); solAssert(kind != HEX, "'e'/'E' must be scanned as part of the hex number");
if (kind != DECIMAL) if (kind != DECIMAL)
return Token::ILLEGAL; return Token::Illegal;
// scan exponent // scan exponent
addLiteralCharAndAdvance(); addLiteralCharAndAdvance();
if (m_char == '+' || m_char == '-') if (m_char == '+' || m_char == '-')
addLiteralCharAndAdvance(); addLiteralCharAndAdvance();
if (!isDecimalDigit(m_char)) 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(); scanDecimalDigits();
} }
// The source character immediately following a numeric literal must // 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 // section 7.8.3, page 17 (note that we read only one decimal digit
// if the value is 0). // if the value is 0).
if (isDecimalDigit(m_char) || isIdentifierStart(m_char)) if (isDecimalDigit(m_char) || isIdentifierStart(m_char))
return Token::ILLEGAL; return Token::Illegal;
literal.complete(); literal.complete();
return Token::NUMBER; 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;
} }
Token::Value Scanner::scanIdentifierOrKeyword() Token::Value Scanner::scanIdentifierOrKeyword()
@ -723,7 +712,7 @@ Token::Value Scanner::scanIdentifierOrKeyword()
while (isIdentifierPart(m_char)) while (isIdentifierPart(m_char))
addLiteralCharAndAdvance(); addLiteralCharAndAdvance();
literal.complete(); literal.complete();
return keywordOrIdentifierToken(m_nextToken.literal); return Token::fromIdentifierOrKeyword(m_nextToken.literal);
} }
char CharStream::advanceAndGet(size_t _chars) char CharStream::advanceAndGet(size_t _chars)

View File

@ -119,6 +119,7 @@ public:
{ {
return m_currentToken.token; return m_currentToken.token;
} }
Location getCurrentLocation() const { return m_currentToken.location; } Location getCurrentLocation() const { return m_currentToken.location; }
std::string const& getCurrentLiteral() const { return m_currentToken.literal; } std::string const& getCurrentLiteral() const { return m_currentToken.literal; }
///@} ///@}

View File

@ -40,8 +40,11 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. // along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
#include <map>
#include <libsolidity/Token.h> #include <libsolidity/Token.h>
using namespace std;
namespace dev namespace dev
{ {
namespace solidity namespace solidity
@ -77,6 +80,19 @@ char const Token::m_tokenType[] =
{ {
TOKEN_LIST(KT, KK) 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 KT
#undef KK #undef KK

496
Token.h
View File

@ -67,263 +67,274 @@ namespace solidity
#define IGNORE_TOKEN(name, string, precedence) #define IGNORE_TOKEN(name, string, precedence)
#define TOKEN_LIST(T, K) \ #define TOKEN_LIST(T, K) \
/* End of source indicator. */ \ /* End of source indicator. */ \
T(EOS, "EOS", 0) \ 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). */ \ /* Assignment operators. */ \
T(LPAREN, "(", 0) \ /* IsAssignmentOp() relies on this block of enum values being */ \
T(RPAREN, ")", 0) \ /* contiguous and sorted in the same order!*/ \
T(LBRACK, "[", 0) \ T(Assign, "=", 2) \
T(RBRACK, "]", 0) \
T(LBRACE, "{", 0) \
T(RBRACE, "}", 0) \
T(COLON, ":", 0) \
T(SEMICOLON, ";", 0) \
T(PERIOD, ".", 0) \
T(CONDITIONAL, "?", 3) \
T(ARROW, "=>", 0) \
\
/* Assignment operators. */ \
/* IsAssignmentOp() relies on this block of enum values being */ \
/* contiguous and sorted in the same order!*/ \
T(ASSIGN, "=", 2) \
/* The following have to be in exactly the same order as the simple binary operators*/ \ /* The following have to be in exactly the same order as the simple binary operators*/ \
T(ASSIGN_BIT_OR, "|=", 2) \ T(AssignBitOr, "|=", 2) \
T(ASSIGN_BIT_XOR, "^=", 2) \ T(AssignBitXor, "^=", 2) \
T(ASSIGN_BIT_AND, "&=", 2) \ T(AssignBitAnd, "&=", 2) \
T(ASSIGN_SHL, "<<=", 2) \ T(AssignShl, "<<=", 2) \
T(ASSIGN_SAR, ">>=", 2) \ T(AssignSar, ">>=", 2) \
T(ASSIGN_SHR, ">>>=", 2) \ T(AssignShr, ">>>=", 2) \
T(ASSIGN_ADD, "+=", 2) \ T(AssignAdd, "+=", 2) \
T(ASSIGN_SUB, "-=", 2) \ T(AssignSub, "-=", 2) \
T(ASSIGN_MUL, "*=", 2) \ T(AssignMul, "*=", 2) \
T(ASSIGN_DIV, "/=", 2) \ T(AssignDiv, "/=", 2) \
T(ASSIGN_MOD, "%=", 2) \ T(AssignMod, "%=", 2) \
\ \
/* Binary operators sorted by precedence. */ \ /* Binary operators sorted by precedence. */ \
/* IsBinaryOp() relies on this block of enum values */ \ /* IsBinaryOp() relies on this block of enum values */ \
/* being contiguous and sorted in the same order! */ \ /* being contiguous and sorted in the same order! */ \
T(COMMA, ",", 1) \ T(Comma, ",", 1) \
T(OR, "||", 4) \ T(Or, "||", 4) \
T(AND, "&&", 5) \ T(And, "&&", 5) \
T(BIT_OR, "|", 8) \ T(BitOr, "|", 8) \
T(BIT_XOR, "^", 9) \ T(BitXor, "^", 9) \
T(BIT_AND, "&", 10) \ T(BitAnd, "&", 10) \
T(SHL, "<<", 11) \ T(SHL, "<<", 11) \
T(SAR, ">>", 11) \ T(SAR, ">>", 11) \
T(SHR, ">>>", 11) \ T(SHR, ">>>", 11) \
T(ADD, "+", 12) \ T(Add, "+", 12) \
T(SUB, "-", 12) \ T(Sub, "-", 12) \
T(MUL, "*", 13) \ T(Mul, "*", 13) \
T(DIV, "/", 13) \ T(Div, "/", 13) \
T(MOD, "%", 13) \ T(Mod, "%", 13) \
T(Exp, "**", 14) \
\ \
/* Compare operators sorted by precedence. */ \ /* Compare operators sorted by precedence. */ \
/* IsCompareOp() relies on this block of enum values */ \ /* IsCompareOp() relies on this block of enum values */ \
/* being contiguous and sorted in the same order! */ \ /* being contiguous and sorted in the same order! */ \
T(EQ, "==", 6) \ T(Equal, "==", 6) \
T(NE, "!=", 6) \ T(NotEqual, "!=", 6) \
T(LT, "<", 7) \ T(LessThan, "<", 7) \
T(GT, ">", 7) \ T(GreaterThan, ">", 7) \
T(LTE, "<=", 7) \ T(LessThanOrEqual, "<=", 7) \
T(GTE, ">=", 7) \ T(GreaterThanOrEqual, ">=", 7) \
K(IN, "in", 7) \ K(In, "in", 7) \
\ \
/* Unary operators. */ \ /* Unary operators. */ \
/* IsUnaryOp() relies on this block of enum values */ \ /* IsUnaryOp() relies on this block of enum values */ \
/* being contiguous and sorted in the same order! */ \ /* being contiguous and sorted in the same order! */ \
T(NOT, "!", 0) \ T(Not, "!", 0) \
T(BIT_NOT, "~", 0) \ T(BitNot, "~", 0) \
T(INC, "++", 0) \ T(Inc, "++", 0) \
T(DEC, "--", 0) \ T(Dec, "--", 0) \
K(DELETE, "delete", 0) \ K(Delete, "delete", 0) \
\ \
/* Keywords */ \ /* Keywords */ \
K(BREAK, "break", 0) \ K(Break, "break", 0) \
K(CASE, "case", 0) \ K(Case, "case", 0) \
K(CONST, "constant", 0) \ K(Const, "constant", 0) \
K(CONTINUE, "continue", 0) \ K(Continue, "continue", 0) \
K(CONTRACT, "contract", 0) \ K(Contract, "contract", 0) \
K(DEFAULT, "default", 0) \ K(Default, "default", 0) \
K(DO, "do", 0) \ K(Do, "do", 0) \
K(ELSE, "else", 0) \ K(Else, "else", 0) \
K(EXTENDS, "extends", 0) \ K(Event, "event", 0) \
K(FOR, "for", 0) \ K(External, "external", 0) \
K(FUNCTION, "function", 0) \ K(Is, "is", 0) \
K(IF, "if", 0) \ K(Indexed, "indexed", 0) \
K(IMPORT, "import", 0) \ K(For, "for", 0) \
K(MAPPING, "mapping", 0) \ K(Function, "function", 0) \
K(NEW, "new", 0) \ K(If, "if", 0) \
K(PUBLIC, "public", 0) \ K(Import, "import", 0) \
K(PRIVATE, "private", 0) \ K(Mapping, "mapping", 0) \
K(RETURN, "return", 0) \ K(Modifier, "modifier", 0) \
K(RETURNS, "returns", 0) \ K(New, "new", 0) \
K(STRUCT, "struct", 0) \ K(Public, "public", 0) \
K(SWITCH, "switch", 0) \ K(Private, "private", 0) \
K(VAR, "var", 0) \ K(Inheritable, "inheritable", 0) \
K(WHILE, "while", 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 /* type keywords, keep them in this order, keep int as first keyword
* the implementation in Types.cpp has to be synced to this here * the implementation in Types.cpp has to be synced to this here */\
* TODO more to be added */ \ K(Int, "int", 0) \
K(INT, "int", 0) \ K(Int8, "int8", 0) \
K(INT8, "int8", 0) \ K(Int16, "int16", 0) \
K(INT16, "int16", 0) \ K(Int24, "int24", 0) \
K(INT24, "int24", 0) \ K(Int32, "int32", 0) \
K(INT32, "int32", 0) \ K(Int40, "int40", 0) \
K(INT40, "int40", 0) \ K(Int48, "int48", 0) \
K(INT48, "int48", 0) \ K(Int56, "int56", 0) \
K(INT56, "int56", 0) \ K(Int64, "int64", 0) \
K(INT64, "int64", 0) \ K(Int72, "int72", 0) \
K(INT72, "int72", 0) \ K(Int80, "int80", 0) \
K(INT80, "int80", 0) \ K(Int88, "int88", 0) \
K(INT88, "int88", 0) \ K(Int96, "int96", 0) \
K(INT96, "int96", 0) \ K(Int104, "int104", 0) \
K(INT104, "int104", 0) \ K(Int112, "int112", 0) \
K(INT112, "int112", 0) \ K(Int120, "int120", 0) \
K(INT120, "int120", 0) \ K(Int128, "int128", 0) \
K(INT128, "int128", 0) \ K(Int136, "int136", 0) \
K(INT136, "int136", 0) \ K(Int144, "int144", 0) \
K(INT144, "int144", 0) \ K(Int152, "int152", 0) \
K(INT152, "int152", 0) \ K(Int160, "int160", 0) \
K(INT160, "int160", 0) \ K(Int168, "int168", 0) \
K(INT168, "int168", 0) \ K(Int176, "int178", 0) \
K(INT176, "int178", 0) \ K(Int184, "int184", 0) \
K(INT184, "int184", 0) \ K(Int192, "int192", 0) \
K(INT192, "int192", 0) \ K(Int200, "int200", 0) \
K(INT200, "int200", 0) \ K(Int208, "int208", 0) \
K(INT208, "int208", 0) \ K(Int216, "int216", 0) \
K(INT216, "int216", 0) \ K(Int224, "int224", 0) \
K(INT224, "int224", 0) \ K(Int232, "int232", 0) \
K(INT232, "int232", 0) \ K(Int240, "int240", 0) \
K(INT240, "int240", 0) \ K(Int248, "int248", 0) \
K(INT248, "int248", 0) \ K(Int256, "int256", 0) \
K(INT256, "int256", 0) \ K(UInt, "uint", 0) \
K(UINT, "uint", 0) \ K(UInt8, "uint8", 0) \
K(UINT8, "uint8", 0) \ K(UInt16, "uint16", 0) \
K(UINT16, "uint16", 0) \ K(UInt24, "uint24", 0) \
K(UINT24, "uint24", 0) \ K(UInt32, "uint32", 0) \
K(UINT32, "uint32", 0) \ K(UInt40, "uint40", 0) \
K(UINT40, "uint40", 0) \ K(UInt48, "uint48", 0) \
K(UINT48, "uint48", 0) \ K(UInt56, "uint56", 0) \
K(UINT56, "uint56", 0) \ K(UInt64, "uint64", 0) \
K(UINT64, "uint64", 0) \ K(UInt72, "uint72", 0) \
K(UINT72, "uint72", 0) \ K(UInt80, "uint80", 0) \
K(UINT80, "uint80", 0) \ K(UInt88, "uint88", 0) \
K(UINT88, "uint88", 0) \ K(UInt96, "uint96", 0) \
K(UINT96, "uint96", 0) \ K(UInt104, "uint104", 0) \
K(UINT104, "uint104", 0) \ K(UInt112, "uint112", 0) \
K(UINT112, "uint112", 0) \ K(UInt120, "uint120", 0) \
K(UINT120, "uint120", 0) \ K(UInt128, "uint128", 0) \
K(UINT128, "uint128", 0) \ K(UInt136, "uint136", 0) \
K(UINT136, "uint136", 0) \ K(UInt144, "uint144", 0) \
K(UINT144, "uint144", 0) \ K(UInt152, "uint152", 0) \
K(UINT152, "uint152", 0) \ K(UInt160, "uint160", 0) \
K(UINT160, "uint160", 0) \ K(UInt168, "uint168", 0) \
K(UINT168, "uint168", 0) \ K(UInt176, "uint178", 0) \
K(UINT176, "uint178", 0) \ K(UInt184, "uint184", 0) \
K(UINT184, "uint184", 0) \ K(UInt192, "uint192", 0) \
K(UINT192, "uint192", 0) \ K(UInt200, "uint200", 0) \
K(UINT200, "uint200", 0) \ K(UInt208, "uint208", 0) \
K(UINT208, "uint208", 0) \ K(UInt216, "uint216", 0) \
K(UINT216, "uint216", 0) \ K(UInt224, "uint224", 0) \
K(UINT224, "uint224", 0) \ K(UInt232, "uint232", 0) \
K(UINT232, "uint232", 0) \ K(UInt240, "uint240", 0) \
K(UINT240, "uint240", 0) \ K(UInt248, "uint248", 0) \
K(UINT248, "uint248", 0) \ K(UInt256, "uint256", 0) \
K(UINT256, "uint256", 0) \ K(Hash, "hash", 0) \
K(HASH, "hash", 0) \ K(Hash8, "hash8", 0) \
K(HASH8, "hash8", 0) \ K(Hash16, "hash16", 0) \
K(HASH16, "hash16", 0) \ K(Hash24, "hash24", 0) \
K(HASH24, "hash24", 0) \ K(Hash32, "hash32", 0) \
K(HASH32, "hash32", 0) \ K(Hash40, "hash40", 0) \
K(HASH40, "hash40", 0) \ K(Hash48, "hash48", 0) \
K(HASH48, "hash48", 0) \ K(Hash56, "hash56", 0) \
K(HASH56, "hash56", 0) \ K(Hash64, "hash64", 0) \
K(HASH64, "hash64", 0) \ K(Hash72, "hash72", 0) \
K(HASH72, "hash72", 0) \ K(Hash80, "hash80", 0) \
K(HASH80, "hash80", 0) \ K(Hash88, "hash88", 0) \
K(HASH88, "hash88", 0) \ K(Hash96, "hash96", 0) \
K(HASH96, "hash96", 0) \ K(Hash104, "hash104", 0) \
K(HASH104, "hash104", 0) \ K(Hash112, "hash112", 0) \
K(HASH112, "hash112", 0) \ K(Hash120, "hash120", 0) \
K(HASH120, "hash120", 0) \ K(Hash128, "hash128", 0) \
K(HASH128, "hash128", 0) \ K(Hash136, "hash136", 0) \
K(HASH136, "hash136", 0) \ K(Hash144, "hash144", 0) \
K(HASH144, "hash144", 0) \ K(Hash152, "hash152", 0) \
K(HASH152, "hash152", 0) \ K(Hash160, "hash160", 0) \
K(HASH160, "hash160", 0) \ K(Hash168, "hash168", 0) \
K(HASH168, "hash168", 0) \ K(Hash176, "hash178", 0) \
K(HASH176, "hash178", 0) \ K(Hash184, "hash184", 0) \
K(HASH184, "hash184", 0) \ K(Hash192, "hash192", 0) \
K(HASH192, "hash192", 0) \ K(Hash200, "hash200", 0) \
K(HASH200, "hash200", 0) \ K(Hash208, "hash208", 0) \
K(HASH208, "hash208", 0) \ K(Hash216, "hash216", 0) \
K(HASH216, "hash216", 0) \ K(Hash224, "hash224", 0) \
K(HASH224, "hash224", 0) \ K(Hash232, "hash232", 0) \
K(HASH232, "hash232", 0) \ K(Hash240, "hash240", 0) \
K(HASH240, "hash240", 0) \ K(Hash248, "hash248", 0) \
K(HASH248, "hash248", 0) \ K(Hash256, "hash256", 0) \
K(HASH256, "hash256", 0) \ K(Address, "address", 0) \
K(ADDRESS, "address", 0) \ K(Bool, "bool", 0) \
K(BOOL, "bool", 0) \ K(Bytes, "bytes", 0) \
K(STRING_TYPE, "string", 0) \ K(StringType, "string", 0) \
K(STRING0, "string0", 0) \ K(String0, "string0", 0) \
K(STRING1, "string1", 0) \ K(String1, "string1", 0) \
K(STRING2, "string2", 0) \ K(String2, "string2", 0) \
K(STRING3, "string3", 0) \ K(String3, "string3", 0) \
K(STRING4, "string4", 0) \ K(String4, "string4", 0) \
K(STRING5, "string5", 0) \ K(String5, "string5", 0) \
K(STRING6, "string6", 0) \ K(String6, "string6", 0) \
K(STRING7, "string7", 0) \ K(String7, "string7", 0) \
K(STRING8, "string8", 0) \ K(String8, "string8", 0) \
K(STRING9, "string9", 0) \ K(String9, "string9", 0) \
K(STRING10, "string10", 0) \ K(String10, "string10", 0) \
K(STRING11, "string11", 0) \ K(String11, "string11", 0) \
K(STRING12, "string12", 0) \ K(String12, "string12", 0) \
K(STRING13, "string13", 0) \ K(String13, "string13", 0) \
K(STRING14, "string14", 0) \ K(String14, "string14", 0) \
K(STRING15, "string15", 0) \ K(String15, "string15", 0) \
K(STRING16, "string16", 0) \ K(String16, "string16", 0) \
K(STRING17, "string17", 0) \ K(String17, "string17", 0) \
K(STRING18, "string18", 0) \ K(String18, "string18", 0) \
K(STRING19, "string19", 0) \ K(String19, "string19", 0) \
K(STRING20, "string20", 0) \ K(String20, "string20", 0) \
K(STRING21, "string21", 0) \ K(String21, "string21", 0) \
K(STRING22, "string22", 0) \ K(String22, "string22", 0) \
K(STRING23, "string23", 0) \ K(String23, "string23", 0) \
K(STRING24, "string24", 0) \ K(String24, "string24", 0) \
K(STRING25, "string25", 0) \ K(String25, "string25", 0) \
K(STRING26, "string26", 0) \ K(String26, "string26", 0) \
K(STRING27, "string27", 0) \ K(String27, "string27", 0) \
K(STRING28, "string28", 0) \ K(String28, "string28", 0) \
K(STRING29, "string29", 0) \ K(String29, "string29", 0) \
K(STRING30, "string30", 0) \ K(String30, "string30", 0) \
K(STRING31, "string31", 0) \ K(String31, "string31", 0) \
K(STRING32, "string32", 0) \ K(String32, "string32", 0) \
K(TEXT, "text", 0) \ K(Text, "text", 0) \
K(REAL, "real", 0) \ K(Real, "real", 0) \
K(UREAL, "ureal", 0) \ K(UReal, "ureal", 0) \
T(TYPES_END, NULL, 0) /* used as type enum end marker */ \ T(TypesEnd, NULL, 0) /* used as type enum end marker */ \
\ \
/* Literals */ \ /* Literals */ \
K(NULL_LITERAL, "null", 0) \ K(NullLiteral, "null", 0) \
K(TRUE_LITERAL, "true", 0) \ K(TrueLiteral, "true", 0) \
K(FALSE_LITERAL, "false", 0) \ K(FalseLiteral, "false", 0) \
T(NUMBER, NULL, 0) \ T(Number, NULL, 0) \
T(STRING_LITERAL, NULL, 0) \ T(StringLiteral, NULL, 0) \
T(COMMENT_LITERAL, NULL, 0) \ T(CommentLiteral, NULL, 0) \
\ \
/* Identifiers (not keywords or future reserved words). */ \ /* Identifiers (not keywords or future reserved words). */ \
T(IDENTIFIER, NULL, 0) \ T(Identifier, NULL, 0) \
\ \
/* Illegal token - not able to scan. */ \ /* Illegal token - not able to scan. */ \
T(ILLEGAL, "ILLEGAL", 0) \ T(Illegal, "ILLEGAL", 0) \
\ \
/* Scanner-internal use only. */ \ /* Scanner-internal use only. */ \
T(WHITESPACE, NULL, 0) T(Whitespace, NULL, 0)
class Token class Token
@ -350,24 +361,27 @@ public:
} }
// Predicates // Predicates
static bool isElementaryTypeName(Value tok) { return INT <= tok && tok < TYPES_END; } static bool isElementaryTypeName(Value tok) { return Int <= tok && tok < TypesEnd; }
static bool isAssignmentOp(Value tok) { return ASSIGN <= tok && tok <= ASSIGN_MOD; } static bool isAssignmentOp(Value tok) { return Assign <= tok && tok <= AssignMod; }
static bool isBinaryOp(Value op) { return COMMA <= op && op <= MOD; } static bool isBinaryOp(Value op) { return Comma <= op && op <= Exp; }
static bool isCommutativeOp(Value op) { return op == BIT_OR || op == BIT_XOR || op == BIT_AND || static bool isCommutativeOp(Value op) { return op == BitOr || op == BitXor || op == BitAnd ||
op == ADD || op == MUL || op == EQ || op == NE; } op == Add || op == Mul || op == Equal || op == NotEqual; }
static bool isArithmeticOp(Value op) { return ADD <= op && op <= MOD; } static bool isArithmeticOp(Value op) { return Add <= op && op <= Exp; }
static bool isCompareOp(Value op) { return EQ <= op && op <= IN; } static bool isCompareOp(Value op) { return Equal <= op && op <= In; }
static Value AssignmentToBinaryOp(Value op) static Value AssignmentToBinaryOp(Value op)
{ {
solAssert(isAssignmentOp(op) && op != ASSIGN, ""); solAssert(isAssignmentOp(op) && op != Assign, "");
return Token::Value(op + (BIT_OR - ASSIGN_BIT_OR)); return Value(op + (BitOr - AssignBitOr));
} }
static bool isBitOp(Value op) { return (BIT_OR <= op && op <= SHR) || op == BIT_NOT; } 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 isUnaryOp(Value op) { return (Not <= op && op <= Delete) || op == Add || op == Sub; }
static bool isCountOp(Value op) { return op == INC || op == DEC; } static bool isCountOp(Value op) { return op == Inc || op == Dec; }
static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); } 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 // Returns a string corresponding to the JS token string
// (.e., "<" for the token LT) or NULL if the token doesn't // (.e., "<" for the token LT) or NULL if the token doesn't
@ -386,6 +400,8 @@ public:
return m_precedence[tok]; return m_precedence[tok];
} }
static Token::Value fromIdentifierOrKeyword(std::string const& _name);
private: private:
static char const* const m_name[NUM_TOKENS]; static char const* const m_name[NUM_TOKENS];
static char const* const m_string[NUM_TOKENS]; static char const* const m_string[NUM_TOKENS];

828
Types.cpp

File diff suppressed because it is too large Load Diff

352
Types.h
View File

@ -41,6 +41,7 @@ namespace solidity
class Type; // forward class Type; // forward
class FunctionType; // forward class FunctionType; // forward
using TypePointer = std::shared_ptr<Type const>; using TypePointer = std::shared_ptr<Type const>;
using FunctionTypePointer = std::shared_ptr<FunctionType const>;
using TypePointers = std::vector<TypePointer>; using TypePointers = std::vector<TypePointer>;
/** /**
@ -49,14 +50,16 @@ using TypePointers = std::vector<TypePointer>;
class MemberList class MemberList
{ {
public: public:
using MemberMap = std::map<std::string, TypePointer>; using MemberMap = std::vector<std::pair<std::string, TypePointer>>;
MemberList() {} MemberList() {}
explicit MemberList(MemberMap const& _members): m_memberTypes(_members) {} explicit MemberList(MemberMap const& _members): m_memberTypes(_members) {}
TypePointer getMemberType(std::string const& _name) const TypePointer getMemberType(std::string const& _name) const
{ {
auto it = m_memberTypes.find(_name); for (auto const& it: m_memberTypes)
return it != m_memberTypes.end() ? it->second : TypePointer(); if (it.first == _name)
return it.second;
return TypePointer();
} }
MemberMap::const_iterator begin() const { return m_memberTypes.begin(); } 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. * 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: public:
enum class Category 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 ///@name Factory functions
/// Factory functions that convert an AST @ref TypeName to a Type. /// Factory functions that convert an AST @ref TypeName to a Type.
static std::shared_ptr<Type const> fromElementaryTypeName(Token::Value _typeToken); static TypePointer fromElementaryTypeName(Token::Value _typeToken);
static std::shared_ptr<Type const> fromUserDefinedTypeName(UserDefinedTypeName const& _typeName); static TypePointer fromElementaryTypeName(std::string const& _name);
static std::shared_ptr<Type const> fromMapping(Mapping const& _typeName); static TypePointer fromUserDefinedTypeName(UserDefinedTypeName const& _typeName);
static std::shared_ptr<Type const> fromFunction(FunctionDefinition const& _function); 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 /// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does
/// not fit any type. /// 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 Category getCategory() const = 0;
virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; } virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; }
@ -97,15 +105,27 @@ public:
{ {
return isImplicitlyConvertibleTo(_convertTo); return isImplicitlyConvertibleTo(_convertTo);
} }
virtual bool acceptsBinaryOperator(Token::Value) const { return false; } /// @returns the resulting type of applying the given unary operator or an empty pointer if
virtual bool acceptsUnaryOperator(Token::Value) const { return false; } /// 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 getCategory() == _other.getCategory(); }
virtual bool operator!=(Type const& _other) const { return !this->operator ==(_other); } 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 /// @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; } 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. /// @returns number of bytes required to hold this value in storage.
/// For dynamically "allocated" types, it returns the size of the statically allocated head, /// For dynamically "allocated" types, it returns the size of the statically allocated head,
virtual u256 getStorageSize() const { return 1; } virtual u256 getStorageSize() const { return 1; }
@ -117,6 +137,8 @@ public:
/// i.e. it behaves differently in lvalue context and in value context. /// i.e. it behaves differently in lvalue context and in value context.
virtual bool isValueType() const { return false; } virtual bool isValueType() const { return false; }
virtual unsigned getSizeOnStack() const { return 1; } 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. /// Returns the list of all members of this type. Default implementation: no members.
virtual MemberList const& getMembers() const { return EmptyMemberList; } virtual MemberList const& getMembers() const { return EmptyMemberList; }
@ -124,10 +146,10 @@ public:
TypePointer getMemberType(std::string const& _name) const { return getMembers().getMemberType(_name); } TypePointer getMemberType(std::string const& _name) const { return getMembers().getMemberType(_name); }
virtual std::string toString() const = 0; 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 " BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Literal value requested "
"for type without literals.")); "for type without literals."));
} }
protected: protected:
@ -143,20 +165,16 @@ class IntegerType: public Type
public: public:
enum class Modifier 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 explicit IntegerType(int _bits, Modifier _modifier = Modifier::Unsigned);
/// if no type fits.
static std::shared_ptr<IntegerType const> smallestTypeForLiteral(std::string const& _literal);
explicit IntegerType(int _bits, Modifier _modifier = Modifier::UNSIGNED);
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool acceptsBinaryOperator(Token::Value _operator) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual bool acceptsUnaryOperator(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 operator==(Type const& _other) const override;
@ -166,17 +184,51 @@ public:
virtual MemberList const& getMembers() const { return isAddress() ? AddressMemberList : EmptyMemberList; } virtual MemberList const& getMembers() const { return isAddress() ? AddressMemberList : EmptyMemberList; }
virtual std::string toString() const override; virtual std::string toString() const override;
virtual u256 literalValue(Literal const& _literal) const override;
int getNumBits() const { return m_bits; } int getNumBits() const { return m_bits; }
bool isHash() const { return m_modifier == Modifier::HASH || m_modifier == Modifier::ADDRESS; } bool isHash() const { return m_modifier == Modifier::Hash || m_modifier == Modifier::Address; }
bool isAddress() const { return m_modifier == Modifier::ADDRESS; } bool isAddress() const { return m_modifier == Modifier::Address; }
int isSigned() const { return m_modifier == Modifier::SIGNED; } bool isSigned() const { return m_modifier == Modifier::Signed; }
static const MemberList AddressMemberList;
private: private:
int m_bits; int m_bits;
Modifier m_modifier; 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 class StaticStringType: public Type
{ {
public: 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 /// @returns the smallest string type for the given literal or an empty pointer
/// if no type fits. /// if no type fits.
static std::shared_ptr<StaticStringType> smallestTypeForLiteral(std::string const& _literal); 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 isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool operator==(Type const& _other) const override; virtual bool operator==(Type const& _other) const override;
virtual unsigned getCalldataEncodedSize() const override { return m_bytes; } virtual unsigned getCalldataEncodedSize() const override { return m_bytes; }
virtual bool isValueType() const override { return true; } virtual bool isValueType() const override { return true; }
virtual std::string toString() const override { return "string" + dev::toString(m_bytes); } 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; } int getNumBytes() const { return m_bytes; }
@ -215,22 +268,45 @@ class BoolType: public Type
{ {
public: public:
BoolType() {} 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 isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool acceptsBinaryOperator(Token::Value _operator) const override virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
{ virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) 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 unsigned getCalldataEncodedSize() const { return 1; } virtual unsigned getCalldataEncodedSize() const { return 1; }
virtual bool isValueType() const override { return true; } virtual bool isValueType() const override { return true; }
virtual std::string toString() const override { return "bool"; } 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 class ContractType: public Type
{ {
public: public:
virtual Category getCategory() const override { return Category::CONTRACT; } virtual Category getCategory() const override { return Category::Contract; }
ContractType(ContractDefinition const& _contract): m_contract(_contract) {} explicit ContractType(ContractDefinition const& _contract, bool _super = false):
/// Contracts can be converted to themselves and to addresses. 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 bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual bool operator==(Type const& _other) const override; virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const override;
virtual bool isValueType() const override { return true; } virtual bool isValueType() const override { return true; }
virtual std::string toString() const override; virtual std::string toString() const override;
virtual MemberList const& getMembers() 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 /// 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. /// 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: private:
ContractDefinition const& m_contract; 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. /// 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. /// List of member types, will be lazy-initialized because of recursive references.
mutable std::unique_ptr<MemberList> m_members; mutable std::unique_ptr<MemberList> m_members;
}; };
@ -270,13 +357,9 @@ private:
class StructType: public Type class StructType: public Type
{ {
public: public:
virtual Category getCategory() const override { return Category::STRUCT; } virtual Category getCategory() const override { return Category::Struct; }
StructType(StructDefinition const& _struct): m_struct(_struct) {} explicit StructType(StructDefinition const& _struct): m_struct(_struct) {}
virtual bool acceptsUnaryOperator(Token::Value _operator) const override virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
{
return _operator == Token::DELETE;
}
virtual bool operator==(Type const& _other) const override; virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const override; virtual u256 getStorageSize() const override;
virtual bool canLiveOutsideStorage() const override; virtual bool canLiveOutsideStorage() const override;
@ -293,6 +376,32 @@ private:
mutable std::unique_ptr<MemberList> m_members; 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. * 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 * @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: public:
/// The meaning of the value(s) on the stack referencing the function: /// 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) /// BARE: contract address (non-abi contract call)
/// OTHERS: special virtual function, nothing on the stack /// 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); explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
FunctionType(TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes, explicit FunctionType(VariableDeclaration const& _varDecl);
Location _location = Location::INTERNAL): explicit FunctionType(EventDefinition const& _event);
m_parameterTypes(_parameterTypes), m_returnParameterTypes(_returnParameterTypes), FunctionType(strings const& _parameterTypes, strings const& _returnParameterTypes,
m_location(_location) {} 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; } 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; } 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 bool operator==(Type const& _other) const override;
virtual std::string toString() 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 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 bool canLiveOutsideStorage() const override { return false; }
virtual unsigned getSizeOnStack() const override; virtual unsigned getSizeOnStack() const override;
virtual MemberList const& getMembers() const override;
Location const& getLocation() const { return m_location; } 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: private:
static TypePointers parseElementaryTypeVector(strings const& _types);
TypePointers m_parameterTypes; TypePointers m_parameterTypes;
TypePointers m_returnParameterTypes; 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 class MappingType: public Type
{ {
public: public:
virtual Category getCategory() const override { return Category::MAPPING; } virtual Category getCategory() const override { return Category::Mapping; }
MappingType(TypePointer const& _keyType, TypePointer const& _valueType): MappingType(TypePointer const& _keyType, TypePointer const& _valueType):
m_keyType(_keyType), m_valueType(_valueType) {} m_keyType(_keyType), m_valueType(_valueType) {}
@ -361,9 +533,10 @@ private:
class VoidType: public Type class VoidType: public Type
{ {
public: public:
virtual Category getCategory() const override { return Category::VOID; } virtual Category getCategory() const override { return Category::Void; }
VoidType() {} VoidType() {}
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
virtual std::string toString() const override { return "void"; } virtual std::string toString() const override { return "void"; }
virtual bool canBeStored() const override { return false; } virtual bool canBeStored() const override { return false; }
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable void type requested.")); } virtual 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 class TypeType: public Type
{ {
public: public:
virtual Category getCategory() const override { return Category::TYPE; } virtual Category getCategory() const override { return Category::TypeType; }
TypeType(TypePointer const& _actualType): m_actualType(_actualType) {} explicit TypeType(TypePointer const& _actualType, ContractDefinition const* _currentContract = nullptr):
m_actualType(_actualType), m_currentContract(_currentContract) {}
TypePointer const& getActualType() const { return m_actualType; } 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 operator==(Type const& _other) const override;
virtual bool canBeStored() const override { return false; } 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 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 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 std::string toString() const override { return "type(" + m_actualType->toString() + ")"; }
virtual MemberList const& getMembers() const override;
private: private:
TypePointer m_actualType; 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 class MagicType: public Type
{ {
public: public:
enum class Kind { BLOCK, MSG, TX }; enum class Kind { Block, Message, Transaction };
virtual Category getCategory() const override { return Category::MAGIC; } 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 operator==(Type const& _other) const;
virtual bool canBeStored() const override { return false; } virtual bool canBeStored() const override { return false; }
virtual bool canLiveOutsideStorage() const override { return true; } virtual bool canLiveOutsideStorage() const override { return true; }

View File

@ -45,5 +45,11 @@ inline void solAssertAux(bool _condition, std::string const& _errorDescription,
<< ::boost::throw_line(_line)); << ::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);
}
} }
} }

View File

@ -1,12 +1,19 @@
ContractDefinition = 'contract' Identifier '{' ContractPart* '}' ContractDefinition = 'contract' Identifier
ContractPart = VariableDeclaration ';' | StructDefinition | ( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )?
FunctionDefinition | 'public:' | 'private:' '{' ContractPart* '}'
ContractPart = StateVariableDeclaration | StructDefinition | ModifierDefinition | FunctionDefinition | EnumDefinition
InheritanceSpecifier = Identifier ( '(' Expression ( ',' Expression )* ')' )?
StructDefinition = 'struct' Identifier '{' StructDefinition = 'struct' Identifier '{'
( VariableDeclaration (';' VariableDeclaration)* )? '} ( VariableDeclaration (';' VariableDeclaration)* )? '}
StateVariableDeclaration = TypeName ( 'public' | 'inheritable' | 'private' )? Identifier ';'
FunctionDefinition = 'function' Identifier ParameterList 'const'? ModifierDefinition = 'modifier' Identifier ParameterList? Block
FunctionDefinition = 'function' Identifier ParameterList
( Identifier | 'constant' | 'external' | 'public' | 'inheritable' | 'private' )*
( 'returns' ParameterList )? Block ( 'returns' ParameterList )? Block
EnumValue = Identifier
EnumDefinition = 'enum' '{' EnumValue (',' EnumValue)* '}'
ParameterList = '(' ( VariableDeclaration (',' VariableDeclaration)* )? ')' ParameterList = '(' ( VariableDeclaration (',' VariableDeclaration)* )? ')'
// semantic restriction: mappings and structs (recursively) containing mappings // semantic restriction: mappings and structs (recursively) containing mappings
// are not allowed in argument lists // are not allowed in argument lists
@ -33,7 +40,7 @@ Expression = Assignment | UnaryOperation | BinaryOperation | FunctionCall | NewE
// The expression syntax is actually much more complicated // The expression syntax is actually much more complicated
Assignment = Expression (AssignmentOp Expression) Assignment = Expression (AssignmentOp Expression)
FunctionCall = Expression '(' Expression ( ',' Expression )* ')' FunctionCall = Expression '(' Expression ( ',' Expression )* ')'
NewExpression = 'new' Identifier '(' ( Expression ( ',' Expression )* ) ')' NewExpression = 'new' Identifier
MemberAccess = Expression '.' Identifier MemberAccess = Expression '.' Identifier
IndexAccess = Expression '[' Expresison ']' IndexAccess = Expression '[' Expresison ']'
PrimaryExpression = Identifier | NumberLiteral | StringLiteral | ElementaryTypeName | '(' Expression ')' PrimaryExpression = Identifier | NumberLiteral | StringLiteral | ElementaryTypeName | '(' Expression ')'