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/AST_accept.h>
#include <libdevcrypto/SHA3.h>
using namespace std;
namespace dev
@ -39,40 +41,199 @@ TypeError ASTNode::createTypeError(string const& _description) const
return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description);
}
TypePointer ContractDefinition::getType(ContractDefinition const* _currentContract) const
{
return make_shared<TypeType>(make_shared<ContractType>(*this), _currentContract);
}
void ContractDefinition::checkTypeRequirements()
{
for (ASTPointer<InheritanceSpecifier> const& baseSpecifier: getBaseContracts())
baseSpecifier->checkTypeRequirements();
checkIllegalOverrides();
FunctionDefinition const* constructor = getConstructor();
if (constructor && !constructor->getReturnParameters().empty())
BOOST_THROW_EXCEPTION(constructor->getReturnParameterList()->createTypeError(
"Non-empty \"returns\" directive for constructor."));
"Non-empty \"returns\" directive for constructor."));
FunctionDefinition const* fallbackFunction = nullptr;
for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions())
if (function->getName().empty())
{
if (fallbackFunction)
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Only one fallback function is allowed."));
else
{
fallbackFunction = function.get();
if (!fallbackFunction->getParameters().empty())
BOOST_THROW_EXCEPTION(fallbackFunction->getParameterList().createTypeError("Fallback function cannot take parameters."));
}
}
for (ASTPointer<ModifierDefinition> const& modifier: getFunctionModifiers())
modifier->checkTypeRequirements();
for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions())
function->checkTypeRequirements();
// check for hash collisions in function signatures
set<FixedHash<4>> hashes;
for (auto const& it: getInterfaceFunctionList())
{
FixedHash<4> const& hash = it.first;
if (hashes.count(hash))
BOOST_THROW_EXCEPTION(createTypeError(
std::string("Function signature hash collision for ") +
it.second->getCanonicalSignature()));
hashes.insert(hash);
}
}
vector<FunctionDefinition const*> ContractDefinition::getInterfaceFunctions() const
map<FixedHash<4>, FunctionTypePointer> ContractDefinition::getInterfaceFunctions() const
{
vector<FunctionDefinition const*> exportedFunctions;
for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions)
if (f->isPublic() && f->getName() != getName())
exportedFunctions.push_back(f.get());
auto compareNames = [](FunctionDefinition const* _a, FunctionDefinition const* _b)
{
return _a->getName().compare(_b->getName()) < 0;
};
auto exportedFunctionList = getInterfaceFunctionList();
map<FixedHash<4>, FunctionTypePointer> exportedFunctions;
for (auto const& it: exportedFunctionList)
exportedFunctions.insert(it);
solAssert(exportedFunctionList.size() == exportedFunctions.size(),
"Hash collision at Function Definition Hash calculation");
sort(exportedFunctions.begin(), exportedFunctions.end(), compareNames);
return exportedFunctions;
}
FunctionDefinition const* ContractDefinition::getConstructor() const
{
for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions)
if (f->getName() == getName())
if (f->isConstructor())
return f.get();
return nullptr;
}
FunctionDefinition const* ContractDefinition::getFallbackFunction() const
{
for (ContractDefinition const* contract: getLinearizedBaseContracts())
for (ASTPointer<FunctionDefinition> const& f: contract->getDefinedFunctions())
if (f->getName().empty())
return f.get();
return nullptr;
}
void ContractDefinition::checkIllegalOverrides() const
{
// TODO unify this at a later point. for this we need to put the constness and the access specifier
// into the types
map<string, FunctionDefinition const*> functions;
map<string, ModifierDefinition const*> modifiers;
// We search from derived to base, so the stored item causes the error.
for (ContractDefinition const* contract: getLinearizedBaseContracts())
{
for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions())
{
if (function->isConstructor())
continue; // constructors can neither be overridden nor override anything
string const& name = function->getName();
if (modifiers.count(name))
BOOST_THROW_EXCEPTION(modifiers[name]->createTypeError("Override changes function to modifier."));
FunctionDefinition const*& override = functions[name];
if (!override)
override = function.get();
else if (override->getVisibility() != function->getVisibility() ||
override->isDeclaredConst() != function->isDeclaredConst() ||
FunctionType(*override) != FunctionType(*function))
BOOST_THROW_EXCEPTION(override->createTypeError("Override changes extended function signature."));
}
for (ASTPointer<ModifierDefinition> const& modifier: contract->getFunctionModifiers())
{
string const& name = modifier->getName();
if (functions.count(name))
BOOST_THROW_EXCEPTION(functions[name]->createTypeError("Override changes modifier to function."));
ModifierDefinition const*& override = modifiers[name];
if (!override)
override = modifier.get();
else if (ModifierType(*override) != ModifierType(*modifier))
BOOST_THROW_EXCEPTION(override->createTypeError("Override changes modifier signature."));
}
}
}
std::vector<ASTPointer<EventDefinition>> const& ContractDefinition::getInterfaceEvents() const
{
if (!m_interfaceEvents)
{
set<string> eventsSeen;
m_interfaceEvents.reset(new std::vector<ASTPointer<EventDefinition>>());
for (ContractDefinition const* contract: getLinearizedBaseContracts())
for (ASTPointer<EventDefinition> const& e: contract->getEvents())
if (eventsSeen.count(e->getName()) == 0)
{
eventsSeen.insert(e->getName());
m_interfaceEvents->push_back(e);
}
}
return *m_interfaceEvents;
}
vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::getInterfaceFunctionList() const
{
if (!m_interfaceFunctionList)
{
set<string> functionsSeen;
m_interfaceFunctionList.reset(new vector<pair<FixedHash<4>, FunctionTypePointer>>());
for (ContractDefinition const* contract: getLinearizedBaseContracts())
{
for (ASTPointer<FunctionDefinition> const& f: contract->getDefinedFunctions())
if (f->isPublic() && !f->isConstructor() && !f->getName().empty() && functionsSeen.count(f->getName()) == 0)
{
functionsSeen.insert(f->getName());
FixedHash<4> hash(dev::sha3(f->getCanonicalSignature()));
m_interfaceFunctionList->push_back(make_pair(hash, make_shared<FunctionType>(*f, false)));
}
for (ASTPointer<VariableDeclaration> const& v: contract->getStateVariables())
if (v->isPublic() && functionsSeen.count(v->getName()) == 0)
{
FunctionType ftype(*v);
functionsSeen.insert(v->getName());
FixedHash<4> hash(dev::sha3(ftype.getCanonicalSignature(v->getName())));
m_interfaceFunctionList->push_back(make_pair(hash, make_shared<FunctionType>(*v)));
}
}
}
return *m_interfaceFunctionList;
}
TypePointer EnumValue::getType(ContractDefinition const*) const
{
EnumDefinition const* parentDef = dynamic_cast<EnumDefinition const*>(getScope());
solAssert(parentDef, "Enclosing Scope of EnumValue was not set");
return make_shared<EnumType>(*parentDef);
}
void InheritanceSpecifier::checkTypeRequirements()
{
m_baseName->checkTypeRequirements();
for (ASTPointer<Expression> const& argument: m_arguments)
argument->checkTypeRequirements();
ContractDefinition const* base = dynamic_cast<ContractDefinition const*>(m_baseName->getReferencedDeclaration());
solAssert(base, "Base contract not available.");
TypePointers parameterTypes = ContractType(*base).getConstructorType()->getParameterTypes();
if (parameterTypes.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for constructor call."));
for (size_t i = 0; i < m_arguments.size(); ++i)
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i]))
BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in constructer call."));
}
TypePointer StructDefinition::getType(ContractDefinition const*) const
{
return make_shared<TypeType>(make_shared<StructType>(*this));
}
void StructDefinition::checkMemberTypes() const
{
for (ASTPointer<VariableDeclaration> const& member: getMembers())
@ -93,7 +254,7 @@ void StructDefinition::checkRecursion() const
<< errinfo_comment("Recursive struct definition."));
definitionsSeen.insert(def);
for (ASTPointer<VariableDeclaration> const& member: def->getMembers())
if (member->getType()->getCategory() == Type::Category::STRUCT)
if (member->getType()->getCategory() == Type::Category::Struct)
{
UserDefinedTypeName const& typeName = dynamic_cast<UserDefinedTypeName const&>(*member->getTypeName());
queue.push_back(&dynamic_cast<StructDefinition const&>(*typeName.getReferencedDeclaration()));
@ -101,15 +262,89 @@ void StructDefinition::checkRecursion() const
}
}
TypePointer EnumDefinition::getType(ContractDefinition const*) const
{
return make_shared<TypeType>(make_shared<EnumType>(*this));
}
TypePointer FunctionDefinition::getType(ContractDefinition const*) const
{
return make_shared<FunctionType>(*this);
}
void FunctionDefinition::checkTypeRequirements()
{
for (ASTPointer<VariableDeclaration> const& var: getParameters() + getReturnParameters())
if (!var->getType()->canLiveOutsideStorage())
BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage."));
for (ASTPointer<ModifierInvocation> const& modifier: m_functionModifiers)
modifier->checkTypeRequirements();
m_body->checkTypeRequirements();
}
string FunctionDefinition::getCanonicalSignature() const
{
return FunctionType(*this).getCanonicalSignature(getName());
}
bool VariableDeclaration::isLValue() const
{
// External function parameters are Read-Only
return !isExternalFunctionParameter();
}
bool VariableDeclaration::isExternalFunctionParameter() const
{
auto const* function = dynamic_cast<FunctionDefinition const*>(getScope());
if (!function || function->getVisibility() != Declaration::Visibility::External)
return false;
for (auto const& variable: function->getParameters())
if (variable.get() == this)
return true;
return false;
}
TypePointer ModifierDefinition::getType(ContractDefinition const*) const
{
return make_shared<ModifierType>(*this);
}
void ModifierDefinition::checkTypeRequirements()
{
m_body->checkTypeRequirements();
}
void ModifierInvocation::checkTypeRequirements()
{
m_modifierName->checkTypeRequirements();
for (ASTPointer<Expression> const& argument: m_arguments)
argument->checkTypeRequirements();
ModifierDefinition const* modifier = dynamic_cast<ModifierDefinition const*>(m_modifierName->getReferencedDeclaration());
solAssert(modifier, "Function modifier not found.");
vector<ASTPointer<VariableDeclaration>> const& parameters = modifier->getParameters();
if (parameters.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for modifier invocation."));
for (size_t i = 0; i < m_arguments.size(); ++i)
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameters[i]->getType()))
BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in modifier invocation."));
}
void EventDefinition::checkTypeRequirements()
{
int numIndexed = 0;
for (ASTPointer<VariableDeclaration> const& var: getParameters())
{
if (var->isIndexed())
numIndexed++;
if (!var->getType()->canLiveOutsideStorage())
BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage."));
}
if (numIndexed > 3)
BOOST_THROW_EXCEPTION(createTypeError("More than 3 indexed arguments for event."));
}
void Block::checkTypeRequirements()
{
for (shared_ptr<Statement> const& statement: m_statements)
@ -145,7 +380,8 @@ void Return::checkTypeRequirements()
{
if (!m_expression)
return;
solAssert(m_returnParameters, "Return parameters not assigned.");
if (!m_returnParameters)
BOOST_THROW_EXCEPTION(createTypeError("Return arguments not allowed."));
if (m_returnParameters->getParameters().size() != 1)
BOOST_THROW_EXCEPTION(createTypeError("Different number of arguments in return statement "
"than in returns declaration."));
@ -168,7 +404,17 @@ void VariableDefinition::checkTypeRequirements()
{
// no type declared and no previous assignment, infer the type
m_value->checkTypeRequirements();
m_variable->setType(m_value->getType());
TypePointer type = m_value->getType();
if (type->getCategory() == Type::Category::IntegerConstant)
{
auto intType = dynamic_pointer_cast<IntegerConstantType const>(type)->getIntegerType();
if (!intType)
BOOST_THROW_EXCEPTION(m_value->createTypeError("Invalid integer constant " + type->toString()));
type = intType;
}
else if (type->getCategory() == Type::Category::Void)
BOOST_THROW_EXCEPTION(m_variable->createTypeError("var cannot be void type"));
m_variable->setType(type);
}
}
}
@ -177,20 +423,31 @@ void Assignment::checkTypeRequirements()
{
m_leftHandSide->checkTypeRequirements();
m_leftHandSide->requireLValue();
//@todo later, assignments to structs might be possible, but not to mappings
if (!m_leftHandSide->getType()->isValueType() && !m_leftHandSide->isLocalLValue())
BOOST_THROW_EXCEPTION(createTypeError("Assignment to non-local non-value lvalue."));
m_rightHandSide->expectType(*m_leftHandSide->getType());
if (m_leftHandSide->getType()->getCategory() == Type::Category::Mapping)
BOOST_THROW_EXCEPTION(createTypeError("Mappings cannot be assigned to."));
m_type = m_leftHandSide->getType();
if (m_assigmentOperator != Token::ASSIGN)
if (m_assigmentOperator == Token::Assign)
m_rightHandSide->expectType(*m_type);
else
{
// compound assignment
if (!m_type->acceptsBinaryOperator(Token::AssignmentToBinaryOp(m_assigmentOperator)))
BOOST_THROW_EXCEPTION(createTypeError("Operator not compatible with type."));
m_rightHandSide->checkTypeRequirements();
TypePointer resultType = m_type->binaryOperatorResult(Token::AssignmentToBinaryOp(m_assigmentOperator),
m_rightHandSide->getType());
if (!resultType || *resultType != *m_type)
BOOST_THROW_EXCEPTION(createTypeError("Operator " + string(Token::toString(m_assigmentOperator)) +
" not compatible with types " +
m_type->toString() + " and " +
m_rightHandSide->getType()->toString()));
}
}
void ExpressionStatement::checkTypeRequirements()
{
m_expression->checkTypeRequirements();
if (m_expression->getType()->getCategory() == Type::Category::IntegerConstant)
if (!dynamic_pointer_cast<IntegerConstantType const>(m_expression->getType())->getIntegerType())
BOOST_THROW_EXCEPTION(m_expression->createTypeError("Invalid integer constant."));
}
void Expression::expectType(Type const& _expectedType)
@ -212,12 +469,12 @@ void Expression::requireLValue()
void UnaryOperation::checkTypeRequirements()
{
// INC, DEC, ADD, SUB, NOT, BIT_NOT, DELETE
// Inc, Dec, Add, Sub, Not, BitNot, Delete
m_subExpression->checkTypeRequirements();
if (m_operator == Token::Value::INC || m_operator == Token::Value::DEC || m_operator == Token::Value::DELETE)
if (m_operator == Token::Value::Inc || m_operator == Token::Value::Dec || m_operator == Token::Value::Delete)
m_subExpression->requireLValue();
m_type = m_subExpression->getType();
if (!m_type->acceptsUnaryOperator(m_operator))
m_type = m_subExpression->getType()->unaryOperatorResult(m_operator);
if (!m_type)
BOOST_THROW_EXCEPTION(createTypeError("Unary operator not compatible with type."));
}
@ -225,24 +482,13 @@ void BinaryOperation::checkTypeRequirements()
{
m_left->checkTypeRequirements();
m_right->checkTypeRequirements();
if (m_right->getType()->isImplicitlyConvertibleTo(*m_left->getType()))
m_commonType = m_left->getType();
else if (m_left->getType()->isImplicitlyConvertibleTo(*m_right->getType()))
m_commonType = m_right->getType();
else
BOOST_THROW_EXCEPTION(createTypeError("No common type found in binary operation: " +
m_left->getType()->toString() + " vs. " +
m_commonType = m_left->getType()->binaryOperatorResult(m_operator, m_right->getType());
if (!m_commonType)
BOOST_THROW_EXCEPTION(createTypeError("Operator " + string(Token::toString(m_operator)) +
" not compatible with types " +
m_left->getType()->toString() + " and " +
m_right->getType()->toString()));
if (Token::isCompareOp(m_operator))
m_type = make_shared<BoolType>();
else
{
m_type = m_commonType;
if (!m_commonType->acceptsBinaryOperator(m_operator))
BOOST_THROW_EXCEPTION(createTypeError("Operator " + string(Token::toString(m_operator)) +
" not compatible with type " +
m_commonType->toString()));
}
m_type = Token::isCompareOp(m_operator) ? make_shared<BoolType>() : m_commonType;
}
void FunctionCall::checkTypeRequirements()
@ -258,8 +504,9 @@ void FunctionCall::checkTypeRequirements()
//@todo for structs, we have to check the number of arguments to be equal to the
// number of non-mapping members
if (m_arguments.size() != 1)
BOOST_THROW_EXCEPTION(createTypeError("More than one argument for "
"explicit type conersion."));
BOOST_THROW_EXCEPTION(createTypeError("More than one argument for explicit type conversion."));
if (!m_names.empty())
BOOST_THROW_EXCEPTION(createTypeError("Type conversion cannot allow named arguments."));
if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type.getActualType()))
BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed."));
m_type = type.getActualType();
@ -270,11 +517,48 @@ void FunctionCall::checkTypeRequirements()
// and then ask if that is implicitly convertible to the struct represented by the
// function parameters
TypePointers const& parameterTypes = functionType->getParameterTypes();
if (parameterTypes.size() != m_arguments.size())
if (!functionType->takesArbitraryParameters() && parameterTypes.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call."));
for (size_t i = 0; i < m_arguments.size(); ++i)
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i]))
BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in function call."));
if (m_names.empty())
{
for (size_t i = 0; i < m_arguments.size(); ++i)
if (!functionType->takesArbitraryParameters() &&
!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i]))
BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError("Invalid type for argument in function call."));
}
else
{
if (functionType->takesArbitraryParameters())
BOOST_THROW_EXCEPTION(createTypeError("Named arguments cannnot be used for functions "
"that take arbitrary parameters."));
auto const& parameterNames = functionType->getParameterNames();
if (parameterNames.size() != m_names.size())
BOOST_THROW_EXCEPTION(createTypeError("Some argument names are missing."));
// check duplicate names
for (size_t i = 0; i < m_names.size(); i++)
for (size_t j = i + 1; j < m_names.size(); j++)
if (*m_names[i] == *m_names[j])
BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError("Duplicate named argument."));
for (size_t i = 0; i < m_names.size(); i++) {
bool found = false;
for (size_t j = 0; j < parameterNames.size(); j++) {
if (parameterNames[j] == *m_names[i]) {
// check type convertible
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[j]))
BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in function call."));
found = true;
break;
}
}
if (!found)
BOOST_THROW_EXCEPTION(createTypeError("Named argument does not match function declaration."));
}
}
// @todo actually the return type should be an anonymous struct,
// but we change it to the type of the first return value until we have structs
if (functionType->getReturnParameterTypes().empty())
@ -288,26 +572,19 @@ void FunctionCall::checkTypeRequirements()
bool FunctionCall::isTypeConversion() const
{
return m_expression->getType()->getCategory() == Type::Category::TYPE;
return m_expression->getType()->getCategory() == Type::Category::TypeType;
}
void NewExpression::checkTypeRequirements()
{
m_contractName->checkTypeRequirements();
for (ASTPointer<Expression> const& argument: m_arguments)
argument->checkTypeRequirements();
m_contract = dynamic_cast<ContractDefinition const*>(m_contractName->getReferencedDeclaration());
if (!m_contract)
BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract."));
shared_ptr<ContractType const> type = make_shared<ContractType const>(*m_contract);
m_type = type;
TypePointers const& parameterTypes = type->getConstructorType()->getParameterTypes();
if (parameterTypes.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for constructor call."));
for (size_t i = 0; i < m_arguments.size(); ++i)
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i]))
BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in constructor call."));
shared_ptr<ContractType const> contractType = make_shared<ContractType>(*m_contract);
TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes();
m_type = make_shared<FunctionType>(parameterTypes, TypePointers{contractType},
FunctionType::Location::Creation);
}
void MemberAccess::checkTypeRequirements()
@ -316,78 +593,43 @@ void MemberAccess::checkTypeRequirements()
Type const& type = *m_expression->getType();
m_type = type.getMemberType(*m_memberName);
if (!m_type)
BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found in " + type.toString()));
//@todo later, this will not always be STORAGE
m_lvalue = type.getCategory() == Type::Category::STRUCT ? LValueType::STORAGE : LValueType::NONE;
BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found or not "
"visible in " + type.toString()));
m_isLValue = (type.getCategory() == Type::Category::Struct);
}
void IndexAccess::checkTypeRequirements()
{
m_base->checkTypeRequirements();
if (m_base->getType()->getCategory() != Type::Category::MAPPING)
if (m_base->getType()->getCategory() != Type::Category::Mapping)
BOOST_THROW_EXCEPTION(m_base->createTypeError("Indexed expression has to be a mapping (is " +
m_base->getType()->toString() + ")"));
MappingType const& type = dynamic_cast<MappingType const&>(*m_base->getType());
m_index->expectType(*type.getKeyType());
m_type = type.getValueType();
m_lvalue = LValueType::STORAGE;
m_isLValue = true;
}
void Identifier::checkTypeRequirements()
{
solAssert(m_referencedDeclaration, "Identifier not resolved.");
VariableDeclaration const* variable = dynamic_cast<VariableDeclaration const*>(m_referencedDeclaration);
if (variable)
{
if (!variable->getType())
BOOST_THROW_EXCEPTION(createTypeError("Variable referenced before type could be determined."));
m_type = variable->getType();
m_lvalue = variable->isLocalVariable() ? LValueType::LOCAL : LValueType::STORAGE;
return;
}
//@todo can we unify these with TypeName::toType()?
StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(m_referencedDeclaration);
if (structDef)
{
// note that we do not have a struct type here
m_type = make_shared<TypeType const>(make_shared<StructType const>(*structDef));
return;
}
FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(m_referencedDeclaration);
if (functionDef)
{
// a function reference is not a TypeType, because calling a TypeType converts to the type.
// Calling a function (e.g. function(12), otherContract.function(34)) does not do a type
// conversion.
m_type = make_shared<FunctionType const>(*functionDef);
return;
}
ContractDefinition const* contractDef = dynamic_cast<ContractDefinition const*>(m_referencedDeclaration);
if (contractDef)
{
m_type = make_shared<TypeType const>(make_shared<ContractType>(*contractDef));
return;
}
MagicVariableDeclaration const* magicVariable = dynamic_cast<MagicVariableDeclaration const*>(m_referencedDeclaration);
if (magicVariable)
{
m_type = magicVariable->getType();
return;
}
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Declaration reference of unknown/forbidden type."));
m_isLValue = m_referencedDeclaration->isLValue();
m_type = m_referencedDeclaration->getType(m_currentContract);
if (!m_type)
BOOST_THROW_EXCEPTION(createTypeError("Declaration referenced before type could be determined."));
}
void ElementaryTypeNameExpression::checkTypeRequirements()
{
m_type = make_shared<TypeType const>(Type::fromElementaryTypeName(m_typeToken));
m_type = make_shared<TypeType>(Type::fromElementaryTypeName(m_typeToken));
}
void Literal::checkTypeRequirements()
{
m_type = Type::forLiteral(*this);
if (!m_type)
BOOST_THROW_EXCEPTION(createTypeError("Literal value too large."));
BOOST_THROW_EXCEPTION(createTypeError("Invalid literal value."));
}
}

393
AST.h
View File

@ -132,68 +132,167 @@ private:
class Declaration: public ASTNode
{
public:
Declaration(Location const& _location, ASTPointer<ASTString> const& _name):
ASTNode(_location), m_name(_name), m_scope(nullptr) {}
/// Visibility ordered from restricted to unrestricted.
enum class Visibility { Default, Private, Inheritable, Public, External };
Declaration(Location const& _location, ASTPointer<ASTString> const& _name,
Visibility _visibility = Visibility::Default):
ASTNode(_location), m_name(_name), m_visibility(_visibility), m_scope(nullptr) {}
/// @returns the declared name.
ASTString const& getName() const { return *m_name; }
Visibility getVisibility() const { return m_visibility == Visibility::Default ? getDefaultVisibility() : m_visibility; }
bool isPublic() const { return getVisibility() >= Visibility::Public; }
bool isVisibleInContract() const { return getVisibility() != Visibility::External; }
bool isVisibleInDerivedContracts() const { return isVisibleInContract() && getVisibility() >= Visibility::Inheritable; }
/// @returns the scope this declaration resides in. Can be nullptr if it is the global scope.
/// Available only after name and type resolution step.
Declaration const* getScope() const { return m_scope; }
void setScope(Declaration const* _scope) { m_scope = _scope; }
/// @returns the type of expressions referencing this declaration.
/// The current contract has to be given since this context can change the type, especially of
/// contract types.
virtual TypePointer getType(ContractDefinition const* m_currentContract = nullptr) const = 0;
virtual bool isLValue() const { return false; }
protected:
virtual Visibility getDefaultVisibility() const { return Visibility::Public; }
private:
ASTPointer<ASTString> m_name;
Visibility m_visibility;
Declaration const* m_scope;
};
/**
* Abstract class that is added to each AST node that can store local variables.
*/
class VariableScope
{
public:
void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); }
std::vector<VariableDeclaration const*> const& getLocalVariables() const { return m_localVariables; }
private:
std::vector<VariableDeclaration const*> m_localVariables;
};
/**
* Abstract class that is added to each AST node that can receive documentation.
*/
class Documented
{
public:
explicit Documented(ASTPointer<ASTString> const& _documentation): m_documentation(_documentation) {}
/// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
protected:
ASTPointer<ASTString> m_documentation;
};
/// @}
/**
* Definition of a contract. This is the only AST nodes where child nodes are not visited in
* document order. It first visits all struct declarations, then all variable declarations and
* finally all function declarations.
*/
class ContractDefinition: public Declaration
class ContractDefinition: public Declaration, public Documented
{
public:
ContractDefinition(Location const& _location,
ASTPointer<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation,
std::vector<ASTPointer<InheritanceSpecifier>> const& _baseContracts,
std::vector<ASTPointer<StructDefinition>> const& _definedStructs,
std::vector<ASTPointer<EnumDefinition>> const& _definedEnums,
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables,
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions):
Declaration(_location, _name),
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions,
std::vector<ASTPointer<ModifierDefinition>> const& _functionModifiers,
std::vector<ASTPointer<EventDefinition>> const& _events):
Declaration(_location, _name), Documented(_documentation),
m_baseContracts(_baseContracts),
m_definedStructs(_definedStructs),
m_definedEnums(_definedEnums),
m_stateVariables(_stateVariables),
m_definedFunctions(_definedFunctions),
m_documentation(_documentation)
m_functionModifiers(_functionModifiers),
m_events(_events)
{}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<InheritanceSpecifier>> const& getBaseContracts() const { return m_baseContracts; }
std::vector<ASTPointer<StructDefinition>> const& getDefinedStructs() const { return m_definedStructs; }
std::vector<ASTPointer<EnumDefinition>> const& getDefinedEnums() const { return m_definedEnums; }
std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() const { return m_stateVariables; }
std::vector<ASTPointer<ModifierDefinition>> const& getFunctionModifiers() const { return m_functionModifiers; }
std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() const { return m_definedFunctions; }
std::vector<ASTPointer<EventDefinition>> const& getEvents() const { return m_events; }
std::vector<ASTPointer<EventDefinition>> const& getInterfaceEvents() const;
/// Checks that the constructor does not have a "returns" statement and calls
/// checkTypeRequirements on all its functions.
virtual TypePointer getType(ContractDefinition const* m_currentContract) const override;
/// Checks that there are no illegal overrides, that the constructor does not have a "returns"
/// and calls checkTypeRequirements on all its functions.
void checkTypeRequirements();
/// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
/// @returns a map of canonical function signatures to FunctionDefinitions
/// as intended for use by the ABI.
std::map<FixedHash<4>, FunctionTypePointer> getInterfaceFunctions() const;
/// Returns the functions that make up the calling interface in the intended order.
std::vector<FunctionDefinition const*> getInterfaceFunctions() const;
/// List of all (direct and indirect) base contracts in order from derived to base, including
/// the contract itself. Available after name resolution
std::vector<ContractDefinition const*> const& getLinearizedBaseContracts() const { return m_linearizedBaseContracts; }
void setLinearizedBaseContracts(std::vector<ContractDefinition const*> const& _bases) { m_linearizedBaseContracts = _bases; }
/// Returns the constructor or nullptr if no constructor was specified
/// Returns the constructor or nullptr if no constructor was specified.
FunctionDefinition const* getConstructor() const;
/// Returns the fallback function or nullptr if no fallback function was specified.
FunctionDefinition const* getFallbackFunction() const;
private:
void checkIllegalOverrides() const;
std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& getInterfaceFunctionList() const;
std::vector<ASTPointer<InheritanceSpecifier>> m_baseContracts;
std::vector<ASTPointer<StructDefinition>> m_definedStructs;
std::vector<ASTPointer<EnumDefinition>> m_definedEnums;
std::vector<ASTPointer<VariableDeclaration>> m_stateVariables;
std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions;
ASTPointer<ASTString> m_documentation;
std::vector<ASTPointer<ModifierDefinition>> m_functionModifiers;
std::vector<ASTPointer<EventDefinition>> m_events;
std::vector<ContractDefinition const*> m_linearizedBaseContracts;
mutable std::unique_ptr<std::vector<std::pair<FixedHash<4>, FunctionTypePointer>>> m_interfaceFunctionList;
mutable std::unique_ptr<std::vector<ASTPointer<EventDefinition>>> m_interfaceEvents;
};
class InheritanceSpecifier: public ASTNode
{
public:
InheritanceSpecifier(Location const& _location, ASTPointer<Identifier> const& _baseName,
std::vector<ASTPointer<Expression>> _arguments):
ASTNode(_location), m_baseName(_baseName), m_arguments(_arguments) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
ASTPointer<Identifier> const& getName() const { return m_baseName; }
std::vector<ASTPointer<Expression>> const& getArguments() const { return m_arguments; }
void checkTypeRequirements();
private:
ASTPointer<Identifier> m_baseName;
std::vector<ASTPointer<Expression>> m_arguments;
};
class StructDefinition: public Declaration
@ -208,6 +307,8 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& getMembers() const { return m_members; }
virtual TypePointer getType(ContractDefinition const*) const override;
/// Checks that the members do not include any recursive structs and have valid types
/// (e.g. no functions).
void checkValidityOfMembers() const;
@ -219,6 +320,39 @@ private:
std::vector<ASTPointer<VariableDeclaration>> m_members;
};
class EnumDefinition: public Declaration
{
public:
EnumDefinition(Location const& _location,
ASTPointer<ASTString> const& _name,
std::vector<ASTPointer<EnumValue>> const& _members):
Declaration(_location, _name), m_members(_members) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<EnumValue>> const& getMembers() const { return m_members; }
virtual TypePointer getType(ContractDefinition const*) const override;
private:
std::vector<ASTPointer<EnumValue>> m_members;
};
/**
* Declaration of an Enum Value
*/
class EnumValue: public Declaration
{
public:
EnumValue(Location const& _location,
ASTPointer<ASTString> const& _name):
Declaration(_location, _name) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
TypePointer getType(ContractDefinition const* = nullptr) const;
};
/**
* Parameter list, used as function parameter list and return list.
* None of the parameters is allowed to contain mappings (not even recursively
@ -239,53 +373,55 @@ private:
std::vector<ASTPointer<VariableDeclaration>> m_parameters;
};
class FunctionDefinition: public Declaration
class FunctionDefinition: public Declaration, public VariableScope, public Documented
{
public:
FunctionDefinition(Location const& _location, ASTPointer<ASTString> const& _name,
bool _isPublic,
Declaration::Visibility _visibility, bool _isConstructor,
ASTPointer<ASTString> const& _documentation,
ASTPointer<ParameterList> const& _parameters,
bool _isDeclaredConst,
std::vector<ASTPointer<ModifierInvocation>> const& _modifiers,
ASTPointer<ParameterList> const& _returnParameters,
ASTPointer<Block> const& _body):
Declaration(_location, _name), m_isPublic(_isPublic),
Declaration(_location, _name, _visibility), Documented(_documentation),
m_isConstructor(_isConstructor),
m_parameters(_parameters),
m_isDeclaredConst(_isDeclaredConst),
m_functionModifiers(_modifiers),
m_returnParameters(_returnParameters),
m_body(_body),
m_documentation(_documentation)
m_body(_body)
{}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
bool isPublic() const { return m_isPublic; }
bool isConstructor() const { return m_isConstructor; }
bool isDeclaredConst() const { return m_isDeclaredConst; }
std::vector<ASTPointer<ModifierInvocation>> const& getModifiers() const { return m_functionModifiers; }
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); }
ParameterList const& getParameterList() const { return *m_parameters; }
std::vector<ASTPointer<VariableDeclaration>> const& getReturnParameters() const { return m_returnParameters->getParameters(); }
ASTPointer<ParameterList> const& getReturnParameterList() const { return m_returnParameters; }
Block const& getBody() const { return *m_body; }
/// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); }
std::vector<VariableDeclaration const*> const& getLocalVariables() const { return m_localVariables; }
virtual TypePointer getType(ContractDefinition const*) const override;
/// Checks that all parameters have allowed types and calls checkTypeRequirements on the body.
void checkTypeRequirements();
/// @returns the canonical signature of the function
/// That consists of the name of the function followed by the types of the
/// arguments separated by commas all enclosed in parentheses without any spaces.
std::string getCanonicalSignature() const;
private:
bool m_isPublic;
bool m_isConstructor;
ASTPointer<ParameterList> m_parameters;
bool m_isDeclaredConst;
std::vector<ASTPointer<ModifierInvocation>> m_functionModifiers;
ASTPointer<ParameterList> m_returnParameters;
ASTPointer<Block> m_body;
ASTPointer<ASTString> m_documentation;
std::vector<VariableDeclaration const*> m_localVariables;
};
/**
@ -296,8 +432,10 @@ class VariableDeclaration: public Declaration
{
public:
VariableDeclaration(Location const& _location, ASTPointer<TypeName> const& _type,
ASTPointer<ASTString> const& _name):
Declaration(_location, _name), m_typeName(_type) {}
ASTPointer<ASTString> const& _name, Visibility _visibility,
bool _isStateVar = false, bool _isIndexed = false):
Declaration(_location, _name, _visibility), m_typeName(_type),
m_isStateVariable(_isStateVar), m_isIndexed(_isIndexed) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
@ -305,17 +443,108 @@ public:
/// Returns the declared or inferred type. Can be an empty pointer if no type was explicitly
/// declared and there is no assignment to the variable that fixes the type.
std::shared_ptr<Type const> const& getType() const { return m_type; }
TypePointer getType(ContractDefinition const* = nullptr) const { return m_type; }
void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; }
virtual bool isLValue() const override;
bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); }
bool isExternalFunctionParameter() const;
bool isStateVariable() const { return m_isStateVariable; }
bool isIndexed() const { return m_isIndexed; }
protected:
Visibility getDefaultVisibility() const override { return Visibility::Inheritable; }
private:
ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
bool m_isStateVariable; ///< Whether or not this is a contract state variable
bool m_isIndexed; ///< Whether this is an indexed variable (used by events).
std::shared_ptr<Type const> m_type; ///< derived type, initially empty
};
/**
* Definition of a function modifier.
*/
class ModifierDefinition: public Declaration, public VariableScope, public Documented
{
public:
ModifierDefinition(Location const& _location,
ASTPointer<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation,
ASTPointer<ParameterList> const& _parameters,
ASTPointer<Block> const& _body):
Declaration(_location, _name), Documented(_documentation),
m_parameters(_parameters), m_body(_body) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); }
ParameterList const& getParameterList() const { return *m_parameters; }
Block const& getBody() const { return *m_body; }
virtual TypePointer getType(ContractDefinition const* = nullptr) const override;
void checkTypeRequirements();
private:
ASTPointer<ParameterList> m_parameters;
ASTPointer<Block> m_body;
};
/**
* Invocation/usage of a modifier in a function header.
*/
class ModifierInvocation: public ASTNode
{
public:
ModifierInvocation(Location const& _location, ASTPointer<Identifier> const& _name,
std::vector<ASTPointer<Expression>> _arguments):
ASTNode(_location), m_modifierName(_name), m_arguments(_arguments) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
ASTPointer<Identifier> const& getName() const { return m_modifierName; }
std::vector<ASTPointer<Expression>> const& getArguments() const { return m_arguments; }
void checkTypeRequirements();
private:
ASTPointer<Identifier> m_modifierName;
std::vector<ASTPointer<Expression>> m_arguments;
};
/**
* Definition of a (loggable) event.
*/
class EventDefinition: public Declaration, public VariableScope, public Documented
{
public:
EventDefinition(Location const& _location,
ASTPointer<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation,
ASTPointer<ParameterList> const& _parameters):
Declaration(_location, _name), Documented(_documentation), m_parameters(_parameters) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); }
ParameterList const& getParameterList() const { return *m_parameters; }
virtual TypePointer getType(ContractDefinition const* = nullptr) const override
{
return std::make_shared<FunctionType>(*this);
}
void checkTypeRequirements();
private:
ASTPointer<ParameterList> m_parameters;
};
/**
* Pseudo AST node that is used as declaration for "this", "msg", "tx", "block" and the global
* functions when such an identifier is encountered. Will never have a valid location in the source code.
@ -330,7 +559,7 @@ public:
virtual void accept(ASTConstVisitor&) const override { BOOST_THROW_EXCEPTION(InternalCompilerError()
<< errinfo_comment("MagicVariableDeclaration used inside real AST.")); }
std::shared_ptr<Type const> const& getType() const { return m_type; }
virtual TypePointer getType(ContractDefinition const* = nullptr) const override { return m_type; }
private:
std::shared_ptr<Type const> m_type;
@ -457,6 +686,21 @@ private:
std::vector<ASTPointer<Statement>> m_statements;
};
/**
* Special placeholder statement denoted by "_" used in function modifiers. This is replaced by
* the original function when the modifier is applied.
*/
class PlaceholderStatement: public Statement
{
public:
PlaceholderStatement(Location const& _location): Statement(_location) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override { }
};
/**
* If-statement with an optional "else" part. Note that "else if" is modeled by having a new
* if-statement as the false (else) body.
@ -474,7 +718,7 @@ public:
Expression const& getCondition() const { return *m_condition; }
Statement const& getTrueStatement() const { return *m_trueBody; }
/// @returns the "else" part of the if statement or nullptr if there is no "else" part.
/// @returns the "else" part of the if statement or nullptr if there is no "else" part.
Statement const* getFalseStatement() const { return m_falseBody.get(); }
private:
@ -573,19 +817,15 @@ public:
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override;
void setFunctionReturnParameters(ParameterList& _parameters) { m_returnParameters = &_parameters; }
ParameterList const& getFunctionReturnParameters() const
{
solAssert(m_returnParameters, "");
return *m_returnParameters;
}
void setFunctionReturnParameters(ParameterList const* _parameters) { m_returnParameters = _parameters; }
ParameterList const* getFunctionReturnParameters() const { return m_returnParameters; }
Expression const* getExpression() const { return m_expression.get(); }
private:
ASTPointer<Expression> m_expression; ///< value to return, optional
/// Pointer to the parameter list of the function, filled by the @ref NameAndTypeResolver.
ParameterList* m_returnParameters;
ParameterList const* m_returnParameters;
};
/**
@ -641,16 +881,12 @@ private:
*/
class Expression: public ASTNode
{
protected:
enum class LValueType { NONE, LOCAL, STORAGE };
public:
Expression(Location const& _location): ASTNode(_location), m_lvalue(LValueType::NONE), m_lvalueRequested(false) {}
Expression(Location const& _location): ASTNode(_location) {}
virtual void checkTypeRequirements() = 0;
std::shared_ptr<Type const> const& getType() const { return m_type; }
bool isLValue() const { return m_lvalue != LValueType::NONE; }
bool isLocalLValue() const { return m_lvalue == LValueType::LOCAL; }
bool isLValue() const { return m_isLValue; }
/// Helper function, infer the type via @ref checkTypeRequirements and then check that it
/// is implicitly convertible to @a _expectedType. If not, throw exception.
@ -665,11 +901,11 @@ public:
protected:
//! Inferred type of the expression, only filled after a call to checkTypeRequirements().
std::shared_ptr<Type const> m_type;
//! If this expression is an lvalue (i.e. something that can be assigned to) and is stored
//! locally or in storage. This is set during calls to @a checkTypeRequirements()
LValueType m_lvalue;
//! If this expression is an lvalue (i.e. something that can be assigned to).
//! This is set during calls to @a checkTypeRequirements()
bool m_isLValue = false;
//! Whether the outer expression requested the address (true) or the value (false) of this expression.
bool m_lvalueRequested;
bool m_lvalueRequested = false;
};
/// Assignment, can also be a compound assignment.
@ -765,14 +1001,15 @@ class FunctionCall: public Expression
{
public:
FunctionCall(Location const& _location, ASTPointer<Expression> const& _expression,
std::vector<ASTPointer<Expression>> const& _arguments):
Expression(_location), m_expression(_expression), m_arguments(_arguments) {}
std::vector<ASTPointer<Expression>> const& _arguments, std::vector<ASTPointer<ASTString>> const& _names):
Expression(_location), m_expression(_expression), m_arguments(_arguments), m_names(_names) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override;
Expression const& getExpression() const { return *m_expression; }
std::vector<ASTPointer<Expression const>> getArguments() const { return {m_arguments.begin(), m_arguments.end()}; }
std::vector<ASTPointer<ASTString>> const& getNames() const { return m_names; }
/// Returns true if this is not an actual function call, but an explicit type conversion
/// or constructor call.
@ -781,29 +1018,26 @@ public:
private:
ASTPointer<Expression> m_expression;
std::vector<ASTPointer<Expression>> m_arguments;
std::vector<ASTPointer<ASTString>> m_names;
};
/**
* Expression that creates a new contract, e.g. "new SomeContract(1, 2)".
* Expression that creates a new contract, e.g. the "new SomeContract" part in "new SomeContract(1, 2)".
*/
class NewExpression: public Expression
{
public:
NewExpression(Location const& _location, ASTPointer<Identifier> const& _contractName,
std::vector<ASTPointer<Expression>> const& _arguments):
Expression(_location), m_contractName(_contractName), m_arguments(_arguments) {}
NewExpression(Location const& _location, ASTPointer<Identifier> const& _contractName):
Expression(_location), m_contractName(_contractName) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override;
std::vector<ASTPointer<Expression const>> getArguments() const { return {m_arguments.begin(), m_arguments.end()}; }
/// Returns the referenced contract. Can only be called after type checking.
ContractDefinition const* getContract() const { solAssert(m_contract, ""); return m_contract; }
private:
ASTPointer<Identifier> m_contractName;
std::vector<ASTPointer<Expression>> m_arguments;
ContractDefinition const* m_contract = nullptr;
};
@ -866,21 +1100,30 @@ class Identifier: public PrimaryExpression
{
public:
Identifier(Location const& _location, ASTPointer<ASTString> const& _name):
PrimaryExpression(_location), m_name(_name), m_referencedDeclaration(nullptr) {}
PrimaryExpression(_location), m_name(_name) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override;
ASTString const& getName() const { return *m_name; }
void setReferencedDeclaration(Declaration const& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; }
void setReferencedDeclaration(Declaration const& _referencedDeclaration,
ContractDefinition const* _currentContract = nullptr)
{
m_referencedDeclaration = &_referencedDeclaration;
m_currentContract = _currentContract;
}
Declaration const* getReferencedDeclaration() const { return m_referencedDeclaration; }
ContractDefinition const* getCurrentContract() const { return m_currentContract; }
private:
ASTPointer<ASTString> m_name;
/// Declaration the name refers to.
Declaration const* m_referencedDeclaration;
Declaration const* m_referencedDeclaration = nullptr;
/// Stores a reference to the current contract. This is needed because types of base contracts
/// change depending on the context.
ContractDefinition const* m_currentContract = nullptr;
};
/**
@ -907,13 +1150,23 @@ private:
};
/**
* A literal string or number. @see Type::literalToBigEndian is used to actually parse its value.
* A literal string or number. @see ExpressionCompiler::endVisit() is used to actually parse its value.
*/
class Literal: public PrimaryExpression
{
public:
Literal(Location const& _location, Token::Value _token, ASTPointer<ASTString> const& _value):
PrimaryExpression(_location), m_token(_token), m_value(_value) {}
enum class SubDenomination
{
None = Token::Illegal,
Wei = Token::SubWei,
Szabo = Token::SubSzabo,
Finney = Token::SubFinney,
Ether = Token::SubEther
};
Literal(Location const& _location, Token::Value _token,
ASTPointer<ASTString> const& _value,
SubDenomination _sub = SubDenomination::None):
PrimaryExpression(_location), m_token(_token), m_value(_value), m_subDenomination(_sub) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override;
@ -922,12 +1175,16 @@ public:
/// @returns the non-parsed value of the literal
ASTString const& getValue() const { return *m_value; }
SubDenomination getSubDenomination() const { return m_subDenomination; }
private:
Token::Value m_token;
ASTPointer<ASTString> m_value;
SubDenomination m_subDenomination;
};
/// @}
}
}

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

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 <boost/range/adaptor/reversed.hpp>
#include <libevmcore/Instruction.h>
#include <libevmcore/Assembly.h>
#include <libsolidity/AST.h>
#include <libsolidity/Compiler.h>
#include <libsolidity/ExpressionCompiler.h>
#include <libsolidity/CompilerUtils.h>
#include <libsolidity/CallGraph.h>
using namespace std;
namespace dev {
namespace solidity {
void Compiler::compileContract(ContractDefinition const& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals,
void Compiler::compileContract(ContractDefinition const& _contract,
map<ContractDefinition const*, bytes const*> const& _contracts)
{
m_context = CompilerContext(); // clear it just in case
initializeContext(_contract, _magicGlobals, _contracts);
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
if (function->getName() != _contract.getName()) // don't add the constructor here
m_context.addFunction(*function);
initializeContext(_contract, _contracts);
appendFunctionSelector(_contract);
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
if (function->getName() != _contract.getName()) // don't add the constructor here
set<Declaration const*> functions = m_context.getFunctionsWithoutCode();
while (!functions.empty())
{
for (Declaration const* function: functions)
function->accept(*this);
functions = m_context.getFunctionsWithoutCode();
}
// Swap the runtime context with the creation-time context
CompilerContext runtimeContext;
swap(m_context, runtimeContext);
initializeContext(_contract, _magicGlobals, _contracts);
packIntoContractCreator(_contract, runtimeContext);
swap(m_context, m_runtimeContext);
initializeContext(_contract, _contracts);
packIntoContractCreator(_contract, m_runtimeContext);
}
void Compiler::initializeContext(ContractDefinition const& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals,
void Compiler::initializeContext(ContractDefinition const& _contract,
map<ContractDefinition const*, bytes const*> const& _contracts)
{
m_context.setCompiledContracts(_contracts);
for (MagicVariableDeclaration const* variable: _magicGlobals)
m_context.addMagicGlobal(*variable);
m_context.setInheritanceHierarchy(_contract.getLinearizedBaseContracts());
registerStateVariables(_contract);
}
void Compiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext)
{
set<FunctionDefinition const*> neededFunctions;
FunctionDefinition const* constructor = _contract.getConstructor();
if (constructor)
neededFunctions = getFunctionsNeededByConstructor(*constructor);
// arguments for base constructors, filled in derived-to-base order
map<ContractDefinition const*, vector<ASTPointer<Expression>> const*> baseArguments;
for (FunctionDefinition const* fun: neededFunctions)
m_context.addFunction(*fun);
// Determine the arguments that are used for the base constructors.
std::vector<ContractDefinition const*> const& bases = _contract.getLinearizedBaseContracts();
for (ContractDefinition const* contract: bases)
for (ASTPointer<InheritanceSpecifier> const& base: contract->getBaseContracts())
{
ContractDefinition const* baseContract = dynamic_cast<ContractDefinition const*>(
base->getName()->getReferencedDeclaration());
solAssert(baseContract, "");
if (baseArguments.count(baseContract) == 0)
baseArguments[baseContract] = &base->getArguments();
}
if (constructor)
appendConstructorCall(*constructor);
// Call constructors in base-to-derived order.
// The Constructor for the most derived contract is called later.
for (unsigned i = 1; i < bases.size(); i++)
{
ContractDefinition const* base = bases[bases.size() - i];
solAssert(base, "");
FunctionDefinition const* baseConstructor = base->getConstructor();
if (!baseConstructor)
continue;
solAssert(baseArguments[base], "");
appendBaseConstructorCall(*baseConstructor, *baseArguments[base]);
}
if (_contract.getConstructor())
appendConstructorCall(*_contract.getConstructor());
eth::AssemblyItem sub = m_context.addSubroutine(_runtimeContext.getAssembly());
// stack contains sub size
m_context << eth::Instruction::DUP1 << sub << u256(0) << eth::Instruction::CODECOPY;
m_context << u256(0) << eth::Instruction::RETURN;
// note that we have to explicitly include all used functions because of absolute jump
// labels
for (FunctionDefinition const* fun: neededFunctions)
fun->accept(*this);
// note that we have to include the functions again because of absolute jump labels
set<Declaration const*> functions = m_context.getFunctionsWithoutCode();
while (!functions.empty())
{
for (Declaration const* function: functions)
function->accept(*this);
functions = m_context.getFunctionsWithoutCode();
}
}
void Compiler::appendBaseConstructorCall(FunctionDefinition const& _constructor,
vector<ASTPointer<Expression>> const& _arguments)
{
FunctionType constructorType(_constructor);
eth::AssemblyItem returnLabel = m_context.pushNewTag();
for (unsigned i = 0; i < _arguments.size(); ++i)
compileExpression(*_arguments[i], constructorType.getParameterTypes()[i]);
m_context.appendJumpTo(m_context.getFunctionEntryLabel(_constructor));
m_context << returnLabel;
}
void Compiler::appendConstructorCall(FunctionDefinition const& _constructor)
@ -95,104 +126,115 @@ void Compiler::appendConstructorCall(FunctionDefinition const& _constructor)
// copy constructor arguments from code to memory and then to stack, they are supplied after the actual program
unsigned argumentSize = 0;
for (ASTPointer<VariableDeclaration> const& var: _constructor.getParameters())
argumentSize += var->getType()->getCalldataEncodedSize();
argumentSize += CompilerUtils::getPaddedSize(var->getType()->getCalldataEncodedSize());
if (argumentSize > 0)
{
m_context << u256(argumentSize);
m_context.appendProgramSize();
m_context << u256(1); // copy it to byte one as expected for ABI calls
m_context << u256(CompilerUtils::dataStartOffset); // copy it to byte four as expected for ABI calls
m_context << eth::Instruction::CODECOPY;
appendCalldataUnpacker(_constructor, true);
appendCalldataUnpacker(FunctionType(_constructor).getParameterTypes(), true);
}
m_context.appendJumpTo(m_context.getFunctionEntryLabel(_constructor));
m_context << returnTag;
}
set<FunctionDefinition const*> Compiler::getFunctionsNeededByConstructor(FunctionDefinition const& _constructor)
{
CallGraph callgraph;
callgraph.addFunction(_constructor);
callgraph.computeCallGraph();
return callgraph.getCalls();
}
void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
{
vector<FunctionDefinition const*> interfaceFunctions = _contract.getInterfaceFunctions();
vector<eth::AssemblyItem> callDataUnpackerEntryPoints;
map<FixedHash<4>, FunctionTypePointer> interfaceFunctions = _contract.getInterfaceFunctions();
map<FixedHash<4>, const eth::AssemblyItem> callDataUnpackerEntryPoints;
if (interfaceFunctions.size() > 255)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("More than 255 public functions for contract."));
// retrieve the function signature hash from the calldata
if (!interfaceFunctions.empty())
CompilerUtils(m_context).loadFromMemory(0, IntegerType(CompilerUtils::dataStartOffset * 8), true);
// retrieve the first byte of the call data, which determines the called function
// @todo This code had a jump table in a previous version which was more efficient but also
// error prone (due to the optimizer and variable length tag addresses)
m_context << u256(1) << u256(0) // some constants
<< eth::dupInstruction(1) << eth::Instruction::CALLDATALOAD
<< eth::dupInstruction(2) << eth::Instruction::BYTE
<< eth::dupInstruction(2);
// stack here: 1 0 <funid> 0, stack top will be counted up until it matches funid
for (unsigned funid = 0; funid < interfaceFunctions.size(); ++funid)
// stack now is: 1 0 <funhash>
for (auto const& it: interfaceFunctions)
{
callDataUnpackerEntryPoints.push_back(m_context.newTag());
m_context << eth::dupInstruction(2) << eth::dupInstruction(2) << eth::Instruction::EQ;
m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.back());
if (funid < interfaceFunctions.size() - 1)
m_context << eth::dupInstruction(4) << eth::Instruction::ADD;
callDataUnpackerEntryPoints.insert(std::make_pair(it.first, m_context.newTag()));
m_context << eth::dupInstruction(1) << u256(FixedHash<4>::Arith(it.first)) << eth::Instruction::EQ;
m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.at(it.first));
}
m_context << eth::Instruction::STOP; // function not found
for (unsigned funid = 0; funid < interfaceFunctions.size(); ++funid)
if (FunctionDefinition const* fallback = _contract.getFallbackFunction())
{
FunctionDefinition const& function = *interfaceFunctions[funid];
m_context << callDataUnpackerEntryPoints[funid];
eth::AssemblyItem returnTag = m_context.pushNewTag();
appendCalldataUnpacker(function);
m_context.appendJumpTo(m_context.getFunctionEntryLabel(function));
fallback->accept(*this);
m_context << returnTag;
appendReturnValuePacker(function);
appendReturnValuePacker(FunctionType(*fallback).getReturnParameterTypes());
}
else
m_context << eth::Instruction::STOP; // function not found
for (auto const& it: interfaceFunctions)
{
FunctionTypePointer const& functionType = it.second;
m_context << callDataUnpackerEntryPoints.at(it.first);
eth::AssemblyItem returnTag = m_context.pushNewTag();
appendCalldataUnpacker(functionType->getParameterTypes());
m_context.appendJumpTo(m_context.getFunctionEntryLabel(it.second->getDeclaration()));
m_context << returnTag;
appendReturnValuePacker(functionType->getReturnParameterTypes());
}
}
unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, bool _fromMemory)
void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory)
{
// We do not check the calldata size, everything is zero-padded.
unsigned dataOffset = 1;
//@todo this can be done more efficiently, saving some CALLDATALOAD calls
for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
{
unsigned const numBytes = var->getType()->getCalldataEncodedSize();
if (numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(var->getLocation())
<< errinfo_comment("Type " + var->getType()->toString() + " not yet supported."));
bool leftAligned = var->getType()->getCategory() == Type::Category::STRING;
CompilerUtils(m_context).loadFromMemory(dataOffset, numBytes, leftAligned, !_fromMemory);
dataOffset += numBytes;
}
return dataOffset;
unsigned offset(CompilerUtils::dataStartOffset);
bool const c_padToWords = true;
unsigned dynamicParameterCount = 0;
for (TypePointer const& type: _typeParameters)
if (type->isDynamicallySized())
dynamicParameterCount++;
offset += dynamicParameterCount * 32;
unsigned currentDynamicParameter = 0;
for (TypePointer const& type: _typeParameters)
if (type->isDynamicallySized())
{
// value on stack: [calldata_offset] (only if we are already in dynamic mode)
if (currentDynamicParameter == 0)
// switch from static to dynamic
m_context << u256(offset);
// retrieve length
CompilerUtils(m_context).loadFromMemory(
CompilerUtils::dataStartOffset + currentDynamicParameter * 32,
IntegerType(256), !_fromMemory, c_padToWords);
// stack: offset length
// add 32-byte padding to copy of length
m_context << u256(32) << eth::Instruction::DUP1 << u256(31)
<< eth::Instruction::DUP4 << eth::Instruction::ADD
<< eth::Instruction::DIV << eth::Instruction::MUL;
// stack: offset length padded_length
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
currentDynamicParameter++;
// stack: offset length next_calldata_offset
}
else if (currentDynamicParameter == 0)
// we can still use static load
offset += CompilerUtils(m_context).loadFromMemory(offset, *type, !_fromMemory, c_padToWords);
else
CompilerUtils(m_context).loadFromMemoryDynamic(*type, !_fromMemory, c_padToWords);
if (dynamicParameterCount > 0)
m_context << eth::Instruction::POP;
}
void Compiler::appendReturnValuePacker(FunctionDefinition const& _function)
void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters)
{
//@todo this can be also done more efficiently
unsigned dataOffset = 0;
vector<ASTPointer<VariableDeclaration>> const& parameters = _function.getReturnParameters();
unsigned stackDepth = CompilerUtils(m_context).getSizeOnStack(parameters);
for (unsigned i = 0; i < parameters.size(); ++i)
unsigned stackDepth = 0;
for (TypePointer const& type: _typeParameters)
stackDepth += type->getSizeOnStack();
for (TypePointer const& type: _typeParameters)
{
Type const& paramType = *parameters[i]->getType();
unsigned numBytes = paramType.getCalldataEncodedSize();
if (numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(parameters[i]->getLocation())
<< errinfo_comment("Type " + paramType.toString() + " not yet supported."));
CompilerUtils(m_context).copyToStackTop(stackDepth, paramType);
bool const leftAligned = paramType.getCategory() == Type::Category::STRING;
CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned);
stackDepth -= paramType.getSizeOnStack();
dataOffset += numBytes;
CompilerUtils(m_context).copyToStackTop(stackDepth, *type);
ExpressionCompiler::appendTypeConversion(m_context, *type, *type, true);
bool const c_padToWords = true;
dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, *type, c_padToWords);
stackDepth -= type->getSizeOnStack();
}
// note that the stack is not cleaned up here
m_context << u256(dataOffset) << u256(0) << eth::Instruction::RETURN;
@ -200,9 +242,23 @@ void Compiler::appendReturnValuePacker(FunctionDefinition const& _function)
void Compiler::registerStateVariables(ContractDefinition const& _contract)
{
//@todo sort them?
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables())
m_context.addStateVariable(*variable);
for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.getLinearizedBaseContracts()))
for (ASTPointer<VariableDeclaration> const& variable: contract->getStateVariables())
m_context.addStateVariable(*variable);
}
bool Compiler::visit(VariableDeclaration const& _variableDeclaration)
{
solAssert(_variableDeclaration.isStateVariable(), "Compiler visit to non-state variable declaration.");
m_context.startFunction(_variableDeclaration);
m_breakTags.clear();
m_continueTags.clear();
m_context << m_context.getFunctionEntryLabel(_variableDeclaration);
ExpressionCompiler::appendStateVariableAccessor(m_context, _variableDeclaration);
return false;
}
bool Compiler::visit(FunctionDefinition const& _function)
@ -211,24 +267,30 @@ bool Compiler::visit(FunctionDefinition const& _function)
// caller puts: [retarg0] ... [retargm] [return address] [arg0] ... [argn]
// although note that this reduces the size of the visible stack
m_context.startNewFunction();
m_context.startFunction(_function);
m_returnTag = m_context.newTag();
m_breakTags.clear();
m_continueTags.clear();
m_context << m_context.getFunctionEntryLabel(_function);
m_stackCleanupForReturn = 0;
m_currentFunction = &_function;
m_modifierDepth = 0;
// stack upon entry: [return address] [arg0] [arg1] ... [argn]
// reserve additional slots: [retarg0] ... [retargm] [localvar0] ... [localvarp]
unsigned parametersSize = CompilerUtils::getSizeOnStack(_function.getParameters());
m_context.adjustStackOffset(parametersSize);
for (ASTPointer<VariableDeclaration const> const& variable: _function.getParameters())
m_context.addVariable(*variable);
{
m_context.addVariable(*variable, parametersSize);
parametersSize -= variable->getType()->getSizeOnStack();
}
for (ASTPointer<VariableDeclaration const> const& variable: _function.getReturnParameters())
m_context.addAndInitializeVariable(*variable);
for (VariableDeclaration const* localVariable: _function.getLocalVariables())
m_context.addAndInitializeVariable(*localVariable);
_function.getBody().accept(*this);
appendModifierOrFunctionCode();
m_context << m_returnTag;
@ -238,16 +300,16 @@ bool Compiler::visit(FunctionDefinition const& _function)
// Note that the fact that the return arguments are of increasing index is vital for this
// algorithm to work.
unsigned const argumentsSize = CompilerUtils::getSizeOnStack(_function.getParameters());
unsigned const returnValuesSize = CompilerUtils::getSizeOnStack(_function.getReturnParameters());
unsigned const localVariablesSize = CompilerUtils::getSizeOnStack(_function.getLocalVariables());
unsigned const c_argumentsSize = CompilerUtils::getSizeOnStack(_function.getParameters());
unsigned const c_returnValuesSize = CompilerUtils::getSizeOnStack(_function.getReturnParameters());
unsigned const c_localVariablesSize = CompilerUtils::getSizeOnStack(_function.getLocalVariables());
vector<int> stackLayout;
stackLayout.push_back(returnValuesSize); // target of return address
stackLayout += vector<int>(argumentsSize, -1); // discard all arguments
for (unsigned i = 0; i < returnValuesSize; ++i)
stackLayout.push_back(c_returnValuesSize); // target of return address
stackLayout += vector<int>(c_argumentsSize, -1); // discard all arguments
for (unsigned i = 0; i < c_returnValuesSize; ++i)
stackLayout.push_back(i);
stackLayout += vector<int>(localVariablesSize, -1);
stackLayout += vector<int>(c_localVariablesSize, -1);
while (stackLayout.back() != int(stackLayout.size() - 1))
if (stackLayout.back() < 0)
@ -355,13 +417,15 @@ bool Compiler::visit(Return const& _return)
//@todo modifications are needed to make this work with functions returning multiple values
if (Expression const* expression = _return.getExpression())
{
compileExpression(*expression);
VariableDeclaration const& firstVariable = *_return.getFunctionReturnParameters().getParameters().front();
ExpressionCompiler::appendTypeConversion(m_context, *expression->getType(), *firstVariable.getType());
solAssert(_return.getFunctionReturnParameters(), "Invalid return parameters pointer.");
VariableDeclaration const& firstVariable = *_return.getFunctionReturnParameters()->getParameters().front();
compileExpression(*expression, firstVariable.getType());
CompilerUtils(m_context).moveToStackVariable(firstVariable);
}
for (unsigned i = 0; i < m_stackCleanupForReturn; ++i)
m_context << eth::Instruction::POP;
m_context.appendJumpTo(m_returnTag);
m_context.adjustStackOffset(m_stackCleanupForReturn);
return false;
}
@ -369,10 +433,7 @@ bool Compiler::visit(VariableDefinition const& _variableDefinition)
{
if (Expression const* expression = _variableDefinition.getExpression())
{
compileExpression(*expression);
ExpressionCompiler::appendTypeConversion(m_context,
*expression->getType(),
*_variableDefinition.getDeclaration().getType());
compileExpression(*expression, _variableDefinition.getDeclaration().getType());
CompilerUtils(m_context).moveToStackVariable(_variableDefinition.getDeclaration());
}
return false;
@ -386,9 +447,51 @@ bool Compiler::visit(ExpressionStatement const& _expressionStatement)
return false;
}
void Compiler::compileExpression(Expression const& _expression)
bool Compiler::visit(PlaceholderStatement const&)
{
++m_modifierDepth;
appendModifierOrFunctionCode();
--m_modifierDepth;
return true;
}
void Compiler::appendModifierOrFunctionCode()
{
solAssert(m_currentFunction, "");
if (m_modifierDepth >= m_currentFunction->getModifiers().size())
m_currentFunction->getBody().accept(*this);
else
{
ASTPointer<ModifierInvocation> const& modifierInvocation = m_currentFunction->getModifiers()[m_modifierDepth];
ModifierDefinition const& modifier = m_context.getFunctionModifier(modifierInvocation->getName()->getName());
solAssert(modifier.getParameters().size() == modifierInvocation->getArguments().size(), "");
for (unsigned i = 0; i < modifier.getParameters().size(); ++i)
{
m_context.addVariable(*modifier.getParameters()[i]);
compileExpression(*modifierInvocation->getArguments()[i],
modifier.getParameters()[i]->getType());
}
for (VariableDeclaration const* localVariable: modifier.getLocalVariables())
m_context.addAndInitializeVariable(*localVariable);
unsigned const c_stackSurplus = CompilerUtils::getSizeOnStack(modifier.getParameters()) +
CompilerUtils::getSizeOnStack(modifier.getLocalVariables());
m_stackCleanupForReturn += c_stackSurplus;
modifier.getBody().accept(*this);
for (unsigned i = 0; i < c_stackSurplus; ++i)
m_context << eth::Instruction::POP;
m_stackCleanupForReturn -= c_stackSurplus;
}
}
void Compiler::compileExpression(Expression const& _expression, TypePointer const& _targetType)
{
ExpressionCompiler::compileExpression(m_context, _expression, m_optimize);
if (_targetType)
ExpressionCompiler::appendTypeConversion(m_context, *_expression.getType(), *_targetType);
}
}

View File

@ -20,7 +20,10 @@
* Solidity AST to EVM bytecode compiler.
*/
#pragma once
#include <ostream>
#include <functional>
#include <libsolidity/ASTVisitor.h>
#include <libsolidity/CompilerContext.h>
@ -30,32 +33,34 @@ namespace solidity {
class Compiler: private ASTConstVisitor
{
public:
explicit Compiler(bool _optimize = false): m_optimize(_optimize), m_returnTag(m_context.newTag()) {}
explicit Compiler(bool _optimize = false): m_optimize(_optimize), m_context(),
m_returnTag(m_context.newTag()) {}
void compileContract(ContractDefinition const& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals,
void compileContract(ContractDefinition const& _contract,
std::map<ContractDefinition const*, bytes const*> const& _contracts);
bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); }
bytes getRuntimeBytecode() { return m_runtimeContext.getAssembledBytecode(m_optimize);}
void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); }
private:
/// Registers the global objects and the non-function objects inside the contract with the context.
void initializeContext(ContractDefinition const& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals,
/// Registers the non-function objects inside the contract with the context.
void initializeContext(ContractDefinition const& _contract,
std::map<ContractDefinition const*, bytes const*> const& _contracts);
/// Adds the code that is run at creation time. Should be run after exchanging the run-time context
/// with a new and initialized context.
/// adds the constructor code.
/// with a new and initialized context. Adds the constructor code.
void packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext);
void appendBaseConstructorCall(FunctionDefinition const& _constructor,
std::vector<ASTPointer<Expression>> const& _arguments);
void appendConstructorCall(FunctionDefinition const& _constructor);
/// Recursively searches the call graph and returns all functions needed by the constructor (including itself).
std::set<FunctionDefinition const*> getFunctionsNeededByConstructor(FunctionDefinition const& _constructor);
void appendFunctionSelector(ContractDefinition const& _contract);
/// Creates code that unpacks the arguments for the given function, from memory if
/// @a _fromMemory is true, otherwise from call data. @returns the size of the data in bytes.
unsigned appendCalldataUnpacker(FunctionDefinition const& _function, bool _fromMemory = false);
void appendReturnValuePacker(FunctionDefinition const& _function);
/// Creates code that unpacks the arguments for the given function represented by a vector of TypePointers.
/// From memory if @a _fromMemory is true, otherwise from call data.
void appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory = false);
void appendReturnValuePacker(TypePointers const& _typeParameters);
void registerStateVariables(ContractDefinition const& _contract);
virtual bool visit(VariableDeclaration const& _variableDeclaration) override;
virtual bool visit(FunctionDefinition const& _function) override;
virtual bool visit(IfStatement const& _ifStatement) override;
virtual bool visit(WhileStatement const& _whileStatement) override;
@ -65,14 +70,23 @@ private:
virtual bool visit(Return const& _return) override;
virtual bool visit(VariableDefinition const& _variableDefinition) override;
virtual bool visit(ExpressionStatement const& _expressionStatement) override;
virtual bool visit(PlaceholderStatement const&) override;
void compileExpression(Expression const& _expression);
/// Appends one layer of function modifier code of the current function, or the function
/// body itself if the last modifier was reached.
void appendModifierOrFunctionCode();
void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer());
bool const m_optimize;
CompilerContext m_context;
CompilerContext m_runtimeContext;
std::vector<eth::AssemblyItem> m_breakTags; ///< tag to jump to for a "break" statement
std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" statement
eth::AssemblyItem m_returnTag; ///< tag to jump to for a "return" statement
unsigned m_modifierDepth = 0;
FunctionDefinition const* m_currentFunction;
unsigned m_stackCleanupForReturn; ///< this number of stack elements need to be removed before jump to m_returnTag
};
}

View File

@ -43,25 +43,28 @@ void CompilerContext::addStateVariable(VariableDeclaration const& _declaration)
m_stateVariablesSize += _declaration.getType()->getStorageSize();
}
void CompilerContext::addVariable(VariableDeclaration const& _declaration)
void CompilerContext::startFunction(Declaration const& _function)
{
m_localVariables[&_declaration] = m_localVariablesSize;
m_localVariablesSize += _declaration.getType()->getSizeOnStack();
m_functionsWithCode.insert(&_function);
m_localVariables.clear();
m_asm.setDeposit(0);
*this << getFunctionEntryLabel(_function);
}
void CompilerContext::addVariable(VariableDeclaration const& _declaration,
unsigned _offsetToCurrent)
{
solAssert(m_asm.deposit() >= 0 && unsigned(m_asm.deposit()) >= _offsetToCurrent, "");
m_localVariables[&_declaration] = unsigned(m_asm.deposit()) - _offsetToCurrent;
}
void CompilerContext::addAndInitializeVariable(VariableDeclaration const& _declaration)
{
addVariable(_declaration);
unsigned const size = _declaration.getType()->getSizeOnStack();
for (unsigned i = 0; i < size; ++i)
int const size = _declaration.getType()->getSizeOnStack();
for (int i = 0; i < size; ++i)
*this << u256(0);
m_asm.adjustDeposit(-size);
}
void CompilerContext::addFunction(FunctionDefinition const& _function)
{
m_functionEntryLabels.insert(std::make_pair(&_function, m_asm.newTag()));
}
bytes const& CompilerContext::getCompiledContract(const ContractDefinition& _contract) const
@ -73,26 +76,82 @@ bytes const& CompilerContext::getCompiledContract(const ContractDefinition& _con
bool CompilerContext::isLocalVariable(Declaration const* _declaration) const
{
return m_localVariables.count(_declaration) > 0;
return !!m_localVariables.count(_declaration);
}
eth::AssemblyItem CompilerContext::getFunctionEntryLabel(FunctionDefinition const& _function) const
eth::AssemblyItem CompilerContext::getFunctionEntryLabel(Declaration const& _declaration)
{
auto res = m_functionEntryLabels.find(&_function);
solAssert(res != m_functionEntryLabels.end(), "Function entry label not found.");
return res->second.tag();
auto res = m_functionEntryLabels.find(&_declaration);
if (res == m_functionEntryLabels.end())
{
eth::AssemblyItem tag(m_asm.newTag());
m_functionEntryLabels.insert(make_pair(&_declaration, tag));
return tag.tag();
}
else
return res->second.tag();
}
eth::AssemblyItem CompilerContext::getVirtualFunctionEntryLabel(FunctionDefinition const& _function)
{
solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");
for (ContractDefinition const* contract: m_inheritanceHierarchy)
for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions())
if (!function->isConstructor() && function->getName() == _function.getName())
return getFunctionEntryLabel(*function);
solAssert(false, "Virtual function " + _function.getName() + " not found.");
return m_asm.newTag(); // not reached
}
eth::AssemblyItem CompilerContext::getSuperFunctionEntryLabel(string const& _name, ContractDefinition const& _base)
{
// search for first contract after _base
solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");
auto it = find(m_inheritanceHierarchy.begin(), m_inheritanceHierarchy.end(), &_base);
solAssert(it != m_inheritanceHierarchy.end(), "Base not found in inheritance hierarchy.");
for (++it; it != m_inheritanceHierarchy.end(); ++it)
for (ASTPointer<FunctionDefinition> const& function: (*it)->getDefinedFunctions())
if (!function->isConstructor() && function->getName() == _name)
return getFunctionEntryLabel(*function);
solAssert(false, "Super function " + _name + " not found.");
return m_asm.newTag(); // not reached
}
set<Declaration const*> CompilerContext::getFunctionsWithoutCode()
{
set<Declaration const*> functions;
for (auto const& it: m_functionEntryLabels)
if (m_functionsWithCode.count(it.first) == 0)
functions.insert(it.first);
return move(functions);
}
ModifierDefinition const& CompilerContext::getFunctionModifier(string const& _name) const
{
solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");
for (ContractDefinition const* contract: m_inheritanceHierarchy)
for (ASTPointer<ModifierDefinition> const& modifier: contract->getFunctionModifiers())
if (modifier->getName() == _name)
return *modifier.get();
BOOST_THROW_EXCEPTION(InternalCompilerError()
<< errinfo_comment("Function modifier " + _name + " not found."));
}
unsigned CompilerContext::getBaseStackOffsetOfVariable(Declaration const& _declaration) const
{
auto res = m_localVariables.find(&_declaration);
solAssert(res != m_localVariables.end(), "Variable not found on stack.");
return m_localVariablesSize - res->second - 1;
return res->second;
}
unsigned CompilerContext::baseToCurrentStackOffset(unsigned _baseOffset) const
{
return _baseOffset + m_asm.deposit();
return m_asm.deposit() - _baseOffset - 1;
}
unsigned CompilerContext::currentToBaseStackOffset(unsigned _offset) const
{
return m_asm.deposit() - _offset - 1;
}
u256 CompilerContext::getStorageLocationOfVariable(const Declaration& _declaration) const

View File

@ -41,27 +41,41 @@ class CompilerContext
public:
void addMagicGlobal(MagicVariableDeclaration const& _declaration);
void addStateVariable(VariableDeclaration const& _declaration);
void startNewFunction() { m_localVariables.clear(); m_asm.setDeposit(0); }
void addVariable(VariableDeclaration const& _declaration);
void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0);
void addAndInitializeVariable(VariableDeclaration const& _declaration);
void addFunction(FunctionDefinition const& _function);
void setCompiledContracts(std::map<ContractDefinition const*, bytes const*> const& _contracts) { m_compiledContracts = _contracts; }
bytes const& getCompiledContract(ContractDefinition const& _contract) const;
void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); }
unsigned getStackHeight() { solAssert(m_asm.deposit() >= 0, ""); return unsigned(m_asm.deposit()); }
bool isMagicGlobal(Declaration const* _declaration) const { return m_magicGlobals.count(_declaration); }
bool isFunctionDefinition(Declaration const* _declaration) const { return m_functionEntryLabels.count(_declaration); }
bool isMagicGlobal(Declaration const* _declaration) const { return m_magicGlobals.count(_declaration) != 0; }
bool isLocalVariable(Declaration const* _declaration) const;
bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration); }
bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration) != 0; }
eth::AssemblyItem getFunctionEntryLabel(FunctionDefinition const& _function) const;
/// Returns the distance of the given local variable from the top of the local variable stack.
eth::AssemblyItem getFunctionEntryLabel(Declaration const& _declaration);
void setInheritanceHierarchy(std::vector<ContractDefinition const*> const& _hierarchy) { m_inheritanceHierarchy = _hierarchy; }
/// @returns the entry label of the given function and takes overrides into account.
eth::AssemblyItem getVirtualFunctionEntryLabel(FunctionDefinition const& _function);
/// @returns the entry label of function with the given name from the most derived class just
/// above _base in the current inheritance hierarchy.
eth::AssemblyItem getSuperFunctionEntryLabel(std::string const& _name, ContractDefinition const& _base);
/// @returns the set of functions for which we still need to generate code
std::set<Declaration const*> getFunctionsWithoutCode();
/// Resets function specific members, inserts the function entry label and marks the function
/// as "having code".
void startFunction(Declaration const& _function);
ModifierDefinition const& getFunctionModifier(std::string const& _name) const;
/// Returns the distance of the given local variable from the bottom of the stack (of the current function).
unsigned getBaseStackOffsetOfVariable(Declaration const& _declaration) const;
/// If supplied by a value returned by @ref getBaseStackOffsetOfVariable(variable), returns
/// the distance of that variable from the current top of the stack.
unsigned baseToCurrentStackOffset(unsigned _baseOffset) const;
/// Converts an offset relative to the current stack height to a value that can be used later
/// with baseToCurrentStackOffset to point to the same stack element.
unsigned currentToBaseStackOffset(unsigned _offset) const;
u256 getStorageLocationOfVariable(Declaration const& _declaration) const;
/// Appends a JUMPI instruction to a new tag and @returns the tag
@ -109,10 +123,12 @@ private:
std::map<Declaration const*, u256> m_stateVariables;
/// Offsets of local variables on the stack (relative to stack base).
std::map<Declaration const*, unsigned> m_localVariables;
/// Sum of stack sizes of local variables
unsigned m_localVariablesSize;
/// Labels pointing to the entry points of funcitons.
/// Labels pointing to the entry points of functions.
std::map<Declaration const*, eth::AssemblyItem> m_functionEntryLabels;
/// Set of functions for which we did not yet generate code.
std::set<Declaration const*> m_functionsWithCode;
/// List of current inheritance hierarchy from derived to base.
std::vector<ContractDefinition const*> m_inheritanceHierarchy;
};
}

View File

@ -16,10 +16,12 @@
*/
/**
* @author Christian <c@ethdev.com>
* @author Gav Wood <g@ethdev.com>
* @date 2014
* Full-stack compiler that converts a source code string to bytecode.
*/
#include <boost/algorithm/string.hpp>
#include <libsolidity/AST.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h>
@ -29,6 +31,8 @@
#include <libsolidity/CompilerStack.h>
#include <libsolidity/InterfaceHandler.h>
#include <libdevcrypto/SHA3.h>
using namespace std;
namespace dev
@ -36,18 +40,39 @@ namespace dev
namespace solidity
{
const map<string, string> StandardSources = map<string, string>{
{"coin", R"(import "CoinReg";import "Config";import "configUser";contract coin is configUser{function coin(string3 name, uint denom) {CoinReg(Config(configAddr()).lookup(3)).register(name, denom);}})"},
{"Coin", R"(contract Coin{function isApprovedFor(address _target,address _proxy)constant returns(bool _r){}function isApproved(address _proxy)constant returns(bool _r){}function sendCoinFrom(address _from,uint256 _val,address _to){}function coinBalanceOf(address _a)constant returns(uint256 _r){}function sendCoin(uint256 _val,address _to){}function coinBalance()constant returns(uint256 _r){}function approve(address _a){}})"},
{"CoinReg", R"(contract CoinReg{function count()constant returns(uint256 r){}function info(uint256 i)constant returns(address addr,string3 name,uint256 denom){}function register(string3 name,uint256 denom){}function unregister(){}})"},
{"configUser", R"(contract configUser{function configAddr()constant returns(address a){ return 0xc6d9d2cd449a754c494264e1809c50e34d64562b;}})"},
{"Config", R"(contract Config{function lookup(uint256 service)constant returns(address a){}function kill(){}function unregister(uint256 id){}function register(uint256 id,address service){}})"},
{"mortal", R"(import "owned";contract mortal is owned {function kill() { if (msg.sender == owner) suicide(owner); }})"},
{"named", R"(import "Config";import "NameReg";import "configUser";contract named is configUser {function named(string32 name) {NameReg(Config(configAddr()).lookup(1)).register(name);}})"},
{"NameReg", R"(contract NameReg{function register(string32 name){}function addressOf(string32 name)constant returns(address addr){}function unregister(){}function nameOf(address addr)constant returns(string32 name){}})"},
{"owned", R"(contract owned{function owned(){owner = msg.sender;}modifier onlyowner(){if(msg.sender==owner)_}address owner;})"},
{"service", R"(import "Config";import "configUser";contract service is configUser{function service(uint _n){Config(configAddr()).register(_n, this);}})"},
{"std", R"(import "owned";import "mortal";import "Config";import "configUser";import "NameReg";import "named";)"}
};
CompilerStack::CompilerStack(bool _addStandardSources):
m_addStandardSources(_addStandardSources), m_parseSuccessful(false)
{
if (m_addStandardSources)
addSources(StandardSources);
}
bool CompilerStack::addSource(string const& _name, string const& _content)
{
bool existed = m_sources.count(_name);
bool existed = m_sources.count(_name) != 0;
reset(true);
m_sources[_name].scanner = make_shared<Scanner>(CharStream(_content), _name);
m_sources[_name].scanner = make_shared<Scanner>(CharStream(expanded(_content)), _name);
return existed;
}
void CompilerStack::setSource(string const& _sourceCode)
{
reset();
addSource("", _sourceCode);
addSource("", expanded(_sourceCode));
}
void CompilerStack::parse()
@ -69,6 +94,7 @@ void CompilerStack::parse()
{
m_globalContext->setCurrentContract(*contract);
resolver.updateDeclaration(*m_globalContext->getCurrentThis());
resolver.updateDeclaration(*m_globalContext->getCurrentSuper());
resolver.resolveNamesAndTypes(*contract);
m_contracts[contract->getName()].contract = contract;
}
@ -100,6 +126,58 @@ vector<string> CompilerStack::getContractNames() const
return contractNames;
}
////// BEGIN: TEMPORARY ONLY
///
/// NOTE: THIS INVALIDATES SOURCE POINTERS AND CAN CRASH THE COMPILER
///
/// remove once import works properly and we have genesis contracts
string CompilerStack::expanded(string const& _sourceCode)
{
const map<string, string> c_standardSources = map<string, string>{
{ "Config", "contract Config{function lookup(uint256 service)constant returns(address a){}function kill(){}function unregister(uint256 id){}function register(uint256 id,address service){}}" },
{ "Coin", "contract Coin{function isApprovedFor(address _target,address _proxy)constant returns(bool _r){}function isApproved(address _proxy)constant returns(bool _r){}function sendCoinFrom(address _from,uint256 _val,address _to){}function coinBalanceOf(address _a)constant returns(uint256 _r){}function sendCoin(uint256 _val,address _to){}function coinBalance()constant returns(uint256 _r){}function approve(address _a){}}"},
{ "CoinReg", "contract CoinReg{function count()constant returns(uint256 r){}function info(uint256 i)constant returns(address addr,string3 name,uint256 denom){}function register(string3 name,uint256 denom){}function unregister(){}}" },
{ "coin", "#require CoinReg\ncontract coin {function coin(string3 name, uint denom) {CoinReg(Config().lookup(3)).register(name, denom);}}" },
{ "service", "#require Config\ncontract service{function service(uint _n){Config().register(_n, this);}}" },
{ "owned", "contract owned{function owned(){owner = msg.sender;}modifier onlyowner(){if(msg.sender==owner)_}address owner;}" },
{ "mortal", "#require owned\ncontract mortal is owned {function kill() { if (msg.sender == owner) suicide(owner); }}" },
{ "NameReg", "contract NameReg{function register(string32 name){}function addressOf(string32 name)constant returns(address addr){}function unregister(){}function nameOf(address addr)constant returns(string32 name){}}" },
{ "named", "#require Config NameReg\ncontract named {function named(string32 name) {NameReg(Config().lookup(1)).register(name);}}" },
{ "std", "#require owned mortal Config NameReg named" },
};
string sub;
set<string> got;
function<string(string const&)> localExpanded;
localExpanded = [&](string const& s) -> string
{
string ret = s;
for (size_t p = 0; p != string::npos;)
if ((p = ret.find("#require ")) != string::npos)
{
string n = ret.substr(p + 9, ret.find_first_of('\n', p + 9) - p - 9);
ret.replace(p, n.size() + 9, "");
vector<string> rs;
boost::split(rs, n, boost::is_any_of(" \t,"), boost::token_compress_on);
for (auto const& r: rs)
if (!got.count(r))
{
if (c_standardSources.count(r))
sub.append("\n" + localExpanded(c_standardSources.at(r)) + "\n");
got.insert(r);
}
}
// TODO: remove once we have genesis contracts.
else if ((p = ret.find("Config()")) != string::npos)
ret.replace(p, 8, "Config(0xc6d9d2cd449a754c494264e1809c50e34d64562b)");
return ret;
};
return sub + localExpanded(_sourceCode);
}
////// END: TEMPORARY ONLY
void CompilerStack::compile(bool _optimize)
{
if (!m_parseSuccessful)
@ -110,12 +188,11 @@ void CompilerStack::compile(bool _optimize)
for (ASTPointer<ASTNode> const& node: source->ast->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
m_globalContext->setCurrentContract(*contract);
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize);
compiler->compileContract(*contract, m_globalContext->getMagicVariables(),
contractBytecode);
compiler->compileContract(*contract, contractBytecode);
Contract& compiledContract = m_contracts[contract->getName()];
compiledContract.bytecode = compiler->getAssembledBytecode();
compiledContract.runtimeBytecode = compiler->getRuntimeBytecode();
compiledContract.compiler = move(compiler);
contractBytecode[compiledContract.contract] = &compiledContract.bytecode;
}
@ -133,6 +210,16 @@ bytes const& CompilerStack::getBytecode(string const& _contractName) const
return getContract(_contractName).bytecode;
}
bytes const& CompilerStack::getRuntimeBytecode(string const& _contractName) const
{
return getContract(_contractName).runtimeBytecode;
}
dev::h256 CompilerStack::getContractCodeHash(string const& _contractName) const
{
return dev::sha3(getRuntimeBytecode(_contractName));
}
void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName) const
{
getContract(_contractName).compiler->streamAssembly(_outStream);
@ -140,10 +227,15 @@ void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractN
string const& CompilerStack::getInterface(string const& _contractName) const
{
return getJsonDocumentation(_contractName, DocumentationType::ABI_INTERFACE);
return getMetadata(_contractName, DocumentationType::ABIInterface);
}
string const& CompilerStack::getJsonDocumentation(string const& _contractName, DocumentationType _type) const
string const& CompilerStack::getSolidityInterface(string const& _contractName) const
{
return getMetadata(_contractName, DocumentationType::ABISolidityInterface);
}
string const& CompilerStack::getMetadata(string const& _contractName, DocumentationType _type) const
{
if (!m_parseSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
@ -153,15 +245,18 @@ string const& CompilerStack::getJsonDocumentation(string const& _contractName, D
std::unique_ptr<string const>* doc;
switch (_type)
{
case DocumentationType::NATSPEC_USER:
case DocumentationType::NatspecUser:
doc = &contract.userDocumentation;
break;
case DocumentationType::NATSPEC_DEV:
case DocumentationType::NatspecDev:
doc = &contract.devDocumentation;
break;
case DocumentationType::ABI_INTERFACE:
case DocumentationType::ABIInterface:
doc = &contract.interface;
break;
case DocumentationType::ABISolidityInterface:
doc = &contract.solidityInterface;
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type."));
}
@ -198,7 +293,11 @@ void CompilerStack::reset(bool _keepSources)
for (auto sourcePair: m_sources)
sourcePair.second.reset();
else
{
m_sources.clear();
if (m_addStandardSources)
addSources(StandardSources);
}
m_globalContext.reset();
m_sourceOrder.clear();
m_contracts.clear();
@ -234,16 +333,23 @@ void CompilerStack::resolveImports()
swap(m_sourceOrder, sourceOrder);
}
std::string CompilerStack::defaultContractName() const
{
return getContract("").contract->getName();
}
CompilerStack::Contract const& CompilerStack::getContract(string const& _contractName) const
{
if (m_contracts.empty())
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No compiled contracts found."));
string contractName = _contractName;
if (_contractName.empty())
// try to find the "last contract"
for (ASTPointer<ASTNode> const& node: m_sourceOrder.back()->ast->getNodes())
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
contractName = contract->getName();
// try to find some user-supplied contract
for (auto const& it: m_sources)
if (!StandardSources.count(it.first))
for (ASTPointer<ASTNode> const& node: it.second.ast->getNodes())
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
contractName = contract->getName();
auto it = m_contracts.find(contractName);
if (it == m_contracts.end())
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Contract " + _contractName + " not found."));

View File

@ -16,6 +16,7 @@
*/
/**
* @author Christian <c@ethdev.com>
* @author Gav Wood <g@ethdev.com>
* @date 2014
* Full-stack compiler that converts a source code string to bytecode.
*/
@ -27,6 +28,7 @@
#include <memory>
#include <boost/noncopyable.hpp>
#include <libdevcore/Common.h>
#include <libdevcore/FixedHash.h>
namespace dev {
namespace solidity {
@ -41,11 +43,14 @@ class InterfaceHandler;
enum class DocumentationType: uint8_t
{
NATSPEC_USER = 1,
NATSPEC_DEV,
ABI_INTERFACE
NatspecUser = 1,
NatspecDev,
ABIInterface,
ABISolidityInterface
};
extern const std::map<std::string, std::string> StandardSources;
/**
* Easy to use and self-contained Solidity compiler with as few header dependencies as possible.
* It holds state and can be used to either step through the compilation stages (and abort e.g.
@ -54,18 +59,21 @@ enum class DocumentationType: uint8_t
class CompilerStack: boost::noncopyable
{
public:
CompilerStack(): m_parseSuccessful(false) {}
/// Creates a new compiler stack. Adds standard sources if @a _addStandardSources.
explicit CompilerStack(bool _addStandardSources = false);
/// Adds a source object (e.g. file) to the parser. After this, parse has to be called again.
/// @returns true if a source object by the name already existed and was replaced.
void addSources(std::map<std::string, std::string> const& _nameContents) { for (auto const& i: _nameContents) addSource(i.first, i.second); }
bool addSource(std::string const& _name, std::string const& _content);
void setSource(std::string const& _sourceCode);
/// Parses all source units that were added
void parse();
/// Sets the given source code as the only source unit and parses it.
/// Sets the given source code as the only source unit apart from standard sources and parses it.
void parse(std::string const& _sourceCode);
/// Returns a list of the contract names in the sources.
std::vector<std::string> getContractNames() const;
std::string defaultContractName() const;
/// Compiles the source units that were previously added and parsed.
void compile(bool _optimize = false);
@ -73,7 +81,13 @@ public:
/// @returns the compiled bytecode
bytes const& compile(std::string const& _sourceCode, bool _optimize = false);
/// @returns the assembled bytecode for a contract.
bytes const& getBytecode(std::string const& _contractName = "") const;
/// @returns the runtime bytecode for the contract, i.e. the code that is returned by the constructor.
bytes const& getRuntimeBytecode(std::string const& _contractName = "") const;
/// @returns hash of the runtime bytecode for the contract, i.e. the code that is returned by the constructor.
dev::h256 getContractCodeHash(std::string const& _contractName = "") const;
/// Streams a verbose version of the assembly to @a _outStream.
/// Prerequisite: Successful compilation.
void streamAssembly(std::ostream& _outStream, std::string const& _contractName = "") const;
@ -81,11 +95,14 @@ public:
/// Returns a string representing the contract interface in JSON.
/// Prerequisite: Successful call to parse or compile.
std::string const& getInterface(std::string const& _contractName = "") const;
/// Returns a string representing the contract interface in Solidity.
/// Prerequisite: Successful call to parse or compile.
std::string const& getSolidityInterface(std::string const& _contractName = "") const;
/// Returns a string representing the contract's documentation in JSON.
/// Prerequisite: Successful call to parse or compile.
/// @param type The type of the documentation to get.
/// Can be one of 3 types defined at @c DocumentationType
std::string const& getJsonDocumentation(std::string const& _contractName, DocumentationType _type) const;
/// Can be one of 4 types defined at @c DocumentationType
std::string const& getMetadata(std::string const& _contractName, DocumentationType _type) const;
/// @returns the previously used scanner, useful for counting lines during error reporting.
Scanner const& getScanner(std::string const& _sourceName = "") const;
@ -113,23 +130,30 @@ private:
struct Contract
{
ContractDefinition const* contract;
ContractDefinition const* contract = nullptr;
std::shared_ptr<Compiler> compiler;
bytes bytecode;
bytes runtimeBytecode;
std::shared_ptr<InterfaceHandler> interfaceHandler;
mutable std::unique_ptr<std::string const> interface;
mutable std::unique_ptr<std::string const> solidityInterface;
mutable std::unique_ptr<std::string const> userDocumentation;
mutable std::unique_ptr<std::string const> devDocumentation;
Contract();
};
/// Expand source code with preprocessor-like includes.
/// @todo Replace with better framework.
std::string expanded(std::string const& _sourceCode);
void reset(bool _keepSources = false);
void resolveImports();
Contract const& getContract(std::string const& _contractName = "") const;
Source const& getSource(std::string const& _sourceName = "") const;
bool m_addStandardSources; ///< If true, standard sources are added.
bool m_parseSuccessful;
std::map<std::string const, Source> m_sources;
std::shared_ptr<GlobalContext> m_globalContext;

View File

@ -31,42 +31,94 @@ namespace dev
namespace solidity
{
void CompilerUtils::loadFromMemory(unsigned _offset, unsigned _bytes, bool _leftAligned, bool _fromCalldata)
const unsigned int CompilerUtils::dataStartOffset = 4;
unsigned CompilerUtils::loadFromMemory(unsigned _offset, Type const& _type,
bool _fromCalldata, bool _padToWordBoundaries)
{
if (_bytes == 0)
{
m_context << u256(0);
return;
}
eth::Instruction load = _fromCalldata ? eth::Instruction::CALLDATALOAD : eth::Instruction::MLOAD;
solAssert(_bytes <= 32, "Memory load of more than 32 bytes requested.");
if (_bytes == 32)
m_context << u256(_offset) << load;
else
{
// load data and add leading or trailing zeros by dividing/multiplying depending on alignment
u256 shiftFactor = u256(1) << ((32 - _bytes) * 8);
m_context << shiftFactor;
if (_leftAligned)
m_context << eth::Instruction::DUP1;
m_context << u256(_offset) << load << eth::Instruction::DIV;
if (_leftAligned)
m_context << eth::Instruction::MUL;
}
solAssert(_type.getCategory() != Type::Category::ByteArray, "Unable to statically load dynamic type.");
m_context << u256(_offset);
return loadFromMemoryHelper(_type, _fromCalldata, _padToWordBoundaries);
}
void CompilerUtils::storeInMemory(unsigned _offset, unsigned _bytes, bool _leftAligned)
void CompilerUtils::loadFromMemoryDynamic(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries)
{
if (_bytes == 0)
solAssert(_type.getCategory() != Type::Category::ByteArray, "Byte arrays not yet implemented.");
m_context << eth::Instruction::DUP1;
unsigned numBytes = loadFromMemoryHelper(_type, _fromCalldata, _padToWordBoundaries);
// update memory counter
for (unsigned i = 0; i < _type.getSizeOnStack(); ++i)
m_context << eth::swapInstruction(1 + i);
m_context << u256(numBytes) << eth::Instruction::ADD;
}
unsigned CompilerUtils::storeInMemory(unsigned _offset, Type const& _type, bool _padToWordBoundaries)
{
solAssert(_type.getCategory() != Type::Category::ByteArray, "Unable to statically store dynamic type.");
unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries);
if (numBytes > 0)
m_context << u256(_offset) << eth::Instruction::MSTORE;
return numBytes;
}
void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries)
{
if (_type.getCategory() == Type::Category::ByteArray)
{
m_context << eth::Instruction::POP;
return;
auto const& type = dynamic_cast<ByteArrayType const&>(_type);
if (type.getLocation() == ByteArrayType::Location::CallData)
{
// stack: target source_offset source_len
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::DUP5
// stack: target source_offset source_len source_len source_offset target
<< eth::Instruction::CALLDATACOPY
<< eth::Instruction::DUP3 << eth::Instruction::ADD
<< eth::Instruction::SWAP2 << eth::Instruction::POP << eth::Instruction::POP;
}
else
{
solAssert(type.getLocation() == ByteArrayType::Location::Storage, "Memory byte arrays not yet implemented.");
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;
// stack here: memory_offset storage_offset length_bytes
// jump to end if length is zero
m_context << eth::Instruction::DUP1 << eth::Instruction::ISZERO;
eth::AssemblyItem loopEnd = m_context.newTag();
m_context.appendConditionalJumpTo(loopEnd);
// compute memory end offset
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD << eth::Instruction::SWAP2;
// actual array data is stored at SHA3(storage_offset)
m_context << eth::Instruction::SWAP1;
CompilerUtils(m_context).computeHashStatic();
m_context << eth::Instruction::SWAP1;
// stack here: memory_end_offset storage_data_offset memory_offset
eth::AssemblyItem loopStart = m_context.newTag();
m_context << loopStart
// load and store
<< eth::Instruction::DUP2 << eth::Instruction::SLOAD
<< eth::Instruction::DUP2 << eth::Instruction::MSTORE
// increment storage_data_offset by 1
<< eth::Instruction::SWAP1 << u256(1) << eth::Instruction::ADD
// increment memory offset by 32
<< eth::Instruction::SWAP1 << u256(32) << eth::Instruction::ADD
// check for loop condition
<< eth::Instruction::DUP1 << eth::Instruction::DUP4 << eth::Instruction::GT;
m_context.appendConditionalJumpTo(loopStart);
m_context << loopEnd << eth::Instruction::POP << eth::Instruction::POP;
}
}
else
{
unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries);
if (numBytes > 0)
{
solAssert(_type.getSizeOnStack() == 1, "Memory store of types with stack size != 1 not implemented.");
m_context << eth::Instruction::DUP2 << eth::Instruction::MSTORE;
m_context << u256(numBytes) << eth::Instruction::ADD;
}
}
solAssert(_bytes <= 32, "Memory store of more than 32 bytes requested.");
if (_bytes != 32 && !_leftAligned)
// shift the value accordingly before storing
m_context << (u256(1) << ((32 - _bytes) * 8)) << eth::Instruction::MUL;
m_context << u256(_offset) << eth::Instruction::MSTORE;
}
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
@ -105,5 +157,220 @@ unsigned CompilerUtils::getSizeOnStack(vector<shared_ptr<Type const>> const& _va
return size;
}
void CompilerUtils::computeHashStatic(Type const& _type, bool _padToWordBoundaries)
{
unsigned length = storeInMemory(0, _type, _padToWordBoundaries);
m_context << u256(length) << u256(0) << eth::Instruction::SHA3;
}
void CompilerUtils::copyByteArrayToStorage(ByteArrayType const& _targetType,
ByteArrayType const& _sourceType) const
{
// stack layout: [source_ref] target_ref (top)
// need to leave target_ref on the stack at the end
solAssert(_targetType.getLocation() == ByteArrayType::Location::Storage, "");
switch (_sourceType.getLocation())
{
case ByteArrayType::Location::CallData:
{
// This also assumes that after "length" we only have zeros, i.e. it cannot be used to
// slice a byte array from calldata.
// stack: source_offset source_len target_ref
// fetch old length and convert to words
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;
m_context << u256(31) << eth::Instruction::ADD
<< u256(32) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
// stack here: source_offset source_len target_ref target_length_words
// actual array data is stored at SHA3(storage_offset)
m_context << eth::Instruction::DUP2;
CompilerUtils(m_context).computeHashStatic();
// compute target_data_end
m_context << eth::Instruction::DUP1 << eth::Instruction::SWAP2 << eth::Instruction::ADD
<< eth::Instruction::SWAP1;
// stack here: source_offset source_len target_ref target_data_end target_data_ref
// store length (in bytes)
m_context << eth::Instruction::DUP4 << eth::Instruction::DUP1 << eth::Instruction::DUP5
<< eth::Instruction::SSTORE;
// jump to end if length is zero
m_context << eth::Instruction::ISZERO;
eth::AssemblyItem copyLoopEnd = m_context.newTag();
m_context.appendConditionalJumpTo(copyLoopEnd);
// store start offset
m_context << eth::Instruction::DUP5;
// stack now: source_offset source_len target_ref target_data_end target_data_ref calldata_offset
eth::AssemblyItem copyLoopStart = m_context.newTag();
m_context << copyLoopStart
// copy from calldata and store
<< eth::Instruction::DUP1 << eth::Instruction::CALLDATALOAD
<< eth::Instruction::DUP3 << eth::Instruction::SSTORE
// increment target_data_ref by 1
<< eth::Instruction::SWAP1 << u256(1) << eth::Instruction::ADD
// increment calldata_offset by 32
<< eth::Instruction::SWAP1 << u256(32) << eth::Instruction::ADD
// check for loop condition
<< eth::Instruction::DUP1 << eth::Instruction::DUP6 << eth::Instruction::GT;
m_context.appendConditionalJumpTo(copyLoopStart);
m_context << eth::Instruction::POP;
m_context << copyLoopEnd;
// now clear leftover bytes of the old value
// stack now: source_offset source_len target_ref target_data_end target_data_ref
clearStorageLoop();
// stack now: source_offset source_len target_ref target_data_end
m_context << eth::Instruction::POP << eth::Instruction::SWAP2
<< eth::Instruction::POP << eth::Instruction::POP;
break;
}
case ByteArrayType::Location::Storage:
{
// this copies source to target and also clears target if it was larger
// stack: source_ref target_ref
// store target_ref
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2;
// fetch lengthes
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP2
<< eth::Instruction::DUP1 << eth::Instruction::SLOAD;
// stack: target_ref target_len_bytes target_ref source_ref source_len_bytes
// store new target length
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP4 << eth::Instruction::SSTORE;
// compute hashes (data positions)
m_context << eth::Instruction::SWAP2;
CompilerUtils(m_context).computeHashStatic();
m_context << eth::Instruction::SWAP1;
CompilerUtils(m_context).computeHashStatic();
// stack: target_ref target_len_bytes source_len_bytes target_data_pos source_data_pos
// convert lengthes from bytes to storage slots
m_context << u256(31) << u256(32) << eth::Instruction::DUP1 << eth::Instruction::DUP3
<< eth::Instruction::DUP8 << eth::Instruction::ADD << eth::Instruction::DIV
<< eth::Instruction::SWAP2
<< eth::Instruction::DUP6 << eth::Instruction::ADD << eth::Instruction::DIV;
// stack: target_ref target_len_bytes source_len_bytes target_data_pos source_data_pos target_len source_len
// @todo we might be able to go without a third counter
m_context << u256(0);
// stack: target_ref target_len_bytes source_len_bytes target_data_pos source_data_pos target_len source_len counter
eth::AssemblyItem copyLoopStart = m_context.newTag();
m_context << copyLoopStart;
// check for loop condition
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3
<< eth::Instruction::GT << eth::Instruction::ISZERO;
eth::AssemblyItem copyLoopEnd = m_context.newTag();
m_context.appendConditionalJumpTo(copyLoopEnd);
// copy
m_context << eth::Instruction::DUP4 << eth::Instruction::DUP2 << eth::Instruction::ADD
<< eth::Instruction::SLOAD
<< eth::Instruction::DUP6 << eth::Instruction::DUP3 << eth::Instruction::ADD
<< eth::Instruction::SSTORE;
// increment
m_context << u256(1) << eth::Instruction::ADD;
m_context.appendJumpTo(copyLoopStart);
m_context << copyLoopEnd;
// zero-out leftovers in target
// stack: target_ref target_len_bytes source_len_bytes target_data_pos source_data_pos target_len source_len counter
// add counter to target_data_pos
m_context << eth::Instruction::DUP5 << eth::Instruction::ADD
<< eth::Instruction::SWAP5 << eth::Instruction::POP;
// stack: target_ref target_len_bytes target_data_pos_updated target_data_pos source_data_pos target_len source_len
// add length to target_data_pos to get target_data_end
m_context << eth::Instruction::POP << eth::Instruction::DUP3 << eth::Instruction::ADD
<< eth::Instruction::SWAP4
<< eth::Instruction::POP << eth::Instruction::POP << eth::Instruction::POP;
// stack: target_ref target_data_end target_data_pos_updated
clearStorageLoop();
m_context << eth::Instruction::POP;
break;
}
default:
solAssert(false, "Given byte array location not implemented.");
}
}
unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries)
{
unsigned _encodedSize = _type.getCalldataEncodedSize();
unsigned numBytes = _padToWordBoundaries ? getPaddedSize(_encodedSize) : _encodedSize;
bool leftAligned = _type.getCategory() == Type::Category::String;
if (numBytes == 0)
m_context << eth::Instruction::POP << u256(0);
else
{
solAssert(numBytes <= 32, "Static memory load of more than 32 bytes requested.");
m_context << (_fromCalldata ? eth::Instruction::CALLDATALOAD : eth::Instruction::MLOAD);
if (numBytes != 32)
{
// add leading or trailing zeros by dividing/multiplying depending on alignment
u256 shiftFactor = u256(1) << ((32 - numBytes) * 8);
m_context << shiftFactor << eth::Instruction::SWAP1 << eth::Instruction::DIV;
if (leftAligned)
m_context << shiftFactor << eth::Instruction::MUL;
}
}
return numBytes;
}
void CompilerUtils::clearByteArray(ByteArrayType const& _type) const
{
solAssert(_type.getLocation() == ByteArrayType::Location::Storage, "");
// fetch length
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;
// set length to zero
m_context << u256(0) << eth::Instruction::DUP3 << eth::Instruction::SSTORE;
// convert length from bytes to storage slots
m_context << u256(31) << eth::Instruction::ADD
<< u256(32) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
// compute data positions
m_context << eth::Instruction::SWAP1;
CompilerUtils(m_context).computeHashStatic();
// stack: len data_pos
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2 << eth::Instruction::ADD
<< eth::Instruction::SWAP1;
clearStorageLoop();
// cleanup
m_context << eth::Instruction::POP;
}
unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const
{
unsigned _encodedSize = _type.getCalldataEncodedSize();
unsigned numBytes = _padToWordBoundaries ? getPaddedSize(_encodedSize) : _encodedSize;
bool leftAligned = _type.getCategory() == Type::Category::String;
if (numBytes == 0)
m_context << eth::Instruction::POP;
else
{
solAssert(numBytes <= 32, "Memory store of more than 32 bytes requested.");
if (numBytes != 32 && !leftAligned && !_padToWordBoundaries)
// shift the value accordingly before storing
m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
}
return numBytes;
}
void CompilerUtils::clearStorageLoop() const
{
// stack: end_pos pos
eth::AssemblyItem loopStart = m_context.newTag();
m_context << loopStart;
// check for loop condition
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3
<< eth::Instruction::GT << eth::Instruction::ISZERO;
eth::AssemblyItem zeroLoopEnd = m_context.newTag();
m_context.appendConditionalJumpTo(zeroLoopEnd);
// zero out
m_context << u256(0) << eth::Instruction::DUP2 << eth::Instruction::SSTORE;
// increment
m_context << u256(1) << eth::Instruction::ADD;
m_context.appendJumpTo(loopStart);
// cleanup
m_context << zeroLoopEnd;
m_context << eth::Instruction::POP;
}
}
}

View File

@ -37,15 +37,31 @@ public:
/// Loads data from memory to the stack.
/// @param _offset offset in memory (or calldata)
/// @param _bytes number of bytes to load
/// @param _leftAligned if true, store left aligned on stack (otherwise right aligned)
/// @param _type data type to load
/// @param _fromCalldata if true, load from calldata, not from memory
void loadFromMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false, bool _fromCalldata = false);
/// @param _padToWordBoundaries if true, assume the data is padded to word (32 byte) boundaries
/// @returns the number of bytes consumed in memory.
unsigned loadFromMemory(unsigned _offset, Type const& _type = IntegerType(256),
bool _fromCalldata = false, bool _padToWordBoundaries = false);
/// Dynamic version of @see loadFromMemory, expects the memory offset on the stack.
/// Stack pre: memory_offset
/// Stack post: value... (memory_offset+length)
void loadFromMemoryDynamic(Type const& _type, bool _fromCalldata = false, bool _padToWordBoundaries = true);
/// Stores data from stack in memory.
/// @param _offset offset in memory
/// @param _bytes number of bytes to store
/// @param _leftAligned if true, data is left aligned on stack (otherwise right aligned)
void storeInMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false);
/// @param _type type of the data on the stack
/// @param _padToWordBoundaries if true, pad the data to word (32 byte) boundaries
/// @returns the number of bytes written to memory (can be different from _bytes if
/// _padToWordBoundaries is true)
unsigned storeInMemory(unsigned _offset, Type const& _type = IntegerType(256), bool _padToWordBoundaries = false);
/// Dynamic version of @see storeInMemory, expects the memory offset below the value on the stack
/// and also updates that.
/// Stack pre: memory_offset value...
/// Stack post: (memory_offset+length)
void storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries = true);
/// @returns _size rounded up to the next multiple of 32 (the number of bytes occupied in the
/// padded calldata)
static unsigned getPaddedSize(unsigned _size) { return ((_size + 31) / 32) * 32; }
/// Moves the value that is at the top of the stack to a stack variable.
void moveToStackVariable(VariableDeclaration const& _variable);
@ -58,10 +74,38 @@ public:
static unsigned getSizeOnStack(std::vector<T> const& _variables);
static unsigned getSizeOnStack(std::vector<std::shared_ptr<Type const>> const& _variableTypes);
/// Appends code that computes tha SHA3 hash of the topmost stack element of type @a _type.
/// If @a _pad is set, padds the type to muliples of 32 bytes.
/// @note Only works for types of fixed size.
void computeHashStatic(Type const& _type = IntegerType(256), bool _padToWordBoundaries = false);
/// Copies a byte array to a byte array in storage.
/// Stack pre: [source_reference] target_reference
/// Stack post: target_reference
void copyByteArrayToStorage(ByteArrayType const& _targetType, ByteArrayType const& _sourceType) const;
/// Clears the length and data elements of the byte array referenced on the stack.
/// Stack pre: reference
/// Stack post:
void clearByteArray(ByteArrayType const& _type) const;
/// Bytes we need to the start of call data.
/// - The size in bytes of the function (hash) identifier.
static const unsigned int dataStartOffset;
private:
/// Prepares the given type for storing in memory by shifting it if necessary.
unsigned prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const;
/// Loads type from memory assuming memory offset is on stack top.
unsigned loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries);
/// Appends a loop that clears a sequence of storage slots (excluding end).
/// Stack pre: end_ref start_ref
/// Stack post: end_ref
void clearStorageLoop() const;
CompilerContext& m_context;
};
template <class T>
unsigned CompilerUtils::getSizeOnStack(std::vector<T> const& _variables)
{

View File

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

View File

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

View File

@ -38,7 +38,7 @@ struct CompilerError: virtual Exception {};
struct InternalCompilerError: virtual Exception {};
struct DocstringParsingError: virtual Exception {};
typedef boost::error_info<struct tag_sourceLocation, Location> errinfo_sourceLocation;
using errinfo_sourceLocation = boost::error_info<struct tag_sourceLocation, Location>;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -16,13 +16,16 @@
*/
/**
* @author Christian <c@ethdev.com>
* @author Gav Wood <g@ethdev.com>
* @date 2014
* Solidity AST to EVM bytecode compiler for expressions.
*/
#include <functional>
#include <memory>
#include <boost/noncopyable.hpp>
#include <libdevcore/Common.h>
#include <libsolidity/BaseTypes.h>
#include <libsolidity/ASTVisitor.h>
namespace dev {
@ -36,6 +39,7 @@ namespace solidity {
class CompilerContext;
class Type;
class IntegerType;
class ByteArrayType;
class StaticStringType;
/**
@ -50,14 +54,17 @@ public:
static void compileExpression(CompilerContext& _context, Expression const& _expression, bool _optimize = false);
/// Appends code to remove dirty higher order bits in case of an implicit promotion to a wider type.
static void appendTypeConversion(CompilerContext& _context, Type const& _typeOnStack, Type const& _targetType);
static void appendTypeConversion(CompilerContext& _context, Type const& _typeOnStack,
Type const& _targetType, bool _cleanupNeeded = false);
/// Appends code for a State Variable accessor function
static void appendStateVariableAccessor(CompilerContext& _context, VariableDeclaration const& _varDecl, bool _optimize = false);
private:
explicit ExpressionCompiler(CompilerContext& _compilerContext, bool _optimize = false):
m_optimize(_optimize), m_context(_compilerContext), m_currentLValue(m_context) {}
virtual bool visit(Assignment const& _assignment) override;
virtual void endVisit(UnaryOperation const& _unaryOperation) override;
virtual bool visit(UnaryOperation const& _unaryOperation) override;
virtual bool visit(BinaryOperation const& _binaryOperation) override;
virtual bool visit(FunctionCall const& _functionCall) override;
virtual bool visit(NewExpression const& _newExpression) override;
@ -85,24 +92,24 @@ private:
//// Appends code that cleans higher-order bits for integer types.
void appendHighBitsCleanup(IntegerType const& _typeOnStack);
/// Additional options used in appendExternalFunctionCall.
struct FunctionCallOptions
{
FunctionCallOptions() {}
/// Invoked to copy the address to the stack
std::function<void()> obtainAddress;
/// Invoked to copy the ethe value to the stack (if not specified, value is 0).
std::function<void()> obtainValue;
/// If true, do not prepend function index to call data
bool bare = false;
/// If false, use calling convention that all arguments and return values are packed as
/// 32 byte values with padding.
bool packDensely = true;
};
/// Appends code to call a function of the given type with the given arguments.
void appendExternalFunctionCall(FunctionType const& _functionType, std::vector<ASTPointer<Expression const>> const& _arguments,
FunctionCallOptions const& _options = FunctionCallOptions());
bool bare = false);
/// Appends code that evaluates the given arguments and moves the result to memory. The memory offset is
/// expected to be on the stack and is updated by this call.
void appendArgumentsCopyToMemory(std::vector<ASTPointer<Expression const>> const& _arguments,
TypePointers const& _types = {},
bool _padToWordBoundaries = true,
bool _padExceptionIfFourBytes = false);
/// Appends code that moves a stack element of the given type to memory. The memory offset is
/// expected below the stack element and is updated by this call.
void appendTypeMoveToMemory(Type const& _type, bool _padToWordBoundaries = true);
/// Appends code that evaluates a single expression and moves the result to memory. The memory offset is
/// expected to be on the stack and is updated by this call.
void appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression);
/// Appends code for a State Variable accessor function
void appendStateVariableAccessor(VariableDeclaration const& _varDecl);
/**
* Helper class to store and retrieve lvalues to and from various locations.
@ -112,46 +119,55 @@ private:
class LValue
{
public:
enum LValueType { NONE, STACK, MEMORY, STORAGE };
enum class LValueType { None, Stack, Memory, Storage };
explicit LValue(CompilerContext& _compilerContext): m_context(&_compilerContext) { reset(); }
LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType, unsigned _baseStackOffset = 0);
LValue(CompilerContext& _compilerContext, LValueType _type,
std::shared_ptr<Type const> const& _dataType, unsigned _baseStackOffset = 0);
/// Set type according to the declaration and retrieve the reference.
/// @a _expression is the current expression
void fromIdentifier(Identifier const& _identifier, Declaration const& _declaration);
void reset() { m_type = NONE; m_baseStackOffset = 0; }
void reset() { m_type = LValueType::None; m_dataType.reset(); m_baseStackOffset = 0; m_size = 0; }
bool isValid() const { return m_type != NONE; }
bool isInOnStack() const { return m_type == STACK; }
bool isInMemory() const { return m_type == MEMORY; }
bool isInStorage() const { return m_type == STORAGE; }
bool isValid() const { return m_type != LValueType::None; }
bool isInOnStack() const { return m_type == LValueType::Stack; }
bool isInMemory() const { return m_type == LValueType::Memory; }
bool isInStorage() const { return m_type == LValueType::Storage; }
/// @returns true if this lvalue reference type occupies a slot on the stack.
bool storesReferenceOnStack() const { return m_type == STORAGE || m_type == MEMORY; }
bool storesReferenceOnStack() const { return m_type == LValueType::Storage || m_type == LValueType::Memory; }
/// Copies the value of the current lvalue to the top of the stack and, if @a _remove is true,
/// also removes the reference from the stack (note that is does not reset the type to @a NONE).
/// @a _expression is the current expression, used for error reporting.
void retrieveValue(Expression const& _expression, bool _remove = false) const;
/// Stores a value (from the stack directly beneath the reference, which is assumed to
/// be on the top of the stack, if any) in the lvalue and removes the reference.
/// Also removes the stored value from the stack if @a _move is
/// true. @a _expression is the current expression, used for error reporting.
void storeValue(Expression const& _expression, bool _move = false) const;
/// @a _location source location of the current expression, used for error reporting.
void retrieveValue(Location const& _location, bool _remove = false) const;
/// Moves a value from the stack to the lvalue. Removes the value if @a _move is true.
/// @a _location is the source location of the expression that caused this operation.
/// Stack pre: value [lvalue_ref]
/// Stack post if !_move: value_of(lvalue_ref)
void storeValue(Type const& _sourceType, Location const& _location = Location(), bool _move = false) const;
/// Stores zero in the lvalue.
/// @a _location is the source location of the requested operation
void setToZero(Location const& _location = Location()) const;
/// Convenience function to convert the stored reference to a value and reset type to NONE if
/// the reference was not requested by @a _expression.
void retrieveValueIfLValueNotRequested(Expression const& _expression);
private:
/// Convenience function to retrieve Value from Storage. Specific version of @ref retrieveValue
void retrieveValueFromStorage(bool _remove = false) const;
/// Copies from a byte array to a byte array in storage, both references on the stack.
void copyByteArrayToStorage(ByteArrayType const& _targetType, ByteArrayType const& _sourceType) const;
CompilerContext* m_context;
LValueType m_type;
LValueType m_type = LValueType::None;
std::shared_ptr<Type const> m_dataType;
/// If m_type is STACK, this is base stack offset (@see
/// CompilerContext::getBaseStackOffsetOfVariable) of a local variable.
unsigned m_baseStackOffset;
/// Size of the value of this lvalue on the stack.
unsigned m_stackSize;
unsigned m_baseStackOffset = 0;
/// Size of the value of this lvalue on the stack or the storage.
unsigned m_size = 0;
};
bool m_optimize;

View File

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

View File

@ -48,16 +48,16 @@ public:
GlobalContext();
void setCurrentContract(ContractDefinition const& _contract);
MagicVariableDeclaration const* getCurrentThis() const;
MagicVariableDeclaration const* getCurrentSuper() const;
/// @returns all magic variables.
std::vector<MagicVariableDeclaration const*> getMagicVariables() const;
/// @returns a vector of all implicit global declarations excluding "this".
std::vector<Declaration const*> getDeclarations() const;
private:
std::vector<std::shared_ptr<MagicVariableDeclaration const>> m_magicVariables;
ContractDefinition const* m_currentContract;
ContractDefinition const* m_currentContract = nullptr;
std::map<ContractDefinition const*, std::shared_ptr<MagicVariableDeclaration const>> mutable m_thisPointer;
std::map<ContractDefinition const*, std::shared_ptr<MagicVariableDeclaration const>> mutable m_superPointer;
};
}

View File

@ -2,6 +2,7 @@
#include <libsolidity/InterfaceHandler.h>
#include <libsolidity/AST.h>
#include <libsolidity/CompilerStack.h>
using namespace std;
namespace dev
{
@ -12,7 +13,7 @@ namespace solidity
InterfaceHandler::InterfaceHandler()
{
m_lastTag = DocTagType::NONE;
m_lastTag = DocTagType::None;
}
std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefinition const& _contractDef,
@ -20,12 +21,14 @@ std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefiniti
{
switch(_type)
{
case DocumentationType::NATSPEC_USER:
case DocumentationType::NatspecUser:
return getUserDocumentation(_contractDef);
case DocumentationType::NATSPEC_DEV:
case DocumentationType::NatspecDev:
return getDevDocumentation(_contractDef);
case DocumentationType::ABI_INTERFACE:
case DocumentationType::ABIInterface:
return getABIInterface(_contractDef);
case DocumentationType::ABISolidityInterface:
return getABISolidityInterface(_contractDef);
}
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type"));
@ -34,34 +37,79 @@ std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefiniti
std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinition const& _contractDef)
{
Json::Value methods(Json::arrayValue);
for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions())
Json::Value abi(Json::arrayValue);
for (auto const& it: _contractDef.getInterfaceFunctions())
{
Json::Value method;
Json::Value inputs(Json::arrayValue);
Json::Value outputs(Json::arrayValue);
auto populateParameters = [](std::vector<ASTPointer<VariableDeclaration>> const& _vars)
auto populateParameters = [](vector<string> const& _paramNames, vector<string> const& _paramTypes)
{
Json::Value params(Json::arrayValue);
for (ASTPointer<VariableDeclaration> const& var: _vars)
solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match");
for (unsigned i = 0; i < _paramNames.size(); ++i)
{
Json::Value input;
input["name"] = var->getName();
input["type"] = var->getType()->toString();
params.append(input);
Json::Value param;
param["name"] = _paramNames[i];
param["type"] = _paramTypes[i];
params.append(param);
}
return params;
};
method["name"] = f->getName();
method["constant"] = f->isDeclaredConst();
method["inputs"] = populateParameters(f->getParameters());
method["outputs"] = populateParameters(f->getReturnParameters());
methods.append(method);
Json::Value method;
method["type"] = "function";
method["name"] = it.second->getDeclaration().getName();
method["constant"] = it.second->isConstant();
method["inputs"] = populateParameters(it.second->getParameterNames(),
it.second->getParameterTypeNames());
method["outputs"] = populateParameters(it.second->getReturnParameterNames(),
it.second->getReturnParameterTypeNames());
abi.append(method);
}
return std::unique_ptr<std::string>(new std::string(m_writer.write(methods)));
for (auto const& it: _contractDef.getInterfaceEvents())
{
Json::Value event;
event["type"] = "event";
event["name"] = it->getName();
Json::Value params(Json::arrayValue);
for (auto const& p: it->getParameters())
{
Json::Value input;
input["name"] = p->getName();
input["type"] = p->getType()->toString();
input["indexed"] = p->isIndexed();
params.append(input);
}
event["inputs"] = params;
abi.append(event);
}
return std::unique_ptr<std::string>(new std::string(m_writer.write(abi)));
}
unique_ptr<string> InterfaceHandler::getABISolidityInterface(ContractDefinition const& _contractDef)
{
string ret = "contract " + _contractDef.getName() + "{";
for (auto const& it: _contractDef.getInterfaceFunctions())
{
auto populateParameters = [](vector<string> const& _paramNames,
vector<string> const& _paramTypes)
{
string r = "";
solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match");
for (unsigned i = 0; i < _paramNames.size(); ++i)
r += (r.size() ? "," : "(") + _paramTypes[i] + " " + _paramNames[i];
return r.size() ? r + ")" : "()";
};
ret += "function " + it.second->getDeclaration().getName() +
populateParameters(it.second->getParameterNames(), it.second->getParameterTypeNames()) +
(it.second->isConstant() ? "constant " : "");
if (it.second->getReturnParameterTypes().size())
ret += "returns" + populateParameters(it.second->getReturnParameterNames(), it.second->getReturnParameterTypeNames());
else if (ret.back() == ' ')
ret.pop_back();
ret += "{}";
}
return unique_ptr<string>(new string(ret + "}"));
}
std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefinition const& _contractDef)
@ -69,18 +117,18 @@ std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefi
Json::Value doc;
Json::Value methods(Json::objectValue);
for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions())
for (auto const& it: _contractDef.getInterfaceFunctions())
{
Json::Value user;
auto strPtr = f->getDocumentation();
auto strPtr = it.second->getDocumentation();
if (strPtr)
{
resetUser();
parseDocString(*strPtr, CommentOwner::FUNCTION);
parseDocString(*strPtr, CommentOwner::Function);
if (!m_notice.empty())
{// since @notice is the only user tag if missing function should not appear
user["notice"] = Json::Value(m_notice);
methods[f->getName()] = user;
methods[it.second->getCanonicalSignature()] = user;
}
}
}
@ -101,7 +149,7 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
{
m_contractAuthor.clear();
m_title.clear();
parseDocString(*contractDoc, CommentOwner::CONTRACT);
parseDocString(*contractDoc, CommentOwner::Contract);
if (!m_contractAuthor.empty())
doc["author"] = m_contractAuthor;
@ -110,14 +158,14 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
doc["title"] = m_title;
}
for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions())
for (auto const& it: _contractDef.getInterfaceFunctions())
{
Json::Value method;
auto strPtr = f->getDocumentation();
auto strPtr = it.second->getDocumentation();
if (strPtr)
{
resetDev();
parseDocString(*strPtr, CommentOwner::FUNCTION);
parseDocString(*strPtr, CommentOwner::Function);
if (!m_dev.empty())
method["details"] = Json::Value(m_dev);
@ -136,7 +184,7 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
method["return"] = m_return;
if (!method.empty()) // add the function, only if we have any documentation to add
methods[f->getName()] = method;
methods[it.second->getCanonicalSignature()] = method;
}
}
doc["methods"] = methods;
@ -194,7 +242,7 @@ std::string::const_iterator InterfaceHandler::parseDocTagParam(std::string::cons
auto paramDesc = std::string(currPos, nlPos);
m_params.push_back(std::make_pair(paramName, paramDesc));
m_lastTag = DocTagType::PARAM;
m_lastTag = DocTagType::Param;
return skipLineOrEOS(nlPos, _end);
}
@ -223,28 +271,28 @@ std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_ite
// LTODO: need to check for @(start of a tag) between here and the end of line
// for all cases. Also somehow automate list of acceptable tags for each
// language construct since current way does not scale well.
if (m_lastTag == DocTagType::NONE || _tag != "")
if (m_lastTag == DocTagType::None || _tag != "")
{
if (_tag == "dev")
return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV, false);
return parseDocTagLine(_pos, _end, m_dev, DocTagType::Dev, false);
else if (_tag == "notice")
return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE, false);
return parseDocTagLine(_pos, _end, m_notice, DocTagType::Notice, false);
else if (_tag == "return")
return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN, false);
return parseDocTagLine(_pos, _end, m_return, DocTagType::Return, false);
else if (_tag == "author")
{
if (_owner == CommentOwner::CONTRACT)
return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::AUTHOR, false);
else if (_owner == CommentOwner::FUNCTION)
return parseDocTagLine(_pos, _end, m_author, DocTagType::AUTHOR, false);
if (_owner == CommentOwner::Contract)
return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::Author, false);
else if (_owner == CommentOwner::Function)
return parseDocTagLine(_pos, _end, m_author, DocTagType::Author, false);
else
// LTODO: for now this else makes no sense but later comments will go to more language constructs
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag is legal only for contracts"));
}
else if (_tag == "title")
{
if (_owner == CommentOwner::CONTRACT)
return parseDocTagLine(_pos, _end, m_title, DocTagType::TITLE, false);
if (_owner == CommentOwner::Contract)
return parseDocTagLine(_pos, _end, m_title, DocTagType::Title, false);
else
// LTODO: Unknown tag, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag is legal only for contracts"));
@ -265,27 +313,27 @@ std::string::const_iterator InterfaceHandler::appendDocTag(std::string::const_it
{
switch (m_lastTag)
{
case DocTagType::DEV:
return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV, true);
case DocTagType::NOTICE:
return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE, true);
case DocTagType::RETURN:
return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN, true);
case DocTagType::AUTHOR:
if (_owner == CommentOwner::CONTRACT)
return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::AUTHOR, true);
else if (_owner == CommentOwner::FUNCTION)
return parseDocTagLine(_pos, _end, m_author, DocTagType::AUTHOR, true);
case DocTagType::Dev:
return parseDocTagLine(_pos, _end, m_dev, DocTagType::Dev, true);
case DocTagType::Notice:
return parseDocTagLine(_pos, _end, m_notice, DocTagType::Notice, true);
case DocTagType::Return:
return parseDocTagLine(_pos, _end, m_return, DocTagType::Return, true);
case DocTagType::Author:
if (_owner == CommentOwner::Contract)
return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::Author, true);
else if (_owner == CommentOwner::Function)
return parseDocTagLine(_pos, _end, m_author, DocTagType::Author, true);
else
// LTODO: Unknown tag, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag in illegal comment"));
case DocTagType::TITLE:
if (_owner == CommentOwner::CONTRACT)
return parseDocTagLine(_pos, _end, m_title, DocTagType::TITLE, true);
case DocTagType::Title:
if (_owner == CommentOwner::Contract)
return parseDocTagLine(_pos, _end, m_title, DocTagType::Title, true);
else
// LTODO: Unknown tag, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag in illegal comment"));
case DocTagType::PARAM:
case DocTagType::Param:
return appendDocTagParam(_pos, _end);
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Internal: Illegal documentation tag type"));
@ -321,10 +369,21 @@ void InterfaceHandler::parseDocString(std::string const& _string, CommentOwner _
currPos = parseDocTag(tagNameEndPos + 1, end, std::string(tagPos + 1, tagNameEndPos), _owner);
}
else if (m_lastTag != DocTagType::NONE) // continuation of the previous tag
else if (m_lastTag != DocTagType::None) // continuation of the previous tag
currPos = appendDocTag(currPos, end, _owner);
else if (currPos != end) // skip the line if a newline was found
else if (currPos != end)
{
// if it begins without a tag then consider it as @notice
if (currPos == _string.begin())
{
currPos = parseDocTag(currPos, end, "notice", CommentOwner::Function);
continue;
}
else if (nlPos == end) //end of text
return;
// else skip the line if a newline was found and we get here
currPos = nlPos + 1;
}
}
}

View File

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

View File

@ -31,7 +31,6 @@ namespace dev
namespace solidity
{
NameAndTypeResolver::NameAndTypeResolver(std::vector<Declaration const*> const& _globals)
{
for (Declaration const* declaration: _globals)
@ -46,18 +45,36 @@ void NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit)
void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
{
m_currentScope = &m_scopes[nullptr];
for (ASTPointer<InheritanceSpecifier> const& baseContract: _contract.getBaseContracts())
ReferencesResolver resolver(*baseContract, *this, &_contract, nullptr);
m_currentScope = &m_scopes[&_contract];
linearizeBaseContracts(_contract);
for (ContractDefinition const* base: _contract.getLinearizedBaseContracts())
importInheritedScope(*base);
for (ASTPointer<StructDefinition> const& structDef: _contract.getDefinedStructs())
ReferencesResolver resolver(*structDef, *this, nullptr);
ReferencesResolver resolver(*structDef, *this, &_contract, nullptr);
for (ASTPointer<EnumDefinition> const& enumDef: _contract.getDefinedEnums())
ReferencesResolver resolver(*enumDef, *this, &_contract, nullptr);
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables())
ReferencesResolver resolver(*variable, *this, nullptr);
ReferencesResolver resolver(*variable, *this, &_contract, nullptr);
for (ASTPointer<EventDefinition> const& event: _contract.getEvents())
ReferencesResolver resolver(*event, *this, &_contract, nullptr);
for (ASTPointer<ModifierDefinition> const& modifier: _contract.getFunctionModifiers())
{
m_currentScope = &m_scopes[modifier.get()];
ReferencesResolver resolver(*modifier, *this, &_contract, nullptr);
}
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
{
m_currentScope = &m_scopes[function.get()];
ReferencesResolver referencesResolver(*function, *this,
ReferencesResolver referencesResolver(*function, *this, &_contract,
function->getReturnParameterList().get());
}
m_currentScope = &m_scopes[nullptr];
}
void NameAndTypeResolver::checkTypeRequirements(ContractDefinition& _contract)
@ -69,7 +86,7 @@ void NameAndTypeResolver::checkTypeRequirements(ContractDefinition& _contract)
void NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
{
m_scopes[nullptr].registerDeclaration(_declaration, true);
m_scopes[nullptr].registerDeclaration(_declaration, false, true);
solAssert(_declaration.getScope() == nullptr, "Updated declaration outside global scope.");
}
@ -86,6 +103,98 @@ Declaration const* NameAndTypeResolver::getNameFromCurrentScope(ASTString const&
return m_currentScope->resolveName(_name, _recursive);
}
void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base)
{
auto iterator = m_scopes.find(&_base);
solAssert(iterator != end(m_scopes), "");
for (auto const& nameAndDeclaration: iterator->second.getDeclarations())
{
Declaration const* declaration = nameAndDeclaration.second;
// Import if it was declared in the base, is not the constructor and is visible in derived classes
if (declaration->getScope() == &_base && declaration->getName() != _base.getName() &&
declaration->isVisibleInDerivedContracts())
m_currentScope->registerDeclaration(*declaration);
}
}
void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract) const
{
// order in the lists is from derived to base
// list of lists to linearize, the last element is the list of direct bases
list<list<ContractDefinition const*>> input(1, {});
for (ASTPointer<InheritanceSpecifier> const& baseSpecifier: _contract.getBaseContracts())
{
ASTPointer<Identifier> baseName = baseSpecifier->getName();
ContractDefinition const* base = dynamic_cast<ContractDefinition const*>(
baseName->getReferencedDeclaration());
if (!base)
BOOST_THROW_EXCEPTION(baseName->createTypeError("Contract expected."));
// "push_front" has the effect that bases mentioned later can overwrite members of bases
// mentioned earlier
input.back().push_front(base);
vector<ContractDefinition const*> const& basesBases = base->getLinearizedBaseContracts();
if (basesBases.empty())
BOOST_THROW_EXCEPTION(baseName->createTypeError("Definition of base has to precede definition of derived contract"));
input.push_front(list<ContractDefinition const*>(basesBases.begin(), basesBases.end()));
}
input.back().push_front(&_contract);
vector<ContractDefinition const*> result = cThreeMerge(input);
if (result.empty())
BOOST_THROW_EXCEPTION(_contract.createTypeError("Linearization of inheritance graph impossible"));
_contract.setLinearizedBaseContracts(result);
}
template <class _T>
vector<_T const*> NameAndTypeResolver::cThreeMerge(list<list<_T const*>>& _toMerge)
{
// returns true iff _candidate appears only as last element of the lists
auto appearsOnlyAtHead = [&](_T const* _candidate) -> bool
{
for (list<_T const*> const& bases: _toMerge)
{
solAssert(!bases.empty(), "");
if (find(++bases.begin(), bases.end(), _candidate) != bases.end())
return false;
}
return true;
};
// returns the next candidate to append to the linearized list or nullptr on failure
auto nextCandidate = [&]() -> _T const*
{
for (list<_T const*> const& bases: _toMerge)
{
solAssert(!bases.empty(), "");
if (appearsOnlyAtHead(bases.front()))
return bases.front();
}
return nullptr;
};
// removes the given contract from all lists
auto removeCandidate = [&](_T const* _candidate)
{
for (auto it = _toMerge.begin(); it != _toMerge.end();)
{
it->remove(_candidate);
if (it->empty())
it = _toMerge.erase(it);
else
++it;
}
};
_toMerge.remove_if([](list<_T const*> const& _bases) { return _bases.empty(); });
vector<_T const*> result;
while (!_toMerge.empty())
{
_T const* candidate = nextCandidate();
if (!candidate)
return vector<_T const*>();
result.push_back(candidate);
removeCandidate(candidate);
}
return result;
}
DeclarationRegistrationHelper::DeclarationRegistrationHelper(map<ASTNode const*, DeclarationContainer>& _scopes,
ASTNode& _astRoot):
m_scopes(_scopes), m_currentScope(nullptr)
@ -115,6 +224,23 @@ void DeclarationRegistrationHelper::endVisit(StructDefinition&)
closeCurrentScope();
}
bool DeclarationRegistrationHelper::visit(EnumDefinition& _enum)
{
registerDeclaration(_enum, true);
return true;
}
void DeclarationRegistrationHelper::endVisit(EnumDefinition&)
{
closeCurrentScope();
}
bool DeclarationRegistrationHelper::visit(EnumValue& _value)
{
registerDeclaration(_value, false);
return true;
}
bool DeclarationRegistrationHelper::visit(FunctionDefinition& _function)
{
registerDeclaration(_function, true);
@ -128,6 +254,19 @@ void DeclarationRegistrationHelper::endVisit(FunctionDefinition&)
closeCurrentScope();
}
bool DeclarationRegistrationHelper::visit(ModifierDefinition& _modifier)
{
registerDeclaration(_modifier, true);
m_currentFunction = &_modifier;
return true;
}
void DeclarationRegistrationHelper::endVisit(ModifierDefinition&)
{
m_currentFunction = nullptr;
closeCurrentScope();
}
void DeclarationRegistrationHelper::endVisit(VariableDefinition& _variableDefinition)
{
// Register the local variables with the function
@ -142,6 +281,17 @@ bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration)
return true;
}
bool DeclarationRegistrationHelper::visit(EventDefinition& _event)
{
registerDeclaration(_event, true);
return true;
}
void DeclarationRegistrationHelper::endVisit(EventDefinition&)
{
closeCurrentScope();
}
void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declaration)
{
map<ASTNode const*, DeclarationContainer>::iterator iter;
@ -159,7 +309,7 @@ void DeclarationRegistrationHelper::closeCurrentScope()
void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope)
{
if (!m_scopes[m_currentScope].registerDeclaration(_declaration))
if (!m_scopes[m_currentScope].registerDeclaration(_declaration, !_declaration.isVisibleInContract()))
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_declaration.getLocation())
<< errinfo_comment("Identifier already declared."));
//@todo the exception should also contain the location of the first declaration
@ -169,8 +319,10 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio
}
ReferencesResolver::ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver,
ParameterList* _returnParameters, bool _allowLazyTypes):
m_resolver(_resolver), m_returnParameters(_returnParameters), m_allowLazyTypes(_allowLazyTypes)
ContractDefinition const* _currentContract,
ParameterList const* _returnParameters, bool _allowLazyTypes):
m_resolver(_resolver), m_currentContract(_currentContract),
m_returnParameters(_returnParameters), m_allowLazyTypes(_allowLazyTypes)
{
_root.accept(*this);
}
@ -181,7 +333,13 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable)
// or mapping
if (_variable.getTypeName())
{
_variable.setType(_variable.getTypeName()->toType());
TypePointer type = _variable.getTypeName()->toType();
// All byte array parameter types should point to call data
if (_variable.isExternalFunctionParameter())
if (auto const* byteArrayType = dynamic_cast<ByteArrayType const*>(type.get()))
type = byteArrayType->copyForLocation(ByteArrayType::Location::CallData);
_variable.setType(type);
if (!_variable.getType())
BOOST_THROW_EXCEPTION(_variable.getTypeName()->createTypeError("Invalid type name"));
}
@ -192,8 +350,7 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable)
bool ReferencesResolver::visit(Return& _return)
{
solAssert(m_returnParameters, "Return parameters not set.");
_return.setFunctionReturnParameters(*m_returnParameters);
_return.setFunctionReturnParameters(m_returnParameters);
return true;
}
@ -218,7 +375,7 @@ bool ReferencesResolver::visit(Identifier& _identifier)
if (!declaration)
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_identifier.getLocation())
<< errinfo_comment("Undeclared identifier."));
_identifier.setReferencedDeclaration(*declaration);
_identifier.setReferencedDeclaration(*declaration, m_currentContract);
return false;
}

View File

@ -23,6 +23,7 @@
#pragma once
#include <map>
#include <list>
#include <boost/noncopyable.hpp>
#include <libsolidity/DeclarationContainer.h>
@ -64,12 +65,23 @@ public:
private:
void reset();
/// Imports all members declared directly in the given contract (i.e. does not import inherited
/// members) into the current scope if they are not present already.
void importInheritedScope(ContractDefinition const& _base);
/// Computes "C3-Linearization" of base contracts and stores it inside the contract.
void linearizeBaseContracts(ContractDefinition& _contract) const;
/// Computes the C3-merge of the given list of lists of bases.
/// @returns the linearized vector or an empty vector if linearization is not possible.
template <class _T>
static std::vector<_T const*> cThreeMerge(std::list<std::list<_T const*>>& _toMerge);
/// Maps nodes declaring a scope to scopes, i.e. ContractDefinition and FunctionDeclaration,
/// where nullptr denotes the global scope. Note that structs are not scope since they do
/// not contain code.
std::map<ASTNode const*, DeclarationContainer> m_scopes;
DeclarationContainer* m_currentScope;
DeclarationContainer* m_currentScope = nullptr;
};
/**
@ -82,14 +94,21 @@ public:
DeclarationRegistrationHelper(std::map<ASTNode const*, DeclarationContainer>& _scopes, ASTNode& _astRoot);
private:
bool visit(ContractDefinition& _contract);
void endVisit(ContractDefinition& _contract);
bool visit(StructDefinition& _struct);
void endVisit(StructDefinition& _struct);
bool visit(FunctionDefinition& _function);
void endVisit(FunctionDefinition& _function);
void endVisit(VariableDefinition& _variableDefinition);
bool visit(VariableDeclaration& _declaration);
bool visit(ContractDefinition& _contract) override;
void endVisit(ContractDefinition& _contract) override;
bool visit(StructDefinition& _struct) override;
void endVisit(StructDefinition& _struct) override;
bool visit(EnumDefinition& _enum) override;
void endVisit(EnumDefinition& _enum) override;
bool visit(EnumValue& _value) override;
bool visit(FunctionDefinition& _function) override;
void endVisit(FunctionDefinition& _function) override;
bool visit(ModifierDefinition& _modifier) override;
void endVisit(ModifierDefinition& _modifier) override;
void endVisit(VariableDefinition& _variableDefinition) override;
bool visit(VariableDeclaration& _declaration) override;
bool visit(EventDefinition& _event) override;
void endVisit(EventDefinition& _event) override;
void enterNewSubScope(Declaration const& _declaration);
void closeCurrentScope();
@ -97,7 +116,7 @@ private:
std::map<ASTNode const*, DeclarationContainer>& m_scopes;
Declaration const* m_currentScope;
FunctionDefinition* m_currentFunction;
VariableScope* m_currentFunction;
};
/**
@ -108,7 +127,9 @@ class ReferencesResolver: private ASTVisitor
{
public:
ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver,
ParameterList* _returnParameters, bool _allowLazyTypes = true);
ContractDefinition const* _currentContract,
ParameterList const* _returnParameters,
bool _allowLazyTypes = true);
private:
virtual void endVisit(VariableDeclaration& _variable) override;
@ -118,7 +139,8 @@ private:
virtual bool visit(Return& _return) override;
NameAndTypeResolver& m_resolver;
ParameterList* m_returnParameters;
ContractDefinition const* m_currentContract;
ParameterList const* m_returnParameters;
bool m_allowLazyTypes;
};

View File

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

View File

@ -45,16 +45,32 @@ private:
/// End position of the current token
int getEndPosition() const;
struct VarDeclParserOptions {
VarDeclParserOptions() {}
bool allowVar = false;
bool isStateVariable = false;
bool allowIndexed = false;
bool allowEmptyName = false;
};
///@{
///@name Parsing functions for the AST nodes
ASTPointer<ImportDirective> parseImportDirective();
ASTPointer<ContractDefinition> parseContractDefinition();
ASTPointer<FunctionDefinition> parseFunctionDefinition(bool _isPublic);
ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier();
Declaration::Visibility parseVisibilitySpecifier(Token::Value _token);
ASTPointer<FunctionDefinition> parseFunctionDefinition(ASTString const* _contractName);
ASTPointer<StructDefinition> parseStructDefinition();
ASTPointer<VariableDeclaration> parseVariableDeclaration(bool _allowVar);
ASTPointer<EnumDefinition> parseEnumDefinition();
ASTPointer<EnumValue> parseEnumValue();
ASTPointer<VariableDeclaration> parseVariableDeclaration(VarDeclParserOptions const& _options = VarDeclParserOptions());
ASTPointer<ModifierDefinition> parseModifierDefinition();
ASTPointer<EventDefinition> parseEventDefinition();
ASTPointer<ModifierInvocation> parseModifierInvocation();
ASTPointer<Identifier> parseIdentifier();
ASTPointer<TypeName> parseTypeName(bool _allowVar);
ASTPointer<Mapping> parseMapping();
ASTPointer<ParameterList> parseParameterList(bool _allowEmpty = true);
ASTPointer<ParameterList> parseParameterList(bool _allowEmpty = true, bool _allowIndexed = false);
ASTPointer<Block> parseBlock();
ASTPointer<Statement> parseStatement();
ASTPointer<IfStatement> parseIfStatement();
@ -68,7 +84,8 @@ private:
ASTPointer<Expression> parseUnaryExpression();
ASTPointer<Expression> parseLeftHandSideExpression();
ASTPointer<Expression> parsePrimaryExpression();
std::vector<ASTPointer<Expression>> parseFunctionCallArguments();
std::vector<ASTPointer<Expression>> parseFunctionCallListArguments();
std::pair<std::vector<ASTPointer<Expression>>, std::vector<ASTPointer<ASTString>>> parseFunctionCallArguments();
///@}
///@{
@ -84,11 +101,16 @@ private:
ASTPointer<ASTString> getLiteralAndAdvance();
///@}
/// Creates an empty ParameterList at the current location (used if parameters can be omitted).
ASTPointer<ParameterList> createEmptyParameterList();
/// Creates a @ref ParserError exception and annotates it with the current position and the
/// given @a _description.
ParserError createParserError(std::string const& _description) const;
std::shared_ptr<Scanner> m_scanner;
/// Flag that signifies whether '_' is parsed as a PlaceholderStatement or a regular identifier.
bool m_insideModifier = false;
};
}

View File

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

View File

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

View File

@ -40,8 +40,11 @@
// You should have received a copy of the GNU General Public License
// along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
#include <map>
#include <libsolidity/Token.h>
using namespace std;
namespace dev
{
namespace solidity
@ -77,6 +80,19 @@ char const Token::m_tokenType[] =
{
TOKEN_LIST(KT, KK)
};
Token::Value Token::fromIdentifierOrKeyword(const std::string& _name)
{
// The following macros are used inside TOKEN_LIST and cause non-keyword tokens to be ignored
// and keywords to be put inside the keywords variable.
#define KEYWORD(name, string, precedence) {string, Token::name},
#define TOKEN(name, string, precedence)
static const map<string, Token::Value> keywords({TOKEN_LIST(TOKEN, KEYWORD)});
#undef KEYWORD
#undef TOKEN
auto it = keywords.find(_name);
return it == keywords.end() ? Token::Identifier : it->second;
}
#undef KT
#undef KK

496
Token.h
View File

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

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 FunctionType; // forward
using TypePointer = std::shared_ptr<Type const>;
using FunctionTypePointer = std::shared_ptr<FunctionType const>;
using TypePointers = std::vector<TypePointer>;
/**
@ -49,14 +50,16 @@ using TypePointers = std::vector<TypePointer>;
class MemberList
{
public:
using MemberMap = std::map<std::string, TypePointer>;
using MemberMap = std::vector<std::pair<std::string, TypePointer>>;
MemberList() {}
explicit MemberList(MemberMap const& _members): m_memberTypes(_members) {}
TypePointer getMemberType(std::string const& _name) const
{
auto it = m_memberTypes.find(_name);
return it != m_memberTypes.end() ? it->second : TypePointer();
for (auto const& it: m_memberTypes)
if (it.first == _name)
return it.second;
return TypePointer();
}
MemberMap::const_iterator begin() const { return m_memberTypes.begin(); }
@ -70,26 +73,31 @@ private:
/**
* Abstract base class that forms the root of the type hierarchy.
*/
class Type: private boost::noncopyable
class Type: private boost::noncopyable, public std::enable_shared_from_this<Type>
{
public:
enum class Category
{
INTEGER, BOOL, REAL, STRING, CONTRACT, STRUCT, FUNCTION, MAPPING, VOID, TYPE, MAGIC
Integer, IntegerConstant, Bool, Real, ByteArray,
String, Contract, Struct, Function, Enum,
Mapping, Void, TypeType, Modifier, Magic
};
///@{
///@name Factory functions
/// Factory functions that convert an AST @ref TypeName to a Type.
static std::shared_ptr<Type const> fromElementaryTypeName(Token::Value _typeToken);
static std::shared_ptr<Type const> fromUserDefinedTypeName(UserDefinedTypeName const& _typeName);
static std::shared_ptr<Type const> fromMapping(Mapping const& _typeName);
static std::shared_ptr<Type const> fromFunction(FunctionDefinition const& _function);
static TypePointer fromElementaryTypeName(Token::Value _typeToken);
static TypePointer fromElementaryTypeName(std::string const& _name);
static TypePointer fromUserDefinedTypeName(UserDefinedTypeName const& _typeName);
static TypePointer fromMapping(Mapping const& _typeName);
static TypePointer fromFunction(FunctionDefinition const& _function);
/// @}
/// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does
/// not fit any type.
static std::shared_ptr<Type const> forLiteral(Literal const& _literal);
static TypePointer forLiteral(Literal const& _literal);
/// @returns a pointer to _a or _b if the other is implicitly convertible to it or nullptr otherwise
static TypePointer commonType(TypePointer const& _a, TypePointer const& _b);
virtual Category getCategory() const = 0;
virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; }
@ -97,15 +105,27 @@ public:
{
return isImplicitlyConvertibleTo(_convertTo);
}
virtual bool acceptsBinaryOperator(Token::Value) const { return false; }
virtual bool acceptsUnaryOperator(Token::Value) const { return false; }
/// @returns the resulting type of applying the given unary operator or an empty pointer if
/// this is not possible.
/// The default implementation does not allow any unary operator.
virtual TypePointer unaryOperatorResult(Token::Value) const { return TypePointer(); }
/// @returns the resulting type of applying the given binary operator or an empty pointer if
/// this is not possible.
/// The default implementation allows comparison operators if a common type exists
virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
{
return Token::isCompareOp(_operator) ? commonType(shared_from_this(), _other) : TypePointer();
}
virtual bool operator==(Type const& _other) const { return getCategory() == _other.getCategory(); }
virtual bool operator!=(Type const& _other) const { return !this->operator ==(_other); }
/// @returns number of bytes used by this type when encoded for CALL, or 0 if the encoding
/// is not a simple big-endian encoding or the type cannot be stored on the stack.
/// is not a simple big-endian encoding or the type cannot be stored in calldata.
/// Note that irrespective of this size, each calldata element is padded to a multiple of 32 bytes.
virtual unsigned getCalldataEncodedSize() const { return 0; }
/// @returns true if the type is dynamically encoded in calldata
virtual bool isDynamicallySized() const { return false; }
/// @returns number of bytes required to hold this value in storage.
/// For dynamically "allocated" types, it returns the size of the statically allocated head,
virtual u256 getStorageSize() const { return 1; }
@ -117,6 +137,8 @@ public:
/// i.e. it behaves differently in lvalue context and in value context.
virtual bool isValueType() const { return false; }
virtual unsigned getSizeOnStack() const { return 1; }
/// @returns the real type of some types, like e.g: IntegerConstant
virtual TypePointer getRealType() const { return shared_from_this(); }
/// Returns the list of all members of this type. Default implementation: no members.
virtual MemberList const& getMembers() const { return EmptyMemberList; }
@ -124,10 +146,10 @@ public:
TypePointer getMemberType(std::string const& _name) const { return getMembers().getMemberType(_name); }
virtual std::string toString() const = 0;
virtual u256 literalValue(Literal const&) const
virtual u256 literalValue(Literal const*) const
{
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Literal value requested "
"for type without literals."));
"for type without literals."));
}
protected:
@ -143,20 +165,16 @@ class IntegerType: public Type
public:
enum class Modifier
{
UNSIGNED, SIGNED, HASH, ADDRESS
Unsigned, Signed, Hash, Address
};
virtual Category getCategory() const override { return Category::INTEGER; }
virtual Category getCategory() const override { return Category::Integer; }
/// @returns the smallest integer type for the given literal or an empty pointer
/// if no type fits.
static std::shared_ptr<IntegerType const> smallestTypeForLiteral(std::string const& _literal);
explicit IntegerType(int _bits, Modifier _modifier = Modifier::UNSIGNED);
explicit IntegerType(int _bits, Modifier _modifier = Modifier::Unsigned);
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool acceptsBinaryOperator(Token::Value _operator) const override;
virtual bool acceptsUnaryOperator(Token::Value _operator) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
virtual bool operator==(Type const& _other) const override;
@ -166,17 +184,51 @@ public:
virtual MemberList const& getMembers() const { return isAddress() ? AddressMemberList : EmptyMemberList; }
virtual std::string toString() const override;
virtual u256 literalValue(Literal const& _literal) const override;
int getNumBits() const { return m_bits; }
bool isHash() const { return m_modifier == Modifier::HASH || m_modifier == Modifier::ADDRESS; }
bool isAddress() const { return m_modifier == Modifier::ADDRESS; }
int isSigned() const { return m_modifier == Modifier::SIGNED; }
bool isHash() const { return m_modifier == Modifier::Hash || m_modifier == Modifier::Address; }
bool isAddress() const { return m_modifier == Modifier::Address; }
bool isSigned() const { return m_modifier == Modifier::Signed; }
static const MemberList AddressMemberList;
private:
int m_bits;
Modifier m_modifier;
static const MemberList AddressMemberList;
};
/**
* Integer constants either literals or computed. Example expressions: 2, 2+10, ~10.
* There is one distinct type per value.
*/
class IntegerConstantType: public Type
{
public:
virtual Category getCategory() const override { return Category::IntegerConstant; }
explicit IntegerConstantType(Literal const& _literal);
explicit IntegerConstantType(bigint _value): m_value(_value) {}
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
virtual bool operator==(Type const& _other) const override;
virtual bool canBeStored() const override { return false; }
virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned getSizeOnStack() const override { return 1; }
virtual std::string toString() const override;
virtual u256 literalValue(Literal const* _literal) const override;
virtual TypePointer getRealType() const override;
/// @returns the smallest integer type that can hold the value or an empty pointer if not possible.
std::shared_ptr<IntegerType const> getIntegerType() const;
private:
bigint m_value;
};
/**
@ -185,22 +237,23 @@ private:
class StaticStringType: public Type
{
public:
virtual Category getCategory() const override { return Category::STRING; }
virtual Category getCategory() const override { return Category::String; }
/// @returns the smallest string type for the given literal or an empty pointer
/// if no type fits.
static std::shared_ptr<StaticStringType> smallestTypeForLiteral(std::string const& _literal);
StaticStringType(int _bytes);
explicit StaticStringType(int _bytes);
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool operator==(Type const& _other) const override;
virtual unsigned getCalldataEncodedSize() const override { return m_bytes; }
virtual bool isValueType() const override { return true; }
virtual std::string toString() const override { return "string" + dev::toString(m_bytes); }
virtual u256 literalValue(Literal const& _literal) const override;
virtual u256 literalValue(Literal const* _literal) const override;
int getNumBytes() const { return m_bytes; }
@ -215,22 +268,45 @@ class BoolType: public Type
{
public:
BoolType() {}
virtual Category getCategory() const { return Category::BOOL; }
virtual Category getCategory() const override { return Category::Bool; }
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool acceptsBinaryOperator(Token::Value _operator) const override
{
return _operator == Token::AND || _operator == Token::OR;
}
virtual bool acceptsUnaryOperator(Token::Value _operator) const override
{
return _operator == Token::NOT || _operator == Token::DELETE;
}
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
virtual unsigned getCalldataEncodedSize() const { return 1; }
virtual bool isValueType() const override { return true; }
virtual std::string toString() const override { return "bool"; }
virtual u256 literalValue(Literal const& _literal) const override;
virtual u256 literalValue(Literal const* _literal) const override;
};
/**
* The type of a byte array, prototype for a general array.
*/
class ByteArrayType: public Type
{
public:
enum class Location { Storage, CallData, Memory };
virtual Category getCategory() const override { return Category::ByteArray; }
explicit ByteArrayType(Location _location): m_location(_location) {}
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual bool operator==(const Type& _other) const override;
virtual bool isDynamicallySized() const { return true; }
virtual unsigned getSizeOnStack() const override;
virtual std::string toString() const override { return "bytes"; }
virtual MemberList const& getMembers() const override { return s_byteArrayMemberList; }
Location getLocation() const { return m_location; }
/// @returns a copy of this type with location changed to @a _location
/// @todo this might move as far up as Type later
std::shared_ptr<ByteArrayType> copyForLocation(Location _location) const;
private:
Location m_location;
static const MemberList s_byteArrayMemberList;
};
/**
@ -239,27 +315,38 @@ public:
class ContractType: public Type
{
public:
virtual Category getCategory() const override { return Category::CONTRACT; }
ContractType(ContractDefinition const& _contract): m_contract(_contract) {}
/// Contracts can be converted to themselves and to addresses.
virtual Category getCategory() const override { return Category::Contract; }
explicit ContractType(ContractDefinition const& _contract, bool _super = false):
m_contract(_contract), m_super(_super) {}
/// Contracts can be implicitly converted to super classes and to addresses.
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
/// Contracts can be converted to themselves and to integers.
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const override;
virtual bool isValueType() const override { return true; }
virtual std::string toString() const override;
virtual MemberList const& getMembers() const override;
bool isSuper() const { return m_super; }
ContractDefinition const& getContractDefinition() const { return m_contract; }
/// Returns the function type of the constructor. Note that the location part of the function type
/// is not used, as this type cannot be the type of a variable or expression.
std::shared_ptr<FunctionType const> const& getConstructorType() const;
FunctionTypePointer const& getConstructorType() const;
unsigned getFunctionIndex(std::string const& _functionName) const;
/// @returns the identifier of the function with the given name or Invalid256 if such a name does
/// not exist.
u256 getFunctionIdentifier(std::string const& _functionName) const;
private:
ContractDefinition const& m_contract;
/// If true, it is the "super" type of the current contract, i.e. it contains only inherited
/// members.
bool m_super;
/// Type of the constructor, @see getConstructorType. Lazily initialized.
mutable std::shared_ptr<FunctionType const> m_constructorType;
mutable FunctionTypePointer m_constructorType;
/// List of member types, will be lazy-initialized because of recursive references.
mutable std::unique_ptr<MemberList> m_members;
};
@ -270,13 +357,9 @@ private:
class StructType: public Type
{
public:
virtual Category getCategory() const override { return Category::STRUCT; }
StructType(StructDefinition const& _struct): m_struct(_struct) {}
virtual bool acceptsUnaryOperator(Token::Value _operator) const override
{
return _operator == Token::DELETE;
}
virtual Category getCategory() const override { return Category::Struct; }
explicit StructType(StructDefinition const& _struct): m_struct(_struct) {}
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const override;
virtual bool canLiveOutsideStorage() const override;
@ -293,6 +376,32 @@ private:
mutable std::unique_ptr<MemberList> m_members;
};
/**
* The type of an enum instance, there is one distinct type per enum definition.
*/
class EnumType: public Type
{
public:
virtual Category getCategory() const override { return Category::Enum; }
explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {}
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual bool operator==(Type const& _other) const override;
virtual unsigned getSizeOnStack() const override { return 1; }
virtual std::string toString() const override;
virtual bool isValueType() const override { return true; }
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
EnumDefinition const& getEnumDefinition() const { return m_enum; }
/// @returns the value that the string has in the Enum
unsigned int getMemberValue(ASTString const& _member) const;
private:
EnumDefinition const& m_enum;
/// List of member types, will be lazy-initialized because of recursive references.
mutable std::unique_ptr<MemberList> m_members;
};
/**
* The type of a function, identified by its (return) parameter types.
* @todo the return parameters should also have names, i.e. return parameters should be a struct
@ -302,20 +411,47 @@ class FunctionType: public Type
{
public:
/// The meaning of the value(s) on the stack referencing the function:
/// INTERNAL: jump tag, EXTERNAL: contract address + function index,
/// INTERNAL: jump tag, EXTERNAL: contract address + function identifier,
/// BARE: contract address (non-abi contract call)
/// OTHERS: special virtual function, nothing on the stack
enum class Location { INTERNAL, EXTERNAL, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160, BARE };
/// @todo This documentation is outdated, and Location should rather be named "Type"
enum class Location { Internal, External, Creation, Send,
SHA3, Suicide,
ECRecover, SHA256, RIPEMD160,
Log0, Log1, Log2, Log3, Log4, Event,
SetGas, SetValue, BlockHash,
Bare };
virtual Category getCategory() const override { return Category::FUNCTION; }
virtual Category getCategory() const override { return Category::Function; }
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
FunctionType(TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes,
Location _location = Location::INTERNAL):
m_parameterTypes(_parameterTypes), m_returnParameterTypes(_returnParameterTypes),
m_location(_location) {}
explicit FunctionType(VariableDeclaration const& _varDecl);
explicit FunctionType(EventDefinition const& _event);
FunctionType(strings const& _parameterTypes, strings const& _returnParameterTypes,
Location _location = Location::Internal, bool _arbitraryParameters = false):
FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes),
_location, _arbitraryParameters) {}
FunctionType(
TypePointers const& _parameterTypes,
TypePointers const& _returnParameterTypes,
Location _location = Location::Internal,
bool _arbitraryParameters = false,
bool _gasSet = false,
bool _valueSet = false
):
m_parameterTypes (_parameterTypes),
m_returnParameterTypes (_returnParameterTypes),
m_location (_location),
m_arbitraryParameters (_arbitraryParameters),
m_gasSet (_gasSet),
m_valueSet (_valueSet)
{}
TypePointers const& getParameterTypes() const { return m_parameterTypes; }
std::vector<std::string> const& getParameterNames() const { return m_parameterNames; }
std::vector<std::string> const getParameterTypeNames() const;
TypePointers const& getReturnParameterTypes() const { return m_returnParameterTypes; }
std::vector<std::string> const& getReturnParameterNames() const { return m_returnParameterNames; }
std::vector<std::string> const getReturnParameterTypeNames() const;
virtual bool operator==(Type const& _other) const override;
virtual std::string toString() const override;
@ -323,13 +459,49 @@ public:
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable function type requested.")); }
virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned getSizeOnStack() const override;
virtual MemberList const& getMembers() const override;
Location const& getLocation() const { return m_location; }
/// @returns the canonical signature of this function type given the function name
/// If @a _name is not provided (empty string) then the @c m_declaration member of the
/// function type is used
std::string getCanonicalSignature(std::string const& _name = "") const;
Declaration const& getDeclaration() const
{
solAssert(m_declaration, "Requested declaration from a FunctionType that has none");
return *m_declaration;
}
bool hasDeclaration() const { return !!m_declaration; }
bool isConstant() const { return m_isConstant; }
/// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> getDocumentation() const;
/// true iff arguments are to be padded to multiples of 32 bytes for external calls
bool padArguments() const { return !(m_location == Location::SHA3 || m_location == Location::SHA256 || m_location == Location::RIPEMD160); }
bool takesArbitraryParameters() const { return m_arbitraryParameters; }
bool gasSet() const { return m_gasSet; }
bool valueSet() const { return m_valueSet; }
/// @returns a copy of this type, where gas or value are set manually. This will never set one
/// of the parameters to fals.
TypePointer copyAndSetGasOrValue(bool _setGas, bool _setValue) const;
private:
static TypePointers parseElementaryTypeVector(strings const& _types);
TypePointers m_parameterTypes;
TypePointers m_returnParameterTypes;
Location m_location;
std::vector<std::string> m_parameterNames;
std::vector<std::string> m_returnParameterNames;
Location const m_location;
/// true iff the function takes an arbitrary number of arguments of arbitrary types
bool const m_arbitraryParameters = false;
bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack
bool const m_valueSet = false; ///< true iff the value to be sent is on the stack
bool m_isConstant = false;
mutable std::unique_ptr<MemberList> m_members;
Declaration const* m_declaration = nullptr;
};
/**
@ -338,7 +510,7 @@ private:
class MappingType: public Type
{
public:
virtual Category getCategory() const override { return Category::MAPPING; }
virtual Category getCategory() const override { return Category::Mapping; }
MappingType(TypePointer const& _keyType, TypePointer const& _valueType):
m_keyType(_keyType), m_valueType(_valueType) {}
@ -361,9 +533,10 @@ private:
class VoidType: public Type
{
public:
virtual Category getCategory() const override { return Category::VOID; }
virtual Category getCategory() const override { return Category::Void; }
VoidType() {}
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
virtual std::string toString() const override { return "void"; }
virtual bool canBeStored() const override { return false; }
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable void type requested.")); }
@ -378,19 +551,48 @@ public:
class TypeType: public Type
{
public:
virtual Category getCategory() const override { return Category::TYPE; }
TypeType(TypePointer const& _actualType): m_actualType(_actualType) {}
virtual Category getCategory() const override { return Category::TypeType; }
explicit TypeType(TypePointer const& _actualType, ContractDefinition const* _currentContract = nullptr):
m_actualType(_actualType), m_currentContract(_currentContract) {}
TypePointer const& getActualType() const { return m_actualType; }
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
virtual bool operator==(Type const& _other) const override;
virtual bool canBeStored() const override { return false; }
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); }
virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned getSizeOnStack() const override { return 0; }
virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; }
virtual MemberList const& getMembers() const override;
private:
TypePointer m_actualType;
/// Context in which this type is used (influences visibility etc.), can be nullptr.
ContractDefinition const* m_currentContract;
/// List of member types, will be lazy-initialized because of recursive references.
mutable std::unique_ptr<MemberList> m_members;
};
/**
* The type of a function modifier. Not used for anything for now.
*/
class ModifierType: public Type
{
public:
virtual Category getCategory() const override { return Category::Modifier; }
explicit ModifierType(ModifierDefinition const& _modifier);
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
virtual bool canBeStored() const override { return false; }
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); }
virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned getSizeOnStack() const override { return 0; }
virtual bool operator==(Type const& _other) const override;
virtual std::string toString() const override;
private:
TypePointers m_parameterTypes;
};
@ -401,10 +603,16 @@ private:
class MagicType: public Type
{
public:
enum class Kind { BLOCK, MSG, TX };
virtual Category getCategory() const override { return Category::MAGIC; }
enum class Kind { Block, Message, Transaction };
virtual Category getCategory() const override { return Category::Magic; }
explicit MagicType(Kind _kind);
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override
{
return TypePointer();
}
MagicType(Kind _kind);
virtual bool operator==(Type const& _other) const;
virtual bool canBeStored() const override { return false; }
virtual bool canLiveOutsideStorage() const override { return true; }

View File

@ -45,5 +45,11 @@ inline void solAssertAux(bool _condition, std::string const& _errorDescription,
<< ::boost::throw_line(_line));
}
inline void solAssertAux(void const* _pointer, std::string const& _errorDescription, unsigned _line,
char const* _file, char const* _function)
{
solAssertAux(_pointer != nullptr, _errorDescription, _line, _file, _function);
}
}
}

View File

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