aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGav Wood <i@gavwood.com>2015-02-21 04:56:37 +0800
committerGav Wood <i@gavwood.com>2015-02-21 04:56:37 +0800
commit89d84edb16812ff9e4b1049ee0257d65c75f5a3c (patch)
treebaeda13e44a8fc0f6ac2f705fdf334da14405f39
parentd552ceb50f8e3888abf5b9a6095a5fb60ee91b5f (diff)
parent852405116654bda9f4dc1d4876a2368184b30a8c (diff)
downloaddexon-solidity-89d84edb16812ff9e4b1049ee0257d65c75f5a3c.tar
dexon-solidity-89d84edb16812ff9e4b1049ee0257d65c75f5a3c.tar.gz
dexon-solidity-89d84edb16812ff9e4b1049ee0257d65c75f5a3c.tar.bz2
dexon-solidity-89d84edb16812ff9e4b1049ee0257d65c75f5a3c.tar.lz
dexon-solidity-89d84edb16812ff9e4b1049ee0257d65c75f5a3c.tar.xz
dexon-solidity-89d84edb16812ff9e4b1049ee0257d65c75f5a3c.tar.zst
dexon-solidity-89d84edb16812ff9e4b1049ee0257d65c75f5a3c.zip
Merge branch 'develop'
Conflicts: README.md evmjit libdevcrypto/CryptoPP.cpp libethereum/State.cpp neth/main.cpp
-rw-r--r--AST.cpp456
-rw-r--r--AST.h393
-rw-r--r--ASTForward.h9
-rw-r--r--ASTJsonConverter.cpp469
-rw-r--r--ASTJsonConverter.h135
-rw-r--r--ASTPrinter.cpp82
-rw-r--r--ASTPrinter.h14
-rw-r--r--ASTVisitor.h28
-rw-r--r--AST_accept.h128
-rw-r--r--BaseTypes.h4
-rw-r--r--CMakeLists.txt5
-rw-r--r--CallGraph.cpp67
-rw-r--r--CallGraph.h55
-rw-r--r--Compiler.cpp347
-rw-r--r--Compiler.h40
-rw-r--r--CompilerContext.cpp95
-rw-r--r--CompilerContext.h40
-rw-r--r--CompilerStack.cpp136
-rw-r--r--CompilerStack.h40
-rw-r--r--CompilerUtils.cpp327
-rw-r--r--CompilerUtils.h56
-rw-r--r--DeclarationContainer.cpp15
-rw-r--r--DeclarationContainer.h11
-rw-r--r--Exceptions.h2
-rw-r--r--ExpressionCompiler.cpp1043
-rw-r--r--ExpressionCompiler.h92
-rw-r--r--GlobalContext.cpp65
-rw-r--r--GlobalContext.h6
-rw-r--r--InterfaceHandler.cpp179
-rw-r--r--InterfaceHandler.h21
-rw-r--r--NameAndTypeResolver.cpp183
-rw-r--r--NameAndTypeResolver.h46
-rw-r--r--Parser.cpp507
-rw-r--r--Parser.h30
-rw-r--r--Scanner.cpp185
-rw-r--r--Scanner.h1
-rw-r--r--Token.cpp16
-rw-r--r--Token.h496
-rw-r--r--Types.cpp826
-rw-r--r--Types.h354
-rw-r--r--Utils.h6
-rw-r--r--grammar.txt19
42 files changed, 5342 insertions, 1687 deletions
diff --git a/AST.cpp b/AST.cpp
index 1fa6d8f6..761427db 100644
--- a/AST.cpp
+++ b/AST.cpp
@@ -27,6 +27,8 @@
#include <libsolidity/Exceptions.h>
#include <libsolidity/AST_accept.h>
+#include <libdevcrypto/SHA3.h>
+
using namespace std;
namespace dev
@@ -39,40 +41,199 @@ TypeError ASTNode::createTypeError(string const& _description) const
return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description);
}
+TypePointer ContractDefinition::getType(ContractDefinition const* _currentContract) const
+{
+ return make_shared<TypeType>(make_shared<ContractType>(*this), _currentContract);
+}
+
void ContractDefinition::checkTypeRequirements()
{
+ for (ASTPointer<InheritanceSpecifier> const& baseSpecifier: getBaseContracts())
+ baseSpecifier->checkTypeRequirements();
+
+ checkIllegalOverrides();
+
FunctionDefinition const* constructor = getConstructor();
if (constructor && !constructor->getReturnParameters().empty())
BOOST_THROW_EXCEPTION(constructor->getReturnParameterList()->createTypeError(
- "Non-empty \"returns\" directive for constructor."));
+ "Non-empty \"returns\" directive for constructor."));
+
+ FunctionDefinition const* fallbackFunction = nullptr;
+ for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions())
+ if (function->getName().empty())
+ {
+ if (fallbackFunction)
+ BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Only one fallback function is allowed."));
+ else
+ {
+ fallbackFunction = function.get();
+ if (!fallbackFunction->getParameters().empty())
+ BOOST_THROW_EXCEPTION(fallbackFunction->getParameterList().createTypeError("Fallback function cannot take parameters."));
+ }
+ }
+ for (ASTPointer<ModifierDefinition> const& modifier: getFunctionModifiers())
+ modifier->checkTypeRequirements();
for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions())
function->checkTypeRequirements();
+
+ // check for hash collisions in function signatures
+ set<FixedHash<4>> hashes;
+ for (auto const& it: getInterfaceFunctionList())
+ {
+ FixedHash<4> const& hash = it.first;
+ if (hashes.count(hash))
+ BOOST_THROW_EXCEPTION(createTypeError(
+ std::string("Function signature hash collision for ") +
+ it.second->getCanonicalSignature()));
+ hashes.insert(hash);
+ }
}
-vector<FunctionDefinition const*> ContractDefinition::getInterfaceFunctions() const
+map<FixedHash<4>, FunctionTypePointer> ContractDefinition::getInterfaceFunctions() const
{
- vector<FunctionDefinition const*> exportedFunctions;
- for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions)
- if (f->isPublic() && f->getName() != getName())
- exportedFunctions.push_back(f.get());
- auto compareNames = [](FunctionDefinition const* _a, FunctionDefinition const* _b)
- {
- return _a->getName().compare(_b->getName()) < 0;
- };
+ auto exportedFunctionList = getInterfaceFunctionList();
+
+ map<FixedHash<4>, FunctionTypePointer> exportedFunctions;
+ for (auto const& it: exportedFunctionList)
+ exportedFunctions.insert(it);
+
+ solAssert(exportedFunctionList.size() == exportedFunctions.size(),
+ "Hash collision at Function Definition Hash calculation");
- sort(exportedFunctions.begin(), exportedFunctions.end(), compareNames);
return exportedFunctions;
}
FunctionDefinition const* ContractDefinition::getConstructor() const
{
for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions)
- if (f->getName() == getName())
+ if (f->isConstructor())
return f.get();
return nullptr;
}
+FunctionDefinition const* ContractDefinition::getFallbackFunction() const
+{
+ for (ContractDefinition const* contract: getLinearizedBaseContracts())
+ for (ASTPointer<FunctionDefinition> const& f: contract->getDefinedFunctions())
+ if (f->getName().empty())
+ return f.get();
+ return nullptr;
+}
+
+void ContractDefinition::checkIllegalOverrides() const
+{
+ // TODO unify this at a later point. for this we need to put the constness and the access specifier
+ // into the types
+ map<string, FunctionDefinition const*> functions;
+ map<string, ModifierDefinition const*> modifiers;
+
+ // We search from derived to base, so the stored item causes the error.
+ for (ContractDefinition const* contract: getLinearizedBaseContracts())
+ {
+ for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions())
+ {
+ if (function->isConstructor())
+ continue; // constructors can neither be overridden nor override anything
+ string const& name = function->getName();
+ if (modifiers.count(name))
+ BOOST_THROW_EXCEPTION(modifiers[name]->createTypeError("Override changes function to modifier."));
+ FunctionDefinition const*& override = functions[name];
+ if (!override)
+ override = function.get();
+ else if (override->getVisibility() != function->getVisibility() ||
+ override->isDeclaredConst() != function->isDeclaredConst() ||
+ FunctionType(*override) != FunctionType(*function))
+ BOOST_THROW_EXCEPTION(override->createTypeError("Override changes extended function signature."));
+ }
+ for (ASTPointer<ModifierDefinition> const& modifier: contract->getFunctionModifiers())
+ {
+ string const& name = modifier->getName();
+ if (functions.count(name))
+ BOOST_THROW_EXCEPTION(functions[name]->createTypeError("Override changes modifier to function."));
+ ModifierDefinition const*& override = modifiers[name];
+ if (!override)
+ override = modifier.get();
+ else if (ModifierType(*override) != ModifierType(*modifier))
+ BOOST_THROW_EXCEPTION(override->createTypeError("Override changes modifier signature."));
+ }
+ }
+}
+
+std::vector<ASTPointer<EventDefinition>> const& ContractDefinition::getInterfaceEvents() const
+{
+ if (!m_interfaceEvents)
+ {
+ set<string> eventsSeen;
+ m_interfaceEvents.reset(new std::vector<ASTPointer<EventDefinition>>());
+ for (ContractDefinition const* contract: getLinearizedBaseContracts())
+ for (ASTPointer<EventDefinition> const& e: contract->getEvents())
+ if (eventsSeen.count(e->getName()) == 0)
+ {
+ eventsSeen.insert(e->getName());
+ m_interfaceEvents->push_back(e);
+ }
+ }
+ return *m_interfaceEvents;
+}
+
+vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::getInterfaceFunctionList() const
+{
+ if (!m_interfaceFunctionList)
+ {
+ set<string> functionsSeen;
+ m_interfaceFunctionList.reset(new vector<pair<FixedHash<4>, FunctionTypePointer>>());
+ for (ContractDefinition const* contract: getLinearizedBaseContracts())
+ {
+ for (ASTPointer<FunctionDefinition> const& f: contract->getDefinedFunctions())
+ if (f->isPublic() && !f->isConstructor() && !f->getName().empty() && functionsSeen.count(f->getName()) == 0)
+ {
+ functionsSeen.insert(f->getName());
+ FixedHash<4> hash(dev::sha3(f->getCanonicalSignature()));
+ m_interfaceFunctionList->push_back(make_pair(hash, make_shared<FunctionType>(*f, false)));
+ }
+
+ for (ASTPointer<VariableDeclaration> const& v: contract->getStateVariables())
+ if (v->isPublic() && functionsSeen.count(v->getName()) == 0)
+ {
+ FunctionType ftype(*v);
+ functionsSeen.insert(v->getName());
+ FixedHash<4> hash(dev::sha3(ftype.getCanonicalSignature(v->getName())));
+ m_interfaceFunctionList->push_back(make_pair(hash, make_shared<FunctionType>(*v)));
+ }
+ }
+ }
+ return *m_interfaceFunctionList;
+}
+
+TypePointer EnumValue::getType(ContractDefinition const*) const
+{
+ EnumDefinition const* parentDef = dynamic_cast<EnumDefinition const*>(getScope());
+ solAssert(parentDef, "Enclosing Scope of EnumValue was not set");
+ return make_shared<EnumType>(*parentDef);
+}
+
+void InheritanceSpecifier::checkTypeRequirements()
+{
+ m_baseName->checkTypeRequirements();
+ for (ASTPointer<Expression> const& argument: m_arguments)
+ argument->checkTypeRequirements();
+
+ ContractDefinition const* base = dynamic_cast<ContractDefinition const*>(m_baseName->getReferencedDeclaration());
+ solAssert(base, "Base contract not available.");
+ TypePointers parameterTypes = ContractType(*base).getConstructorType()->getParameterTypes();
+ if (parameterTypes.size() != m_arguments.size())
+ BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for constructor call."));
+ for (size_t i = 0; i < m_arguments.size(); ++i)
+ if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i]))
+ BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in constructer call."));
+}
+
+TypePointer StructDefinition::getType(ContractDefinition const*) const
+{
+ return make_shared<TypeType>(make_shared<StructType>(*this));
+}
+
void StructDefinition::checkMemberTypes() const
{
for (ASTPointer<VariableDeclaration> const& member: getMembers())
@@ -93,7 +254,7 @@ void StructDefinition::checkRecursion() const
<< errinfo_comment("Recursive struct definition."));
definitionsSeen.insert(def);
for (ASTPointer<VariableDeclaration> const& member: def->getMembers())
- if (member->getType()->getCategory() == Type::Category::STRUCT)
+ if (member->getType()->getCategory() == Type::Category::Struct)
{
UserDefinedTypeName const& typeName = dynamic_cast<UserDefinedTypeName const&>(*member->getTypeName());
queue.push_back(&dynamic_cast<StructDefinition const&>(*typeName.getReferencedDeclaration()));
@@ -101,15 +262,89 @@ void StructDefinition::checkRecursion() const
}
}
+TypePointer EnumDefinition::getType(ContractDefinition const*) const
+{
+ return make_shared<TypeType>(make_shared<EnumType>(*this));
+}
+
+TypePointer FunctionDefinition::getType(ContractDefinition const*) const
+{
+ return make_shared<FunctionType>(*this);
+}
+
void FunctionDefinition::checkTypeRequirements()
{
for (ASTPointer<VariableDeclaration> const& var: getParameters() + getReturnParameters())
if (!var->getType()->canLiveOutsideStorage())
BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage."));
+ for (ASTPointer<ModifierInvocation> const& modifier: m_functionModifiers)
+ modifier->checkTypeRequirements();
+
+ m_body->checkTypeRequirements();
+}
+
+string FunctionDefinition::getCanonicalSignature() const
+{
+ return FunctionType(*this).getCanonicalSignature(getName());
+}
+
+bool VariableDeclaration::isLValue() const
+{
+ // External function parameters are Read-Only
+ return !isExternalFunctionParameter();
+}
+
+bool VariableDeclaration::isExternalFunctionParameter() const
+{
+ auto const* function = dynamic_cast<FunctionDefinition const*>(getScope());
+ if (!function || function->getVisibility() != Declaration::Visibility::External)
+ return false;
+ for (auto const& variable: function->getParameters())
+ if (variable.get() == this)
+ return true;
+ return false;
+}
+TypePointer ModifierDefinition::getType(ContractDefinition const*) const
+{
+ return make_shared<ModifierType>(*this);
+}
+
+void ModifierDefinition::checkTypeRequirements()
+{
m_body->checkTypeRequirements();
}
+void ModifierInvocation::checkTypeRequirements()
+{
+ m_modifierName->checkTypeRequirements();
+ for (ASTPointer<Expression> const& argument: m_arguments)
+ argument->checkTypeRequirements();
+
+ ModifierDefinition const* modifier = dynamic_cast<ModifierDefinition const*>(m_modifierName->getReferencedDeclaration());
+ solAssert(modifier, "Function modifier not found.");
+ vector<ASTPointer<VariableDeclaration>> const& parameters = modifier->getParameters();
+ if (parameters.size() != m_arguments.size())
+ BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for modifier invocation."));
+ for (size_t i = 0; i < m_arguments.size(); ++i)
+ if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameters[i]->getType()))
+ BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in modifier invocation."));
+}
+
+void EventDefinition::checkTypeRequirements()
+{
+ int numIndexed = 0;
+ for (ASTPointer<VariableDeclaration> const& var: getParameters())
+ {
+ if (var->isIndexed())
+ numIndexed++;
+ if (!var->getType()->canLiveOutsideStorage())
+ BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage."));
+ }
+ if (numIndexed > 3)
+ BOOST_THROW_EXCEPTION(createTypeError("More than 3 indexed arguments for event."));
+}
+
void Block::checkTypeRequirements()
{
for (shared_ptr<Statement> const& statement: m_statements)
@@ -145,7 +380,8 @@ void Return::checkTypeRequirements()
{
if (!m_expression)
return;
- solAssert(m_returnParameters, "Return parameters not assigned.");
+ if (!m_returnParameters)
+ BOOST_THROW_EXCEPTION(createTypeError("Return arguments not allowed."));
if (m_returnParameters->getParameters().size() != 1)
BOOST_THROW_EXCEPTION(createTypeError("Different number of arguments in return statement "
"than in returns declaration."));
@@ -168,7 +404,17 @@ void VariableDefinition::checkTypeRequirements()
{
// no type declared and no previous assignment, infer the type
m_value->checkTypeRequirements();
- m_variable->setType(m_value->getType());
+ TypePointer type = m_value->getType();
+ if (type->getCategory() == Type::Category::IntegerConstant)
+ {
+ auto intType = dynamic_pointer_cast<IntegerConstantType const>(type)->getIntegerType();
+ if (!intType)
+ BOOST_THROW_EXCEPTION(m_value->createTypeError("Invalid integer constant " + type->toString()));
+ type = intType;
+ }
+ else if (type->getCategory() == Type::Category::Void)
+ BOOST_THROW_EXCEPTION(m_variable->createTypeError("var cannot be void type"));
+ m_variable->setType(type);
}
}
}
@@ -177,20 +423,31 @@ void Assignment::checkTypeRequirements()
{
m_leftHandSide->checkTypeRequirements();
m_leftHandSide->requireLValue();
- //@todo later, assignments to structs might be possible, but not to mappings
- if (!m_leftHandSide->getType()->isValueType() && !m_leftHandSide->isLocalLValue())
- BOOST_THROW_EXCEPTION(createTypeError("Assignment to non-local non-value lvalue."));
- m_rightHandSide->expectType(*m_leftHandSide->getType());
+ if (m_leftHandSide->getType()->getCategory() == Type::Category::Mapping)
+ BOOST_THROW_EXCEPTION(createTypeError("Mappings cannot be assigned to."));
m_type = m_leftHandSide->getType();
- if (m_assigmentOperator != Token::ASSIGN)
+ if (m_assigmentOperator == Token::Assign)
+ m_rightHandSide->expectType(*m_type);
+ else
+ {
// compound assignment
- if (!m_type->acceptsBinaryOperator(Token::AssignmentToBinaryOp(m_assigmentOperator)))
- BOOST_THROW_EXCEPTION(createTypeError("Operator not compatible with type."));
+ m_rightHandSide->checkTypeRequirements();
+ TypePointer resultType = m_type->binaryOperatorResult(Token::AssignmentToBinaryOp(m_assigmentOperator),
+ m_rightHandSide->getType());
+ if (!resultType || *resultType != *m_type)
+ BOOST_THROW_EXCEPTION(createTypeError("Operator " + string(Token::toString(m_assigmentOperator)) +
+ " not compatible with types " +
+ m_type->toString() + " and " +
+ m_rightHandSide->getType()->toString()));
+ }
}
void ExpressionStatement::checkTypeRequirements()
{
m_expression->checkTypeRequirements();
+ if (m_expression->getType()->getCategory() == Type::Category::IntegerConstant)
+ if (!dynamic_pointer_cast<IntegerConstantType const>(m_expression->getType())->getIntegerType())
+ BOOST_THROW_EXCEPTION(m_expression->createTypeError("Invalid integer constant."));
}
void Expression::expectType(Type const& _expectedType)
@@ -212,12 +469,12 @@ void Expression::requireLValue()
void UnaryOperation::checkTypeRequirements()
{
- // INC, DEC, ADD, SUB, NOT, BIT_NOT, DELETE
+ // Inc, Dec, Add, Sub, Not, BitNot, Delete
m_subExpression->checkTypeRequirements();
- if (m_operator == Token::Value::INC || m_operator == Token::Value::DEC || m_operator == Token::Value::DELETE)
+ if (m_operator == Token::Value::Inc || m_operator == Token::Value::Dec || m_operator == Token::Value::Delete)
m_subExpression->requireLValue();
- m_type = m_subExpression->getType();
- if (!m_type->acceptsUnaryOperator(m_operator))
+ m_type = m_subExpression->getType()->unaryOperatorResult(m_operator);
+ if (!m_type)
BOOST_THROW_EXCEPTION(createTypeError("Unary operator not compatible with type."));
}
@@ -225,24 +482,13 @@ void BinaryOperation::checkTypeRequirements()
{
m_left->checkTypeRequirements();
m_right->checkTypeRequirements();
- if (m_right->getType()->isImplicitlyConvertibleTo(*m_left->getType()))
- m_commonType = m_left->getType();
- else if (m_left->getType()->isImplicitlyConvertibleTo(*m_right->getType()))
- m_commonType = m_right->getType();
- else
- BOOST_THROW_EXCEPTION(createTypeError("No common type found in binary operation: " +
- m_left->getType()->toString() + " vs. " +
+ m_commonType = m_left->getType()->binaryOperatorResult(m_operator, m_right->getType());
+ if (!m_commonType)
+ BOOST_THROW_EXCEPTION(createTypeError("Operator " + string(Token::toString(m_operator)) +
+ " not compatible with types " +
+ m_left->getType()->toString() + " and " +
m_right->getType()->toString()));
- if (Token::isCompareOp(m_operator))
- m_type = make_shared<BoolType>();
- else
- {
- m_type = m_commonType;
- if (!m_commonType->acceptsBinaryOperator(m_operator))
- BOOST_THROW_EXCEPTION(createTypeError("Operator " + string(Token::toString(m_operator)) +
- " not compatible with type " +
- m_commonType->toString()));
- }
+ m_type = Token::isCompareOp(m_operator) ? make_shared<BoolType>() : m_commonType;
}
void FunctionCall::checkTypeRequirements()
@@ -258,8 +504,9 @@ void FunctionCall::checkTypeRequirements()
//@todo for structs, we have to check the number of arguments to be equal to the
// number of non-mapping members
if (m_arguments.size() != 1)
- BOOST_THROW_EXCEPTION(createTypeError("More than one argument for "
- "explicit type conersion."));
+ BOOST_THROW_EXCEPTION(createTypeError("More than one argument for explicit type conversion."));
+ if (!m_names.empty())
+ BOOST_THROW_EXCEPTION(createTypeError("Type conversion cannot allow named arguments."));
if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type.getActualType()))
BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed."));
m_type = type.getActualType();
@@ -270,11 +517,48 @@ void FunctionCall::checkTypeRequirements()
// and then ask if that is implicitly convertible to the struct represented by the
// function parameters
TypePointers const& parameterTypes = functionType->getParameterTypes();
- if (parameterTypes.size() != m_arguments.size())
+ if (!functionType->takesArbitraryParameters() && parameterTypes.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call."));
- for (size_t i = 0; i < m_arguments.size(); ++i)
- if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i]))
- BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in function call."));
+
+ if (m_names.empty())
+ {
+ for (size_t i = 0; i < m_arguments.size(); ++i)
+ if (!functionType->takesArbitraryParameters() &&
+ !m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i]))
+ BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError("Invalid type for argument in function call."));
+ }
+ else
+ {
+ if (functionType->takesArbitraryParameters())
+ BOOST_THROW_EXCEPTION(createTypeError("Named arguments cannnot be used for functions "
+ "that take arbitrary parameters."));
+ auto const& parameterNames = functionType->getParameterNames();
+ if (parameterNames.size() != m_names.size())
+ BOOST_THROW_EXCEPTION(createTypeError("Some argument names are missing."));
+
+ // check duplicate names
+ for (size_t i = 0; i < m_names.size(); i++)
+ for (size_t j = i + 1; j < m_names.size(); j++)
+ if (*m_names[i] == *m_names[j])
+ BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError("Duplicate named argument."));
+
+ for (size_t i = 0; i < m_names.size(); i++) {
+ bool found = false;
+ for (size_t j = 0; j < parameterNames.size(); j++) {
+ if (parameterNames[j] == *m_names[i]) {
+ // check type convertible
+ if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[j]))
+ BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in function call."));
+
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ BOOST_THROW_EXCEPTION(createTypeError("Named argument does not match function declaration."));
+ }
+ }
+
// @todo actually the return type should be an anonymous struct,
// but we change it to the type of the first return value until we have structs
if (functionType->getReturnParameterTypes().empty())
@@ -288,26 +572,19 @@ void FunctionCall::checkTypeRequirements()
bool FunctionCall::isTypeConversion() const
{
- return m_expression->getType()->getCategory() == Type::Category::TYPE;
+ return m_expression->getType()->getCategory() == Type::Category::TypeType;
}
void NewExpression::checkTypeRequirements()
{
m_contractName->checkTypeRequirements();
- for (ASTPointer<Expression> const& argument: m_arguments)
- argument->checkTypeRequirements();
-
m_contract = dynamic_cast<ContractDefinition const*>(m_contractName->getReferencedDeclaration());
if (!m_contract)
BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract."));
- shared_ptr<ContractType const> type = make_shared<ContractType const>(*m_contract);
- m_type = type;
- TypePointers const& parameterTypes = type->getConstructorType()->getParameterTypes();
- if (parameterTypes.size() != m_arguments.size())
- BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for constructor call."));
- for (size_t i = 0; i < m_arguments.size(); ++i)
- if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i]))
- BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in constructor call."));
+ shared_ptr<ContractType const> contractType = make_shared<ContractType>(*m_contract);
+ TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes();
+ m_type = make_shared<FunctionType>(parameterTypes, TypePointers{contractType},
+ FunctionType::Location::Creation);
}
void MemberAccess::checkTypeRequirements()
@@ -316,78 +593,43 @@ void MemberAccess::checkTypeRequirements()
Type const& type = *m_expression->getType();
m_type = type.getMemberType(*m_memberName);
if (!m_type)
- BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found in " + type.toString()));
- //@todo later, this will not always be STORAGE
- m_lvalue = type.getCategory() == Type::Category::STRUCT ? LValueType::STORAGE : LValueType::NONE;
+ BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found or not "
+ "visible in " + type.toString()));
+ m_isLValue = (type.getCategory() == Type::Category::Struct);
}
void IndexAccess::checkTypeRequirements()
{
m_base->checkTypeRequirements();
- if (m_base->getType()->getCategory() != Type::Category::MAPPING)
+ if (m_base->getType()->getCategory() != Type::Category::Mapping)
BOOST_THROW_EXCEPTION(m_base->createTypeError("Indexed expression has to be a mapping (is " +
m_base->getType()->toString() + ")"));
MappingType const& type = dynamic_cast<MappingType const&>(*m_base->getType());
m_index->expectType(*type.getKeyType());
m_type = type.getValueType();
- m_lvalue = LValueType::STORAGE;
+ m_isLValue = true;
}
void Identifier::checkTypeRequirements()
{
solAssert(m_referencedDeclaration, "Identifier not resolved.");
- VariableDeclaration const* variable = dynamic_cast<VariableDeclaration const*>(m_referencedDeclaration);
- if (variable)
- {
- if (!variable->getType())
- BOOST_THROW_EXCEPTION(createTypeError("Variable referenced before type could be determined."));
- m_type = variable->getType();
- m_lvalue = variable->isLocalVariable() ? LValueType::LOCAL : LValueType::STORAGE;
- return;
- }
- //@todo can we unify these with TypeName::toType()?
- StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(m_referencedDeclaration);
- if (structDef)
- {
- // note that we do not have a struct type here
- m_type = make_shared<TypeType const>(make_shared<StructType const>(*structDef));
- return;
- }
- FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(m_referencedDeclaration);
- if (functionDef)
- {
- // a function reference is not a TypeType, because calling a TypeType converts to the type.
- // Calling a function (e.g. function(12), otherContract.function(34)) does not do a type
- // conversion.
- m_type = make_shared<FunctionType const>(*functionDef);
- return;
- }
- ContractDefinition const* contractDef = dynamic_cast<ContractDefinition const*>(m_referencedDeclaration);
- if (contractDef)
- {
- m_type = make_shared<TypeType const>(make_shared<ContractType>(*contractDef));
- return;
- }
- MagicVariableDeclaration const* magicVariable = dynamic_cast<MagicVariableDeclaration const*>(m_referencedDeclaration);
- if (magicVariable)
- {
- m_type = magicVariable->getType();
- return;
- }
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Declaration reference of unknown/forbidden type."));
+ m_isLValue = m_referencedDeclaration->isLValue();
+ m_type = m_referencedDeclaration->getType(m_currentContract);
+ if (!m_type)
+ BOOST_THROW_EXCEPTION(createTypeError("Declaration referenced before type could be determined."));
}
void ElementaryTypeNameExpression::checkTypeRequirements()
{
- m_type = make_shared<TypeType const>(Type::fromElementaryTypeName(m_typeToken));
+ m_type = make_shared<TypeType>(Type::fromElementaryTypeName(m_typeToken));
}
void Literal::checkTypeRequirements()
{
m_type = Type::forLiteral(*this);
if (!m_type)
- BOOST_THROW_EXCEPTION(createTypeError("Literal value too large."));
+ BOOST_THROW_EXCEPTION(createTypeError("Invalid literal value."));
}
}
diff --git a/AST.h b/AST.h
index b7c3dc6c..64dac594 100644
--- a/AST.h
+++ b/AST.h
@@ -132,68 +132,167 @@ private:
class Declaration: public ASTNode
{
public:
- Declaration(Location const& _location, ASTPointer<ASTString> const& _name):
- ASTNode(_location), m_name(_name), m_scope(nullptr) {}
+ /// Visibility ordered from restricted to unrestricted.
+ enum class Visibility { Default, Private, Inheritable, Public, External };
+
+ Declaration(Location const& _location, ASTPointer<ASTString> const& _name,
+ Visibility _visibility = Visibility::Default):
+ ASTNode(_location), m_name(_name), m_visibility(_visibility), m_scope(nullptr) {}
/// @returns the declared name.
ASTString const& getName() const { return *m_name; }
+ Visibility getVisibility() const { return m_visibility == Visibility::Default ? getDefaultVisibility() : m_visibility; }
+ bool isPublic() const { return getVisibility() >= Visibility::Public; }
+ bool isVisibleInContract() const { return getVisibility() != Visibility::External; }
+ bool isVisibleInDerivedContracts() const { return isVisibleInContract() && getVisibility() >= Visibility::Inheritable; }
+
/// @returns the scope this declaration resides in. Can be nullptr if it is the global scope.
/// Available only after name and type resolution step.
Declaration const* getScope() const { return m_scope; }
void setScope(Declaration const* _scope) { m_scope = _scope; }
+ /// @returns the type of expressions referencing this declaration.
+ /// The current contract has to be given since this context can change the type, especially of
+ /// contract types.
+ virtual TypePointer getType(ContractDefinition const* m_currentContract = nullptr) const = 0;
+ virtual bool isLValue() const { return false; }
+
+protected:
+ virtual Visibility getDefaultVisibility() const { return Visibility::Public; }
+
private:
ASTPointer<ASTString> m_name;
+ Visibility m_visibility;
Declaration const* m_scope;
};
/**
+ * Abstract class that is added to each AST node that can store local variables.
+ */
+class VariableScope
+{
+public:
+ void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); }
+ std::vector<VariableDeclaration const*> const& getLocalVariables() const { return m_localVariables; }
+
+private:
+ std::vector<VariableDeclaration const*> m_localVariables;
+};
+
+/**
+ * Abstract class that is added to each AST node that can receive documentation.
+ */
+class Documented
+{
+public:
+ explicit Documented(ASTPointer<ASTString> const& _documentation): m_documentation(_documentation) {}
+
+ /// @return A shared pointer of an ASTString.
+ /// Can contain a nullptr in which case indicates absence of documentation
+ ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
+
+protected:
+ ASTPointer<ASTString> m_documentation;
+};
+
+/// @}
+
+/**
* Definition of a contract. This is the only AST nodes where child nodes are not visited in
* document order. It first visits all struct declarations, then all variable declarations and
* finally all function declarations.
*/
-class ContractDefinition: public Declaration
+class ContractDefinition: public Declaration, public Documented
{
public:
ContractDefinition(Location const& _location,
ASTPointer<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation,
+ std::vector<ASTPointer<InheritanceSpecifier>> const& _baseContracts,
std::vector<ASTPointer<StructDefinition>> const& _definedStructs,
+ std::vector<ASTPointer<EnumDefinition>> const& _definedEnums,
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables,
- std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions):
- Declaration(_location, _name),
+ std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions,
+ std::vector<ASTPointer<ModifierDefinition>> const& _functionModifiers,
+ std::vector<ASTPointer<EventDefinition>> const& _events):
+ Declaration(_location, _name), Documented(_documentation),
+ m_baseContracts(_baseContracts),
m_definedStructs(_definedStructs),
+ m_definedEnums(_definedEnums),
m_stateVariables(_stateVariables),
m_definedFunctions(_definedFunctions),
- m_documentation(_documentation)
+ m_functionModifiers(_functionModifiers),
+ m_events(_events)
{}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
+ std::vector<ASTPointer<InheritanceSpecifier>> const& getBaseContracts() const { return m_baseContracts; }
std::vector<ASTPointer<StructDefinition>> const& getDefinedStructs() const { return m_definedStructs; }
+ std::vector<ASTPointer<EnumDefinition>> const& getDefinedEnums() const { return m_definedEnums; }
std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() const { return m_stateVariables; }
+ std::vector<ASTPointer<ModifierDefinition>> const& getFunctionModifiers() const { return m_functionModifiers; }
std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() const { return m_definedFunctions; }
+ std::vector<ASTPointer<EventDefinition>> const& getEvents() const { return m_events; }
+ std::vector<ASTPointer<EventDefinition>> const& getInterfaceEvents() const;
+
+ virtual TypePointer getType(ContractDefinition const* m_currentContract) const override;
- /// Checks that the constructor does not have a "returns" statement and calls
- /// checkTypeRequirements on all its functions.
+ /// Checks that there are no illegal overrides, that the constructor does not have a "returns"
+ /// and calls checkTypeRequirements on all its functions.
void checkTypeRequirements();
- /// @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,18 +443,109 @@ 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;
@@ -458,6 +687,21 @@ private:
};
/**
+ * 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;
};
/// @}
+
}
}
diff --git a/ASTForward.h b/ASTForward.h
index c960fc8f..0b6817e4 100644
--- a/ASTForward.h
+++ b/ASTForward.h
@@ -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.
diff --git a/ASTJsonConverter.cpp b/ASTJsonConverter.cpp
new file mode 100644
index 00000000..04feafe2
--- /dev/null
+++ b/ASTJsonConverter.cpp
@@ -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";
+}
+
+}
+}
diff --git a/ASTJsonConverter.h b/ASTJsonConverter.h
new file mode 100644
index 00000000..466801e9
--- /dev/null
+++ b/ASTJsonConverter.h
@@ -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;
+};
+
+}
+}
diff --git a/ASTPrinter.cpp b/ASTPrinter.cpp
index 916fca1e..d380b002 100644
--- a/ASTPrinter.cpp
+++ b/ASTPrinter.cpp
@@ -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--;
diff --git a/ASTPrinter.h b/ASTPrinter.h
index fc5fb4ac..d9072aac 100644
--- a/ASTPrinter.h
+++ b/ASTPrinter.h
@@ -44,16 +44,23 @@ public:
bool visit(ImportDirective const& _node) override;
bool visit(ContractDefinition const& _node) override;
+ bool visit(InheritanceSpecifier const& _node) override;
bool visit(StructDefinition const& _node) override;
+ bool visit(EnumDefinition const& _node) override;
+ bool visit(EnumValue const& _node) override;
bool visit(ParameterList const& _node) override;
bool visit(FunctionDefinition const& _node) override;
bool visit(VariableDeclaration const& _node) override;
+ bool visit(ModifierDefinition const& _node) override;
+ bool visit(ModifierInvocation const& _node) override;
+ bool visit(EventDefinition const& _node) override;
bool visit(TypeName const& _node) override;
bool visit(ElementaryTypeName const& _node) override;
bool visit(UserDefinedTypeName const& _node) override;
bool visit(Mapping const& _node) override;
bool visit(Statement const& _node) override;
bool visit(Block const& _node) override;
+ bool visit(PlaceholderStatement const& _node) override;
bool visit(IfStatement const& _node) override;
bool visit(BreakableStatement const& _node) override;
bool visit(WhileStatement const& _node) override;
@@ -78,16 +85,23 @@ public:
void endVisit(ImportDirective const&) override;
void endVisit(ContractDefinition const&) override;
+ void endVisit(InheritanceSpecifier const&) override;
void endVisit(StructDefinition const&) override;
+ void endVisit(EnumDefinition const&) override;
+ void endVisit(EnumValue const&) override;
void endVisit(ParameterList const&) override;
void endVisit(FunctionDefinition const&) override;
void endVisit(VariableDeclaration const&) override;
+ void endVisit(ModifierDefinition const&) override;
+ void endVisit(ModifierInvocation const&) override;
+ void endVisit(EventDefinition const&) override;
void endVisit(TypeName const&) override;
void endVisit(ElementaryTypeName const&) override;
void endVisit(UserDefinedTypeName const&) override;
void endVisit(Mapping const&) override;
void endVisit(Statement const&) override;
void endVisit(Block const&) override;
+ void endVisit(PlaceholderStatement const&) override;
void endVisit(IfStatement const&) override;
void endVisit(BreakableStatement const&) override;
void endVisit(WhileStatement const&) override;
diff --git a/ASTVisitor.h b/ASTVisitor.h
index 33a8a338..a7fa6b1c 100644
--- a/ASTVisitor.h
+++ b/ASTVisitor.h
@@ -45,16 +45,23 @@ public:
virtual bool visit(SourceUnit&) { return true; }
virtual bool visit(ImportDirective&) { return true; }
virtual bool visit(ContractDefinition&) { return true; }
+ virtual bool visit(InheritanceSpecifier&) { return true; }
virtual bool visit(StructDefinition&) { return true; }
+ virtual bool visit(EnumDefinition&) { return true; }
+ virtual bool visit(EnumValue&) { return true; }
virtual bool visit(ParameterList&) { return true; }
virtual bool visit(FunctionDefinition&) { return true; }
virtual bool visit(VariableDeclaration&) { return true; }
+ virtual bool visit(ModifierDefinition&) { return true; }
+ virtual bool visit(ModifierInvocation&) { return true; }
+ virtual bool visit(EventDefinition&) { return true; }
virtual bool visit(TypeName&) { return true; }
virtual bool visit(ElementaryTypeName&) { return true; }
virtual bool visit(UserDefinedTypeName&) { return true; }
virtual bool visit(Mapping&) { return true; }
virtual bool visit(Statement&) { return true; }
virtual bool visit(Block&) { return true; }
+ virtual bool visit(PlaceholderStatement&) { return true; }
virtual bool visit(IfStatement&) { return true; }
virtual bool visit(BreakableStatement&) { return true; }
virtual bool visit(WhileStatement&) { return true; }
@@ -81,16 +88,23 @@ public:
virtual void endVisit(SourceUnit&) { }
virtual void endVisit(ImportDirective&) { }
virtual void endVisit(ContractDefinition&) { }
+ virtual void endVisit(InheritanceSpecifier&) { }
virtual void endVisit(StructDefinition&) { }
+ virtual void endVisit(EnumDefinition&) { }
+ virtual void endVisit(EnumValue&) { }
virtual void endVisit(ParameterList&) { }
virtual void endVisit(FunctionDefinition&) { }
virtual void endVisit(VariableDeclaration&) { }
+ virtual void endVisit(ModifierDefinition&) { }
+ virtual void endVisit(ModifierInvocation&) { }
+ virtual void endVisit(EventDefinition&) { }
virtual void endVisit(TypeName&) { }
virtual void endVisit(ElementaryTypeName&) { }
virtual void endVisit(UserDefinedTypeName&) { }
virtual void endVisit(Mapping&) { }
virtual void endVisit(Statement&) { }
virtual void endVisit(Block&) { }
+ virtual void endVisit(PlaceholderStatement&) { }
virtual void endVisit(IfStatement&) { }
virtual void endVisit(BreakableStatement&) { }
virtual void endVisit(WhileStatement&) { }
@@ -121,16 +135,23 @@ public:
virtual bool visit(SourceUnit const&) { return true; }
virtual bool visit(ImportDirective const&) { return true; }
virtual bool visit(ContractDefinition const&) { return true; }
+ virtual bool visit(InheritanceSpecifier const&) { return true; }
virtual bool visit(StructDefinition const&) { return true; }
+ virtual bool visit(EnumDefinition const&) { return true; }
+ virtual bool visit(EnumValue const&) { return true; }
virtual bool visit(ParameterList const&) { return true; }
virtual bool visit(FunctionDefinition const&) { return true; }
virtual bool visit(VariableDeclaration const&) { return true; }
+ virtual bool visit(ModifierDefinition const&) { return true; }
+ virtual bool visit(ModifierInvocation const&) { return true; }
+ virtual bool visit(EventDefinition const&) { return true; }
virtual bool visit(TypeName const&) { return true; }
virtual bool visit(ElementaryTypeName const&) { return true; }
virtual bool visit(UserDefinedTypeName const&) { return true; }
virtual bool visit(Mapping const&) { return true; }
virtual bool visit(Statement const&) { return true; }
virtual bool visit(Block const&) { return true; }
+ virtual bool visit(PlaceholderStatement const&) { return true; }
virtual bool visit(IfStatement const&) { return true; }
virtual bool visit(BreakableStatement const&) { return true; }
virtual bool visit(WhileStatement const&) { return true; }
@@ -157,16 +178,23 @@ public:
virtual void endVisit(SourceUnit const&) { }
virtual void endVisit(ImportDirective const&) { }
virtual void endVisit(ContractDefinition const&) { }
+ virtual void endVisit(InheritanceSpecifier const&) { }
virtual void endVisit(StructDefinition const&) { }
+ virtual void endVisit(EnumDefinition const&) { }
+ virtual void endVisit(EnumValue const&) { }
virtual void endVisit(ParameterList const&) { }
virtual void endVisit(FunctionDefinition const&) { }
virtual void endVisit(VariableDeclaration const&) { }
+ virtual void endVisit(ModifierDefinition const&) { }
+ virtual void endVisit(ModifierInvocation const&) { }
+ virtual void endVisit(EventDefinition const&) { }
virtual void endVisit(TypeName const&) { }
virtual void endVisit(ElementaryTypeName const&) { }
virtual void endVisit(UserDefinedTypeName const&) { }
virtual void endVisit(Mapping const&) { }
virtual void endVisit(Statement const&) { }
virtual void endVisit(Block const&) { }
+ virtual void endVisit(PlaceholderStatement const&) { }
virtual void endVisit(IfStatement const&) { }
virtual void endVisit(BreakableStatement const&) { }
virtual void endVisit(WhileStatement const&) { }
diff --git a/AST_accept.h b/AST_accept.h
index 0e5a71b6..b71e103d 100644
--- a/AST_accept.h
+++ b/AST_accept.h
@@ -61,8 +61,12 @@ void ContractDefinition::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
+ listAccept(m_baseContracts, _visitor);
listAccept(m_definedStructs, _visitor);
+ listAccept(m_definedEnums, _visitor);
listAccept(m_stateVariables, _visitor);
+ listAccept(m_events, _visitor);
+ listAccept(m_functionModifiers, _visitor);
listAccept(m_definedFunctions, _visitor);
}
_visitor.endVisit(*this);
@@ -72,13 +76,63 @@ void ContractDefinition::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
+ listAccept(m_baseContracts, _visitor);
listAccept(m_definedStructs, _visitor);
+ listAccept(m_definedEnums, _visitor);
listAccept(m_stateVariables, _visitor);
+ listAccept(m_events, _visitor);
+ listAccept(m_functionModifiers, _visitor);
listAccept(m_definedFunctions, _visitor);
}
_visitor.endVisit(*this);
}
+void InheritanceSpecifier::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ {
+ m_baseName->accept(_visitor);
+ listAccept(m_arguments, _visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void InheritanceSpecifier::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ {
+ m_baseName->accept(_visitor);
+ listAccept(m_arguments, _visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void EnumDefinition::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ listAccept(m_members, _visitor);
+ _visitor.endVisit(*this);
+}
+
+void EnumDefinition::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ listAccept(m_members, _visitor);
+ _visitor.endVisit(*this);
+}
+
+void EnumValue::accept(ASTVisitor& _visitor)
+{
+ _visitor.visit(*this);
+ _visitor.endVisit(*this);
+}
+
+void EnumValue::accept(ASTConstVisitor& _visitor) const
+{
+ _visitor.visit(*this);
+ _visitor.endVisit(*this);
+}
+
void StructDefinition::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
@@ -120,6 +174,7 @@ void FunctionDefinition::accept(ASTVisitor& _visitor)
m_parameters->accept(_visitor);
if (m_returnParameters)
m_returnParameters->accept(_visitor);
+ listAccept(m_functionModifiers, _visitor);
m_body->accept(_visitor);
}
_visitor.endVisit(*this);
@@ -132,6 +187,7 @@ void FunctionDefinition::accept(ASTConstVisitor& _visitor) const
m_parameters->accept(_visitor);
if (m_returnParameters)
m_returnParameters->accept(_visitor);
+ listAccept(m_functionModifiers, _visitor);
m_body->accept(_visitor);
}
_visitor.endVisit(*this);
@@ -153,6 +209,60 @@ void VariableDeclaration::accept(ASTConstVisitor& _visitor) const
_visitor.endVisit(*this);
}
+void ModifierDefinition::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ {
+ m_parameters->accept(_visitor);
+ m_body->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void ModifierDefinition::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ {
+ m_parameters->accept(_visitor);
+ m_body->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void ModifierInvocation::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ {
+ m_modifierName->accept(_visitor);
+ listAccept(m_arguments, _visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void ModifierInvocation::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ {
+ m_modifierName->accept(_visitor);
+ listAccept(m_arguments, _visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void EventDefinition::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ m_parameters->accept(_visitor);
+ _visitor.endVisit(*this);
+}
+
+void EventDefinition::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ m_parameters->accept(_visitor);
+ _visitor.endVisit(*this);
+}
+
void TypeName::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
@@ -223,6 +333,18 @@ void Block::accept(ASTConstVisitor& _visitor) const
_visitor.endVisit(*this);
}
+void PlaceholderStatement::accept(ASTVisitor& _visitor)
+{
+ _visitor.visit(*this);
+ _visitor.endVisit(*this);
+}
+
+void PlaceholderStatement::accept(ASTConstVisitor& _visitor) const
+{
+ _visitor.visit(*this);
+ _visitor.endVisit(*this);
+}
+
void IfStatement::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
@@ -452,20 +574,14 @@ void FunctionCall::accept(ASTConstVisitor& _visitor) const
void NewExpression::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
- {
m_contractName->accept(_visitor);
- listAccept(m_arguments, _visitor);
- }
_visitor.endVisit(*this);
}
void NewExpression::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
- {
m_contractName->accept(_visitor);
- listAccept(m_arguments, _visitor);
- }
_visitor.endVisit(*this);
}
diff --git a/BaseTypes.h b/BaseTypes.h
index a8fd77c8..057289ef 100644
--- a/BaseTypes.h
+++ b/BaseTypes.h
@@ -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 << ")";
}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0a0b62bd..2b1059c6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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} )
diff --git a/CallGraph.cpp b/CallGraph.cpp
deleted file mode 100644
index b30afb61..00000000
--- a/CallGraph.cpp
+++ /dev/null
@@ -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;
-}
-
-}
-}
diff --git a/CallGraph.h b/CallGraph.h
deleted file mode 100644
index f7af64bf..00000000
--- a/CallGraph.h
+++ /dev/null
@@ -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;
-};
-
-}
-}
diff --git a/Compiler.cpp b/Compiler.cpp
index 394ae5f8..14acc011 100644
--- a/Compiler.cpp
+++ b/Compiler.cpp
@@ -21,72 +21,103 @@
*/
#include <algorithm>
+#include <boost/range/adaptor/reversed.hpp>
#include <libevmcore/Instruction.h>
#include <libevmcore/Assembly.h>
#include <libsolidity/AST.h>
#include <libsolidity/Compiler.h>
#include <libsolidity/ExpressionCompiler.h>
#include <libsolidity/CompilerUtils.h>
-#include <libsolidity/CallGraph.h>
using namespace std;
namespace dev {
namespace solidity {
-void Compiler::compileContract(ContractDefinition const& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals,
+void Compiler::compileContract(ContractDefinition const& _contract,
map<ContractDefinition const*, bytes const*> const& _contracts)
{
m_context = CompilerContext(); // clear it just in case
- initializeContext(_contract, _magicGlobals, _contracts);
-
- for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
- if (function->getName() != _contract.getName()) // don't add the constructor here
- m_context.addFunction(*function);
-
+ initializeContext(_contract, _contracts);
appendFunctionSelector(_contract);
- for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
- if (function->getName() != _contract.getName()) // don't add the constructor here
+ set<Declaration const*> functions = m_context.getFunctionsWithoutCode();
+ while (!functions.empty())
+ {
+ for (Declaration const* function: functions)
function->accept(*this);
+ functions = m_context.getFunctionsWithoutCode();
+ }
// Swap the runtime context with the creation-time context
- CompilerContext runtimeContext;
- swap(m_context, runtimeContext);
- initializeContext(_contract, _magicGlobals, _contracts);
- packIntoContractCreator(_contract, runtimeContext);
+ swap(m_context, m_runtimeContext);
+ initializeContext(_contract, _contracts);
+ packIntoContractCreator(_contract, m_runtimeContext);
}
-void Compiler::initializeContext(ContractDefinition const& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals,
+void Compiler::initializeContext(ContractDefinition const& _contract,
map<ContractDefinition const*, bytes const*> const& _contracts)
{
m_context.setCompiledContracts(_contracts);
- for (MagicVariableDeclaration const* variable: _magicGlobals)
- m_context.addMagicGlobal(*variable);
+ m_context.setInheritanceHierarchy(_contract.getLinearizedBaseContracts());
registerStateVariables(_contract);
}
void Compiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext)
{
- set<FunctionDefinition const*> neededFunctions;
- FunctionDefinition const* constructor = _contract.getConstructor();
- if (constructor)
- neededFunctions = getFunctionsNeededByConstructor(*constructor);
+ // arguments for base constructors, filled in derived-to-base order
+ map<ContractDefinition const*, vector<ASTPointer<Expression>> const*> baseArguments;
- for (FunctionDefinition const* fun: neededFunctions)
- m_context.addFunction(*fun);
+ // Determine the arguments that are used for the base constructors.
+ std::vector<ContractDefinition const*> const& bases = _contract.getLinearizedBaseContracts();
+ for (ContractDefinition const* contract: bases)
+ for (ASTPointer<InheritanceSpecifier> const& base: contract->getBaseContracts())
+ {
+ ContractDefinition const* baseContract = dynamic_cast<ContractDefinition const*>(
+ base->getName()->getReferencedDeclaration());
+ solAssert(baseContract, "");
+ if (baseArguments.count(baseContract) == 0)
+ baseArguments[baseContract] = &base->getArguments();
+ }
- if (constructor)
- appendConstructorCall(*constructor);
+ // Call constructors in base-to-derived order.
+ // The Constructor for the most derived contract is called later.
+ for (unsigned i = 1; i < bases.size(); i++)
+ {
+ ContractDefinition const* base = bases[bases.size() - i];
+ solAssert(base, "");
+ FunctionDefinition const* baseConstructor = base->getConstructor();
+ if (!baseConstructor)
+ continue;
+ solAssert(baseArguments[base], "");
+ appendBaseConstructorCall(*baseConstructor, *baseArguments[base]);
+ }
+ if (_contract.getConstructor())
+ appendConstructorCall(*_contract.getConstructor());
eth::AssemblyItem sub = m_context.addSubroutine(_runtimeContext.getAssembly());
// stack contains sub size
m_context << eth::Instruction::DUP1 << sub << u256(0) << eth::Instruction::CODECOPY;
m_context << u256(0) << eth::Instruction::RETURN;
- // note that we have to explicitly include all used functions because of absolute jump
- // labels
- for (FunctionDefinition const* fun: neededFunctions)
- fun->accept(*this);
+ // note that we have to include the functions again because of absolute jump labels
+ set<Declaration const*> functions = m_context.getFunctionsWithoutCode();
+ while (!functions.empty())
+ {
+ for (Declaration const* function: functions)
+ function->accept(*this);
+ functions = m_context.getFunctionsWithoutCode();
+ }
+}
+
+void Compiler::appendBaseConstructorCall(FunctionDefinition const& _constructor,
+ vector<ASTPointer<Expression>> const& _arguments)
+{
+ FunctionType constructorType(_constructor);
+ eth::AssemblyItem returnLabel = m_context.pushNewTag();
+ for (unsigned i = 0; i < _arguments.size(); ++i)
+ compileExpression(*_arguments[i], constructorType.getParameterTypes()[i]);
+ m_context.appendJumpTo(m_context.getFunctionEntryLabel(_constructor));
+ m_context << returnLabel;
}
void Compiler::appendConstructorCall(FunctionDefinition const& _constructor)
@@ -95,104 +126,115 @@ void Compiler::appendConstructorCall(FunctionDefinition const& _constructor)
// copy constructor arguments from code to memory and then to stack, they are supplied after the actual program
unsigned argumentSize = 0;
for (ASTPointer<VariableDeclaration> const& var: _constructor.getParameters())
- argumentSize += var->getType()->getCalldataEncodedSize();
+ argumentSize += CompilerUtils::getPaddedSize(var->getType()->getCalldataEncodedSize());
+
if (argumentSize > 0)
{
m_context << u256(argumentSize);
m_context.appendProgramSize();
- m_context << u256(1); // copy it to byte one as expected for ABI calls
+ m_context << u256(CompilerUtils::dataStartOffset); // copy it to byte four as expected for ABI calls
m_context << eth::Instruction::CODECOPY;
- appendCalldataUnpacker(_constructor, true);
+ appendCalldataUnpacker(FunctionType(_constructor).getParameterTypes(), true);
}
m_context.appendJumpTo(m_context.getFunctionEntryLabel(_constructor));
m_context << returnTag;
}
-set<FunctionDefinition const*> Compiler::getFunctionsNeededByConstructor(FunctionDefinition const& _constructor)
-{
- CallGraph callgraph;
- callgraph.addFunction(_constructor);
- callgraph.computeCallGraph();
- return callgraph.getCalls();
-}
-
void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
{
- vector<FunctionDefinition const*> interfaceFunctions = _contract.getInterfaceFunctions();
- vector<eth::AssemblyItem> callDataUnpackerEntryPoints;
-
- if (interfaceFunctions.size() > 255)
- BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("More than 255 public functions for contract."));
-
- // 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)
+ map<FixedHash<4>, FunctionTypePointer> interfaceFunctions = _contract.getInterfaceFunctions();
+ map<FixedHash<4>, const eth::AssemblyItem> callDataUnpackerEntryPoints;
+
+ // retrieve the function signature hash from the calldata
+ if (!interfaceFunctions.empty())
+ CompilerUtils(m_context).loadFromMemory(0, IntegerType(CompilerUtils::dataStartOffset * 8), true);
+
+ // stack now is: 1 0 <funhash>
+ for (auto const& it: interfaceFunctions)
{
- callDataUnpackerEntryPoints.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
+ if (FunctionDefinition const* fallback = _contract.getFallbackFunction())
+ {
+ eth::AssemblyItem returnTag = m_context.pushNewTag();
+ fallback->accept(*this);
+ m_context << returnTag;
+ appendReturnValuePacker(FunctionType(*fallback).getReturnParameterTypes());
+ }
+ else
+ m_context << eth::Instruction::STOP; // function not found
- for (unsigned funid = 0; funid < interfaceFunctions.size(); ++funid)
+ for (auto const& it: interfaceFunctions)
{
- FunctionDefinition const& function = *interfaceFunctions[funid];
- m_context << callDataUnpackerEntryPoints[funid];
+ FunctionTypePointer const& functionType = it.second;
+ m_context << callDataUnpackerEntryPoints.at(it.first);
eth::AssemblyItem returnTag = m_context.pushNewTag();
- appendCalldataUnpacker(function);
- m_context.appendJumpTo(m_context.getFunctionEntryLabel(function));
+ appendCalldataUnpacker(functionType->getParameterTypes());
+ m_context.appendJumpTo(m_context.getFunctionEntryLabel(it.second->getDeclaration()));
m_context << returnTag;
- appendReturnValuePacker(function);
+ 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);
}
}
diff --git a/Compiler.h b/Compiler.h
index 8471fae2..1aeaee88 100644
--- a/Compiler.h
+++ b/Compiler.h
@@ -20,7 +20,10 @@
* Solidity AST to EVM bytecode compiler.
*/
+#pragma once
+
#include <ostream>
+#include <functional>
#include <libsolidity/ASTVisitor.h>
#include <libsolidity/CompilerContext.h>
@@ -30,32 +33,34 @@ namespace solidity {
class Compiler: private ASTConstVisitor
{
public:
- explicit Compiler(bool _optimize = false): m_optimize(_optimize), m_returnTag(m_context.newTag()) {}
+ explicit Compiler(bool _optimize = false): m_optimize(_optimize), m_context(),
+ m_returnTag(m_context.newTag()) {}
- void compileContract(ContractDefinition const& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals,
+ void compileContract(ContractDefinition const& _contract,
std::map<ContractDefinition const*, bytes const*> const& _contracts);
bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); }
+ bytes getRuntimeBytecode() { return m_runtimeContext.getAssembledBytecode(m_optimize);}
void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); }
private:
- /// Registers the global objects and the non-function objects inside the contract with the context.
- void initializeContext(ContractDefinition const& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals,
+ /// Registers the non-function objects inside the contract with the context.
+ void initializeContext(ContractDefinition const& _contract,
std::map<ContractDefinition const*, bytes const*> const& _contracts);
/// Adds the code that is run at creation time. Should be run after exchanging the run-time context
- /// with a new and initialized context.
- /// adds the constructor code.
+ /// with a new and initialized context. Adds the constructor code.
void packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext);
+ void appendBaseConstructorCall(FunctionDefinition const& _constructor,
+ std::vector<ASTPointer<Expression>> const& _arguments);
void appendConstructorCall(FunctionDefinition const& _constructor);
- /// Recursively searches the call graph and returns all functions needed by the constructor (including itself).
- std::set<FunctionDefinition const*> getFunctionsNeededByConstructor(FunctionDefinition const& _constructor);
void appendFunctionSelector(ContractDefinition const& _contract);
- /// Creates code that unpacks the arguments for the given function, from memory if
- /// @a _fromMemory is true, otherwise from call data. @returns the size of the data in bytes.
- unsigned appendCalldataUnpacker(FunctionDefinition const& _function, bool _fromMemory = false);
- void appendReturnValuePacker(FunctionDefinition const& _function);
+ /// Creates code that unpacks the arguments for the given function represented by a vector of TypePointers.
+ /// From memory if @a _fromMemory is true, otherwise from call data.
+ void appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory = false);
+ void appendReturnValuePacker(TypePointers const& _typeParameters);
void registerStateVariables(ContractDefinition const& _contract);
+ virtual bool visit(VariableDeclaration const& _variableDeclaration) override;
virtual bool visit(FunctionDefinition const& _function) override;
virtual bool visit(IfStatement const& _ifStatement) override;
virtual bool visit(WhileStatement const& _whileStatement) override;
@@ -65,14 +70,23 @@ private:
virtual bool visit(Return const& _return) override;
virtual bool visit(VariableDefinition const& _variableDefinition) override;
virtual bool visit(ExpressionStatement const& _expressionStatement) override;
+ virtual bool visit(PlaceholderStatement const&) override;
+
+ /// 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);
+ 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
};
}
diff --git a/CompilerContext.cpp b/CompilerContext.cpp
index 18357bf0..01a71d7c 100644
--- a/CompilerContext.cpp
+++ b/CompilerContext.cpp
@@ -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
diff --git a/CompilerContext.h b/CompilerContext.h
index 795f447a..f202d7f4 100644
--- a/CompilerContext.h
+++ b/CompilerContext.h
@@ -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); }
-
- eth::AssemblyItem getFunctionEntryLabel(FunctionDefinition const& _function) const;
- /// Returns the distance of the given local variable from the top of the local variable stack.
+ bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration) != 0; }
+
+ eth::AssemblyItem getFunctionEntryLabel(Declaration const& _declaration);
+ void setInheritanceHierarchy(std::vector<ContractDefinition const*> const& _hierarchy) { m_inheritanceHierarchy = _hierarchy; }
+ /// @returns the entry label of the given function and takes overrides into account.
+ eth::AssemblyItem getVirtualFunctionEntryLabel(FunctionDefinition const& _function);
+ /// @returns the entry label of function with the given name from the most derived class just
+ /// above _base in the current inheritance hierarchy.
+ eth::AssemblyItem getSuperFunctionEntryLabel(std::string const& _name, ContractDefinition const& _base);
+ /// @returns the set of functions for which we still need to generate code
+ std::set<Declaration const*> getFunctionsWithoutCode();
+ /// Resets function specific members, inserts the function entry label and marks the function
+ /// as "having code".
+ void startFunction(Declaration const& _function);
+
+ ModifierDefinition const& getFunctionModifier(std::string const& _name) const;
+ /// Returns the distance of the given local variable from the bottom of the stack (of the current function).
unsigned getBaseStackOffsetOfVariable(Declaration const& _declaration) const;
/// If supplied by a value returned by @ref getBaseStackOffsetOfVariable(variable), returns
/// the distance of that variable from the current top of the stack.
unsigned baseToCurrentStackOffset(unsigned _baseOffset) const;
+ /// Converts an offset relative to the current stack height to a value that can be used later
+ /// with baseToCurrentStackOffset to point to the same stack element.
+ unsigned currentToBaseStackOffset(unsigned _offset) const;
u256 getStorageLocationOfVariable(Declaration const& _declaration) const;
/// Appends a JUMPI instruction to a new tag and @returns the tag
@@ -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;
};
}
diff --git a/CompilerStack.cpp b/CompilerStack.cpp
index 79716fde..ca9c75bc 100644
--- a/CompilerStack.cpp
+++ b/CompilerStack.cpp
@@ -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."));
diff --git a/CompilerStack.h b/CompilerStack.h
index 358c8fb7..cb4770cd 100644
--- a/CompilerStack.h
+++ b/CompilerStack.h
@@ -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;
diff --git a/CompilerUtils.cpp b/CompilerUtils.cpp
index 680e9190..047bc6d6 100644
--- a/CompilerUtils.cpp
+++ b/CompilerUtils.cpp
@@ -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::loadFromMemoryDynamic(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries)
+{
+ solAssert(_type.getCategory() != Type::Category::ByteArray, "Byte arrays not yet implemented.");
+ m_context << eth::Instruction::DUP1;
+ unsigned numBytes = loadFromMemoryHelper(_type, _fromCalldata, _padToWordBoundaries);
+ // update memory counter
+ for (unsigned i = 0; i < _type.getSizeOnStack(); ++i)
+ m_context << eth::swapInstruction(1 + i);
+ m_context << u256(numBytes) << eth::Instruction::ADD;
+}
+
+
+unsigned CompilerUtils::storeInMemory(unsigned _offset, Type const& _type, bool _padToWordBoundaries)
+{
+ solAssert(_type.getCategory() != Type::Category::ByteArray, "Unable to statically store dynamic type.");
+ unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries);
+ if (numBytes > 0)
+ m_context << u256(_offset) << eth::Instruction::MSTORE;
+ return numBytes;
}
-void CompilerUtils::storeInMemory(unsigned _offset, unsigned _bytes, bool _leftAligned)
+void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries)
{
- if (_bytes == 0)
+ 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;
+}
+
}
}
diff --git a/CompilerUtils.h b/CompilerUtils.h
index 928f0e2d..5369d3bf 100644
--- a/CompilerUtils.h
+++ b/CompilerUtils.h
@@ -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)
{
diff --git a/DeclarationContainer.cpp b/DeclarationContainer.cpp
index c7081bc7..2594d428 100644
--- a/DeclarationContainer.cpp
+++ b/DeclarationContainer.cpp
@@ -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;
diff --git a/DeclarationContainer.h b/DeclarationContainer.h
index c0a0b42c..f70881f5 100644
--- a/DeclarationContainer.h
+++ b/DeclarationContainer.h
@@ -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;
};
}
diff --git a/Exceptions.h b/Exceptions.h
index 14f91977..0b25abee 100644
--- a/Exceptions.h
+++ b/Exceptions.h
@@ -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>;
}
}
diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp
index cf641935..a8bc53e0 100644
--- a/ExpressionCompiler.cpp
+++ b/ExpressionCompiler.cpp
@@ -22,7 +22,9 @@
#include <utility>
#include <numeric>
+#include <boost/range/adaptor/reversed.hpp>
#include <libdevcore/Common.h>
+#include <libdevcrypto/SHA3.h>
#include <libsolidity/AST.h>
#include <libsolidity/ExpressionCompiler.h>
#include <libsolidity/CompilerContext.h>
@@ -41,64 +43,75 @@ void ExpressionCompiler::compileExpression(CompilerContext& _context, Expression
_expression.accept(compiler);
}
-void ExpressionCompiler::appendTypeConversion(CompilerContext& _context,
- Type const& _typeOnStack, Type const& _targetType)
+void ExpressionCompiler::appendTypeConversion(CompilerContext& _context, Type const& _typeOnStack,
+ Type const& _targetType, bool _cleanupNeeded)
{
ExpressionCompiler compiler(_context);
- compiler.appendTypeConversion(_typeOnStack, _targetType);
+ compiler.appendTypeConversion(_typeOnStack, _targetType, _cleanupNeeded);
+}
+
+void ExpressionCompiler::appendStateVariableAccessor(CompilerContext& _context, VariableDeclaration const& _varDecl, bool _optimize)
+{
+ ExpressionCompiler compiler(_context, _optimize);
+ compiler.appendStateVariableAccessor(_varDecl);
}
bool ExpressionCompiler::visit(Assignment const& _assignment)
{
_assignment.getRightHandSide().accept(*this);
- appendTypeConversion(*_assignment.getRightHandSide().getType(), *_assignment.getType());
+ if (_assignment.getType()->isValueType())
+ appendTypeConversion(*_assignment.getRightHandSide().getType(), *_assignment.getType());
_assignment.getLeftHandSide().accept(*this);
solAssert(m_currentLValue.isValid(), "LValue not retrieved.");
Token::Value op = _assignment.getAssignmentOperator();
- if (op != Token::ASSIGN) // compound assignment
+ if (op != Token::Assign) // compound assignment
{
+ solAssert(_assignment.getType()->isValueType(), "Compound operators not implemented for non-value types.");
if (m_currentLValue.storesReferenceOnStack())
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2;
- m_currentLValue.retrieveValue(_assignment, true);
+ m_currentLValue.retrieveValue(_assignment.getLocation(), true);
appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.getType());
if (m_currentLValue.storesReferenceOnStack())
m_context << eth::Instruction::SWAP1;
}
- m_currentLValue.storeValue(_assignment);
+ m_currentLValue.storeValue(*_assignment.getRightHandSide().getType(), _assignment.getLocation());
m_currentLValue.reset();
return false;
}
-void ExpressionCompiler::endVisit(UnaryOperation const& _unaryOperation)
+bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
{
//@todo type checking and creating code for an operator should be in the same place:
// the operator should know how to convert itself and to which types it applies, so
// put this code together with "Type::acceptsBinary/UnaryOperator" into a class that
// represents the operator
+ if (_unaryOperation.getType()->getCategory() == Type::Category::IntegerConstant)
+ {
+ m_context << _unaryOperation.getType()->literalValue(nullptr);
+ return false;
+ }
+
+ _unaryOperation.getSubExpression().accept(*this);
+
switch (_unaryOperation.getOperator())
{
- case Token::NOT: // !
+ case Token::Not: // !
m_context << eth::Instruction::ISZERO;
break;
- case Token::BIT_NOT: // ~
+ case Token::BitNot: // ~
m_context << eth::Instruction::NOT;
break;
- case Token::DELETE: // delete
- // @todo semantics change for complex types
+ case Token::Delete: // delete
solAssert(m_currentLValue.isValid(), "LValue not retrieved.");
-
- m_context << u256(0);
- if (m_currentLValue.storesReferenceOnStack())
- m_context << eth::Instruction::SWAP1;
- m_currentLValue.storeValue(_unaryOperation);
+ m_currentLValue.setToZero(_unaryOperation.getLocation());
m_currentLValue.reset();
break;
- case Token::INC: // ++ (pre- or postfix)
- case Token::DEC: // -- (pre- or postfix)
+ case Token::Inc: // ++ (pre- or postfix)
+ case Token::Dec: // -- (pre- or postfix)
solAssert(m_currentLValue.isValid(), "LValue not retrieved.");
- m_currentLValue.retrieveValue(_unaryOperation);
+ m_currentLValue.retrieveValue(_unaryOperation.getLocation());
if (!_unaryOperation.isPrefixOperation())
{
if (m_currentLValue.storesReferenceOnStack())
@@ -107,7 +120,7 @@ void ExpressionCompiler::endVisit(UnaryOperation const& _unaryOperation)
m_context << eth::Instruction::DUP1;
}
m_context << u256(1);
- if (_unaryOperation.getOperator() == Token::INC)
+ if (_unaryOperation.getOperator() == Token::Inc)
m_context << eth::Instruction::ADD;
else
m_context << eth::Instruction::SWAP1 << eth::Instruction::SUB; // @todo avoid the swap
@@ -115,19 +128,21 @@ void ExpressionCompiler::endVisit(UnaryOperation const& _unaryOperation)
// Stack for postfix: *ref [ref] (*ref)+-1
if (m_currentLValue.storesReferenceOnStack())
m_context << eth::Instruction::SWAP1;
- m_currentLValue.storeValue(_unaryOperation, !_unaryOperation.isPrefixOperation());
+ m_currentLValue.storeValue(*_unaryOperation.getType(), _unaryOperation.getLocation(),
+ !_unaryOperation.isPrefixOperation());
m_currentLValue.reset();
break;
- case Token::ADD: // +
+ case Token::Add: // +
// unary add, so basically no-op
break;
- case Token::SUB: // -
+ case Token::Sub: // -
m_context << u256(0) << eth::Instruction::SUB;
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid unary operator: " +
string(Token::toString(_unaryOperation.getOperator()))));
}
+ return false;
}
bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
@@ -135,21 +150,23 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
Expression const& leftExpression = _binaryOperation.getLeftExpression();
Expression const& rightExpression = _binaryOperation.getRightExpression();
Type const& commonType = _binaryOperation.getCommonType();
- Token::Value const op = _binaryOperation.getOperator();
+ Token::Value const c_op = _binaryOperation.getOperator();
- if (op == Token::AND || op == Token::OR) // special case: short-circuiting
+ if (c_op == Token::And || c_op == Token::Or) // special case: short-circuiting
appendAndOrOperatorCode(_binaryOperation);
+ else if (commonType.getCategory() == Type::Category::IntegerConstant)
+ m_context << commonType.literalValue(nullptr);
else
{
- bool cleanupNeeded = false;
- if (commonType.getCategory() == Type::Category::INTEGER)
- if (Token::isCompareOp(op) || op == Token::DIV || op == Token::MOD)
- cleanupNeeded = true;
+ bool cleanupNeeded = commonType.getCategory() == Type::Category::Integer &&
+ (Token::isCompareOp(c_op) || c_op == Token::Div || c_op == Token::Mod);
// for commutative operators, push the literal as late as possible to allow improved optimization
- //@todo this has to be extended for literal expressions
- bool swap = (m_optimize && Token::isCommutativeOp(op) && dynamic_cast<Literal const*>(&rightExpression)
- && !dynamic_cast<Literal const*>(&leftExpression));
+ auto isLiteral = [](Expression const& _e)
+ {
+ return dynamic_cast<Literal const*>(&_e) || _e.getType()->getCategory() == Type::Category::IntegerConstant;
+ };
+ bool swap = m_optimize && Token::isCommutativeOp(c_op) && isLiteral(rightExpression) && !isLiteral(leftExpression);
if (swap)
{
leftExpression.accept(*this);
@@ -164,10 +181,10 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
leftExpression.accept(*this);
appendTypeConversion(*leftExpression.getType(), commonType, cleanupNeeded);
}
- if (Token::isCompareOp(op))
- appendCompareOperatorCode(op, commonType);
+ if (Token::isCompareOp(c_op))
+ appendCompareOperatorCode(c_op, commonType);
else
- appendOrdinaryBinaryOperatorCode(op, commonType);
+ appendOrdinaryBinaryOperatorCode(c_op, commonType);
}
// do not visit the child nodes, we already did that explicitly
@@ -181,25 +198,39 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
{
//@todo struct construction
solAssert(_functionCall.getArguments().size() == 1, "");
+ solAssert(_functionCall.getNames().empty(), "");
Expression const& firstArgument = *_functionCall.getArguments().front();
firstArgument.accept(*this);
- if (firstArgument.getType()->getCategory() == Type::Category::CONTRACT &&
- _functionCall.getType()->getCategory() == Type::Category::INTEGER)
- {
- // explicit type conversion contract -> address, nothing to do.
- }
- else
- appendTypeConversion(*firstArgument.getType(), *_functionCall.getType());
+ appendTypeConversion(*firstArgument.getType(), *_functionCall.getType());
}
else
{
FunctionType const& function = dynamic_cast<FunctionType const&>(*_functionCall.getExpression().getType());
- vector<ASTPointer<Expression const>> arguments = _functionCall.getArguments();
- solAssert(arguments.size() == function.getParameterTypes().size(), "");
+ TypePointers const& parameterTypes = function.getParameterTypes();
+ vector<ASTPointer<Expression const>> const& callArguments = _functionCall.getArguments();
+ vector<ASTPointer<ASTString>> const& callArgumentNames = _functionCall.getNames();
+ if (!function.takesArbitraryParameters())
+ solAssert(callArguments.size() == parameterTypes.size(), "");
+
+ vector<ASTPointer<Expression const>> arguments;
+ if (callArgumentNames.empty())
+ // normal arguments
+ arguments = callArguments;
+ else
+ // named arguments
+ for (auto const& parameterName: function.getParameterNames())
+ {
+ bool found = false;
+ for (size_t j = 0; j < callArgumentNames.size() && !found; j++)
+ if ((found = (parameterName == *callArgumentNames[j])))
+ // we found the actual parameter position
+ arguments.push_back(callArguments[j]);
+ solAssert(found, "");
+ }
switch (function.getLocation())
{
- case Location::INTERNAL:
+ case Location::Internal:
{
// Calling convention: Caller pushes return address and arguments
// Callee removes them and pushes return values
@@ -217,7 +248,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
unsigned returnParametersSize = CompilerUtils::getSizeOnStack(function.getReturnParameterTypes());
// callee adds return parameters, but removes arguments and return label
- m_context.adjustStackOffset(returnParametersSize - CompilerUtils::getSizeOnStack(arguments) - 1);
+ m_context.adjustStackOffset(returnParametersSize - CompilerUtils::getSizeOnStack(function.getParameterTypes()) - 1);
// @todo for now, the return value of a function is its first return value, so remove
// all others
@@ -225,50 +256,143 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
CompilerUtils(m_context).popStackElement(*function.getReturnParameterTypes()[i]);
break;
}
- case Location::EXTERNAL:
- case Location::BARE:
+ case Location::External:
+ case Location::Bare:
+ _functionCall.getExpression().accept(*this);
+ appendExternalFunctionCall(function, arguments, function.getLocation() == Location::Bare);
+ break;
+ case Location::Creation:
{
- FunctionCallOptions options;
- options.bare = function.getLocation() == Location::BARE;
- options.obtainAddress = [&]() { _functionCall.getExpression().accept(*this); };
- appendExternalFunctionCall(function, arguments, options);
+ _functionCall.getExpression().accept(*this);
+ solAssert(!function.gasSet(), "Gas limit set for contract creation.");
+ solAssert(function.getReturnParameterTypes().size() == 1, "");
+ ContractDefinition const& contract = dynamic_cast<ContractType const&>(
+ *function.getReturnParameterTypes().front()).getContractDefinition();
+ // copy the contract's code into memory
+ bytes const& bytecode = m_context.getCompiledContract(contract);
+ m_context << u256(bytecode.size());
+ //@todo could be done by actually appending the Assembly, but then we probably need to compile
+ // multiple times. Will revisit once external fuctions are inlined.
+ m_context.appendData(bytecode);
+ //@todo copy to memory position 0, shift as soon as we use memory
+ m_context << u256(0) << eth::Instruction::CODECOPY;
+
+ m_context << u256(bytecode.size());
+ appendArgumentsCopyToMemory(arguments, function.getParameterTypes());
+ // size, offset, endowment
+ m_context << u256(0);
+ if (function.valueSet())
+ m_context << eth::dupInstruction(3);
+ else
+ m_context << u256(0);
+ m_context << eth::Instruction::CREATE;
+ if (function.valueSet())
+ m_context << eth::swapInstruction(1) << eth::Instruction::POP;
break;
}
- case Location::SEND:
+ case Location::SetGas:
{
- FunctionCallOptions options;
- options.bare = true;
- options.obtainAddress = [&]() { _functionCall.getExpression().accept(*this); };
- options.obtainValue = [&]() { arguments.front()->accept(*this); };
- appendExternalFunctionCall(FunctionType({}, {}, Location::EXTERNAL), {}, options);
+ // stack layout: contract_address function_id [gas] [value]
+ _functionCall.getExpression().accept(*this);
+ arguments.front()->accept(*this);
+ appendTypeConversion(*arguments.front()->getType(), IntegerType(256), true);
+ // Note that function is not the original function, but the ".gas" function.
+ // Its values of gasSet and valueSet is equal to the original function's though.
+ unsigned stackDepth = (function.gasSet() ? 1 : 0) + (function.valueSet() ? 1 : 0);
+ if (stackDepth > 0)
+ m_context << eth::swapInstruction(stackDepth);
+ if (function.gasSet())
+ m_context << eth::Instruction::POP;
break;
}
- case Location::SUICIDE:
+ case Location::SetValue:
+ // stack layout: contract_address function_id [gas] [value]
+ _functionCall.getExpression().accept(*this);
+ // Note that function is not the original function, but the ".value" function.
+ // Its values of gasSet and valueSet is equal to the original function's though.
+ if (function.valueSet())
+ m_context << eth::Instruction::POP;
+ arguments.front()->accept(*this);
+ break;
+ case Location::Send:
+ _functionCall.getExpression().accept(*this);
+ m_context << u256(0); // 0 gas, we do not want to execute code
+ arguments.front()->accept(*this);
+ appendTypeConversion(*arguments.front()->getType(),
+ *function.getParameterTypes().front(), true);
+ appendExternalFunctionCall(FunctionType(TypePointers{}, TypePointers{},
+ Location::External, false, true, true), {}, true);
+ break;
+ case Location::Suicide:
arguments.front()->accept(*this);
- //@todo might not be necessary
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
m_context << eth::Instruction::SUICIDE;
break;
case Location::SHA3:
- arguments.front()->accept(*this);
- appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
- // @todo move this once we actually use memory
- CompilerUtils(m_context).storeInMemory(0);
- m_context << u256(32) << u256(0) << eth::Instruction::SHA3;
+ {
+ m_context << u256(0);
+ appendArgumentsCopyToMemory(arguments, TypePointers(), function.padArguments());
+ m_context << u256(0) << eth::Instruction::SHA3;
+ break;
+ }
+ case Location::Log0:
+ case Location::Log1:
+ case Location::Log2:
+ case Location::Log3:
+ case Location::Log4:
+ {
+ unsigned logNumber = int(function.getLocation()) - int(Location::Log0);
+ for (unsigned arg = logNumber; arg > 0; --arg)
+ {
+ arguments[arg]->accept(*this);
+ appendTypeConversion(*arguments[arg]->getType(), *function.getParameterTypes()[arg], true);
+ }
+ m_context << u256(0);
+ appendExpressionCopyToMemory(*function.getParameterTypes().front(), *arguments.front());
+ m_context << u256(0) << eth::logInstruction(logNumber);
+ break;
+ }
+ case Location::Event:
+ {
+ _functionCall.getExpression().accept(*this);
+ auto const& event = dynamic_cast<EventDefinition const&>(function.getDeclaration());
+ unsigned numIndexed = 0;
+ // All indexed arguments go to the stack
+ for (unsigned arg = arguments.size(); arg > 0; --arg)
+ if (event.getParameters()[arg - 1]->isIndexed())
+ {
+ ++numIndexed;
+ arguments[arg - 1]->accept(*this);
+ appendTypeConversion(*arguments[arg - 1]->getType(),
+ *function.getParameterTypes()[arg - 1], true);
+ }
+ m_context << u256(h256::Arith(dev::sha3(function.getCanonicalSignature(event.getName()))));
+ ++numIndexed;
+ solAssert(numIndexed <= 4, "Too many indexed arguments.");
+ // Copy all non-indexed arguments to memory (data)
+ m_context << u256(0);
+ for (unsigned arg = 0; arg < arguments.size(); ++arg)
+ if (!event.getParameters()[arg]->isIndexed())
+ appendExpressionCopyToMemory(*function.getParameterTypes()[arg], *arguments[arg]);
+ m_context << u256(0) << eth::logInstruction(numIndexed);
break;
- case Location::ECRECOVER:
+ }
+ case Location::BlockHash:
+ {
+ arguments[0]->accept(*this);
+ appendTypeConversion(*arguments[0]->getType(), *function.getParameterTypes()[0], true);
+ m_context << eth::Instruction::BLOCKHASH;
+ break;
+ }
+ case Location::ECRecover:
case Location::SHA256:
case Location::RIPEMD160:
{
- static const map<Location, u256> contractAddresses{{Location::ECRECOVER, 1},
+ static const map<Location, u256> contractAddresses{{Location::ECRecover, 1},
{Location::SHA256, 2},
{Location::RIPEMD160, 3}};
- u256 contractAddress = contractAddresses.find(function.getLocation())->second;
- FunctionCallOptions options;
- options.bare = true;
- options.obtainAddress = [&]() { m_context << contractAddress; };
- options.packDensely = false;
- appendExternalFunctionCall(function, arguments, options);
+ m_context << contractAddresses.find(function.getLocation())->second;
+ appendExternalFunctionCall(function, arguments, true);
break;
}
default:
@@ -278,39 +402,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
return false;
}
-bool ExpressionCompiler::visit(NewExpression const& _newExpression)
-{
- ContractType const* type = dynamic_cast<ContractType const*>(_newExpression.getType().get());
- solAssert(type, "");
- TypePointers const& types = type->getConstructorType()->getParameterTypes();
- vector<ASTPointer<Expression const>> arguments = _newExpression.getArguments();
- solAssert(arguments.size() == types.size(), "");
-
- // copy the contracts code into memory
- bytes const& bytecode = m_context.getCompiledContract(*_newExpression.getContract());
- m_context << u256(bytecode.size());
- //@todo could be done by actually appending the Assembly, but then we probably need to compile
- // multiple times. Will revisit once external fuctions are inlined.
- m_context.appendData(bytecode);
- //@todo copy to memory position 0, shift as soon as we use memory
- m_context << u256(0) << eth::Instruction::CODECOPY;
-
- unsigned dataOffset = bytecode.size();
- for (unsigned i = 0; i < arguments.size(); ++i)
- {
- arguments[i]->accept(*this);
- appendTypeConversion(*arguments[i]->getType(), *types[i]);
- unsigned const numBytes = types[i]->getCalldataEncodedSize();
- if (numBytes > 32)
- BOOST_THROW_EXCEPTION(CompilerError()
- << errinfo_sourceLocation(arguments[i]->getLocation())
- << errinfo_comment("Type " + types[i]->toString() + " not yet supported."));
- bool const leftAligned = types[i]->getCategory() == Type::Category::STRING;
- CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned);
- dataOffset += numBytes;
- }
- // size, offset, endowment
- m_context << u256(dataOffset) << u256(0) << u256(0) << eth::Instruction::CREATE;
+bool ExpressionCompiler::visit(NewExpression const&)
+{
+ // code is created for the function call (CREATION) only
return false;
}
@@ -319,33 +413,51 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
ASTString const& member = _memberAccess.getMemberName();
switch (_memberAccess.getExpression().getType()->getCategory())
{
- case Type::Category::INTEGER:
+ case Type::Category::Contract:
+ {
+ bool alsoSearchInteger = false;
+ ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.getExpression().getType());
+ if (type.isSuper())
+ m_context << m_context.getSuperFunctionEntryLabel(member, type.getContractDefinition()).pushTag();
+ else
+ {
+ // ordinary contract type
+ u256 identifier = type.getFunctionIdentifier(member);
+ if (identifier != Invalid256)
+ {
+ appendTypeConversion(type, IntegerType(0, IntegerType::Modifier::Address), true);
+ m_context << identifier;
+ }
+ else
+ // not found in contract, search in members inherited from address
+ alsoSearchInteger = true;
+ }
+ if (!alsoSearchInteger)
+ break;
+ }
+ case Type::Category::Integer:
if (member == "balance")
{
appendTypeConversion(*_memberAccess.getExpression().getType(),
- IntegerType(0, IntegerType::Modifier::ADDRESS), true);
+ IntegerType(0, IntegerType::Modifier::Address), true);
m_context << eth::Instruction::BALANCE;
}
else if (member == "send" || member.substr(0, min<size_t>(member.size(), 4)) == "call")
appendTypeConversion(*_memberAccess.getExpression().getType(),
- IntegerType(0, IntegerType::Modifier::ADDRESS), true);
+ IntegerType(0, IntegerType::Modifier::Address), true);
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer."));
break;
- case Type::Category::CONTRACT:
- {
- ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.getExpression().getType());
- m_context << type.getFunctionIndex(member);
+ case Type::Category::Function:
+ solAssert(!!_memberAccess.getExpression().getType()->getMemberType(member),
+ "Invalid member access to function.");
break;
- }
- case Type::Category::MAGIC:
+ case Type::Category::Magic:
// we can ignore the kind of magic and only look at the name of the member
if (member == "coinbase")
m_context << eth::Instruction::COINBASE;
else if (member == "timestamp")
m_context << eth::Instruction::TIMESTAMP;
- else if (member == "prevhash")
- m_context << eth::Instruction::PREVHASH;
else if (member == "difficulty")
m_context << eth::Instruction::DIFFICULTY;
else if (member == "number")
@@ -362,17 +474,64 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
m_context << eth::Instruction::GAS;
else if (member == "gasprice")
m_context << eth::Instruction::GASPRICE;
+ else if (member == "data")
+ m_context << u256(0) << eth::Instruction::CALLDATASIZE;
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown magic member."));
break;
- case Type::Category::STRUCT:
+ case Type::Category::Struct:
{
StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.getExpression().getType());
m_context << type.getStorageOffsetOfMember(member) << eth::Instruction::ADD;
- m_currentLValue = LValue(m_context, LValue::STORAGE, *_memberAccess.getType());
+ m_currentLValue = LValue(m_context, LValue::LValueType::Storage, _memberAccess.getType());
m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess);
break;
}
+ case Type::Category::Enum:
+ {
+ EnumType const& type = dynamic_cast<EnumType const&>(*_memberAccess.getExpression().getType());
+ m_context << type.getMemberValue(_memberAccess.getMemberName());
+ break;
+ }
+ case Type::Category::TypeType:
+ {
+ TypeType const& type = dynamic_cast<TypeType const&>(*_memberAccess.getExpression().getType());
+ if (!type.getMembers().getMemberType(member))
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to " + type.toString()));
+
+ if (auto contractType = dynamic_cast<ContractType const*>(type.getActualType().get()))
+ {
+ ContractDefinition const& contract = contractType->getContractDefinition();
+ for (ASTPointer<FunctionDefinition> const& function: contract.getDefinedFunctions())
+ if (function->getName() == member)
+ {
+ m_context << m_context.getFunctionEntryLabel(*function).pushTag();
+ return;
+ }
+ solAssert(false, "Function not found in member access.");
+ }
+ else if (auto enumType = dynamic_cast<EnumType const*>(type.getActualType().get()))
+ m_context << enumType->getMemberValue(_memberAccess.getMemberName());
+ break;
+ }
+ case Type::Category::ByteArray:
+ {
+ solAssert(member == "length", "Illegal bytearray member.");
+ auto const& type = dynamic_cast<ByteArrayType const&>(*_memberAccess.getExpression().getType());
+ switch (type.getLocation())
+ {
+ case ByteArrayType::Location::CallData:
+ m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
+ break;
+ case ByteArrayType::Location::Storage:
+ m_context << eth::Instruction::SLOAD;
+ break;
+ default:
+ solAssert(false, "Unsupported byte array location.");
+ break;
+ }
+ break;
+ }
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Member access to unknown type."));
}
@@ -381,16 +540,19 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
{
_indexAccess.getBaseExpression().accept(*this);
- _indexAccess.getIndexExpression().accept(*this);
- appendTypeConversion(*_indexAccess.getIndexExpression().getType(),
- *dynamic_cast<MappingType const&>(*_indexAccess.getBaseExpression().getType()).getKeyType(),
- true);
- // @todo move this once we actually use memory
- CompilerUtils(m_context).storeInMemory(0);
- CompilerUtils(m_context).storeInMemory(32);
- m_context << u256(64) << u256(0) << eth::Instruction::SHA3;
-
- m_currentLValue = LValue(m_context, LValue::STORAGE, *_indexAccess.getType());
+
+ Type const& baseType = *_indexAccess.getBaseExpression().getType();
+ solAssert(baseType.getCategory() == Type::Category::Mapping, "");
+ Type const& keyType = *dynamic_cast<MappingType const&>(baseType).getKeyType();
+ m_context << u256(0);
+ appendExpressionCopyToMemory(keyType, _indexAccess.getIndexExpression());
+ solAssert(baseType.getSizeOnStack() == 1,
+ "Unexpected: Not exactly one stack slot taken by subscriptable expression.");
+ m_context << eth::Instruction::SWAP1;
+ appendTypeMoveToMemory(IntegerType(256));
+ m_context << u256(0) << eth::Instruction::SHA3;
+
+ m_currentLValue = LValue(m_context, LValue::LValueType::Storage, _indexAccess.getType());
m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess);
return false;
@@ -401,32 +563,44 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
Declaration const* declaration = _identifier.getReferencedDeclaration();
if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration))
{
- if (magicVar->getType()->getCategory() == Type::Category::CONTRACT) // must be "this"
- m_context << eth::Instruction::ADDRESS;
- return;
+ if (magicVar->getType()->getCategory() == Type::Category::Contract)
+ // "this" or "super"
+ if (!dynamic_cast<ContractType const&>(*magicVar->getType()).isSuper())
+ m_context << eth::Instruction::ADDRESS;
}
- if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
- {
- m_context << m_context.getFunctionEntryLabel(*functionDef).pushTag();
- return;
- }
- if (dynamic_cast<VariableDeclaration const*>(declaration))
+ else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
+ m_context << m_context.getVirtualFunctionEntryLabel(*functionDef).pushTag();
+ else if (dynamic_cast<VariableDeclaration const*>(declaration))
{
m_currentLValue.fromIdentifier(_identifier, *declaration);
m_currentLValue.retrieveValueIfLValueNotRequested(_identifier);
- return;
}
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context."));
+ else if (dynamic_cast<ContractDefinition const*>(declaration))
+ {
+ // no-op
+ }
+ else if (dynamic_cast<EventDefinition const*>(declaration))
+ {
+ // no-op
+ }
+ else if (dynamic_cast<EnumDefinition const*>(declaration))
+ {
+ // no-op
+ }
+ else
+ {
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context."));
+ }
}
void ExpressionCompiler::endVisit(Literal const& _literal)
{
switch (_literal.getType()->getCategory())
{
- case Type::Category::INTEGER:
- case Type::Category::BOOL:
- case Type::Category::STRING:
- m_context << _literal.getType()->literalValue(_literal);
+ case Type::Category::IntegerConstant:
+ case Type::Category::Bool:
+ case Type::Category::String:
+ m_context << _literal.getType()->literalValue(&_literal);
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Only integer, boolean and string literals implemented for now."));
@@ -435,12 +609,12 @@ void ExpressionCompiler::endVisit(Literal const& _literal)
void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryOperation)
{
- Token::Value const op = _binaryOperation.getOperator();
- solAssert(op == Token::OR || op == Token::AND, "");
+ Token::Value const c_op = _binaryOperation.getOperator();
+ solAssert(c_op == Token::Or || c_op == Token::And, "");
_binaryOperation.getLeftExpression().accept(*this);
m_context << eth::Instruction::DUP1;
- if (op == Token::AND)
+ if (c_op == Token::And)
m_context << eth::Instruction::ISZERO;
eth::AssemblyItem endLabel = m_context.appendConditionalJump();
m_context << eth::Instruction::POP;
@@ -450,32 +624,32 @@ void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryO
void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type const& _type)
{
- if (_operator == Token::EQ || _operator == Token::NE)
+ if (_operator == Token::Equal || _operator == Token::NotEqual)
{
m_context << eth::Instruction::EQ;
- if (_operator == Token::NE)
+ if (_operator == Token::NotEqual)
m_context << eth::Instruction::ISZERO;
}
else
{
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
- bool const isSigned = type.isSigned();
+ bool const c_isSigned = type.isSigned();
switch (_operator)
{
- case Token::GTE:
- m_context << (isSigned ? eth::Instruction::SLT : eth::Instruction::LT)
+ case Token::GreaterThanOrEqual:
+ m_context << (c_isSigned ? eth::Instruction::SLT : eth::Instruction::LT)
<< eth::Instruction::ISZERO;
break;
- case Token::LTE:
- m_context << (isSigned ? eth::Instruction::SGT : eth::Instruction::GT)
+ case Token::LessThanOrEqual:
+ m_context << (c_isSigned ? eth::Instruction::SGT : eth::Instruction::GT)
<< eth::Instruction::ISZERO;
break;
- case Token::GT:
- m_context << (isSigned ? eth::Instruction::SGT : eth::Instruction::GT);
+ case Token::GreaterThan:
+ m_context << (c_isSigned ? eth::Instruction::SGT : eth::Instruction::GT);
break;
- case Token::LT:
- m_context << (isSigned ? eth::Instruction::SLT : eth::Instruction::LT);
+ case Token::LessThan:
+ m_context << (c_isSigned ? eth::Instruction::SLT : eth::Instruction::LT);
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown comparison operator."));
@@ -498,24 +672,27 @@ void ExpressionCompiler::appendOrdinaryBinaryOperatorCode(Token::Value _operator
void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Type const& _type)
{
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
- bool const isSigned = type.isSigned();
+ bool const c_isSigned = type.isSigned();
switch (_operator)
{
- case Token::ADD:
+ case Token::Add:
m_context << eth::Instruction::ADD;
break;
- case Token::SUB:
+ case Token::Sub:
m_context << eth::Instruction::SUB;
break;
- case Token::MUL:
+ case Token::Mul:
m_context << eth::Instruction::MUL;
break;
- case Token::DIV:
- m_context << (isSigned ? eth::Instruction::SDIV : eth::Instruction::DIV);
+ case Token::Div:
+ m_context << (c_isSigned ? eth::Instruction::SDIV : eth::Instruction::DIV);
break;
- case Token::MOD:
- m_context << (isSigned ? eth::Instruction::SMOD : eth::Instruction::MOD);
+ case Token::Mod:
+ m_context << (c_isSigned ? eth::Instruction::SMOD : eth::Instruction::MOD);
+ break;
+ case Token::Exp:
+ m_context << eth::Instruction::EXP;
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown arithmetic operator."));
@@ -526,13 +703,13 @@ void ExpressionCompiler::appendBitOperatorCode(Token::Value _operator)
{
switch (_operator)
{
- case Token::BIT_OR:
+ case Token::BitOr:
m_context << eth::Instruction::OR;
break;
- case Token::BIT_AND:
+ case Token::BitAnd:
m_context << eth::Instruction::AND;
break;
- case Token::BIT_XOR:
+ case Token::BitXor:
m_context << eth::Instruction::XOR;
break;
default:
@@ -562,12 +739,82 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con
if (_typeOnStack == _targetType && !_cleanupNeeded)
return;
- if (_typeOnStack.getCategory() == Type::Category::INTEGER)
- appendHighBitsCleanup(dynamic_cast<IntegerType const&>(_typeOnStack));
- else if (_typeOnStack.getCategory() == Type::Category::STRING)
+ Type::Category stackTypeCategory = _typeOnStack.getCategory();
+ Type::Category targetTypeCategory = _targetType.getCategory();
+
+ if (stackTypeCategory == Type::Category::String)
+ {
+ StaticStringType const& typeOnStack = dynamic_cast<StaticStringType const&>(_typeOnStack);
+ if (targetTypeCategory == Type::Category::Integer)
+ {
+ // conversion from string to hash. no need to clean the high bit
+ // only to shift right because of opposite alignment
+ IntegerType const& targetIntegerType = dynamic_cast<IntegerType const&>(_targetType);
+ solAssert(targetIntegerType.isHash(), "Only conversion between String and Hash is allowed.");
+ solAssert(targetIntegerType.getNumBits() == typeOnStack.getNumBytes() * 8, "The size should be the same.");
+ m_context << (u256(1) << (256 - typeOnStack.getNumBytes() * 8)) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
+ }
+ else
+ {
+ // clear lower-order bytes for conversion to shorter strings - we always clean
+ solAssert(targetTypeCategory == Type::Category::String, "Invalid type conversion requested.");
+ StaticStringType const& targetType = dynamic_cast<StaticStringType const&>(_targetType);
+ if (targetType.getNumBytes() < typeOnStack.getNumBytes())
+ {
+ if (targetType.getNumBytes() == 0)
+ m_context << eth::Instruction::DUP1 << eth::Instruction::XOR;
+ else
+ m_context << (u256(1) << (256 - targetType.getNumBytes() * 8))
+ << eth::Instruction::DUP1 << eth::Instruction::SWAP2
+ << eth::Instruction::DIV << eth::Instruction::MUL;
+ }
+ }
+ }
+ else if (stackTypeCategory == Type::Category::Enum)
+ solAssert(targetTypeCategory == Type::Category::Integer ||
+ targetTypeCategory == Type::Category::Enum, "");
+ else if (stackTypeCategory == Type::Category::Integer || stackTypeCategory == Type::Category::Contract ||
+ stackTypeCategory == Type::Category::IntegerConstant)
{
- // nothing to do, strings are high-order-bit-aligned
- //@todo clear lower-order bytes if we allow explicit conversion to shorter strings
+ if (targetTypeCategory == Type::Category::String && stackTypeCategory == Type::Category::Integer)
+ {
+ // conversion from hash to string. no need to clean the high bit
+ // only to shift left because of opposite alignment
+ StaticStringType const& targetStringType = dynamic_cast<StaticStringType const&>(_targetType);
+ IntegerType const& typeOnStack = dynamic_cast<IntegerType const&>(_typeOnStack);
+ solAssert(typeOnStack.isHash(), "Only conversion between String and Hash is allowed.");
+ solAssert(typeOnStack.getNumBits() == targetStringType.getNumBytes() * 8, "The size should be the same.");
+ m_context << (u256(1) << (256 - typeOnStack.getNumBits())) << eth::Instruction::MUL;
+ }
+ else if (targetTypeCategory == Type::Category::Enum)
+ // just clean
+ appendTypeConversion(_typeOnStack, *_typeOnStack.getRealType(), true);
+ else
+ {
+ solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Contract, "");
+ IntegerType addressType(0, IntegerType::Modifier::Address);
+ IntegerType const& targetType = targetTypeCategory == Type::Category::Integer
+ ? dynamic_cast<IntegerType const&>(_targetType) : addressType;
+ if (stackTypeCategory == Type::Category::IntegerConstant)
+ {
+ IntegerConstantType const& constType = dynamic_cast<IntegerConstantType const&>(_typeOnStack);
+ // We know that the stack is clean, we only have to clean for a narrowing conversion
+ // where cleanup is forced.
+ if (targetType.getNumBits() < constType.getIntegerType()->getNumBits() && _cleanupNeeded)
+ appendHighBitsCleanup(targetType);
+ }
+ else
+ {
+ IntegerType const& typeOnStack = stackTypeCategory == Type::Category::Integer
+ ? dynamic_cast<IntegerType const&>(_typeOnStack) : addressType;
+ // Widening: clean up according to source type width
+ // Non-widening and force: clean up according to target type bits
+ if (targetType.getNumBits() > typeOnStack.getNumBits())
+ appendHighBitsCleanup(typeOnStack);
+ else if (_cleanupNeeded)
+ appendHighBitsCleanup(targetType);
+ }
+ }
}
else if (_typeOnStack != _targetType)
// All other types should not be convertible to non-equal types.
@@ -586,181 +833,415 @@ void ExpressionCompiler::appendHighBitsCleanup(IntegerType const& _typeOnStack)
void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functionType,
vector<ASTPointer<Expression const>> const& _arguments,
- FunctionCallOptions const& _options)
+ bool bare)
{
- solAssert(_arguments.size() == _functionType.getParameterTypes().size(), "");
+ solAssert(_functionType.takesArbitraryParameters() ||
+ _arguments.size() == _functionType.getParameterTypes().size(), "");
+
+ // Assumed stack content here:
+ // <stack top>
+ // value [if _functionType.valueSet()]
+ // gas [if _functionType.gasSet()]
+ // function identifier [unless bare]
+ // contract address
+
+ unsigned gasValueSize = (_functionType.gasSet() ? 1 : 0) + (_functionType.valueSet() ? 1 : 0);
+
+ unsigned contractStackPos = m_context.currentToBaseStackOffset(1 + gasValueSize + (bare ? 0 : 1));
+ unsigned gasStackPos = m_context.currentToBaseStackOffset(gasValueSize);
+ unsigned valueStackPos = m_context.currentToBaseStackOffset(1);
- unsigned dataOffset = _options.bare ? 0 : 1; // reserve one byte for the function index
- for (unsigned i = 0; i < _arguments.size(); ++i)
- {
- _arguments[i]->accept(*this);
- Type const& type = *_functionType.getParameterTypes()[i];
- appendTypeConversion(*_arguments[i]->getType(), type);
- unsigned const numBytes = _options.packDensely ? type.getCalldataEncodedSize() : 32;
- if (numBytes == 0 || numBytes > 32)
- BOOST_THROW_EXCEPTION(CompilerError()
- << errinfo_sourceLocation(_arguments[i]->getLocation())
- << errinfo_comment("Type " + type.toString() + " not yet supported."));
- bool const leftAligned = type.getCategory() == Type::Category::STRING;
- CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned);
- dataOffset += numBytes;
- }
//@todo only return the first return value for now
Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr :
_functionType.getReturnParameterTypes().front().get();
- unsigned retSize = firstType ? firstType->getCalldataEncodedSize() : 0;
- if (!_options.packDensely && retSize > 0)
- retSize = 32;
- // CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top)
- m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0);
- if (_options.obtainValue)
- _options.obtainValue();
+ unsigned retSize = firstType ? CompilerUtils::getPaddedSize(firstType->getCalldataEncodedSize()) : 0;
+ m_context << u256(retSize) << u256(0);
+
+ if (bare)
+ m_context << u256(0);
+ else
+ {
+ // copy function identifier
+ m_context << eth::dupInstruction(gasValueSize + 3);
+ CompilerUtils(m_context).storeInMemory(0, IntegerType(CompilerUtils::dataStartOffset * 8));
+ m_context << u256(CompilerUtils::dataStartOffset);
+ }
+
+ // For bare call, activate "4 byte pad exception": If the first argument has exactly 4 bytes,
+ // do not pad it to 32 bytes.
+ appendArgumentsCopyToMemory(_arguments, _functionType.getParameterTypes(),
+ _functionType.padArguments(), bare);
+
+ // CALL arguments: outSize, outOff, inSize, (already present up to here)
+ // inOff, value, addr, gas (stack top)
+ m_context << u256(0);
+ if (_functionType.valueSet())
+ m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos));
else
m_context << u256(0);
- _options.obtainAddress();
- if (!_options.bare)
- m_context << u256(0) << eth::Instruction::MSTORE8;
- m_context << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB
- << eth::Instruction::CALL
+ m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(contractStackPos));
+
+ if (_functionType.gasSet())
+ m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos));
+ else
+ // send all gas except for the 21 needed to execute "SUB" and "CALL"
+ m_context << u256(21) << eth::Instruction::GAS << eth::Instruction::SUB;
+ m_context << eth::Instruction::CALL
<< eth::Instruction::POP; // @todo do not ignore failure indicator
- if (retSize > 0)
+ if (_functionType.valueSet())
+ m_context << eth::Instruction::POP;
+ if (_functionType.gasSet())
+ m_context << eth::Instruction::POP;
+ if (!bare)
+ m_context << eth::Instruction::POP;
+ m_context << eth::Instruction::POP; // pop contract address
+
+ if (firstType)
+ CompilerUtils(m_context).loadFromMemory(0, *firstType, false, true);
+}
+
+void ExpressionCompiler::appendArgumentsCopyToMemory(vector<ASTPointer<Expression const>> const& _arguments,
+ TypePointers const& _types,
+ bool _padToWordBoundaries,
+ bool _padExceptionIfFourBytes)
+{
+ solAssert(_types.empty() || _types.size() == _arguments.size(), "");
+ for (size_t i = 0; i < _arguments.size(); ++i)
{
- bool const leftAligned = firstType->getCategory() == Type::Category::STRING;
- CompilerUtils(m_context).loadFromMemory(0, retSize, leftAligned);
+ _arguments[i]->accept(*this);
+ TypePointer const& expectedType = _types.empty() ? _arguments[i]->getType()->getRealType() : _types[i];
+ appendTypeConversion(*_arguments[i]->getType(), *expectedType, true);
+ bool pad = _padToWordBoundaries;
+ // Do not pad if the first argument has exactly four bytes
+ if (i == 0 && pad && _padExceptionIfFourBytes && expectedType->getCalldataEncodedSize() == 4)
+ pad = false;
+ appendTypeMoveToMemory(*expectedType, pad);
}
}
-ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType,
- unsigned _baseStackOffset):
- m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset),
- m_stackSize(_dataType.getSizeOnStack())
+void ExpressionCompiler::appendTypeMoveToMemory(Type const& _type, bool _padToWordBoundaries)
+{
+ CompilerUtils(m_context).storeInMemoryDynamic(_type, _padToWordBoundaries);
+}
+
+void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression)
{
+ _expression.accept(*this);
+ appendTypeConversion(*_expression.getType(), _expectedType, true);
+ appendTypeMoveToMemory(_expectedType);
}
-void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bool _remove) const
+void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl)
+{
+ FunctionType accessorType(_varDecl);
+
+ unsigned length = 0;
+ TypePointers const& paramTypes = accessorType.getParameterTypes();
+ // move arguments to memory
+ for (TypePointer const& paramType: boost::adaptors::reverse(paramTypes))
+ length += CompilerUtils(m_context).storeInMemory(length, *paramType, true);
+
+ // retrieve the position of the variable
+ m_context << m_context.getStorageLocationOfVariable(_varDecl);
+ TypePointer returnType = _varDecl.getType();
+
+ for (TypePointer const& paramType: paramTypes)
+ {
+ // move offset to memory
+ CompilerUtils(m_context).storeInMemory(length);
+ unsigned argLen = CompilerUtils::getPaddedSize(paramType->getCalldataEncodedSize());
+ length -= argLen;
+ m_context << u256(argLen + 32) << u256(length) << eth::Instruction::SHA3;
+
+ returnType = dynamic_cast<MappingType const&>(*returnType).getValueType();
+ }
+
+ unsigned retSizeOnStack = 0;
+ solAssert(accessorType.getReturnParameterTypes().size() >= 1, "");
+ if (StructType const* structType = dynamic_cast<StructType const*>(returnType.get()))
+ {
+ auto const& names = accessorType.getReturnParameterNames();
+ auto const& types = accessorType.getReturnParameterTypes();
+ // struct
+ for (size_t i = 0; i < names.size(); ++i)
+ {
+ m_context << eth::Instruction::DUP1
+ << structType->getStorageOffsetOfMember(names[i])
+ << eth::Instruction::ADD;
+ m_currentLValue = LValue(m_context, LValue::LValueType::Storage, types[i]);
+ m_currentLValue.retrieveValue(Location(), true);
+ solAssert(types[i]->getSizeOnStack() == 1, "Returning struct elements with stack size != 1 not yet implemented.");
+ m_context << eth::Instruction::SWAP1;
+ retSizeOnStack += types[i]->getSizeOnStack();
+ }
+ m_context << eth::Instruction::POP;
+ }
+ else
+ {
+ // simple value
+ solAssert(accessorType.getReturnParameterTypes().size() == 1, "");
+ m_currentLValue = LValue(m_context, LValue::LValueType::Storage, returnType);
+ m_currentLValue.retrieveValue(Location(), true);
+ retSizeOnStack = returnType->getSizeOnStack();
+ }
+ solAssert(retSizeOnStack <= 15, "Stack too deep.");
+ m_context << eth::dupInstruction(retSizeOnStack + 1) << eth::Instruction::JUMP;
+}
+
+ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type,
+ TypePointer const& _dataType, unsigned _baseStackOffset):
+ m_context(&_compilerContext), m_type(_type), m_dataType(_dataType),
+ m_baseStackOffset(_baseStackOffset)
+{
+ //@todo change the type cast for arrays
+ solAssert(m_dataType->getStorageSize() <= numeric_limits<unsigned>::max(),
+ "The storage size of " + m_dataType->toString() + " should fit in unsigned");
+ if (m_type == LValueType::Storage)
+ m_size = unsigned(m_dataType->getStorageSize());
+ else
+ m_size = unsigned(m_dataType->getSizeOnStack());
+}
+
+void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, Declaration const& _declaration)
+{
+ if (m_context->isLocalVariable(&_declaration))
+ {
+ m_type = LValueType::Stack;
+ m_dataType = _identifier.getType();
+ m_size = m_dataType->getSizeOnStack();
+ m_baseStackOffset = m_context->getBaseStackOffsetOfVariable(_declaration);
+ }
+ else if (m_context->isStateVariable(&_declaration))
+ {
+ *m_context << m_context->getStorageLocationOfVariable(_declaration);
+ m_type = LValueType::Storage;
+ m_dataType = _identifier.getType();
+ solAssert(m_dataType->getStorageSize() <= numeric_limits<unsigned>::max(),
+ "The storage size of " + m_dataType->toString() + " should fit in an unsigned");
+ m_size = unsigned(m_dataType->getStorageSize()); }
+ else
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_identifier.getLocation())
+ << errinfo_comment("Identifier type not supported or identifier not found."));
+}
+
+void ExpressionCompiler::LValue::retrieveValue(Location const& _location, bool _remove) const
{
switch (m_type)
{
- case STACK:
+ case LValueType::Stack:
{
unsigned stackPos = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset));
if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory
- BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Stack too deep."));
- for (unsigned i = 0; i < m_stackSize; ++i)
+ for (unsigned i = 0; i < m_size; ++i)
*m_context << eth::dupInstruction(stackPos + 1);
break;
}
- case STORAGE:
- if (!_expression.getType()->isValueType())
- break; // no distinction between value and reference for non-value types
- if (!_remove)
- *m_context << eth::Instruction::DUP1;
- if (m_stackSize == 1)
- *m_context << eth::Instruction::SLOAD;
- else
- for (unsigned i = 0; i < m_stackSize; ++i)
- {
- *m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1;
- if (i + 1 < m_stackSize)
- *m_context << u256(1) << eth::Instruction::ADD;
- else
- *m_context << eth::Instruction::POP;
- }
+ case LValueType::Storage:
+ retrieveValueFromStorage(_remove);
break;
- case MEMORY:
- if (!_expression.getType()->isValueType())
+ case LValueType::Memory:
+ if (!m_dataType->isValueType())
break; // no distinction between value and reference for non-value types
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Location type not yet implemented."));
break;
default:
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Unsupported location type."));
break;
}
}
-void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool _move) const
+void ExpressionCompiler::LValue::retrieveValueFromStorage(bool _remove) const
+{
+ if (!m_dataType->isValueType())
+ return; // no distinction between value and reference for non-value types
+ if (!_remove)
+ *m_context << eth::Instruction::DUP1;
+ if (m_size == 1)
+ *m_context << eth::Instruction::SLOAD;
+ else
+ for (unsigned i = 0; i < m_size; ++i)
+ {
+ *m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1;
+ if (i + 1 < m_size)
+ *m_context << u256(1) << eth::Instruction::ADD;
+ else
+ *m_context << eth::Instruction::POP;
+ }
+}
+
+void ExpressionCompiler::LValue::storeValue(Type const& _sourceType, Location const& _location, bool _move) const
{
switch (m_type)
{
- case STACK:
+ case LValueType::Stack:
{
- unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)) - m_stackSize + 1;
+ unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)) - m_size + 1;
if (stackDiff > 16)
- BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Stack too deep."));
else if (stackDiff > 0)
- for (unsigned i = 0; i < m_stackSize; ++i)
+ for (unsigned i = 0; i < m_size; ++i)
*m_context << eth::swapInstruction(stackDiff) << eth::Instruction::POP;
if (!_move)
- retrieveValue(_expression);
+ retrieveValue(_location);
break;
}
- case LValue::STORAGE:
- if (!_expression.getType()->isValueType())
- break; // no distinction between value and reference for non-value types
- // stack layout: value value ... value ref
- if (!_move) // copy values
+ case LValueType::Storage:
+ // stack layout: value value ... value target_ref
+ if (m_dataType->isValueType())
{
- if (m_stackSize + 1 > 16)
- BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
- << errinfo_comment("Stack too deep."));
- for (unsigned i = 0; i < m_stackSize; ++i)
- *m_context << eth::dupInstruction(m_stackSize + 1) << eth::Instruction::SWAP1;
+ if (!_move) // copy values
+ {
+ if (m_size + 1 > 16)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_location)
+ << errinfo_comment("Stack too deep."));
+ for (unsigned i = 0; i < m_size; ++i)
+ *m_context << eth::dupInstruction(m_size + 1) << eth::Instruction::SWAP1;
+ }
+ if (m_size > 0) // store high index value first
+ *m_context << u256(m_size - 1) << eth::Instruction::ADD;
+ for (unsigned i = 0; i < m_size; ++i)
+ {
+ if (i + 1 >= m_size)
+ *m_context << eth::Instruction::SSTORE;
+ else
+ // stack here: value value ... value value (target_ref+offset)
+ *m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2
+ << eth::Instruction::SSTORE
+ << u256(1) << eth::Instruction::SWAP1 << eth::Instruction::SUB;
+ }
}
- if (m_stackSize > 0) // store high index value first
- *m_context << u256(m_stackSize - 1) << eth::Instruction::ADD;
- for (unsigned i = 0; i < m_stackSize; ++i)
+ else
{
- if (i + 1 >= m_stackSize)
- *m_context << eth::Instruction::SSTORE;
+ solAssert(_sourceType.getCategory() == m_dataType->getCategory(), "");
+ if (m_dataType->getCategory() == Type::Category::ByteArray)
+ {
+ CompilerUtils(*m_context).copyByteArrayToStorage(
+ dynamic_cast<ByteArrayType const&>(*m_dataType),
+ dynamic_cast<ByteArrayType const&>(_sourceType));
+ if (_move)
+ *m_context << eth::Instruction::POP;
+ }
+ else if (m_dataType->getCategory() == Type::Category::Struct)
+ {
+ // stack layout: source_ref target_ref
+ auto const& structType = dynamic_cast<StructType const&>(*m_dataType);
+ solAssert(structType == _sourceType, "Struct assignment with conversion.");
+ for (auto const& member: structType.getMembers())
+ {
+ // assign each member that is not a mapping
+ TypePointer const& memberType = member.second;
+ if (memberType->getCategory() == Type::Category::Mapping)
+ continue;
+ *m_context << structType.getStorageOffsetOfMember(member.first)
+ << eth::Instruction::DUP3 << eth::Instruction::DUP2
+ << eth::Instruction::ADD;
+ // stack: source_ref target_ref member_offset source_member_ref
+ LValue rightHandSide(*m_context, LValueType::Storage, memberType);
+ rightHandSide.retrieveValue(_location, true);
+ // stack: source_ref target_ref member_offset source_value...
+ *m_context << eth::dupInstruction(2 + memberType->getSizeOnStack())
+ << eth::dupInstruction(2 + memberType->getSizeOnStack())
+ << eth::Instruction::ADD;
+ // stack: source_ref target_ref member_offset source_value... target_member_ref
+ LValue memberLValue(*m_context, LValueType::Storage, memberType);
+ memberLValue.storeValue(*memberType, _location, true);
+ *m_context << eth::Instruction::POP;
+ }
+ if (_move)
+ *m_context << eth::Instruction::POP;
+ else
+ *m_context << eth::Instruction::SWAP1;
+ *m_context << eth::Instruction::POP;
+ }
else
- // v v ... v v r+x
- *m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2
- << eth::Instruction::SSTORE
- << u256(1) << eth::Instruction::SWAP1 << eth::Instruction::SUB;
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location)
+ << errinfo_comment("Invalid non-value type for assignment."));
}
break;
- case LValue::MEMORY:
- if (!_expression.getType()->isValueType())
+ case LValueType::Memory:
+ if (!m_dataType->isValueType())
break; // no distinction between value and reference for non-value types
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Location type not yet implemented."));
break;
default:
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Unsupported location type."));
break;
}
}
-void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression const& _expression)
+void ExpressionCompiler::LValue::setToZero(Location const& _location) const
{
- if (!_expression.lvalueRequested())
+ switch (m_type)
{
- retrieveValue(_expression, true);
- reset();
+ case LValueType::Stack:
+ {
+ unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset));
+ if (stackDiff > 16)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_location)
+ << errinfo_comment("Stack too deep."));
+ solAssert(stackDiff >= m_size - 1, "");
+ for (unsigned i = 0; i < m_size; ++i)
+ *m_context << u256(0) << eth::swapInstruction(stackDiff + 1 - i)
+ << eth::Instruction::POP;
+ break;
+ }
+ case LValueType::Storage:
+ if (m_dataType->getCategory() == Type::Category::ByteArray)
+ CompilerUtils(*m_context).clearByteArray(dynamic_cast<ByteArrayType const&>(*m_dataType));
+ else if (m_dataType->getCategory() == Type::Category::Struct)
+ {
+ // stack layout: ref
+ auto const& structType = dynamic_cast<StructType const&>(*m_dataType);
+ for (auto const& member: structType.getMembers())
+ {
+ // zero each member that is not a mapping
+ TypePointer const& memberType = member.second;
+ if (memberType->getCategory() == Type::Category::Mapping)
+ continue;
+ *m_context << structType.getStorageOffsetOfMember(member.first)
+ << eth::Instruction::DUP2 << eth::Instruction::ADD;
+ LValue memberValue(*m_context, LValueType::Storage, memberType);
+ memberValue.setToZero();
+ }
+ *m_context << eth::Instruction::POP;
+ }
+ else
+ {
+ if (m_size == 0)
+ *m_context << eth::Instruction::POP;
+ for (unsigned i = 0; i < m_size; ++i)
+ if (i + 1 >= m_size)
+ *m_context << u256(0) << eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
+ else
+ *m_context << u256(0) << eth::Instruction::DUP2 << eth::Instruction::SSTORE
+ << u256(1) << eth::Instruction::ADD;
+ }
+ break;
+ case LValueType::Memory:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location)
+ << errinfo_comment("Location type not yet implemented."));
+ break;
+ default:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location)
+ << errinfo_comment("Unsupported location type."));
+ break;
}
}
-void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, Declaration const& _declaration)
+void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression const& _expression)
{
- m_stackSize = _identifier.getType()->getSizeOnStack();
- if (m_context->isLocalVariable(&_declaration))
- {
- m_type = STACK;
- m_baseStackOffset = m_context->getBaseStackOffsetOfVariable(_declaration);
- }
- else if (m_context->isStateVariable(&_declaration))
+ if (!_expression.lvalueRequested())
{
- m_type = STORAGE;
- *m_context << m_context->getStorageLocationOfVariable(_declaration);
+ retrieveValue(_expression.getLocation(), true);
+ reset();
}
- else
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_identifier.getLocation())
- << errinfo_comment("Identifier type not supported or identifier not found."));
}
}
diff --git a/ExpressionCompiler.h b/ExpressionCompiler.h
index 67b16aac..471d8186 100644
--- a/ExpressionCompiler.h
+++ b/ExpressionCompiler.h
@@ -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;
diff --git a/GlobalContext.cpp b/GlobalContext.cpp
index f4805b1f..60de5105 100644
--- a/GlobalContext.cpp
+++ b/GlobalContext.cpp
@@ -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();
}
}
diff --git a/GlobalContext.h b/GlobalContext.h
index 50a21f70..f861c67d 100644
--- a/GlobalContext.h
+++ b/GlobalContext.h
@@ -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;
};
}
diff --git a/InterfaceHandler.cpp b/InterfaceHandler.cpp
index 224234cb..99a7db96 100644
--- a/InterfaceHandler.cpp
+++ b/InterfaceHandler.cpp
@@ -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;
+ }
}
}
diff --git a/InterfaceHandler.h b/InterfaceHandler.h
index c8399d71..6aa3f72d 100644
--- a/InterfaceHandler.h
+++ b/InterfaceHandler.h
@@ -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
diff --git a/NameAndTypeResolver.cpp b/NameAndTypeResolver.cpp
index 3774537d..e19b0bf9 100644
--- a/NameAndTypeResolver.cpp
+++ b/NameAndTypeResolver.cpp
@@ -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;
}
diff --git a/NameAndTypeResolver.h b/NameAndTypeResolver.h
index 23ac5fe7..d9ac98ce 100644
--- a/NameAndTypeResolver.h
+++ b/NameAndTypeResolver.h
@@ -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;
};
diff --git a/Parser.cpp b/Parser.cpp
index e287319d..72334c6c 100644
--- a/Parser.cpp
+++ b/Parser.cpp
@@ -69,10 +69,10 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
{
switch (m_scanner->getCurrentToken())
{
- case Token::IMPORT:
+ case Token::Import:
nodes.push_back(parseImportDirective());
break;
- case Token::CONTRACT:
+ case Token::Contract:
nodes.push_back(parseContractDefinition());
break;
default:
@@ -100,119 +100,298 @@ int Parser::getEndPosition() const
ASTPointer<ImportDirective> Parser::parseImportDirective()
{
ASTNodeFactory nodeFactory(*this);
- expectToken(Token::IMPORT);
- if (m_scanner->getCurrentToken() != Token::STRING_LITERAL)
+ expectToken(Token::Import);
+ if (m_scanner->getCurrentToken() != Token::StringLiteral)
BOOST_THROW_EXCEPTION(createParserError("Expected string literal (URL)."));
ASTPointer<ASTString> url = getLiteralAndAdvance();
nodeFactory.markEndPosition();
- expectToken(Token::SEMICOLON);
+ expectToken(Token::Semicolon);
return nodeFactory.createNode<ImportDirective>(url);
}
ASTPointer<ContractDefinition> Parser::parseContractDefinition()
{
ASTNodeFactory nodeFactory(*this);
- ASTPointer<ASTString> docstring;
+ ASTPointer<ASTString> docString;
if (m_scanner->getCurrentCommentLiteral() != "")
- docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
- expectToken(Token::CONTRACT);
+ docString = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
+ expectToken(Token::Contract);
ASTPointer<ASTString> name = expectIdentifierToken();
- expectToken(Token::LBRACE);
+ vector<ASTPointer<InheritanceSpecifier>> baseContracts;
vector<ASTPointer<StructDefinition>> structs;
+ vector<ASTPointer<EnumDefinition>> enums;
vector<ASTPointer<VariableDeclaration>> stateVariables;
vector<ASTPointer<FunctionDefinition>> functions;
- bool visibilityIsPublic = true;
+ vector<ASTPointer<ModifierDefinition>> modifiers;
+ vector<ASTPointer<EventDefinition>> events;
+ if (m_scanner->getCurrentToken() == Token::Is)
+ do
+ {
+ m_scanner->next();
+ baseContracts.push_back(parseInheritanceSpecifier());
+ }
+ while (m_scanner->getCurrentToken() == Token::Comma);
+ expectToken(Token::LBrace);
while (true)
{
Token::Value currentToken = m_scanner->getCurrentToken();
- if (currentToken == Token::RBRACE)
+ if (currentToken == Token::RBrace)
break;
- else if (currentToken == Token::PUBLIC || currentToken == Token::PRIVATE)
- {
- visibilityIsPublic = (m_scanner->getCurrentToken() == Token::PUBLIC);
- m_scanner->next();
- expectToken(Token::COLON);
- }
- else if (currentToken == Token::FUNCTION)
- functions.push_back(parseFunctionDefinition(visibilityIsPublic));
- else if (currentToken == Token::STRUCT)
+ else if (currentToken == Token::Function)
+ functions.push_back(parseFunctionDefinition(name.get()));
+ else if (currentToken == Token::Struct)
structs.push_back(parseStructDefinition());
- else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING ||
+ else if (currentToken == Token::Enum)
+ enums.push_back(parseEnumDefinition());
+ else if (currentToken == Token::Identifier || currentToken == Token::Mapping ||
Token::isElementaryTypeName(currentToken))
{
- bool const allowVar = false;
- stateVariables.push_back(parseVariableDeclaration(allowVar));
- expectToken(Token::SEMICOLON);
+ VarDeclParserOptions options;
+ options.isStateVariable = true;
+ stateVariables.push_back(parseVariableDeclaration(options));
+ expectToken(Token::Semicolon);
}
+ else if (currentToken == Token::Modifier)
+ modifiers.push_back(parseModifierDefinition());
+ else if (currentToken == Token::Event)
+ events.push_back(parseEventDefinition());
else
- BOOST_THROW_EXCEPTION(createParserError("Function, variable or struct declaration expected."));
+ BOOST_THROW_EXCEPTION(createParserError("Function, variable, struct or modifier declaration expected."));
}
nodeFactory.markEndPosition();
- expectToken(Token::RBRACE);
- return nodeFactory.createNode<ContractDefinition>(name, docstring, structs, stateVariables, functions);
+ expectToken(Token::RBrace);
+ return nodeFactory.createNode<ContractDefinition>(name, docString, baseContracts, structs, enums,
+ stateVariables, functions, modifiers, events);
}
-ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic)
+ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier()
+{
+ ASTNodeFactory nodeFactory(*this);
+ ASTPointer<Identifier> name(parseIdentifier());
+ vector<ASTPointer<Expression>> arguments;
+ if (m_scanner->getCurrentToken() == Token::LParen)
+ {
+ m_scanner->next();
+ arguments = parseFunctionCallListArguments();
+ nodeFactory.markEndPosition();
+ expectToken(Token::RParen);
+ }
+ else
+ nodeFactory.setEndPositionFromNode(name);
+ return nodeFactory.createNode<InheritanceSpecifier>(name, arguments);
+}
+
+Declaration::Visibility Parser::parseVisibilitySpecifier(Token::Value _token)
+{
+ Declaration::Visibility visibility(Declaration::Visibility::Default);
+ if (_token == Token::Public)
+ visibility = Declaration::Visibility::Public;
+ else if (_token == Token::Inheritable)
+ visibility = Declaration::Visibility::Inheritable;
+ else if (_token == Token::Private)
+ visibility = Declaration::Visibility::Private;
+ else if (_token == Token::External)
+ visibility = Declaration::Visibility::External;
+ else
+ solAssert(false, "Invalid visibility specifier.");
+ m_scanner->next();
+ return visibility;
+}
+
+ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const* _contractName)
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docstring;
if (m_scanner->getCurrentCommentLiteral() != "")
docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
- expectToken(Token::FUNCTION);
- ASTPointer<ASTString> name(expectIdentifierToken());
+ expectToken(Token::Function);
+ ASTPointer<ASTString> name;
+ if (m_scanner->getCurrentToken() == Token::LParen)
+ name = make_shared<ASTString>(); // anonymous function
+ else
+ name = expectIdentifierToken();
ASTPointer<ParameterList> parameters(parseParameterList());
bool isDeclaredConst = false;
- if (m_scanner->getCurrentToken() == Token::CONST)
+ Declaration::Visibility visibility(Declaration::Visibility::Default);
+ vector<ASTPointer<ModifierInvocation>> modifiers;
+ while (true)
{
- isDeclaredConst = true;
- m_scanner->next();
+ Token::Value token = m_scanner->getCurrentToken();
+ if (token == Token::Const)
+ {
+ isDeclaredConst = true;
+ m_scanner->next();
+ }
+ else if (token == Token::Identifier)
+ modifiers.push_back(parseModifierInvocation());
+ else if (Token::isVisibilitySpecifier(token))
+ {
+ if (visibility != Declaration::Visibility::Default)
+ BOOST_THROW_EXCEPTION(createParserError("Multiple visibility specifiers."));
+ visibility = parseVisibilitySpecifier(token);
+ }
+ else
+ break;
}
ASTPointer<ParameterList> returnParameters;
- if (m_scanner->getCurrentToken() == Token::RETURNS)
+ if (m_scanner->getCurrentToken() == Token::Returns)
{
bool const permitEmptyParameterList = false;
m_scanner->next();
returnParameters = parseParameterList(permitEmptyParameterList);
}
else
- {
- // create an empty parameter list at a zero-length location
- ASTNodeFactory nodeFactory(*this);
- nodeFactory.setLocationEmpty();
- returnParameters = nodeFactory.createNode<ParameterList>(vector<ASTPointer<VariableDeclaration>>());
- }
+ returnParameters = createEmptyParameterList();
ASTPointer<Block> block = parseBlock();
nodeFactory.setEndPositionFromNode(block);
- return nodeFactory.createNode<FunctionDefinition>(name, _isPublic, docstring,
- parameters,
- isDeclaredConst, returnParameters, block);
+ bool const c_isConstructor = (_contractName && *name == *_contractName);
+ return nodeFactory.createNode<FunctionDefinition>(name, visibility, c_isConstructor, docstring,
+ parameters, isDeclaredConst, modifiers,
+ returnParameters, block);
}
ASTPointer<StructDefinition> Parser::parseStructDefinition()
{
ASTNodeFactory nodeFactory(*this);
- expectToken(Token::STRUCT);
+ expectToken(Token::Struct);
ASTPointer<ASTString> name = expectIdentifierToken();
vector<ASTPointer<VariableDeclaration>> members;
- expectToken(Token::LBRACE);
- while (m_scanner->getCurrentToken() != Token::RBRACE)
+ expectToken(Token::LBrace);
+ while (m_scanner->getCurrentToken() != Token::RBrace)
{
- bool const allowVar = false;
- members.push_back(parseVariableDeclaration(allowVar));
- expectToken(Token::SEMICOLON);
+ members.push_back(parseVariableDeclaration());
+ expectToken(Token::Semicolon);
}
nodeFactory.markEndPosition();
- expectToken(Token::RBRACE);
+ expectToken(Token::RBrace);
return nodeFactory.createNode<StructDefinition>(name, members);
}
-ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(bool _allowVar)
+ASTPointer<EnumValue> Parser::parseEnumValue()
{
ASTNodeFactory nodeFactory(*this);
- ASTPointer<TypeName> type = parseTypeName(_allowVar);
nodeFactory.markEndPosition();
- return nodeFactory.createNode<VariableDeclaration>(type, expectIdentifierToken());
+ return nodeFactory.createNode<EnumValue>(expectIdentifierToken());
+}
+
+ASTPointer<EnumDefinition> Parser::parseEnumDefinition()
+{
+ ASTNodeFactory nodeFactory(*this);
+ expectToken(Token::Enum);
+ ASTPointer<ASTString> name = expectIdentifierToken();
+ vector<ASTPointer<EnumValue>> members;
+ expectToken(Token::LBrace);
+
+ while (m_scanner->getCurrentToken() != Token::RBrace)
+ {
+ members.push_back(parseEnumValue());
+ if (m_scanner->getCurrentToken() == Token::RBrace)
+ break;
+ expectToken(Token::Comma);
+ if (m_scanner->getCurrentToken() != Token::Identifier)
+ BOOST_THROW_EXCEPTION(createParserError("Expected Identifier after ','"));
+ }
+
+ nodeFactory.markEndPosition();
+ expectToken(Token::RBrace);
+ return nodeFactory.createNode<EnumDefinition>(name, members);
+}
+
+ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(VarDeclParserOptions const& _options)
+{
+ ASTNodeFactory nodeFactory(*this);
+ ASTPointer<TypeName> type = parseTypeName(_options.allowVar);
+ if (type != nullptr)
+ nodeFactory.setEndPositionFromNode(type);
+ bool isIndexed = false;
+ ASTPointer<ASTString> identifier;
+ Token::Value token = m_scanner->getCurrentToken();
+ Declaration::Visibility visibility(Declaration::Visibility::Default);
+ if (_options.isStateVariable && Token::isVariableVisibilitySpecifier(token))
+ visibility = parseVisibilitySpecifier(token);
+ if (_options.allowIndexed && token == Token::Indexed)
+ {
+ isIndexed = true;
+ m_scanner->next();
+ }
+ nodeFactory.markEndPosition();
+ if (_options.allowEmptyName && m_scanner->getCurrentToken() != Token::Identifier)
+ {
+ identifier = make_shared<ASTString>("");
+ solAssert(type != nullptr, "");
+ nodeFactory.setEndPositionFromNode(type);
+ }
+ else
+ identifier = expectIdentifierToken();
+ return nodeFactory.createNode<VariableDeclaration>(type, identifier,
+ visibility, _options.isStateVariable,
+ isIndexed);
+}
+
+ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
+{
+ ScopeGuard resetModifierFlag([this]() { m_insideModifier = false; });
+ m_insideModifier = true;
+
+ ASTNodeFactory nodeFactory(*this);
+ ASTPointer<ASTString> docstring;
+ if (m_scanner->getCurrentCommentLiteral() != "")
+ docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
+
+ expectToken(Token::Modifier);
+ ASTPointer<ASTString> name(expectIdentifierToken());
+ ASTPointer<ParameterList> parameters;
+ if (m_scanner->getCurrentToken() == Token::LParen)
+ parameters = parseParameterList();
+ else
+ parameters = createEmptyParameterList();
+ ASTPointer<Block> block = parseBlock();
+ nodeFactory.setEndPositionFromNode(block);
+ return nodeFactory.createNode<ModifierDefinition>(name, docstring, parameters, block);
+}
+
+ASTPointer<EventDefinition> Parser::parseEventDefinition()
+{
+ ASTNodeFactory nodeFactory(*this);
+ ASTPointer<ASTString> docstring;
+ if (m_scanner->getCurrentCommentLiteral() != "")
+ docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
+
+ expectToken(Token::Event);
+ ASTPointer<ASTString> name(expectIdentifierToken());
+ ASTPointer<ParameterList> parameters;
+ if (m_scanner->getCurrentToken() == Token::LParen)
+ parameters = parseParameterList(true, true);
+ else
+ parameters = createEmptyParameterList();
+ nodeFactory.markEndPosition();
+ expectToken(Token::Semicolon);
+ return nodeFactory.createNode<EventDefinition>(name, docstring, parameters);
+}
+
+ASTPointer<ModifierInvocation> Parser::parseModifierInvocation()
+{
+ ASTNodeFactory nodeFactory(*this);
+ ASTPointer<Identifier> name(parseIdentifier());
+ vector<ASTPointer<Expression>> arguments;
+ if (m_scanner->getCurrentToken() == Token::LParen)
+ {
+ m_scanner->next();
+ arguments = parseFunctionCallListArguments();
+ nodeFactory.markEndPosition();
+ expectToken(Token::RParen);
+ }
+ else
+ nodeFactory.setEndPositionFromNode(name);
+ return nodeFactory.createNode<ModifierInvocation>(name, arguments);
+}
+
+ASTPointer<Identifier> Parser::parseIdentifier()
+{
+ ASTNodeFactory nodeFactory(*this);
+ nodeFactory.markEndPosition();
+ return nodeFactory.createNode<Identifier>(expectIdentifierToken());
}
ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
@@ -224,17 +403,17 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
type = ASTNodeFactory(*this).createNode<ElementaryTypeName>(token);
m_scanner->next();
}
- else if (token == Token::VAR)
+ else if (token == Token::Var)
{
if (!_allowVar)
BOOST_THROW_EXCEPTION(createParserError("Expected explicit type name."));
m_scanner->next();
}
- else if (token == Token::MAPPING)
+ else if (token == Token::Mapping)
{
type = parseMapping();
}
- else if (token == Token::IDENTIFIER)
+ else if (token == Token::Identifier)
{
ASTNodeFactory nodeFactory(*this);
nodeFactory.markEndPosition();
@@ -248,34 +427,36 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
ASTPointer<Mapping> Parser::parseMapping()
{
ASTNodeFactory nodeFactory(*this);
- expectToken(Token::MAPPING);
- expectToken(Token::LPAREN);
+ expectToken(Token::Mapping);
+ expectToken(Token::LParen);
if (!Token::isElementaryTypeName(m_scanner->getCurrentToken()))
BOOST_THROW_EXCEPTION(createParserError("Expected elementary type name for mapping key type"));
ASTPointer<ElementaryTypeName> keyType;
keyType = ASTNodeFactory(*this).createNode<ElementaryTypeName>(m_scanner->getCurrentToken());
m_scanner->next();
- expectToken(Token::ARROW);
+ expectToken(Token::Arrow);
bool const allowVar = false;
ASTPointer<TypeName> valueType = parseTypeName(allowVar);
nodeFactory.markEndPosition();
- expectToken(Token::RPAREN);
+ expectToken(Token::RParen);
return nodeFactory.createNode<Mapping>(keyType, valueType);
}
-ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty)
+ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty, bool _allowIndexed)
{
ASTNodeFactory nodeFactory(*this);
vector<ASTPointer<VariableDeclaration>> parameters;
- expectToken(Token::LPAREN);
- if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN)
+ VarDeclParserOptions options;
+ options.allowIndexed = _allowIndexed;
+ options.allowEmptyName = true;
+ expectToken(Token::LParen);
+ if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RParen)
{
- bool const allowVar = false;
- parameters.push_back(parseVariableDeclaration(allowVar));
- while (m_scanner->getCurrentToken() != Token::RPAREN)
+ parameters.push_back(parseVariableDeclaration(options));
+ while (m_scanner->getCurrentToken() != Token::RParen)
{
- expectToken(Token::COMMA);
- parameters.push_back(parseVariableDeclaration(allowVar));
+ expectToken(Token::Comma);
+ parameters.push_back(parseVariableDeclaration(options));
}
}
nodeFactory.markEndPosition();
@@ -286,12 +467,12 @@ ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty)
ASTPointer<Block> Parser::parseBlock()
{
ASTNodeFactory nodeFactory(*this);
- expectToken(Token::LBRACE);
+ expectToken(Token::LBrace);
vector<ASTPointer<Statement>> statements;
- while (m_scanner->getCurrentToken() != Token::RBRACE)
+ while (m_scanner->getCurrentToken() != Token::RBrace)
statements.push_back(parseStatement());
nodeFactory.markEndPosition();
- expectToken(Token::RBRACE);
+ expectToken(Token::RBrace);
return nodeFactory.createNode<Block>(statements);
}
@@ -300,52 +481,60 @@ ASTPointer<Statement> Parser::parseStatement()
ASTPointer<Statement> statement;
switch (m_scanner->getCurrentToken())
{
- case Token::IF:
+ case Token::If:
return parseIfStatement();
- case Token::WHILE:
+ case Token::While:
return parseWhileStatement();
- case Token::FOR:
+ case Token::For:
return parseForStatement();
- case Token::LBRACE:
+ case Token::LBrace:
return parseBlock();
// starting from here, all statements must be terminated by a semicolon
- case Token::CONTINUE:
+ case Token::Continue:
statement = ASTNodeFactory(*this).createNode<Continue>();
m_scanner->next();
break;
- case Token::BREAK:
+ case Token::Break:
statement = ASTNodeFactory(*this).createNode<Break>();
m_scanner->next();
break;
- case Token::RETURN:
+ case Token::Return:
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<Expression> expression;
- if (m_scanner->next() != Token::SEMICOLON)
+ if (m_scanner->next() != Token::Semicolon)
{
expression = parseExpression();
nodeFactory.setEndPositionFromNode(expression);
}
statement = nodeFactory.createNode<Return>(expression);
+ break;
}
- break;
+ case Token::Identifier:
+ if (m_insideModifier && m_scanner->getCurrentLiteral() == "_")
+ {
+ statement = ASTNodeFactory(*this).createNode<PlaceholderStatement>();
+ m_scanner->next();
+ return statement;
+ }
+ // fall-through
default:
statement = parseVarDefOrExprStmt();
}
- expectToken(Token::SEMICOLON);
+ expectToken(Token::Semicolon);
return statement;
}
ASTPointer<IfStatement> Parser::parseIfStatement()
{
ASTNodeFactory nodeFactory(*this);
- expectToken(Token::IF);
- expectToken(Token::LPAREN);
+ expectToken(Token::If);
+ expectToken(Token::LParen);
ASTPointer<Expression> condition = parseExpression();
- expectToken(Token::RPAREN);
+ expectToken(Token::RParen);
ASTPointer<Statement> trueBody = parseStatement();
ASTPointer<Statement> falseBody;
- if (m_scanner->getCurrentToken() == Token::ELSE)
+ if (m_scanner->getCurrentToken() == Token::Else)
{
m_scanner->next();
falseBody = parseStatement();
@@ -359,10 +548,10 @@ ASTPointer<IfStatement> Parser::parseIfStatement()
ASTPointer<WhileStatement> Parser::parseWhileStatement()
{
ASTNodeFactory nodeFactory(*this);
- expectToken(Token::WHILE);
- expectToken(Token::LPAREN);
+ expectToken(Token::While);
+ expectToken(Token::LParen);
ASTPointer<Expression> condition = parseExpression();
- expectToken(Token::RPAREN);
+ expectToken(Token::RParen);
ASTPointer<Statement> body = parseStatement();
nodeFactory.setEndPositionFromNode(body);
return nodeFactory.createNode<WhileStatement>(condition, body);
@@ -374,21 +563,21 @@ ASTPointer<ForStatement> Parser::parseForStatement()
ASTPointer<Statement> initExpression;
ASTPointer<Expression> conditionExpression;
ASTPointer<ExpressionStatement> loopExpression;
- expectToken(Token::FOR);
- expectToken(Token::LPAREN);
+ expectToken(Token::For);
+ expectToken(Token::LParen);
- // LTODO: Maybe here have some predicate like peekExpression() instead of checking for semicolon and RPAREN?
- if (m_scanner->getCurrentToken() != Token::SEMICOLON)
+ // LTODO: Maybe here have some predicate like peekExpression() instead of checking for semicolon and RParen?
+ if (m_scanner->getCurrentToken() != Token::Semicolon)
initExpression = parseVarDefOrExprStmt();
- expectToken(Token::SEMICOLON);
+ expectToken(Token::Semicolon);
- if (m_scanner->getCurrentToken() != Token::SEMICOLON)
+ if (m_scanner->getCurrentToken() != Token::Semicolon)
conditionExpression = parseExpression();
- expectToken(Token::SEMICOLON);
+ expectToken(Token::Semicolon);
- if (m_scanner->getCurrentToken() != Token::RPAREN)
+ if (m_scanner->getCurrentToken() != Token::RParen)
loopExpression = parseExpressionStatement();
- expectToken(Token::RPAREN);
+ expectToken(Token::RParen);
ASTPointer<Statement> body = parseStatement();
nodeFactory.setEndPositionFromNode(body);
@@ -409,10 +598,11 @@ ASTPointer<Statement> Parser::parseVarDefOrExprStmt()
ASTPointer<VariableDefinition> Parser::parseVariableDefinition()
{
ASTNodeFactory nodeFactory(*this);
- bool const allowVar = true;
- ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(allowVar);
+ VarDeclParserOptions options;
+ options.allowVar = true;
+ ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(options);
ASTPointer<Expression> value;
- if (m_scanner->getCurrentToken() == Token::ASSIGN)
+ if (m_scanner->getCurrentToken() == Token::Assign)
{
m_scanner->next();
value = parseExpression();
@@ -449,7 +639,6 @@ ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence)
ASTPointer<Expression> expression = parseUnaryExpression();
int precedence = Token::precedence(m_scanner->getCurrentToken());
for (; precedence >= _minPrecedence; --precedence)
- {
while (Token::precedence(m_scanner->getCurrentToken()) == precedence)
{
Token::Value op = m_scanner->getCurrentToken();
@@ -458,7 +647,6 @@ ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence)
nodeFactory.setEndPositionFromNode(right);
expression = nodeFactory.createNode<BinaryOperation>(expression, op, right);
}
- }
return expression;
}
@@ -466,17 +654,7 @@ ASTPointer<Expression> Parser::parseUnaryExpression()
{
ASTNodeFactory nodeFactory(*this);
Token::Value token = m_scanner->getCurrentToken();
- if (token == Token::NEW)
- {
- expectToken(Token::NEW);
- ASTPointer<Identifier> contractName = ASTNodeFactory(*this).createNode<Identifier>(expectIdentifierToken());
- expectToken(Token::LPAREN);
- vector<ASTPointer<Expression>> arguments(parseFunctionCallArguments());
- expectToken(Token::RPAREN);
- nodeFactory.markEndPosition();
- return nodeFactory.createNode<NewExpression>(contractName, arguments);
- }
- else if (Token::isUnaryOp(token) || Token::isCountOp(token))
+ if (Token::isUnaryOp(token) || Token::isCountOp(token))
{
// prefix expression
m_scanner->next();
@@ -500,34 +678,46 @@ ASTPointer<Expression> Parser::parseUnaryExpression()
ASTPointer<Expression> Parser::parseLeftHandSideExpression()
{
ASTNodeFactory nodeFactory(*this);
- ASTPointer<Expression> expression = parsePrimaryExpression();
+ ASTPointer<Expression> expression;
+ if (m_scanner->getCurrentToken() == Token::New)
+ {
+ expectToken(Token::New);
+ ASTPointer<Identifier> contractName(parseIdentifier());
+ nodeFactory.setEndPositionFromNode(contractName);
+ expression = nodeFactory.createNode<NewExpression>(contractName);
+ }
+ else
+ expression = parsePrimaryExpression();
+
while (true)
{
switch (m_scanner->getCurrentToken())
{
- case Token::LBRACK:
+ case Token::LBrack:
{
m_scanner->next();
ASTPointer<Expression> index = parseExpression();
nodeFactory.markEndPosition();
- expectToken(Token::RBRACK);
+ expectToken(Token::RBrack);
expression = nodeFactory.createNode<IndexAccess>(expression, index);
}
break;
- case Token::PERIOD:
+ case Token::Period:
{
m_scanner->next();
nodeFactory.markEndPosition();
expression = nodeFactory.createNode<MemberAccess>(expression, expectIdentifierToken());
}
break;
- case Token::LPAREN:
+ case Token::LParen:
{
m_scanner->next();
- vector<ASTPointer<Expression>> arguments = parseFunctionCallArguments();
+ vector<ASTPointer<Expression>> arguments;
+ vector<ASTPointer<ASTString>> names;
+ std::tie(arguments, names) = parseFunctionCallArguments();
nodeFactory.markEndPosition();
- expectToken(Token::RPAREN);
- expression = nodeFactory.createNode<FunctionCall>(expression, arguments);
+ expectToken(Token::RParen);
+ expression = nodeFactory.createNode<FunctionCall>(expression, arguments, names);
}
break;
default:
@@ -543,24 +733,34 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
ASTPointer<Expression> expression;
switch (token)
{
- case Token::TRUE_LITERAL:
- case Token::FALSE_LITERAL:
+ case Token::TrueLiteral:
+ case Token::FalseLiteral:
expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance());
break;
- case Token::NUMBER:
- case Token::STRING_LITERAL:
+ case Token::Number:
+ if (Token::isEtherSubdenomination(m_scanner->peekNextToken()))
+ {
+ ASTPointer<ASTString> literal = getLiteralAndAdvance();
+ nodeFactory.markEndPosition();
+ Literal::SubDenomination subdenomination = static_cast<Literal::SubDenomination>(m_scanner->getCurrentToken());
+ m_scanner->next();
+ expression = nodeFactory.createNode<Literal>(token, literal, subdenomination);
+ break;
+ }
+ // fall-through
+ case Token::StringLiteral:
nodeFactory.markEndPosition();
expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance());
break;
- case Token::IDENTIFIER:
+ case Token::Identifier:
nodeFactory.markEndPosition();
expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance());
break;
- case Token::LPAREN:
+ case Token::LParen:
{
m_scanner->next();
ASTPointer<Expression> expression = parseExpression();
- expectToken(Token::RPAREN);
+ expectToken(Token::RParen);
return expression;
}
default:
@@ -580,21 +780,47 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
return expression;
}
-vector<ASTPointer<Expression>> Parser::parseFunctionCallArguments()
+vector<ASTPointer<Expression>> Parser::parseFunctionCallListArguments()
{
vector<ASTPointer<Expression>> arguments;
- if (m_scanner->getCurrentToken() != Token::RPAREN)
+ if (m_scanner->getCurrentToken() != Token::RParen)
{
arguments.push_back(parseExpression());
- while (m_scanner->getCurrentToken() != Token::RPAREN)
+ while (m_scanner->getCurrentToken() != Token::RParen)
{
- expectToken(Token::COMMA);
+ expectToken(Token::Comma);
arguments.push_back(parseExpression());
}
}
return arguments;
}
+pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> Parser::parseFunctionCallArguments()
+{
+ pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> ret;
+ Token::Value token = m_scanner->getCurrentToken();
+ if (token == Token::LBrace)
+ {
+ // call({arg1 : 1, arg2 : 2 })
+ expectToken(Token::LBrace);
+ while (m_scanner->getCurrentToken() != Token::RBrace)
+ {
+ ret.second.push_back(expectIdentifierToken());
+ expectToken(Token::Colon);
+ ret.first.push_back(parseExpression());
+
+ if (m_scanner->getCurrentToken() == Token::Comma)
+ expectToken(Token::Comma);
+ else
+ break;
+ }
+ expectToken(Token::RBrace);
+ }
+ else
+ ret.first = parseFunctionCallListArguments();
+ return ret;
+}
+
bool Parser::peekVariableDefinition()
{
@@ -602,11 +828,11 @@ bool Parser::peekVariableDefinition()
// (which include assignments to other expressions and pre-declared variables)
// We have a variable definition if we get a keyword that specifies a type name, or
// in the case of a user-defined type, we have two identifiers following each other.
- return (m_scanner->getCurrentToken() == Token::MAPPING ||
- m_scanner->getCurrentToken() == Token::VAR ||
+ return (m_scanner->getCurrentToken() == Token::Mapping ||
+ m_scanner->getCurrentToken() == Token::Var ||
((Token::isElementaryTypeName(m_scanner->getCurrentToken()) ||
- m_scanner->getCurrentToken() == Token::IDENTIFIER) &&
- m_scanner->peekNextToken() == Token::IDENTIFIER));
+ m_scanner->getCurrentToken() == Token::Identifier) &&
+ m_scanner->peekNextToken() == Token::Identifier));
}
void Parser::expectToken(Token::Value _value)
@@ -627,7 +853,7 @@ Token::Value Parser::expectAssignmentOperator()
ASTPointer<ASTString> Parser::expectIdentifierToken()
{
- if (m_scanner->getCurrentToken() != Token::IDENTIFIER)
+ if (m_scanner->getCurrentToken() != Token::Identifier)
BOOST_THROW_EXCEPTION(createParserError("Expected identifier"));
return getLiteralAndAdvance();
}
@@ -639,6 +865,13 @@ ASTPointer<ASTString> Parser::getLiteralAndAdvance()
return identifier;
}
+ASTPointer<ParameterList> Parser::createEmptyParameterList()
+{
+ ASTNodeFactory nodeFactory(*this);
+ nodeFactory.setLocationEmpty();
+ return nodeFactory.createNode<ParameterList>(vector<ASTPointer<VariableDeclaration>>());
+}
+
ParserError Parser::createParserError(string const& _description) const
{
return ParserError() << errinfo_sourceLocation(Location(getPosition(), getPosition(), getSourceName()))
diff --git a/Parser.h b/Parser.h
index bf3a6bea..1bb4ea97 100644
--- a/Parser.h
+++ b/Parser.h
@@ -45,16 +45,32 @@ private:
/// End position of the current token
int getEndPosition() const;
+ struct VarDeclParserOptions {
+ VarDeclParserOptions() {}
+ bool allowVar = false;
+ bool isStateVariable = false;
+ bool allowIndexed = false;
+ bool allowEmptyName = false;
+ };
+
///@{
///@name Parsing functions for the AST nodes
ASTPointer<ImportDirective> parseImportDirective();
ASTPointer<ContractDefinition> parseContractDefinition();
- ASTPointer<FunctionDefinition> parseFunctionDefinition(bool _isPublic);
+ ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier();
+ Declaration::Visibility parseVisibilitySpecifier(Token::Value _token);
+ ASTPointer<FunctionDefinition> parseFunctionDefinition(ASTString const* _contractName);
ASTPointer<StructDefinition> parseStructDefinition();
- ASTPointer<VariableDeclaration> parseVariableDeclaration(bool _allowVar);
+ ASTPointer<EnumDefinition> parseEnumDefinition();
+ ASTPointer<EnumValue> parseEnumValue();
+ ASTPointer<VariableDeclaration> parseVariableDeclaration(VarDeclParserOptions const& _options = VarDeclParserOptions());
+ ASTPointer<ModifierDefinition> parseModifierDefinition();
+ ASTPointer<EventDefinition> parseEventDefinition();
+ ASTPointer<ModifierInvocation> parseModifierInvocation();
+ ASTPointer<Identifier> parseIdentifier();
ASTPointer<TypeName> parseTypeName(bool _allowVar);
ASTPointer<Mapping> parseMapping();
- ASTPointer<ParameterList> parseParameterList(bool _allowEmpty = true);
+ ASTPointer<ParameterList> parseParameterList(bool _allowEmpty = true, bool _allowIndexed = false);
ASTPointer<Block> parseBlock();
ASTPointer<Statement> parseStatement();
ASTPointer<IfStatement> parseIfStatement();
@@ -68,7 +84,8 @@ private:
ASTPointer<Expression> parseUnaryExpression();
ASTPointer<Expression> parseLeftHandSideExpression();
ASTPointer<Expression> parsePrimaryExpression();
- std::vector<ASTPointer<Expression>> parseFunctionCallArguments();
+ std::vector<ASTPointer<Expression>> parseFunctionCallListArguments();
+ std::pair<std::vector<ASTPointer<Expression>>, std::vector<ASTPointer<ASTString>>> parseFunctionCallArguments();
///@}
///@{
@@ -84,11 +101,16 @@ private:
ASTPointer<ASTString> getLiteralAndAdvance();
///@}
+ /// Creates an empty ParameterList at the current location (used if parameters can be omitted).
+ ASTPointer<ParameterList> createEmptyParameterList();
+
/// Creates a @ref ParserError exception and annotates it with the current position and the
/// given @a _description.
ParserError createParserError(std::string const& _description) const;
std::shared_ptr<Scanner> m_scanner;
+ /// Flag that signifies whether '_' is parsed as a PlaceholderStatement or a regular identifier.
+ bool m_insideModifier = false;
};
}
diff --git a/Scanner.cpp b/Scanner.cpp
index f22e69bc..fbe3ea97 100644
--- a/Scanner.cpp
+++ b/Scanner.cpp
@@ -225,7 +225,7 @@ Token::Value Scanner::skipSingleLineComment()
// separately by the lexical grammar and becomes part of the
// stream of input elements for the syntactic grammar
while (advance() && !isLineTerminator(m_char)) { };
- return Token::WHITESPACE;
+ return Token::Whitespace;
}
Token::Value Scanner::scanSingleLineDocComment()
@@ -255,7 +255,7 @@ Token::Value Scanner::scanSingleLineDocComment()
advance();
}
literal.complete();
- return Token::COMMENT_LITERAL;
+ return Token::CommentLiteral;
}
Token::Value Scanner::skipMultiLineComment()
@@ -272,11 +272,11 @@ Token::Value Scanner::skipMultiLineComment()
if (ch == '*' && m_char == '/')
{
m_char = ' ';
- return Token::WHITESPACE;
+ return Token::Whitespace;
}
}
// Unterminated multi-line comment.
- return Token::ILLEGAL;
+ return Token::Illegal;
}
Token::Value Scanner::scanMultiLineDocComment()
@@ -285,8 +285,6 @@ Token::Value Scanner::scanMultiLineDocComment()
bool endFound = false;
bool charsAdded = false;
- advance(); //consume the last '*' at /**
- skipWhitespaceExceptLF();
while (!isSourcePastEndOfInput())
{
//handle newlines in multline comments
@@ -321,9 +319,9 @@ Token::Value Scanner::scanMultiLineDocComment()
}
literal.complete();
if (!endFound)
- return Token::ILLEGAL;
+ return Token::Illegal;
else
- return Token::COMMENT_LITERAL;
+ return Token::CommentLiteral;
}
Token::Value Scanner::scanSlash()
@@ -333,7 +331,7 @@ Token::Value Scanner::scanSlash()
if (m_char == '/')
{
if (!advance()) /* double slash comment directly before EOS */
- return Token::WHITESPACE;
+ return Token::Whitespace;
else if (m_char == '/')
{
// doxygen style /// comment
@@ -342,7 +340,7 @@ Token::Value Scanner::scanSlash()
comment = scanSingleLineDocComment();
m_nextSkippedComment.location.end = getSourcePos();
m_nextSkippedComment.token = comment;
- return Token::WHITESPACE;
+ return Token::Whitespace;
}
else
return skipSingleLineComment();
@@ -351,23 +349,32 @@ Token::Value Scanner::scanSlash()
{
// doxygen style /** natspec comment
if (!advance()) /* slash star comment before EOS */
- return Token::WHITESPACE;
+ return Token::Whitespace;
else if (m_char == '*')
{
- Token::Value comment;
- m_nextSkippedComment.location.start = firstSlashPosition;
- comment = scanMultiLineDocComment();
- m_nextSkippedComment.location.end = getSourcePos();
- m_nextSkippedComment.token = comment;
- return Token::WHITESPACE;
+ advance(); //consume the last '*' at /**
+ skipWhitespaceExceptLF();
+
+ // special case of a closed normal multiline comment
+ if (!m_source.isPastEndOfInput() && m_source.get(0) == '/')
+ advance(); //skip the closing slash
+ else // we actually have a multiline documentation comment
+ {
+ Token::Value comment;
+ m_nextSkippedComment.location.start = firstSlashPosition;
+ comment = scanMultiLineDocComment();
+ m_nextSkippedComment.location.end = getSourcePos();
+ m_nextSkippedComment.token = comment;
+ }
+ return Token::Whitespace;
}
else
return skipMultiLineComment();
}
else if (m_char == '=')
- return selectToken(Token::ASSIGN_DIV);
+ return selectToken(Token::AssignDiv);
else
- return Token::DIV;
+ return Token::Div;
}
void Scanner::scanToken()
@@ -384,7 +391,7 @@ void Scanner::scanToken()
case '\n': // fall-through
case ' ':
case '\t':
- token = selectToken(Token::WHITESPACE);
+ token = selectToken(Token::Whitespace);
break;
case '"':
case '\'':
@@ -394,81 +401,82 @@ void Scanner::scanToken()
// < <= << <<=
advance();
if (m_char == '=')
- token = selectToken(Token::LTE);
+ token = selectToken(Token::LessThanOrEqual);
else if (m_char == '<')
- token = selectToken('=', Token::ASSIGN_SHL, Token::SHL);
+ token = selectToken('=', Token::AssignShl, Token::SHL);
else
- token = Token::LT;
+ token = Token::LessThan;
break;
case '>':
// > >= >> >>= >>> >>>=
advance();
if (m_char == '=')
- token = selectToken(Token::GTE);
+ token = selectToken(Token::GreaterThanOrEqual);
else if (m_char == '>')
{
// >> >>= >>> >>>=
advance();
if (m_char == '=')
- token = selectToken(Token::ASSIGN_SAR);
+ token = selectToken(Token::AssignSar);
else if (m_char == '>')
- token = selectToken('=', Token::ASSIGN_SHR, Token::SHR);
+ token = selectToken('=', Token::AssignShr, Token::SHR);
else
token = Token::SAR;
}
else
- token = Token::GT;
+ token = Token::GreaterThan;
break;
case '=':
// = == =>
advance();
if (m_char == '=')
- token = selectToken(Token::EQ);
+ token = selectToken(Token::Equal);
else if (m_char == '>')
- token = selectToken(Token::ARROW);
+ token = selectToken(Token::Arrow);
else
- token = Token::ASSIGN;
+ token = Token::Assign;
break;
case '!':
// ! !=
advance();
if (m_char == '=')
- token = selectToken(Token::NE);
+ token = selectToken(Token::NotEqual);
else
- token = Token::NOT;
+ token = Token::Not;
break;
case '+':
// + ++ +=
advance();
if (m_char == '+')
- token = selectToken(Token::INC);
+ token = selectToken(Token::Inc);
else if (m_char == '=')
- token = selectToken(Token::ASSIGN_ADD);
+ token = selectToken(Token::AssignAdd);
else
- token = Token::ADD;
+ token = Token::Add;
break;
case '-':
- // - -- -= Number
+ // - -- -=
advance();
if (m_char == '-')
- {
- advance();
- token = Token::DEC;
- }
+ token = selectToken(Token::Dec);
else if (m_char == '=')
- token = selectToken(Token::ASSIGN_SUB);
- else if (m_char == '.' || isDecimalDigit(m_char))
- token = scanNumber('-');
+ token = selectToken(Token::AssignSub);
else
- token = Token::SUB;
+ token = Token::Sub;
break;
case '*':
- // * *=
- token = selectToken('=', Token::ASSIGN_MUL, Token::MUL);
+ // * ** *=
+ advance();
+ if (m_char == '*')
+ token = selectToken(Token::Exp);
+ else if (m_char == '=')
+ token = selectToken(Token::AssignMul);
+ else
+ token = Token::Mul;
break;
case '%':
// % %=
- token = selectToken('=', Token::ASSIGN_MOD, Token::MOD);
+ token = selectToken('=', Token::AssignMod, Token::Mod);
break;
case '/':
// / // /* /=
@@ -478,25 +486,25 @@ void Scanner::scanToken()
// & && &=
advance();
if (m_char == '&')
- token = selectToken(Token::AND);
+ token = selectToken(Token::And);
else if (m_char == '=')
- token = selectToken(Token::ASSIGN_BIT_AND);
+ token = selectToken(Token::AssignBitAnd);
else
- token = Token::BIT_AND;
+ token = Token::BitAnd;
break;
case '|':
// | || |=
advance();
if (m_char == '|')
- token = selectToken(Token::OR);
+ token = selectToken(Token::Or);
else if (m_char == '=')
- token = selectToken(Token::ASSIGN_BIT_OR);
+ token = selectToken(Token::AssignBitOr);
else
- token = Token::BIT_OR;
+ token = Token::BitOr;
break;
case '^':
// ^ ^=
- token = selectToken('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR);
+ token = selectToken('=', Token::AssignBitXor, Token::BitXor);
break;
case '.':
// . Number
@@ -504,40 +512,40 @@ void Scanner::scanToken()
if (isDecimalDigit(m_char))
token = scanNumber('.');
else
- token = Token::PERIOD;
+ token = Token::Period;
break;
case ':':
- token = selectToken(Token::COLON);
+ token = selectToken(Token::Colon);
break;
case ';':
- token = selectToken(Token::SEMICOLON);
+ token = selectToken(Token::Semicolon);
break;
case ',':
- token = selectToken(Token::COMMA);
+ token = selectToken(Token::Comma);
break;
case '(':
- token = selectToken(Token::LPAREN);
+ token = selectToken(Token::LParen);
break;
case ')':
- token = selectToken(Token::RPAREN);
+ token = selectToken(Token::RParen);
break;
case '[':
- token = selectToken(Token::LBRACK);
+ token = selectToken(Token::LBrack);
break;
case ']':
- token = selectToken(Token::RBRACK);
+ token = selectToken(Token::RBrack);
break;
case '{':
- token = selectToken(Token::LBRACE);
+ token = selectToken(Token::LBrace);
break;
case '}':
- token = selectToken(Token::RBRACE);
+ token = selectToken(Token::RBrace);
break;
case '?':
- token = selectToken(Token::CONDITIONAL);
+ token = selectToken(Token::Conditional);
break;
case '~':
- token = selectToken(Token::BIT_NOT);
+ token = selectToken(Token::BitNot);
break;
default:
if (isIdentifierStart(m_char))
@@ -545,17 +553,17 @@ void Scanner::scanToken()
else if (isDecimalDigit(m_char))
token = scanNumber();
else if (skipWhitespace())
- token = Token::WHITESPACE;
+ token = Token::Whitespace;
else if (isSourcePastEndOfInput())
token = Token::EOS;
else
- token = selectToken(Token::ILLEGAL);
+ token = selectToken(Token::Illegal);
break;
}
// Continue scanning for tokens as long as we're just skipping
// whitespace.
}
- while (token == Token::WHITESPACE);
+ while (token == Token::Whitespace);
m_nextToken.location.end = getSourcePos();
m_nextToken.token = token;
}
@@ -613,16 +621,16 @@ Token::Value Scanner::scanString()
if (c == '\\')
{
if (isSourcePastEndOfInput() || !scanEscape())
- return Token::ILLEGAL;
+ return Token::Illegal;
}
else
addLiteralChar(c);
}
if (m_char != quote)
- return Token::ILLEGAL;
+ return Token::Illegal;
literal.complete();
advance(); // consume quote
- return Token::STRING_LITERAL;
+ return Token::StringLiteral;
}
void Scanner::scanDecimalDigits()
@@ -643,8 +651,7 @@ Token::Value Scanner::scanNumber(char _charSeen)
}
else
{
- if (_charSeen == '-')
- addLiteralChar('-');
+ solAssert(_charSeen == 0, "");
// if the first character is '0' we must check for octals and hex
if (m_char == '0')
{
@@ -656,7 +663,7 @@ Token::Value Scanner::scanNumber(char _charSeen)
kind = HEX;
addLiteralCharAndAdvance();
if (!isHexDigit(m_char))
- return Token::ILLEGAL; // we must have at least one hex digit after 'x'/'X'
+ return Token::Illegal; // we must have at least one hex digit after 'x'/'X'
while (isHexDigit(m_char))
addLiteralCharAndAdvance();
}
@@ -677,13 +684,13 @@ Token::Value Scanner::scanNumber(char _charSeen)
{
solAssert(kind != HEX, "'e'/'E' must be scanned as part of the hex number");
if (kind != DECIMAL)
- return Token::ILLEGAL;
+ return Token::Illegal;
// scan exponent
addLiteralCharAndAdvance();
if (m_char == '+' || m_char == '-')
addLiteralCharAndAdvance();
if (!isDecimalDigit(m_char))
- return Token::ILLEGAL; // we must have at least one decimal digit after 'e'/'E'
+ return Token::Illegal; // we must have at least one decimal digit after 'e'/'E'
scanDecimalDigits();
}
// The source character immediately following a numeric literal must
@@ -691,27 +698,9 @@ Token::Value Scanner::scanNumber(char _charSeen)
// section 7.8.3, page 17 (note that we read only one decimal digit
// if the value is 0).
if (isDecimalDigit(m_char) || isIdentifierStart(m_char))
- return Token::ILLEGAL;
+ return Token::Illegal;
literal.complete();
- return Token::NUMBER;
-}
-
-
-// ----------------------------------------------------------------------------
-// Keyword Matcher
-
-
-static Token::Value keywordOrIdentifierToken(string const& _input)
-{
- // The following macros are used inside TOKEN_LIST and cause non-keyword tokens to be ignored
- // and keywords to be put inside the keywords variable.
-#define KEYWORD(name, string, precedence) {string, Token::name},
-#define TOKEN(name, string, precedence)
- static const map<string, Token::Value> keywords({TOKEN_LIST(TOKEN, KEYWORD)});
-#undef KEYWORD
-#undef TOKEN
- auto it = keywords.find(_input);
- return it == keywords.end() ? Token::IDENTIFIER : it->second;
+ return Token::Number;
}
Token::Value Scanner::scanIdentifierOrKeyword()
@@ -723,7 +712,7 @@ Token::Value Scanner::scanIdentifierOrKeyword()
while (isIdentifierPart(m_char))
addLiteralCharAndAdvance();
literal.complete();
- return keywordOrIdentifierToken(m_nextToken.literal);
+ return Token::fromIdentifierOrKeyword(m_nextToken.literal);
}
char CharStream::advanceAndGet(size_t _chars)
diff --git a/Scanner.h b/Scanner.h
index 5b90a94e..d93b79df 100644
--- a/Scanner.h
+++ b/Scanner.h
@@ -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; }
///@}
diff --git a/Token.cpp b/Token.cpp
index 093bd9c1..d07d7502 100644
--- a/Token.cpp
+++ b/Token.cpp
@@ -40,8 +40,11 @@
// You should have received a copy of the GNU General Public License
// along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+#include <map>
#include <libsolidity/Token.h>
+using namespace std;
+
namespace dev
{
namespace solidity
@@ -77,6 +80,19 @@ char const Token::m_tokenType[] =
{
TOKEN_LIST(KT, KK)
};
+Token::Value Token::fromIdentifierOrKeyword(const std::string& _name)
+{
+ // The following macros are used inside TOKEN_LIST and cause non-keyword tokens to be ignored
+ // and keywords to be put inside the keywords variable.
+#define KEYWORD(name, string, precedence) {string, Token::name},
+#define TOKEN(name, string, precedence)
+ static const map<string, Token::Value> keywords({TOKEN_LIST(TOKEN, KEYWORD)});
+#undef KEYWORD
+#undef TOKEN
+ auto it = keywords.find(_name);
+ return it == keywords.end() ? Token::Identifier : it->second;
+}
+
#undef KT
#undef KK
diff --git a/Token.h b/Token.h
index f2ffd076..4aa00047 100644
--- a/Token.h
+++ b/Token.h
@@ -67,263 +67,274 @@ namespace solidity
#define IGNORE_TOKEN(name, string, precedence)
-#define TOKEN_LIST(T, K) \
- /* End of source indicator. */ \
- T(EOS, "EOS", 0) \
+#define TOKEN_LIST(T, K) \
+ /* End of source indicator. */ \
+ T(EOS, "EOS", 0) \
+ \
+ /* Punctuators (ECMA-262, section 7.7, page 15). */ \
+ T(LParen, "(", 0) \
+ T(RParen, ")", 0) \
+ T(LBrack, "[", 0) \
+ T(RBrack, "]", 0) \
+ T(LBrace, "{", 0) \
+ T(RBrace, "}", 0) \
+ T(Colon, ":", 0) \
+ T(Semicolon, ";", 0) \
+ T(Period, ".", 0) \
+ T(Conditional, "?", 3) \
+ T(Arrow, "=>", 0) \
\
- /* Punctuators (ECMA-262, section 7.7, page 15). */ \
- T(LPAREN, "(", 0) \
- T(RPAREN, ")", 0) \
- T(LBRACK, "[", 0) \
- T(RBRACK, "]", 0) \
- T(LBRACE, "{", 0) \
- T(RBRACE, "}", 0) \
- T(COLON, ":", 0) \
- T(SEMICOLON, ";", 0) \
- T(PERIOD, ".", 0) \
- T(CONDITIONAL, "?", 3) \
- T(ARROW, "=>", 0) \
- \
- /* Assignment operators. */ \
- /* IsAssignmentOp() relies on this block of enum values being */ \
- /* contiguous and sorted in the same order!*/ \
- T(ASSIGN, "=", 2) \
+ /* Assignment operators. */ \
+ /* IsAssignmentOp() relies on this block of enum values being */ \
+ /* contiguous and sorted in the same order!*/ \
+ T(Assign, "=", 2) \
/* The following have to be in exactly the same order as the simple binary operators*/ \
- T(ASSIGN_BIT_OR, "|=", 2) \
- T(ASSIGN_BIT_XOR, "^=", 2) \
- T(ASSIGN_BIT_AND, "&=", 2) \
- T(ASSIGN_SHL, "<<=", 2) \
- T(ASSIGN_SAR, ">>=", 2) \
- T(ASSIGN_SHR, ">>>=", 2) \
- T(ASSIGN_ADD, "+=", 2) \
- T(ASSIGN_SUB, "-=", 2) \
- T(ASSIGN_MUL, "*=", 2) \
- T(ASSIGN_DIV, "/=", 2) \
- T(ASSIGN_MOD, "%=", 2) \
+ T(AssignBitOr, "|=", 2) \
+ T(AssignBitXor, "^=", 2) \
+ T(AssignBitAnd, "&=", 2) \
+ T(AssignShl, "<<=", 2) \
+ T(AssignSar, ">>=", 2) \
+ T(AssignShr, ">>>=", 2) \
+ T(AssignAdd, "+=", 2) \
+ T(AssignSub, "-=", 2) \
+ T(AssignMul, "*=", 2) \
+ T(AssignDiv, "/=", 2) \
+ T(AssignMod, "%=", 2) \
\
/* Binary operators sorted by precedence. */ \
/* IsBinaryOp() relies on this block of enum values */ \
/* being contiguous and sorted in the same order! */ \
- T(COMMA, ",", 1) \
- T(OR, "||", 4) \
- T(AND, "&&", 5) \
- T(BIT_OR, "|", 8) \
- T(BIT_XOR, "^", 9) \
- T(BIT_AND, "&", 10) \
+ T(Comma, ",", 1) \
+ T(Or, "||", 4) \
+ T(And, "&&", 5) \
+ T(BitOr, "|", 8) \
+ T(BitXor, "^", 9) \
+ T(BitAnd, "&", 10) \
T(SHL, "<<", 11) \
T(SAR, ">>", 11) \
T(SHR, ">>>", 11) \
- T(ADD, "+", 12) \
- T(SUB, "-", 12) \
- T(MUL, "*", 13) \
- T(DIV, "/", 13) \
- T(MOD, "%", 13) \
+ T(Add, "+", 12) \
+ T(Sub, "-", 12) \
+ T(Mul, "*", 13) \
+ T(Div, "/", 13) \
+ T(Mod, "%", 13) \
+ T(Exp, "**", 14) \
\
/* Compare operators sorted by precedence. */ \
/* IsCompareOp() relies on this block of enum values */ \
/* being contiguous and sorted in the same order! */ \
- T(EQ, "==", 6) \
- T(NE, "!=", 6) \
- T(LT, "<", 7) \
- T(GT, ">", 7) \
- T(LTE, "<=", 7) \
- T(GTE, ">=", 7) \
- K(IN, "in", 7) \
+ T(Equal, "==", 6) \
+ T(NotEqual, "!=", 6) \
+ T(LessThan, "<", 7) \
+ T(GreaterThan, ">", 7) \
+ T(LessThanOrEqual, "<=", 7) \
+ T(GreaterThanOrEqual, ">=", 7) \
+ K(In, "in", 7) \
\
/* Unary operators. */ \
/* IsUnaryOp() relies on this block of enum values */ \
/* being contiguous and sorted in the same order! */ \
- T(NOT, "!", 0) \
- T(BIT_NOT, "~", 0) \
- T(INC, "++", 0) \
- T(DEC, "--", 0) \
- K(DELETE, "delete", 0) \
+ T(Not, "!", 0) \
+ T(BitNot, "~", 0) \
+ T(Inc, "++", 0) \
+ T(Dec, "--", 0) \
+ K(Delete, "delete", 0) \
\
/* Keywords */ \
- K(BREAK, "break", 0) \
- K(CASE, "case", 0) \
- K(CONST, "constant", 0) \
- K(CONTINUE, "continue", 0) \
- K(CONTRACT, "contract", 0) \
- K(DEFAULT, "default", 0) \
- K(DO, "do", 0) \
- K(ELSE, "else", 0) \
- K(EXTENDS, "extends", 0) \
- K(FOR, "for", 0) \
- K(FUNCTION, "function", 0) \
- K(IF, "if", 0) \
- K(IMPORT, "import", 0) \
- K(MAPPING, "mapping", 0) \
- K(NEW, "new", 0) \
- K(PUBLIC, "public", 0) \
- K(PRIVATE, "private", 0) \
- K(RETURN, "return", 0) \
- K(RETURNS, "returns", 0) \
- K(STRUCT, "struct", 0) \
- K(SWITCH, "switch", 0) \
- K(VAR, "var", 0) \
- K(WHILE, "while", 0) \
- \
+ K(Break, "break", 0) \
+ K(Case, "case", 0) \
+ K(Const, "constant", 0) \
+ K(Continue, "continue", 0) \
+ K(Contract, "contract", 0) \
+ K(Default, "default", 0) \
+ K(Do, "do", 0) \
+ K(Else, "else", 0) \
+ K(Event, "event", 0) \
+ K(External, "external", 0) \
+ K(Is, "is", 0) \
+ K(Indexed, "indexed", 0) \
+ K(For, "for", 0) \
+ K(Function, "function", 0) \
+ K(If, "if", 0) \
+ K(Import, "import", 0) \
+ K(Mapping, "mapping", 0) \
+ K(Modifier, "modifier", 0) \
+ K(New, "new", 0) \
+ K(Public, "public", 0) \
+ K(Private, "private", 0) \
+ K(Inheritable, "inheritable", 0) \
+ K(Return, "return", 0) \
+ K(Returns, "returns", 0) \
+ K(Struct, "struct", 0) \
+ K(Switch, "switch", 0) \
+ K(Var, "var", 0) \
+ K(While, "while", 0) \
+ K(Enum, "enum", 0) \
\
+ /* Ether subdenominations */ \
+ K(SubWei, "wei", 0) \
+ K(SubSzabo, "szabo", 0) \
+ K(SubFinney, "finney", 0) \
+ K(SubEther, "ether", 0) \
/* type keywords, keep them in this order, keep int as first keyword
- * the implementation in Types.cpp has to be synced to this here
- * TODO more to be added */ \
- K(INT, "int", 0) \
- K(INT8, "int8", 0) \
- K(INT16, "int16", 0) \
- K(INT24, "int24", 0) \
- K(INT32, "int32", 0) \
- K(INT40, "int40", 0) \
- K(INT48, "int48", 0) \
- K(INT56, "int56", 0) \
- K(INT64, "int64", 0) \
- K(INT72, "int72", 0) \
- K(INT80, "int80", 0) \
- K(INT88, "int88", 0) \
- K(INT96, "int96", 0) \
- K(INT104, "int104", 0) \
- K(INT112, "int112", 0) \
- K(INT120, "int120", 0) \
- K(INT128, "int128", 0) \
- K(INT136, "int136", 0) \
- K(INT144, "int144", 0) \
- K(INT152, "int152", 0) \
- K(INT160, "int160", 0) \
- K(INT168, "int168", 0) \
- K(INT176, "int178", 0) \
- K(INT184, "int184", 0) \
- K(INT192, "int192", 0) \
- K(INT200, "int200", 0) \
- K(INT208, "int208", 0) \
- K(INT216, "int216", 0) \
- K(INT224, "int224", 0) \
- K(INT232, "int232", 0) \
- K(INT240, "int240", 0) \
- K(INT248, "int248", 0) \
- K(INT256, "int256", 0) \
- K(UINT, "uint", 0) \
- K(UINT8, "uint8", 0) \
- K(UINT16, "uint16", 0) \
- K(UINT24, "uint24", 0) \
- K(UINT32, "uint32", 0) \
- K(UINT40, "uint40", 0) \
- K(UINT48, "uint48", 0) \
- K(UINT56, "uint56", 0) \
- K(UINT64, "uint64", 0) \
- K(UINT72, "uint72", 0) \
- K(UINT80, "uint80", 0) \
- K(UINT88, "uint88", 0) \
- K(UINT96, "uint96", 0) \
- K(UINT104, "uint104", 0) \
- K(UINT112, "uint112", 0) \
- K(UINT120, "uint120", 0) \
- K(UINT128, "uint128", 0) \
- K(UINT136, "uint136", 0) \
- K(UINT144, "uint144", 0) \
- K(UINT152, "uint152", 0) \
- K(UINT160, "uint160", 0) \
- K(UINT168, "uint168", 0) \
- K(UINT176, "uint178", 0) \
- K(UINT184, "uint184", 0) \
- K(UINT192, "uint192", 0) \
- K(UINT200, "uint200", 0) \
- K(UINT208, "uint208", 0) \
- K(UINT216, "uint216", 0) \
- K(UINT224, "uint224", 0) \
- K(UINT232, "uint232", 0) \
- K(UINT240, "uint240", 0) \
- K(UINT248, "uint248", 0) \
- K(UINT256, "uint256", 0) \
- K(HASH, "hash", 0) \
- K(HASH8, "hash8", 0) \
- K(HASH16, "hash16", 0) \
- K(HASH24, "hash24", 0) \
- K(HASH32, "hash32", 0) \
- K(HASH40, "hash40", 0) \
- K(HASH48, "hash48", 0) \
- K(HASH56, "hash56", 0) \
- K(HASH64, "hash64", 0) \
- K(HASH72, "hash72", 0) \
- K(HASH80, "hash80", 0) \
- K(HASH88, "hash88", 0) \
- K(HASH96, "hash96", 0) \
- K(HASH104, "hash104", 0) \
- K(HASH112, "hash112", 0) \
- K(HASH120, "hash120", 0) \
- K(HASH128, "hash128", 0) \
- K(HASH136, "hash136", 0) \
- K(HASH144, "hash144", 0) \
- K(HASH152, "hash152", 0) \
- K(HASH160, "hash160", 0) \
- K(HASH168, "hash168", 0) \
- K(HASH176, "hash178", 0) \
- K(HASH184, "hash184", 0) \
- K(HASH192, "hash192", 0) \
- K(HASH200, "hash200", 0) \
- K(HASH208, "hash208", 0) \
- K(HASH216, "hash216", 0) \
- K(HASH224, "hash224", 0) \
- K(HASH232, "hash232", 0) \
- K(HASH240, "hash240", 0) \
- K(HASH248, "hash248", 0) \
- K(HASH256, "hash256", 0) \
- K(ADDRESS, "address", 0) \
- K(BOOL, "bool", 0) \
- K(STRING_TYPE, "string", 0) \
- K(STRING0, "string0", 0) \
- K(STRING1, "string1", 0) \
- K(STRING2, "string2", 0) \
- K(STRING3, "string3", 0) \
- K(STRING4, "string4", 0) \
- K(STRING5, "string5", 0) \
- K(STRING6, "string6", 0) \
- K(STRING7, "string7", 0) \
- K(STRING8, "string8", 0) \
- K(STRING9, "string9", 0) \
- K(STRING10, "string10", 0) \
- K(STRING11, "string11", 0) \
- K(STRING12, "string12", 0) \
- K(STRING13, "string13", 0) \
- K(STRING14, "string14", 0) \
- K(STRING15, "string15", 0) \
- K(STRING16, "string16", 0) \
- K(STRING17, "string17", 0) \
- K(STRING18, "string18", 0) \
- K(STRING19, "string19", 0) \
- K(STRING20, "string20", 0) \
- K(STRING21, "string21", 0) \
- K(STRING22, "string22", 0) \
- K(STRING23, "string23", 0) \
- K(STRING24, "string24", 0) \
- K(STRING25, "string25", 0) \
- K(STRING26, "string26", 0) \
- K(STRING27, "string27", 0) \
- K(STRING28, "string28", 0) \
- K(STRING29, "string29", 0) \
- K(STRING30, "string30", 0) \
- K(STRING31, "string31", 0) \
- K(STRING32, "string32", 0) \
- K(TEXT, "text", 0) \
- K(REAL, "real", 0) \
- K(UREAL, "ureal", 0) \
- T(TYPES_END, NULL, 0) /* used as type enum end marker */ \
+ * the implementation in Types.cpp has to be synced to this here */\
+ K(Int, "int", 0) \
+ K(Int8, "int8", 0) \
+ K(Int16, "int16", 0) \
+ K(Int24, "int24", 0) \
+ K(Int32, "int32", 0) \
+ K(Int40, "int40", 0) \
+ K(Int48, "int48", 0) \
+ K(Int56, "int56", 0) \
+ K(Int64, "int64", 0) \
+ K(Int72, "int72", 0) \
+ K(Int80, "int80", 0) \
+ K(Int88, "int88", 0) \
+ K(Int96, "int96", 0) \
+ K(Int104, "int104", 0) \
+ K(Int112, "int112", 0) \
+ K(Int120, "int120", 0) \
+ K(Int128, "int128", 0) \
+ K(Int136, "int136", 0) \
+ K(Int144, "int144", 0) \
+ K(Int152, "int152", 0) \
+ K(Int160, "int160", 0) \
+ K(Int168, "int168", 0) \
+ K(Int176, "int178", 0) \
+ K(Int184, "int184", 0) \
+ K(Int192, "int192", 0) \
+ K(Int200, "int200", 0) \
+ K(Int208, "int208", 0) \
+ K(Int216, "int216", 0) \
+ K(Int224, "int224", 0) \
+ K(Int232, "int232", 0) \
+ K(Int240, "int240", 0) \
+ K(Int248, "int248", 0) \
+ K(Int256, "int256", 0) \
+ K(UInt, "uint", 0) \
+ K(UInt8, "uint8", 0) \
+ K(UInt16, "uint16", 0) \
+ K(UInt24, "uint24", 0) \
+ K(UInt32, "uint32", 0) \
+ K(UInt40, "uint40", 0) \
+ K(UInt48, "uint48", 0) \
+ K(UInt56, "uint56", 0) \
+ K(UInt64, "uint64", 0) \
+ K(UInt72, "uint72", 0) \
+ K(UInt80, "uint80", 0) \
+ K(UInt88, "uint88", 0) \
+ K(UInt96, "uint96", 0) \
+ K(UInt104, "uint104", 0) \
+ K(UInt112, "uint112", 0) \
+ K(UInt120, "uint120", 0) \
+ K(UInt128, "uint128", 0) \
+ K(UInt136, "uint136", 0) \
+ K(UInt144, "uint144", 0) \
+ K(UInt152, "uint152", 0) \
+ K(UInt160, "uint160", 0) \
+ K(UInt168, "uint168", 0) \
+ K(UInt176, "uint178", 0) \
+ K(UInt184, "uint184", 0) \
+ K(UInt192, "uint192", 0) \
+ K(UInt200, "uint200", 0) \
+ K(UInt208, "uint208", 0) \
+ K(UInt216, "uint216", 0) \
+ K(UInt224, "uint224", 0) \
+ K(UInt232, "uint232", 0) \
+ K(UInt240, "uint240", 0) \
+ K(UInt248, "uint248", 0) \
+ K(UInt256, "uint256", 0) \
+ K(Hash, "hash", 0) \
+ K(Hash8, "hash8", 0) \
+ K(Hash16, "hash16", 0) \
+ K(Hash24, "hash24", 0) \
+ K(Hash32, "hash32", 0) \
+ K(Hash40, "hash40", 0) \
+ K(Hash48, "hash48", 0) \
+ K(Hash56, "hash56", 0) \
+ K(Hash64, "hash64", 0) \
+ K(Hash72, "hash72", 0) \
+ K(Hash80, "hash80", 0) \
+ K(Hash88, "hash88", 0) \
+ K(Hash96, "hash96", 0) \
+ K(Hash104, "hash104", 0) \
+ K(Hash112, "hash112", 0) \
+ K(Hash120, "hash120", 0) \
+ K(Hash128, "hash128", 0) \
+ K(Hash136, "hash136", 0) \
+ K(Hash144, "hash144", 0) \
+ K(Hash152, "hash152", 0) \
+ K(Hash160, "hash160", 0) \
+ K(Hash168, "hash168", 0) \
+ K(Hash176, "hash178", 0) \
+ K(Hash184, "hash184", 0) \
+ K(Hash192, "hash192", 0) \
+ K(Hash200, "hash200", 0) \
+ K(Hash208, "hash208", 0) \
+ K(Hash216, "hash216", 0) \
+ K(Hash224, "hash224", 0) \
+ K(Hash232, "hash232", 0) \
+ K(Hash240, "hash240", 0) \
+ K(Hash248, "hash248", 0) \
+ K(Hash256, "hash256", 0) \
+ K(Address, "address", 0) \
+ K(Bool, "bool", 0) \
+ K(Bytes, "bytes", 0) \
+ K(StringType, "string", 0) \
+ K(String0, "string0", 0) \
+ K(String1, "string1", 0) \
+ K(String2, "string2", 0) \
+ K(String3, "string3", 0) \
+ K(String4, "string4", 0) \
+ K(String5, "string5", 0) \
+ K(String6, "string6", 0) \
+ K(String7, "string7", 0) \
+ K(String8, "string8", 0) \
+ K(String9, "string9", 0) \
+ K(String10, "string10", 0) \
+ K(String11, "string11", 0) \
+ K(String12, "string12", 0) \
+ K(String13, "string13", 0) \
+ K(String14, "string14", 0) \
+ K(String15, "string15", 0) \
+ K(String16, "string16", 0) \
+ K(String17, "string17", 0) \
+ K(String18, "string18", 0) \
+ K(String19, "string19", 0) \
+ K(String20, "string20", 0) \
+ K(String21, "string21", 0) \
+ K(String22, "string22", 0) \
+ K(String23, "string23", 0) \
+ K(String24, "string24", 0) \
+ K(String25, "string25", 0) \
+ K(String26, "string26", 0) \
+ K(String27, "string27", 0) \
+ K(String28, "string28", 0) \
+ K(String29, "string29", 0) \
+ K(String30, "string30", 0) \
+ K(String31, "string31", 0) \
+ K(String32, "string32", 0) \
+ K(Text, "text", 0) \
+ K(Real, "real", 0) \
+ K(UReal, "ureal", 0) \
+ T(TypesEnd, NULL, 0) /* used as type enum end marker */ \
\
/* Literals */ \
- K(NULL_LITERAL, "null", 0) \
- K(TRUE_LITERAL, "true", 0) \
- K(FALSE_LITERAL, "false", 0) \
- T(NUMBER, NULL, 0) \
- T(STRING_LITERAL, NULL, 0) \
- T(COMMENT_LITERAL, NULL, 0) \
+ K(NullLiteral, "null", 0) \
+ K(TrueLiteral, "true", 0) \
+ K(FalseLiteral, "false", 0) \
+ T(Number, NULL, 0) \
+ T(StringLiteral, NULL, 0) \
+ T(CommentLiteral, NULL, 0) \
\
/* Identifiers (not keywords or future reserved words). */ \
- T(IDENTIFIER, NULL, 0) \
+ T(Identifier, NULL, 0) \
\
/* Illegal token - not able to scan. */ \
- T(ILLEGAL, "ILLEGAL", 0) \
+ T(Illegal, "ILLEGAL", 0) \
\
/* Scanner-internal use only. */ \
- T(WHITESPACE, NULL, 0)
+ T(Whitespace, NULL, 0)
class Token
@@ -350,24 +361,27 @@ public:
}
// Predicates
- static bool isElementaryTypeName(Value tok) { return INT <= tok && tok < TYPES_END; }
- static bool isAssignmentOp(Value tok) { return ASSIGN <= tok && tok <= ASSIGN_MOD; }
- static bool isBinaryOp(Value op) { return COMMA <= op && op <= MOD; }
- static bool isCommutativeOp(Value op) { return op == BIT_OR || op == BIT_XOR || op == BIT_AND ||
- op == ADD || op == MUL || op == EQ || op == NE; }
- static bool isArithmeticOp(Value op) { return ADD <= op && op <= MOD; }
- static bool isCompareOp(Value op) { return EQ <= op && op <= IN; }
+ static bool isElementaryTypeName(Value tok) { return Int <= tok && tok < TypesEnd; }
+ static bool isAssignmentOp(Value tok) { return Assign <= tok && tok <= AssignMod; }
+ static bool isBinaryOp(Value op) { return Comma <= op && op <= Exp; }
+ static bool isCommutativeOp(Value op) { return op == BitOr || op == BitXor || op == BitAnd ||
+ op == Add || op == Mul || op == Equal || op == NotEqual; }
+ static bool isArithmeticOp(Value op) { return Add <= op && op <= Exp; }
+ static bool isCompareOp(Value op) { return Equal <= op && op <= In; }
static Value AssignmentToBinaryOp(Value op)
{
- solAssert(isAssignmentOp(op) && op != ASSIGN, "");
- return Token::Value(op + (BIT_OR - ASSIGN_BIT_OR));
+ solAssert(isAssignmentOp(op) && op != Assign, "");
+ return Value(op + (BitOr - AssignBitOr));
}
- static bool isBitOp(Value op) { return (BIT_OR <= op && op <= SHR) || op == BIT_NOT; }
- static bool isUnaryOp(Value op) { return (NOT <= op && op <= DELETE) || op == ADD || op == SUB; }
- static bool isCountOp(Value op) { return op == INC || op == DEC; }
+ static bool isBitOp(Value op) { return (BitOr <= op && op <= SHR) || op == BitNot; }
+ static bool isUnaryOp(Value op) { return (Not <= op && op <= Delete) || op == Add || op == Sub; }
+ static bool isCountOp(Value op) { return op == Inc || op == Dec; }
static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); }
+ static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; }
+ static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Inheritable; }
+ static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == Token::SubEther; }
// Returns a string corresponding to the JS token string
// (.e., "<" for the token LT) or NULL if the token doesn't
@@ -386,6 +400,8 @@ public:
return m_precedence[tok];
}
+ static Token::Value fromIdentifierOrKeyword(std::string const& _name);
+
private:
static char const* const m_name[NUM_TOKENS];
static char const* const m_string[NUM_TOKENS];
diff --git a/Types.cpp b/Types.cpp
index 71319c3a..a9c48017 100644
--- a/Types.cpp
+++ b/Types.cpp
@@ -26,6 +26,8 @@
#include <libsolidity/Types.h>
#include <libsolidity/AST.h>
+#include <limits>
+
using namespace std;
namespace dev
@@ -33,88 +35,94 @@ namespace dev
namespace solidity
{
-shared_ptr<Type const> Type::fromElementaryTypeName(Token::Value _typeToken)
+TypePointer Type::fromElementaryTypeName(Token::Value _typeToken)
{
- solAssert(Token::isElementaryTypeName(_typeToken), "");
+ solAssert(Token::isElementaryTypeName(_typeToken), "Elementary type name expected.");
- if (Token::INT <= _typeToken && _typeToken <= Token::HASH256)
+ if (Token::Int <= _typeToken && _typeToken <= Token::Hash256)
{
- int offset = _typeToken - Token::INT;
+ int offset = _typeToken - Token::Int;
int bytes = offset % 33;
if (bytes == 0)
bytes = 32;
int modifier = offset / 33;
- return make_shared<IntegerType const>(bytes * 8,
- modifier == 0 ? IntegerType::Modifier::SIGNED :
- modifier == 1 ? IntegerType::Modifier::UNSIGNED :
- IntegerType::Modifier::HASH);
+ return make_shared<IntegerType>(bytes * 8,
+ modifier == 0 ? IntegerType::Modifier::Signed :
+ modifier == 1 ? IntegerType::Modifier::Unsigned :
+ IntegerType::Modifier::Hash);
}
- else if (_typeToken == Token::ADDRESS)
- return make_shared<IntegerType const>(0, IntegerType::Modifier::ADDRESS);
- else if (_typeToken == Token::BOOL)
- return make_shared<BoolType const>();
- else if (Token::STRING0 <= _typeToken && _typeToken <= Token::STRING32)
- return make_shared<StaticStringType const>(int(_typeToken) - int(Token::STRING0));
+ else if (_typeToken == Token::Address)
+ return make_shared<IntegerType>(0, IntegerType::Modifier::Address);
+ else if (_typeToken == Token::Bool)
+ return make_shared<BoolType>();
+ else if (Token::String0 <= _typeToken && _typeToken <= Token::String32)
+ return make_shared<StaticStringType>(int(_typeToken) - int(Token::String0));
+ else if (_typeToken == Token::Bytes)
+ return make_shared<ByteArrayType>(ByteArrayType::Location::Storage);
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " +
std::string(Token::toString(_typeToken)) + " to type."));
}
-shared_ptr<Type const> Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName)
+TypePointer Type::fromElementaryTypeName(string const& _name)
+{
+ return fromElementaryTypeName(Token::fromIdentifierOrKeyword(_name));
+}
+
+TypePointer Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName)
{
Declaration const* declaration = _typeName.getReferencedDeclaration();
if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration))
- return make_shared<StructType const>(*structDef);
+ return make_shared<StructType>(*structDef);
+ else if (EnumDefinition const* enumDef = dynamic_cast<EnumDefinition const*>(declaration))
+ return make_shared<EnumType>(*enumDef);
else if (FunctionDefinition const* function = dynamic_cast<FunctionDefinition const*>(declaration))
- return make_shared<FunctionType const>(*function);
+ return make_shared<FunctionType>(*function);
else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
- return make_shared<ContractType const>(*contract);
- return shared_ptr<Type const>();
+ return make_shared<ContractType>(*contract);
+ return TypePointer();
}
-shared_ptr<Type const> Type::fromMapping(Mapping const& _typeName)
+TypePointer Type::fromMapping(Mapping const& _typeName)
{
- shared_ptr<Type const> keyType = _typeName.getKeyType().toType();
+ TypePointer keyType = _typeName.getKeyType().toType();
if (!keyType)
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Error resolving type name."));
- shared_ptr<Type const> valueType = _typeName.getValueType().toType();
+ TypePointer valueType = _typeName.getValueType().toType();
if (!valueType)
BOOST_THROW_EXCEPTION(_typeName.getValueType().createTypeError("Invalid type name"));
- return make_shared<MappingType const>(keyType, valueType);
+ return make_shared<MappingType>(keyType, valueType);
}
-shared_ptr<Type const> Type::forLiteral(Literal const& _literal)
+TypePointer Type::forLiteral(Literal const& _literal)
{
switch (_literal.getToken())
{
- case Token::TRUE_LITERAL:
- case Token::FALSE_LITERAL:
- return make_shared<BoolType const>();
- case Token::NUMBER:
- return IntegerType::smallestTypeForLiteral(_literal.getValue());
- case Token::STRING_LITERAL:
+ case Token::TrueLiteral:
+ case Token::FalseLiteral:
+ return make_shared<BoolType>();
+ case Token::Number:
+ return make_shared<IntegerConstantType>(_literal);
+ case Token::StringLiteral:
//@todo put larger strings into dynamic strings
return StaticStringType::smallestTypeForLiteral(_literal.getValue());
default:
- return shared_ptr<Type const>();
+ return shared_ptr<Type>();
}
}
-const MemberList Type::EmptyMemberList = MemberList();
-
-shared_ptr<IntegerType const> IntegerType::smallestTypeForLiteral(string const& _literal)
+TypePointer Type::commonType(TypePointer const& _a, TypePointer const& _b)
{
- bigint value(_literal);
- bool isSigned = value < 0 || (!_literal.empty() && _literal.front() == '-');
- if (isSigned)
- // convert to positive number of same bit requirements
- value = ((-value) - 1) << 1;
- unsigned bytes = max(bytesRequired(value), 1u);
- if (bytes > 32)
- return shared_ptr<IntegerType const>();
- return make_shared<IntegerType const>(bytes * 8, isSigned ? Modifier::SIGNED : Modifier::UNSIGNED);
+ if (_b->isImplicitlyConvertibleTo(*_a))
+ return _a;
+ else if (_a->isImplicitlyConvertibleTo(*_b))
+ return _b;
+ else
+ return TypePointer();
}
+const MemberList Type::EmptyMemberList = MemberList();
+
IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier):
m_bits(_bits), m_modifier(_modifier)
{
@@ -143,31 +151,36 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
- return _convertTo.getCategory() == getCategory() || _convertTo.getCategory() == Category::CONTRACT;
+ if (_convertTo.getCategory() == Category::String)
+ {
+ StaticStringType const& convertTo = dynamic_cast<StaticStringType const&>(_convertTo);
+ return isHash() && (m_bits == convertTo.getNumBytes() * 8);
+ }
+ return _convertTo.getCategory() == getCategory() ||
+ _convertTo.getCategory() == Category::Contract ||
+ _convertTo.getCategory() == Category::Enum;
}
-bool IntegerType::acceptsBinaryOperator(Token::Value _operator) const
+TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const
{
- if (isAddress())
- return Token::isCompareOp(_operator);
+ // "delete" is ok for all integer types
+ if (_operator == Token::Delete)
+ return make_shared<VoidType>();
+ // no further unary operators for addresses
+ else if (isAddress())
+ return TypePointer();
+ // "~" is ok for all other types
+ else if (_operator == Token::BitNot)
+ return shared_from_this();
+ // nothing else for hashes
else if (isHash())
- return Token::isCompareOp(_operator) || Token::isBitOp(_operator);
+ return TypePointer();
+ // for non-hash integers, we allow +, -, ++ and --
+ else if (_operator == Token::Add || _operator == Token::Sub ||
+ _operator == Token::Inc || _operator == Token::Dec)
+ return shared_from_this();
else
- return true;
-}
-
-bool IntegerType::acceptsUnaryOperator(Token::Value _operator) const
-{
- if (_operator == Token::DELETE)
- return true;
- if (isAddress())
- return false;
- if (_operator == Token::BIT_NOT)
- return true;
- if (isHash())
- return false;
- return _operator == Token::ADD || _operator == Token::SUB ||
- _operator == Token::INC || _operator == Token::DEC;
+ return TypePointer();
}
bool IntegerType::operator==(Type const& _other) const
@@ -186,25 +199,202 @@ string IntegerType::toString() const
return prefix + dev::toString(m_bits);
}
-u256 IntegerType::literalValue(Literal const& _literal) const
+TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
{
- bigint value(_literal.getValue());
- return u256(value);
+ if (_other->getCategory() != Category::IntegerConstant && _other->getCategory() != getCategory())
+ return TypePointer();
+ auto commonType = dynamic_pointer_cast<IntegerType const>(Type::commonType(shared_from_this(), _other));
+
+ if (!commonType)
+ return TypePointer();
+
+ // All integer types can be compared
+ if (Token::isCompareOp(_operator))
+ return commonType;
+
+ // Nothing else can be done with addresses, but hashes can receive bit operators
+ if (commonType->isAddress())
+ return TypePointer();
+ else if (commonType->isHash() && !Token::isBitOp(_operator))
+ return TypePointer();
+ else
+ return commonType;
}
const MemberList IntegerType::AddressMemberList =
- MemberList({{"balance",
- make_shared<IntegerType const>(256)},
- {"callstring32",
- make_shared<FunctionType const>(TypePointers({make_shared<StaticStringType const>(32)}),
- TypePointers(), FunctionType::Location::BARE)},
- {"callstring32string32",
- make_shared<FunctionType const>(TypePointers({make_shared<StaticStringType const>(32),
- make_shared<StaticStringType const>(32)}),
- TypePointers(), FunctionType::Location::BARE)},
- {"send",
- make_shared<FunctionType const>(TypePointers({make_shared<IntegerType const>(256)}),
- TypePointers(), FunctionType::Location::SEND)}});
+ MemberList({{"balance", make_shared<IntegerType >(256)},
+ {"call", make_shared<FunctionType>(strings(), strings(), FunctionType::Location::Bare, true)},
+ {"send", make_shared<FunctionType>(strings{"uint"}, strings{}, FunctionType::Location::Send)}});
+
+IntegerConstantType::IntegerConstantType(Literal const& _literal)
+{
+ m_value = bigint(_literal.getValue());
+
+ switch (_literal.getSubDenomination())
+ {
+ case Literal::SubDenomination::Wei:
+ case Literal::SubDenomination::None:
+ break;
+ case Literal::SubDenomination::Szabo:
+ m_value *= bigint("1000000000000");
+ break;
+ case Literal::SubDenomination::Finney:
+ m_value *= bigint("1000000000000000");
+ break;
+ case Literal::SubDenomination::Ether:
+ m_value *= bigint("1000000000000000000");
+ break;
+ }
+}
+
+bool IntegerConstantType::isImplicitlyConvertibleTo(Type const& _convertTo) const
+{
+ TypePointer integerType = getIntegerType();
+ return integerType && integerType->isImplicitlyConvertibleTo(_convertTo);
+}
+
+bool IntegerConstantType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+{
+ TypePointer integerType = getIntegerType();
+ return integerType && integerType->isExplicitlyConvertibleTo(_convertTo);
+}
+
+TypePointer IntegerConstantType::unaryOperatorResult(Token::Value _operator) const
+{
+ bigint value;
+ switch (_operator)
+ {
+ case Token::BitNot:
+ value = ~m_value;
+ break;
+ case Token::Add:
+ value = m_value;
+ break;
+ case Token::Sub:
+ value = -m_value;
+ break;
+ default:
+ return TypePointer();
+ }
+ return make_shared<IntegerConstantType>(value);
+}
+
+TypePointer IntegerConstantType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
+{
+ if (_other->getCategory() == Category::Integer)
+ {
+ shared_ptr<IntegerType const> integerType = getIntegerType();
+ if (!integerType)
+ return TypePointer();
+ return integerType->binaryOperatorResult(_operator, _other);
+ }
+ else if (_other->getCategory() != getCategory())
+ return TypePointer();
+
+ IntegerConstantType const& other = dynamic_cast<IntegerConstantType const&>(*_other);
+ if (Token::isCompareOp(_operator))
+ {
+ shared_ptr<IntegerType const> thisIntegerType = getIntegerType();
+ shared_ptr<IntegerType const> otherIntegerType = other.getIntegerType();
+ if (!thisIntegerType || !otherIntegerType)
+ return TypePointer();
+ return thisIntegerType->binaryOperatorResult(_operator, otherIntegerType);
+ }
+ else
+ {
+ bigint value;
+ switch (_operator)
+ {
+ case Token::BitOr:
+ value = m_value | other.m_value;
+ break;
+ case Token::BitXor:
+ value = m_value ^ other.m_value;
+ break;
+ case Token::BitAnd:
+ value = m_value & other.m_value;
+ break;
+ case Token::Add:
+ value = m_value + other.m_value;
+ break;
+ case Token::Sub:
+ value = m_value - other.m_value;
+ break;
+ case Token::Mul:
+ value = m_value * other.m_value;
+ break;
+ case Token::Div:
+ if (other.m_value == 0)
+ return TypePointer();
+ value = m_value / other.m_value;
+ break;
+ case Token::Mod:
+ if (other.m_value == 0)
+ return TypePointer();
+ value = m_value % other.m_value;
+ break;
+ case Token::Exp:
+ if (other.m_value < 0)
+ return TypePointer();
+ else if (other.m_value > std::numeric_limits<unsigned int>::max())
+ return TypePointer();
+ else
+ value = boost::multiprecision::pow(m_value, other.m_value.convert_to<unsigned int>());
+ break;
+ default:
+ return TypePointer();
+ }
+ return make_shared<IntegerConstantType>(value);
+ }
+}
+
+bool IntegerConstantType::operator==(Type const& _other) const
+{
+ if (_other.getCategory() != getCategory())
+ return false;
+ return m_value == dynamic_cast<IntegerConstantType const&>(_other).m_value;
+}
+
+string IntegerConstantType::toString() const
+{
+ return "int_const " + m_value.str();
+}
+
+u256 IntegerConstantType::literalValue(Literal const*) const
+{
+ u256 value;
+ // we ignore the literal and hope that the type was correctly determined
+ solAssert(m_value <= u256(-1), "Integer constant too large.");
+ solAssert(m_value >= -(bigint(1) << 255), "Integer constant too small.");
+
+ if (m_value >= 0)
+ value = u256(m_value);
+ else
+ value = s2u(s256(m_value));
+
+ return value;
+}
+
+TypePointer IntegerConstantType::getRealType() const
+{
+ auto intType = getIntegerType();
+ solAssert(!!intType, "getRealType called with invalid integer constant " + toString());
+ return intType;
+}
+
+shared_ptr<IntegerType const> IntegerConstantType::getIntegerType() const
+{
+ bigint value = m_value;
+ bool negative = (value < 0);
+ if (negative) // convert to positive number of same bit requirements
+ value = ((-value) - 1) << 1;
+ if (value > u256(-1))
+ return shared_ptr<IntegerType const>();
+ else
+ return make_shared<IntegerType>(max(bytesRequired(value), 1u) * 8,
+ negative ? IntegerType::Modifier::Signed
+ : IntegerType::Modifier::Unsigned);
+}
shared_ptr<StaticStringType> StaticStringType::smallestTypeForLiteral(string const& _literal)
{
@@ -227,6 +417,20 @@ bool StaticStringType::isImplicitlyConvertibleTo(Type const& _convertTo) const
return convertTo.m_bytes >= m_bytes;
}
+bool StaticStringType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+{
+ if (_convertTo.getCategory() == getCategory())
+ return true;
+ if (_convertTo.getCategory() == Category::Integer)
+ {
+ IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo);
+ if (convertTo.isHash() && (m_bytes * 8 == convertTo.getNumBits()))
+ return true;
+ }
+
+ return false;
+}
+
bool StaticStringType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
@@ -235,12 +439,13 @@ bool StaticStringType::operator==(Type const& _other) const
return other.m_bytes == m_bytes;
}
-u256 StaticStringType::literalValue(const Literal& _literal) const
+u256 StaticStringType::literalValue(const Literal* _literal) const
{
+ solAssert(_literal, "");
u256 value = 0;
- for (char c: _literal.getValue())
+ for (char c: _literal->getValue())
value = (value << 8) | byte(c);
- return value << ((32 - _literal.getValue().length()) * 8);
+ return value << ((32 - _literal->getValue().length()) * 8);
}
bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const
@@ -256,44 +461,110 @@ bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const
return isImplicitlyConvertibleTo(_convertTo);
}
-u256 BoolType::literalValue(Literal const& _literal) const
+u256 BoolType::literalValue(Literal const* _literal) const
{
- if (_literal.getToken() == Token::TRUE_LITERAL)
+ solAssert(_literal, "");
+ if (_literal->getToken() == Token::TrueLiteral)
return u256(1);
- else if (_literal.getToken() == Token::FALSE_LITERAL)
+ else if (_literal->getToken() == Token::FalseLiteral)
return u256(0);
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Bool type constructed from non-boolean literal."));
}
-bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+TypePointer BoolType::unaryOperatorResult(Token::Value _operator) const
+{
+ if (_operator == Token::Delete)
+ return make_shared<VoidType>();
+ return (_operator == Token::Not) ? shared_from_this() : TypePointer();
+}
+
+TypePointer BoolType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
{
- if (isImplicitlyConvertibleTo(_convertTo))
+ if (getCategory() != _other->getCategory())
+ return TypePointer();
+ if (Token::isCompareOp(_operator) || _operator == Token::And || _operator == Token::Or)
+ return _other;
+ else
+ return TypePointer();
+}
+
+bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
+{
+ if (*this == _convertTo)
return true;
- if (_convertTo.getCategory() == Category::INTEGER)
+ if (_convertTo.getCategory() == Category::Integer)
return dynamic_cast<IntegerType const&>(_convertTo).isAddress();
+ if (_convertTo.getCategory() == Category::Contract)
+ {
+ auto const& bases = getContractDefinition().getLinearizedBaseContracts();
+ if (m_super && bases.size() <= 1)
+ return false;
+ return find(m_super ? ++bases.begin() : bases.begin(), bases.end(),
+ &dynamic_cast<ContractType const&>(_convertTo).getContractDefinition()) != bases.end();
+ }
return false;
}
-bool ContractType::operator==(Type const& _other) const
+bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+{
+ return isImplicitlyConvertibleTo(_convertTo) || _convertTo.getCategory() == Category::Integer ||
+ _convertTo.getCategory() == Category::Contract;
+}
+
+TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const
+{
+ return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer();
+}
+
+bool ByteArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
+{
+ return _convertTo.getCategory() == getCategory();
+}
+
+TypePointer ByteArrayType::unaryOperatorResult(Token::Value _operator) const
+{
+ if (_operator == Token::Delete)
+ return make_shared<VoidType>();
+ return TypePointer();
+}
+
+bool ByteArrayType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
return false;
- ContractType const& other = dynamic_cast<ContractType const&>(_other);
- return other.m_contract == m_contract;
+ ByteArrayType const& other = dynamic_cast<ByteArrayType const&>(_other);
+ return other.m_location == m_location;
}
-u256 ContractType::getStorageSize() const
+unsigned ByteArrayType::getSizeOnStack() const
{
- u256 size = 0;
- for (ASTPointer<VariableDeclaration> const& variable: m_contract.getStateVariables())
- size += variable->getType()->getStorageSize();
- return max<u256>(1, size);
+ if (m_location == Location::CallData)
+ // offset, length (stack top)
+ return 2;
+ else
+ // offset
+ return 1;
+}
+
+shared_ptr<ByteArrayType> ByteArrayType::copyForLocation(ByteArrayType::Location _location) const
+{
+ return make_shared<ByteArrayType>(_location);
+}
+
+const MemberList ByteArrayType::s_byteArrayMemberList = MemberList({{"length", make_shared<IntegerType>(256)}});
+
+bool ContractType::operator==(Type const& _other) const
+{
+ if (_other.getCategory() != getCategory())
+ return false;
+ ContractType const& other = dynamic_cast<ContractType const&>(_other);
+ return other.m_contract == m_contract && other.m_super == m_super;
}
string ContractType::toString() const
{
- return "contract " + m_contract.getName();
+ return "contract " + string(m_super ? "super " : "") + m_contract.getName();
}
MemberList const& ContractType::getMembers() const
@@ -301,9 +572,20 @@ MemberList const& ContractType::getMembers() const
// We need to lazy-initialize it because of recursive references.
if (!m_members)
{
- map<string, shared_ptr<Type const>> members;
- for (FunctionDefinition const* function: m_contract.getInterfaceFunctions())
- members[function->getName()] = make_shared<FunctionType>(*function, false);
+ // All address members and all interface functions
+ vector<pair<string, TypePointer>> members(IntegerType::AddressMemberList.begin(),
+ IntegerType::AddressMemberList.end());
+ if (m_super)
+ {
+ for (ContractDefinition const* base: m_contract.getLinearizedBaseContracts())
+ for (ASTPointer<FunctionDefinition> const& function: base->getDefinedFunctions())
+ if (!function->isConstructor() && !function->getName().empty()&&
+ function->isVisibleInDerivedContracts())
+ members.push_back(make_pair(function->getName(), make_shared<FunctionType>(*function, true)));
+ }
+ else
+ for (auto const& it: m_contract.getInterfaceFunctions())
+ members.push_back(make_pair(it.second->getDeclaration().getName(), it.second));
m_members.reset(new MemberList(members));
}
return *m_members;
@@ -315,23 +597,26 @@ shared_ptr<FunctionType const> const& ContractType::getConstructorType() const
{
FunctionDefinition const* constructor = m_contract.getConstructor();
if (constructor)
- m_constructorType = make_shared<FunctionType const>(*constructor);
+ m_constructorType = make_shared<FunctionType>(*constructor);
else
- m_constructorType = make_shared<FunctionType const>(TypePointers(), TypePointers());
+ m_constructorType = make_shared<FunctionType>(TypePointers(), TypePointers());
}
return m_constructorType;
}
-unsigned ContractType::getFunctionIndex(string const& _functionName) const
+u256 ContractType::getFunctionIdentifier(string const& _functionName) const
{
- unsigned index = 0;
- for (FunctionDefinition const* function: m_contract.getInterfaceFunctions())
- {
- if (function->getName() == _functionName)
- return index;
- ++index;
- }
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Index of non-existing contract function requested."));
+ auto interfaceFunctions = m_contract.getInterfaceFunctions();
+ for (auto const& it: m_contract.getInterfaceFunctions())
+ if (it.second->getDeclaration().getName() == _functionName)
+ return FixedHash<4>::Arith(it.first);
+
+ return Invalid256;
+}
+
+TypePointer StructType::unaryOperatorResult(Token::Value _operator) const
+{
+ return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer();
}
bool StructType::operator==(Type const& _other) const
@@ -345,14 +630,14 @@ bool StructType::operator==(Type const& _other) const
u256 StructType::getStorageSize() const
{
u256 size = 0;
- for (pair<string, shared_ptr<Type const>> const& member: getMembers())
+ for (pair<string, TypePointer> const& member: getMembers())
size += member.second->getStorageSize();
return max<u256>(1, size);
}
bool StructType::canLiveOutsideStorage() const
{
- for (pair<string, shared_ptr<Type const>> const& member: getMembers())
+ for (pair<string, TypePointer> const& member: getMembers())
if (!member.second->canLiveOutsideStorage())
return false;
return true;
@@ -368,9 +653,9 @@ MemberList const& StructType::getMembers() const
// We need to lazy-initialize it because of recursive references.
if (!m_members)
{
- map<string, shared_ptr<Type const>> members;
+ vector<pair<string, TypePointer>> members;
for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers())
- members[variable->getName()] = variable->getType();
+ members.push_back(make_pair(variable->getName(), variable->getType()));
m_members.reset(new MemberList(members));
}
return *m_members;
@@ -380,7 +665,7 @@ u256 StructType::getStorageOffsetOfMember(string const& _name) const
{
//@todo cache member offset?
u256 offset;
- for (ASTPointer<VariableDeclaration> variable: m_struct.getMembers())
+ for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers())
{
if (variable->getName() == _name)
return offset;
@@ -389,19 +674,122 @@ u256 StructType::getStorageOffsetOfMember(string const& _name) const
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage offset of non-existing member requested."));
}
-FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal)
+TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const
+{
+ return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer();
+}
+
+bool EnumType::operator==(Type const& _other) const
+{
+ if (_other.getCategory() != getCategory())
+ return false;
+ EnumType const& other = dynamic_cast<EnumType const&>(_other);
+ return other.m_enum == m_enum;
+}
+
+string EnumType::toString() const
+{
+ return string("enum ") + m_enum.getName();
+}
+
+bool EnumType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+{
+ return _convertTo.getCategory() == getCategory() || _convertTo.getCategory() == Category::Integer;
+}
+
+unsigned int EnumType::getMemberValue(ASTString const& _member) const
+{
+ unsigned int index = 0;
+ for (ASTPointer<EnumValue> const& decl: m_enum.getMembers())
+ {
+ if (decl->getName() == _member)
+ return index;
+ ++index;
+ }
+ BOOST_THROW_EXCEPTION(m_enum.createTypeError("Requested unknown enum value ." + _member));
+}
+
+FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal):
+ m_location(_isInternal ? Location::Internal : Location::External),
+ m_isConstant(_function.isDeclaredConst()),
+ m_declaration(&_function)
{
TypePointers params;
+ vector<string> paramNames;
TypePointers retParams;
+ vector<string> retParamNames;
+
params.reserve(_function.getParameters().size());
+ paramNames.reserve(_function.getParameters().size());
for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
+ {
+ paramNames.push_back(var->getName());
params.push_back(var->getType());
+ }
retParams.reserve(_function.getReturnParameters().size());
+ retParamNames.reserve(_function.getReturnParameters().size());
for (ASTPointer<VariableDeclaration> const& var: _function.getReturnParameters())
+ {
+ retParamNames.push_back(var->getName());
retParams.push_back(var->getType());
+ }
swap(params, m_parameterTypes);
+ swap(paramNames, m_parameterNames);
swap(retParams, m_returnParameterTypes);
- m_location = _isInternal ? Location::INTERNAL : Location::EXTERNAL;
+ swap(retParamNames, m_returnParameterNames);
+}
+
+FunctionType::FunctionType(VariableDeclaration const& _varDecl):
+ m_location(Location::External), m_isConstant(true), m_declaration(&_varDecl)
+{
+ TypePointers params;
+ vector<string> paramNames;
+ auto returnType = _varDecl.getType();
+
+ while (auto mappingType = dynamic_cast<MappingType const*>(returnType.get()))
+ {
+ params.push_back(mappingType->getKeyType());
+ paramNames.push_back("");
+ returnType = mappingType->getValueType();
+ }
+
+ TypePointers retParams;
+ vector<string> retParamNames;
+ if (auto structType = dynamic_cast<StructType const*>(returnType.get()))
+ {
+ for (pair<string, TypePointer> const& member: structType->getMembers())
+ if (member.second->canLiveOutsideStorage())
+ {
+ retParamNames.push_back(member.first);
+ retParams.push_back(member.second);
+ }
+ }
+ else
+ {
+ retParams.push_back(returnType);
+ retParamNames.push_back("");
+ }
+
+ swap(params, m_parameterTypes);
+ swap(paramNames, m_parameterNames);
+ swap(retParams, m_returnParameterTypes);
+ swap(retParamNames, m_returnParameterNames);
+}
+
+FunctionType::FunctionType(const EventDefinition& _event):
+ m_location(Location::Event), m_isConstant(true), m_declaration(&_event)
+{
+ TypePointers params;
+ vector<string> paramNames;
+ params.reserve(_event.getParameters().size());
+ paramNames.reserve(_event.getParameters().size());
+ for (ASTPointer<VariableDeclaration> const& var: _event.getParameters())
+ {
+ paramNames.push_back(var->getName());
+ params.push_back(var->getType());
+ }
+ swap(params, m_parameterTypes);
+ swap(paramNames, m_parameterNames);
}
bool FunctionType::operator==(Type const& _other) const
@@ -412,6 +800,9 @@ bool FunctionType::operator==(Type const& _other) const
if (m_location != other.m_location)
return false;
+ if (m_isConstant != other.isConstant())
+ return false;
+
if (m_parameterTypes.size() != other.m_parameterTypes.size() ||
m_returnParameterTypes.size() != other.m_returnParameterTypes.size())
return false;
@@ -423,6 +814,9 @@ bool FunctionType::operator==(Type const& _other) const
if (!equal(m_returnParameterTypes.cbegin(), m_returnParameterTypes.cend(),
other.m_returnParameterTypes.cbegin(), typeCompare))
return false;
+ //@todo this is ugly, but cannot be prevented right now
+ if (m_gasSet != other.m_gasSet || m_valueSet != other.m_valueSet)
+ return false;
return true;
}
@@ -439,19 +833,106 @@ string FunctionType::toString() const
unsigned FunctionType::getSizeOnStack() const
{
+ unsigned size = 0;
+ if (m_location == Location::External)
+ size = 2;
+ else if (m_location == Location::Internal || m_location == Location::Bare)
+ size = 1;
+ if (m_gasSet)
+ size++;
+ if (m_valueSet)
+ size++;
+ return size;
+}
+
+MemberList const& FunctionType::getMembers() const
+{
switch (m_location)
{
- case Location::INTERNAL:
- return 1;
- case Location::EXTERNAL:
- return 2;
- case Location::BARE:
- return 1;
+ case Location::External:
+ case Location::Creation:
+ case Location::ECRecover:
+ case Location::SHA256:
+ case Location::RIPEMD160:
+ case Location::Bare:
+ if (!m_members)
+ {
+ vector<pair<string, TypePointer>> members{
+ {"value", make_shared<FunctionType>(parseElementaryTypeVector({"uint"}),
+ TypePointers{copyAndSetGasOrValue(false, true)},
+ Location::SetValue, false, m_gasSet, m_valueSet)}};
+ if (m_location != Location::Creation)
+ members.push_back(make_pair("gas", make_shared<FunctionType>(
+ parseElementaryTypeVector({"uint"}),
+ TypePointers{copyAndSetGasOrValue(true, false)},
+ Location::SetGas, false, m_gasSet, m_valueSet)));
+ m_members.reset(new MemberList(members));
+ }
+ return *m_members;
default:
- return 0;
+ return EmptyMemberList;
}
}
+string FunctionType::getCanonicalSignature(std::string const& _name) const
+{
+ std::string funcName = _name;
+ if (_name == "")
+ {
+ solAssert(m_declaration != nullptr, "Function type without name needs a declaration");
+ funcName = m_declaration->getName();
+ }
+ string ret = funcName + "(";
+
+ for (auto it = m_parameterTypes.cbegin(); it != m_parameterTypes.cend(); ++it)
+ ret += (*it)->toString() + (it + 1 == m_parameterTypes.cend() ? "" : ",");
+
+ return ret + ")";
+}
+
+TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
+{
+ TypePointers pointers;
+ pointers.reserve(_types.size());
+ for (string const& type: _types)
+ pointers.push_back(Type::fromElementaryTypeName(type));
+ return pointers;
+}
+
+TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) const
+{
+ return make_shared<FunctionType>(m_parameterTypes, m_returnParameterTypes, m_location,
+ m_arbitraryParameters,
+ m_gasSet || _setGas, m_valueSet || _setValue);
+}
+
+vector<string> const FunctionType::getParameterTypeNames() const
+{
+ vector<string> names;
+ for (TypePointer const& t: m_parameterTypes)
+ names.push_back(t->toString());
+
+ return names;
+}
+
+vector<string> const FunctionType::getReturnParameterTypeNames() const
+{
+ vector<string> names;
+ for (TypePointer const& t: m_returnParameterTypes)
+ names.push_back(t->toString());
+
+ return names;
+}
+
+ASTPointer<ASTString> FunctionType::getDocumentation() const
+{
+ auto function = dynamic_cast<Documented const*>(m_declaration);
+ if (function)
+ return function->getDocumentation();
+
+ return ASTPointer<ASTString>();
+}
+
bool MappingType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
@@ -473,27 +954,90 @@ bool TypeType::operator==(Type const& _other) const
return *getActualType() == *other.getActualType();
}
+MemberList const& TypeType::getMembers() const
+{
+ // We need to lazy-initialize it because of recursive references.
+ if (!m_members)
+ {
+ vector<pair<string, TypePointer>> members;
+ if (m_actualType->getCategory() == Category::Contract && m_currentContract != nullptr)
+ {
+ ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_actualType).getContractDefinition();
+ vector<ContractDefinition const*> currentBases = m_currentContract->getLinearizedBaseContracts();
+ if (find(currentBases.begin(), currentBases.end(), &contract) != currentBases.end())
+ // We are accessing the type of a base contract, so add all public and protected
+ // functions. Note that this does not add inherited functions on purpose.
+ for (ASTPointer<FunctionDefinition> const& f: contract.getDefinedFunctions())
+ if (!f->isConstructor() && !f->getName().empty() && f->isVisibleInDerivedContracts())
+ members.push_back(make_pair(f->getName(), make_shared<FunctionType>(*f)));
+ }
+ else if (m_actualType->getCategory() == Category::Enum)
+ {
+ EnumDefinition const& enumDef = dynamic_cast<EnumType const&>(*m_actualType).getEnumDefinition();
+ auto enumType = make_shared<EnumType>(enumDef);
+ for (ASTPointer<EnumValue> const& enumValue: enumDef.getMembers())
+ members.push_back(make_pair(enumValue->getName(), enumType));
+ }
+ m_members.reset(new MemberList(members));
+ }
+ return *m_members;
+}
+
+ModifierType::ModifierType(const ModifierDefinition& _modifier)
+{
+ TypePointers params;
+ params.reserve(_modifier.getParameters().size());
+ for (ASTPointer<VariableDeclaration> const& var: _modifier.getParameters())
+ params.push_back(var->getType());
+ swap(params, m_parameterTypes);
+}
+
+bool ModifierType::operator==(Type const& _other) const
+{
+ if (_other.getCategory() != getCategory())
+ return false;
+ ModifierType const& other = dynamic_cast<ModifierType const&>(_other);
+
+ if (m_parameterTypes.size() != other.m_parameterTypes.size())
+ return false;
+ auto typeCompare = [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; };
+
+ if (!equal(m_parameterTypes.cbegin(), m_parameterTypes.cend(),
+ other.m_parameterTypes.cbegin(), typeCompare))
+ return false;
+ return true;
+}
+
+string ModifierType::toString() const
+{
+ string name = "modifier (";
+ for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it)
+ name += (*it)->toString() + (it + 1 == m_parameterTypes.end() ? "" : ",");
+ return name + ")";
+}
+
MagicType::MagicType(MagicType::Kind _kind):
m_kind(_kind)
{
switch (m_kind)
{
- case Kind::BLOCK:
- m_members = MemberList({{"coinbase", make_shared<IntegerType const>(0, IntegerType::Modifier::ADDRESS)},
- {"timestamp", make_shared<IntegerType const>(256)},
- {"prevhash", make_shared<IntegerType const>(256, IntegerType::Modifier::HASH)},
- {"difficulty", make_shared<IntegerType const>(256)},
- {"number", make_shared<IntegerType const>(256)},
- {"gaslimit", make_shared<IntegerType const>(256)}});
+ case Kind::Block:
+ m_members = MemberList({{"coinbase", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
+ {"timestamp", make_shared<IntegerType>(256)},
+ {"blockhash", make_shared<FunctionType>(strings{"uint"}, strings{"hash"}, FunctionType::Location::BlockHash)},
+ {"difficulty", make_shared<IntegerType>(256)},
+ {"number", make_shared<IntegerType>(256)},
+ {"gaslimit", make_shared<IntegerType>(256)}});
break;
- case Kind::MSG:
- m_members = MemberList({{"sender", make_shared<IntegerType const>(0, IntegerType::Modifier::ADDRESS)},
- {"gas", make_shared<IntegerType const>(256)},
- {"value", make_shared<IntegerType const>(256)}});
+ case Kind::Message:
+ m_members = MemberList({{"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
+ {"gas", make_shared<IntegerType>(256)},
+ {"value", make_shared<IntegerType>(256)},
+ {"data", make_shared<ByteArrayType>(ByteArrayType::Location::CallData)}});
break;
- case Kind::TX:
- m_members = MemberList({{"origin", make_shared<IntegerType const>(0, IntegerType::Modifier::ADDRESS)},
- {"gasprice", make_shared<IntegerType const>(256)}});
+ case Kind::Transaction:
+ m_members = MemberList({{"origin", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
+ {"gasprice", make_shared<IntegerType>(256)}});
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of magic."));
@@ -512,11 +1056,11 @@ string MagicType::toString() const
{
switch (m_kind)
{
- case Kind::BLOCK:
+ case Kind::Block:
return "block";
- case Kind::MSG:
+ case Kind::Message:
return "msg";
- case Kind::TX:
+ case Kind::Transaction:
return "tx";
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of magic."));
diff --git a/Types.h b/Types.h
index 48539a1d..af64f1cb 100644
--- a/Types.h
+++ b/Types.h
@@ -41,6 +41,7 @@ namespace solidity
class Type; // forward
class FunctionType; // forward
using TypePointer = std::shared_ptr<Type const>;
+using FunctionTypePointer = std::shared_ptr<FunctionType const>;
using TypePointers = std::vector<TypePointer>;
/**
@@ -49,14 +50,16 @@ using TypePointers = std::vector<TypePointer>;
class MemberList
{
public:
- using MemberMap = std::map<std::string, TypePointer>;
+ using MemberMap = std::vector<std::pair<std::string, TypePointer>>;
MemberList() {}
explicit MemberList(MemberMap const& _members): m_memberTypes(_members) {}
TypePointer getMemberType(std::string const& _name) const
{
- auto it = m_memberTypes.find(_name);
- return it != m_memberTypes.end() ? it->second : TypePointer();
+ for (auto const& it: m_memberTypes)
+ if (it.first == _name)
+ return it.second;
+ return TypePointer();
}
MemberMap::const_iterator begin() const { return m_memberTypes.begin(); }
@@ -70,26 +73,31 @@ private:
/**
* Abstract base class that forms the root of the type hierarchy.
*/
-class Type: private boost::noncopyable
+class Type: private boost::noncopyable, public std::enable_shared_from_this<Type>
{
public:
enum class Category
{
- INTEGER, BOOL, REAL, STRING, CONTRACT, STRUCT, FUNCTION, MAPPING, VOID, TYPE, MAGIC
+ Integer, IntegerConstant, Bool, Real, ByteArray,
+ String, Contract, Struct, Function, Enum,
+ Mapping, Void, TypeType, Modifier, Magic
};
///@{
///@name Factory functions
/// Factory functions that convert an AST @ref TypeName to a Type.
- static std::shared_ptr<Type const> fromElementaryTypeName(Token::Value _typeToken);
- static std::shared_ptr<Type const> fromUserDefinedTypeName(UserDefinedTypeName const& _typeName);
- static std::shared_ptr<Type const> fromMapping(Mapping const& _typeName);
- static std::shared_ptr<Type const> fromFunction(FunctionDefinition const& _function);
+ static TypePointer fromElementaryTypeName(Token::Value _typeToken);
+ static TypePointer fromElementaryTypeName(std::string const& _name);
+ static TypePointer fromUserDefinedTypeName(UserDefinedTypeName const& _typeName);
+ static TypePointer fromMapping(Mapping const& _typeName);
+ static TypePointer fromFunction(FunctionDefinition const& _function);
/// @}
/// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does
/// not fit any type.
- static std::shared_ptr<Type const> forLiteral(Literal const& _literal);
+ static TypePointer forLiteral(Literal const& _literal);
+ /// @returns a pointer to _a or _b if the other is implicitly convertible to it or nullptr otherwise
+ static TypePointer commonType(TypePointer const& _a, TypePointer const& _b);
virtual Category getCategory() const = 0;
virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; }
@@ -97,15 +105,27 @@ public:
{
return isImplicitlyConvertibleTo(_convertTo);
}
- virtual bool acceptsBinaryOperator(Token::Value) const { return false; }
- virtual bool acceptsUnaryOperator(Token::Value) const { return false; }
+ /// @returns the resulting type of applying the given unary operator or an empty pointer if
+ /// this is not possible.
+ /// The default implementation does not allow any unary operator.
+ virtual TypePointer unaryOperatorResult(Token::Value) const { return TypePointer(); }
+ /// @returns the resulting type of applying the given binary operator or an empty pointer if
+ /// this is not possible.
+ /// The default implementation allows comparison operators if a common type exists
+ virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
+ {
+ return Token::isCompareOp(_operator) ? commonType(shared_from_this(), _other) : TypePointer();
+ }
virtual bool operator==(Type const& _other) const { return getCategory() == _other.getCategory(); }
virtual bool operator!=(Type const& _other) const { return !this->operator ==(_other); }
/// @returns number of bytes used by this type when encoded for CALL, or 0 if the encoding
- /// is not a simple big-endian encoding or the type cannot be stored on the stack.
+ /// is not a simple big-endian encoding or the type cannot be stored in calldata.
+ /// Note that irrespective of this size, each calldata element is padded to a multiple of 32 bytes.
virtual unsigned getCalldataEncodedSize() const { return 0; }
+ /// @returns true if the type is dynamically encoded in calldata
+ virtual bool isDynamicallySized() const { return false; }
/// @returns number of bytes required to hold this value in storage.
/// For dynamically "allocated" types, it returns the size of the statically allocated head,
virtual u256 getStorageSize() const { return 1; }
@@ -117,6 +137,8 @@ public:
/// i.e. it behaves differently in lvalue context and in value context.
virtual bool isValueType() const { return false; }
virtual unsigned getSizeOnStack() const { return 1; }
+ /// @returns the real type of some types, like e.g: IntegerConstant
+ virtual TypePointer getRealType() const { return shared_from_this(); }
/// Returns the list of all members of this type. Default implementation: no members.
virtual MemberList const& getMembers() const { return EmptyMemberList; }
@@ -124,10 +146,10 @@ public:
TypePointer getMemberType(std::string const& _name) const { return getMembers().getMemberType(_name); }
virtual std::string toString() const = 0;
- virtual u256 literalValue(Literal const&) const
+ virtual u256 literalValue(Literal const*) const
{
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Literal value requested "
- "for type without literals."));
+ "for type without literals."));
}
protected:
@@ -143,20 +165,16 @@ class IntegerType: public Type
public:
enum class Modifier
{
- UNSIGNED, SIGNED, HASH, ADDRESS
+ Unsigned, Signed, Hash, Address
};
- virtual Category getCategory() const override { return Category::INTEGER; }
-
- /// @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);
+ virtual Category getCategory() const override { return Category::Integer; }
- 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;
@@ -294,6 +377,32 @@ private:
};
/**
+ * The type of an enum instance, there is one distinct type per enum definition.
+ */
+class EnumType: public Type
+{
+public:
+ virtual Category getCategory() const override { return Category::Enum; }
+ explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {}
+ virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
+ virtual bool operator==(Type const& _other) const override;
+ virtual unsigned getSizeOnStack() const override { return 1; }
+ virtual std::string toString() const override;
+ virtual bool isValueType() const override { return true; }
+
+ virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
+
+ EnumDefinition const& getEnumDefinition() const { return m_enum; }
+ /// @returns the value that the string has in the Enum
+ unsigned int getMemberValue(ASTString const& _member) const;
+
+private:
+ EnumDefinition const& m_enum;
+ /// List of member types, will be lazy-initialized because of recursive references.
+ mutable std::unique_ptr<MemberList> m_members;
+};
+
+/**
* The type of a function, identified by its (return) parameter types.
* @todo the return parameters should also have names, i.e. return parameters should be a struct
* type.
@@ -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 };
-
- virtual Category getCategory() const override { return Category::FUNCTION; }
+ /// @todo This documentation is outdated, and Location should rather be named "Type"
+ enum class Location { Internal, External, Creation, Send,
+ SHA3, Suicide,
+ ECRecover, SHA256, RIPEMD160,
+ Log0, Log1, Log2, Log3, Log4, Event,
+ SetGas, SetValue, BlockHash,
+ Bare };
+
+ virtual Category getCategory() const override { return Category::Function; }
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
- 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; }
diff --git a/Utils.h b/Utils.h
index 8d6a3ab0..1411f66b 100644
--- a/Utils.h
+++ b/Utils.h
@@ -45,5 +45,11 @@ inline void solAssertAux(bool _condition, std::string const& _errorDescription,
<< ::boost::throw_line(_line));
}
+inline void solAssertAux(void const* _pointer, std::string const& _errorDescription, unsigned _line,
+ char const* _file, char const* _function)
+{
+ solAssertAux(_pointer != nullptr, _errorDescription, _line, _file, _function);
+}
+
}
}
diff --git a/grammar.txt b/grammar.txt
index 8c34997b..a3b24687 100644
--- a/grammar.txt
+++ b/grammar.txt
@@ -1,12 +1,19 @@
-ContractDefinition = 'contract' Identifier '{' ContractPart* '}'
-ContractPart = VariableDeclaration ';' | StructDefinition |
- FunctionDefinition | 'public:' | 'private:'
+ContractDefinition = 'contract' Identifier
+ ( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )?
+ '{' ContractPart* '}'
+ContractPart = StateVariableDeclaration | StructDefinition | ModifierDefinition | FunctionDefinition | EnumDefinition
+InheritanceSpecifier = Identifier ( '(' Expression ( ',' Expression )* ')' )?
StructDefinition = 'struct' Identifier '{'
( VariableDeclaration (';' VariableDeclaration)* )? '}
-
-FunctionDefinition = 'function' Identifier ParameterList 'const'?
+StateVariableDeclaration = TypeName ( 'public' | 'inheritable' | 'private' )? Identifier ';'
+ModifierDefinition = 'modifier' Identifier ParameterList? Block
+FunctionDefinition = 'function' Identifier ParameterList
+ ( Identifier | 'constant' | 'external' | 'public' | 'inheritable' | 'private' )*
( 'returns' ParameterList )? Block
+
+EnumValue = Identifier
+EnumDefinition = 'enum' '{' EnumValue (',' EnumValue)* '}'
ParameterList = '(' ( VariableDeclaration (',' VariableDeclaration)* )? ')'
// semantic restriction: mappings and structs (recursively) containing mappings
// are not allowed in argument lists
@@ -33,7 +40,7 @@ Expression = Assignment | UnaryOperation | BinaryOperation | FunctionCall | NewE
// The expression syntax is actually much more complicated
Assignment = Expression (AssignmentOp Expression)
FunctionCall = Expression '(' Expression ( ',' Expression )* ')'
-NewExpression = 'new' Identifier '(' ( Expression ( ',' Expression )* ) ')'
+NewExpression = 'new' Identifier
MemberAccess = Expression '.' Identifier
IndexAccess = Expression '[' Expresison ']'
PrimaryExpression = Identifier | NumberLiteral | StringLiteral | ElementaryTypeName | '(' Expression ')'