From 9b3886ec19aa8a77974e820d2f02f2b6078fc577 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 20 Feb 2015 21:59:21 +0100 Subject: [PATCH 01/70] Add EVMJIT. --- AST.cpp | 636 ----------------- AST.h | 1190 -------------------------------- ASTForward.h | 93 --- ASTJsonConverter.cpp | 469 ------------- ASTJsonConverter.h | 135 ---- ASTPrinter.cpp | 571 ---------------- ASTPrinter.h | 141 ---- ASTVisitor.h | 222 ------ AST_accept.h | 659 ------------------ BaseTypes.h | 60 -- CMakeLists.txt | 35 - Compiler.cpp | 498 -------------- Compiler.h | 93 --- CompilerContext.cpp | 165 ----- CompilerContext.h | 135 ---- CompilerStack.cpp | 370 ---------- CompilerStack.h | 165 ----- CompilerUtils.cpp | 376 ---------- CompilerUtils.h | 119 ---- DeclarationContainer.cpp | 59 -- DeclarationContainer.h | 63 -- Exceptions.h | 44 -- ExpressionCompiler.cpp | 1248 ---------------------------------- ExpressionCompiler.h | 180 ----- GlobalContext.cpp | 95 --- GlobalContext.h | 64 -- InterfaceHandler.cpp | 391 ----------- InterfaceHandler.h | 125 ---- NameAndTypeResolver.cpp | 384 ----------- NameAndTypeResolver.h | 148 ---- Parser.cpp | 883 ------------------------ Parser.h | 117 ---- Scanner.cpp | 769 --------------------- Scanner.h | 224 ------ SourceReferenceFormatter.cpp | 86 --- SourceReferenceFormatter.h | 48 -- Token.cpp | 100 --- Token.h | 413 ----------- Types.cpp | 1071 ----------------------------- Types.h | 631 ----------------- Utils.h | 55 -- grammar.txt | 46 -- 42 files changed, 13376 deletions(-) delete mode 100644 AST.cpp delete mode 100644 AST.h delete mode 100644 ASTForward.h delete mode 100644 ASTJsonConverter.cpp delete mode 100644 ASTJsonConverter.h delete mode 100644 ASTPrinter.cpp delete mode 100644 ASTPrinter.h delete mode 100644 ASTVisitor.h delete mode 100644 AST_accept.h delete mode 100644 BaseTypes.h delete mode 100644 CMakeLists.txt delete mode 100644 Compiler.cpp delete mode 100644 Compiler.h delete mode 100644 CompilerContext.cpp delete mode 100644 CompilerContext.h delete mode 100644 CompilerStack.cpp delete mode 100644 CompilerStack.h delete mode 100644 CompilerUtils.cpp delete mode 100644 CompilerUtils.h delete mode 100644 DeclarationContainer.cpp delete mode 100644 DeclarationContainer.h delete mode 100644 Exceptions.h delete mode 100644 ExpressionCompiler.cpp delete mode 100644 ExpressionCompiler.h delete mode 100644 GlobalContext.cpp delete mode 100644 GlobalContext.h delete mode 100644 InterfaceHandler.cpp delete mode 100644 InterfaceHandler.h delete mode 100644 NameAndTypeResolver.cpp delete mode 100644 NameAndTypeResolver.h delete mode 100644 Parser.cpp delete mode 100644 Parser.h delete mode 100644 Scanner.cpp delete mode 100644 Scanner.h delete mode 100644 SourceReferenceFormatter.cpp delete mode 100644 SourceReferenceFormatter.h delete mode 100644 Token.cpp delete mode 100644 Token.h delete mode 100644 Types.cpp delete mode 100644 Types.h delete mode 100644 Utils.h delete mode 100644 grammar.txt diff --git a/AST.cpp b/AST.cpp deleted file mode 100644 index 761427db4..000000000 --- a/AST.cpp +++ /dev/null @@ -1,636 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @date 2014 - * Solidity abstract syntax tree. - */ - -#include -#include -#include -#include -#include -#include - -#include - -using namespace std; - -namespace dev -{ -namespace solidity -{ - -TypeError ASTNode::createTypeError(string const& _description) const -{ - return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description); -} - -TypePointer ContractDefinition::getType(ContractDefinition const* _currentContract) const -{ - return make_shared(make_shared(*this), _currentContract); -} - -void ContractDefinition::checkTypeRequirements() -{ - for (ASTPointer const& baseSpecifier: getBaseContracts()) - baseSpecifier->checkTypeRequirements(); - - checkIllegalOverrides(); - - FunctionDefinition const* constructor = getConstructor(); - if (constructor && !constructor->getReturnParameters().empty()) - BOOST_THROW_EXCEPTION(constructor->getReturnParameterList()->createTypeError( - "Non-empty \"returns\" directive for constructor.")); - - FunctionDefinition const* fallbackFunction = nullptr; - for (ASTPointer 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 const& modifier: getFunctionModifiers()) - modifier->checkTypeRequirements(); - - for (ASTPointer const& function: getDefinedFunctions()) - function->checkTypeRequirements(); - - // check for hash collisions in function signatures - set> hashes; - for (auto const& it: getInterfaceFunctionList()) - { - FixedHash<4> const& hash = it.first; - if (hashes.count(hash)) - BOOST_THROW_EXCEPTION(createTypeError( - std::string("Function signature hash collision for ") + - it.second->getCanonicalSignature())); - hashes.insert(hash); - } -} - -map, FunctionTypePointer> ContractDefinition::getInterfaceFunctions() const -{ - auto exportedFunctionList = getInterfaceFunctionList(); - - map, FunctionTypePointer> exportedFunctions; - for (auto const& it: exportedFunctionList) - exportedFunctions.insert(it); - - solAssert(exportedFunctionList.size() == exportedFunctions.size(), - "Hash collision at Function Definition Hash calculation"); - - return exportedFunctions; -} - -FunctionDefinition const* ContractDefinition::getConstructor() const -{ - for (ASTPointer const& f: m_definedFunctions) - if (f->isConstructor()) - return f.get(); - return nullptr; -} - -FunctionDefinition const* ContractDefinition::getFallbackFunction() const -{ - for (ContractDefinition const* contract: getLinearizedBaseContracts()) - for (ASTPointer 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 functions; - map modifiers; - - // We search from derived to base, so the stored item causes the error. - for (ContractDefinition const* contract: getLinearizedBaseContracts()) - { - for (ASTPointer 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 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> const& ContractDefinition::getInterfaceEvents() const -{ - if (!m_interfaceEvents) - { - set eventsSeen; - m_interfaceEvents.reset(new std::vector>()); - for (ContractDefinition const* contract: getLinearizedBaseContracts()) - for (ASTPointer const& e: contract->getEvents()) - if (eventsSeen.count(e->getName()) == 0) - { - eventsSeen.insert(e->getName()); - m_interfaceEvents->push_back(e); - } - } - return *m_interfaceEvents; -} - -vector, FunctionTypePointer>> const& ContractDefinition::getInterfaceFunctionList() const -{ - if (!m_interfaceFunctionList) - { - set functionsSeen; - m_interfaceFunctionList.reset(new vector, FunctionTypePointer>>()); - for (ContractDefinition const* contract: getLinearizedBaseContracts()) - { - for (ASTPointer 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(*f, false))); - } - - for (ASTPointer 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(*v))); - } - } - } - return *m_interfaceFunctionList; -} - -TypePointer EnumValue::getType(ContractDefinition const*) const -{ - EnumDefinition const* parentDef = dynamic_cast(getScope()); - solAssert(parentDef, "Enclosing Scope of EnumValue was not set"); - return make_shared(*parentDef); -} - -void InheritanceSpecifier::checkTypeRequirements() -{ - m_baseName->checkTypeRequirements(); - for (ASTPointer const& argument: m_arguments) - argument->checkTypeRequirements(); - - ContractDefinition const* base = dynamic_cast(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(make_shared(*this)); -} - -void StructDefinition::checkMemberTypes() const -{ - for (ASTPointer const& member: getMembers()) - if (!member->getType()->canBeStored()) - BOOST_THROW_EXCEPTION(member->createTypeError("Type cannot be used in struct.")); -} - -void StructDefinition::checkRecursion() const -{ - set definitionsSeen; - vector queue = {this}; - while (!queue.empty()) - { - StructDefinition const* def = queue.back(); - queue.pop_back(); - if (definitionsSeen.count(def)) - BOOST_THROW_EXCEPTION(ParserError() << errinfo_sourceLocation(def->getLocation()) - << errinfo_comment("Recursive struct definition.")); - definitionsSeen.insert(def); - for (ASTPointer const& member: def->getMembers()) - if (member->getType()->getCategory() == Type::Category::Struct) - { - UserDefinedTypeName const& typeName = dynamic_cast(*member->getTypeName()); - queue.push_back(&dynamic_cast(*typeName.getReferencedDeclaration())); - } - } -} - -TypePointer EnumDefinition::getType(ContractDefinition const*) const -{ - return make_shared(make_shared(*this)); -} - -TypePointer FunctionDefinition::getType(ContractDefinition const*) const -{ - return make_shared(*this); -} - -void FunctionDefinition::checkTypeRequirements() -{ - for (ASTPointer const& var: getParameters() + getReturnParameters()) - if (!var->getType()->canLiveOutsideStorage()) - BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); - for (ASTPointer 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(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(*this); -} - -void ModifierDefinition::checkTypeRequirements() -{ - m_body->checkTypeRequirements(); -} - -void ModifierInvocation::checkTypeRequirements() -{ - m_modifierName->checkTypeRequirements(); - for (ASTPointer const& argument: m_arguments) - argument->checkTypeRequirements(); - - ModifierDefinition const* modifier = dynamic_cast(m_modifierName->getReferencedDeclaration()); - solAssert(modifier, "Function modifier not found."); - vector> 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 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 const& statement: m_statements) - statement->checkTypeRequirements(); -} - -void IfStatement::checkTypeRequirements() -{ - m_condition->expectType(BoolType()); - m_trueBody->checkTypeRequirements(); - if (m_falseBody) - m_falseBody->checkTypeRequirements(); -} - -void WhileStatement::checkTypeRequirements() -{ - m_condition->expectType(BoolType()); - m_body->checkTypeRequirements(); -} - -void ForStatement::checkTypeRequirements() -{ - if (m_initExpression) - m_initExpression->checkTypeRequirements(); - if (m_condExpression) - m_condExpression->expectType(BoolType()); - if (m_loopExpression) - m_loopExpression->checkTypeRequirements(); - m_body->checkTypeRequirements(); -} - -void Return::checkTypeRequirements() -{ - if (!m_expression) - return; - if (!m_returnParameters) - BOOST_THROW_EXCEPTION(createTypeError("Return arguments not allowed.")); - if (m_returnParameters->getParameters().size() != 1) - BOOST_THROW_EXCEPTION(createTypeError("Different number of arguments in return statement " - "than in returns declaration.")); - // this could later be changed such that the paramaters type is an anonymous struct type, - // but for now, we only allow one return parameter - m_expression->expectType(*m_returnParameters->getParameters().front()->getType()); -} - -void VariableDefinition::checkTypeRequirements() -{ - // Variables can be declared without type (with "var"), in which case the first assignment - // sets the type. - // Note that assignments before the first declaration are legal because of the special scoping - // rules inherited from JavaScript. - if (m_value) - { - if (m_variable->getType()) - m_value->expectType(*m_variable->getType()); - else - { - // no type declared and no previous assignment, infer the type - m_value->checkTypeRequirements(); - TypePointer type = m_value->getType(); - if (type->getCategory() == Type::Category::IntegerConstant) - { - auto intType = dynamic_pointer_cast(type)->getIntegerType(); - if (!intType) - BOOST_THROW_EXCEPTION(m_value->createTypeError("Invalid integer constant " + type->toString())); - type = intType; - } - else if (type->getCategory() == Type::Category::Void) - BOOST_THROW_EXCEPTION(m_variable->createTypeError("var cannot be void type")); - m_variable->setType(type); - } - } -} - -void Assignment::checkTypeRequirements() -{ - m_leftHandSide->checkTypeRequirements(); - m_leftHandSide->requireLValue(); - if (m_leftHandSide->getType()->getCategory() == Type::Category::Mapping) - BOOST_THROW_EXCEPTION(createTypeError("Mappings cannot be assigned to.")); - m_type = m_leftHandSide->getType(); - if (m_assigmentOperator == Token::Assign) - m_rightHandSide->expectType(*m_type); - else - { - // compound assignment - m_rightHandSide->checkTypeRequirements(); - TypePointer resultType = m_type->binaryOperatorResult(Token::AssignmentToBinaryOp(m_assigmentOperator), - m_rightHandSide->getType()); - if (!resultType || *resultType != *m_type) - BOOST_THROW_EXCEPTION(createTypeError("Operator " + string(Token::toString(m_assigmentOperator)) + - " not compatible with types " + - m_type->toString() + " and " + - m_rightHandSide->getType()->toString())); - } -} - -void ExpressionStatement::checkTypeRequirements() -{ - m_expression->checkTypeRequirements(); - if (m_expression->getType()->getCategory() == Type::Category::IntegerConstant) - if (!dynamic_pointer_cast(m_expression->getType())->getIntegerType()) - BOOST_THROW_EXCEPTION(m_expression->createTypeError("Invalid integer constant.")); -} - -void Expression::expectType(Type const& _expectedType) -{ - checkTypeRequirements(); - Type const& type = *getType(); - if (!type.isImplicitlyConvertibleTo(_expectedType)) - BOOST_THROW_EXCEPTION(createTypeError("Type " + type.toString() + - " not implicitly convertible to expected type " - + _expectedType.toString() + ".")); -} - -void Expression::requireLValue() -{ - if (!isLValue()) - BOOST_THROW_EXCEPTION(createTypeError("Expression has to be an lvalue.")); - m_lvalueRequested = true; -} - -void UnaryOperation::checkTypeRequirements() -{ - // Inc, Dec, Add, Sub, Not, BitNot, Delete - m_subExpression->checkTypeRequirements(); - if (m_operator == Token::Value::Inc || m_operator == Token::Value::Dec || m_operator == Token::Value::Delete) - m_subExpression->requireLValue(); - m_type = m_subExpression->getType()->unaryOperatorResult(m_operator); - if (!m_type) - BOOST_THROW_EXCEPTION(createTypeError("Unary operator not compatible with type.")); -} - -void BinaryOperation::checkTypeRequirements() -{ - m_left->checkTypeRequirements(); - m_right->checkTypeRequirements(); - m_commonType = m_left->getType()->binaryOperatorResult(m_operator, m_right->getType()); - if (!m_commonType) - BOOST_THROW_EXCEPTION(createTypeError("Operator " + string(Token::toString(m_operator)) + - " not compatible with types " + - m_left->getType()->toString() + " and " + - m_right->getType()->toString())); - m_type = Token::isCompareOp(m_operator) ? make_shared() : m_commonType; -} - -void FunctionCall::checkTypeRequirements() -{ - m_expression->checkTypeRequirements(); - for (ASTPointer const& argument: m_arguments) - argument->checkTypeRequirements(); - - Type const* expressionType = m_expression->getType().get(); - if (isTypeConversion()) - { - TypeType const& type = dynamic_cast(*expressionType); - //@todo for structs, we have to check the number of arguments to be equal to the - // number of non-mapping members - if (m_arguments.size() != 1) - BOOST_THROW_EXCEPTION(createTypeError("More than one argument for explicit type conversion.")); - if (!m_names.empty()) - BOOST_THROW_EXCEPTION(createTypeError("Type conversion cannot allow named arguments.")); - if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type.getActualType())) - BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed.")); - m_type = type.getActualType(); - } - else if (FunctionType const* functionType = dynamic_cast(expressionType)) - { - //@todo would be nice to create a struct type from the arguments - // and then ask if that is implicitly convertible to the struct represented by the - // function parameters - TypePointers const& parameterTypes = functionType->getParameterTypes(); - if (!functionType->takesArbitraryParameters() && parameterTypes.size() != m_arguments.size()) - BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call.")); - - if (m_names.empty()) - { - for (size_t i = 0; i < m_arguments.size(); ++i) - if (!functionType->takesArbitraryParameters() && - !m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i])) - BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError("Invalid type for argument in function call.")); - } - else - { - if (functionType->takesArbitraryParameters()) - BOOST_THROW_EXCEPTION(createTypeError("Named arguments cannnot be used for functions " - "that take arbitrary parameters.")); - auto const& parameterNames = functionType->getParameterNames(); - if (parameterNames.size() != m_names.size()) - BOOST_THROW_EXCEPTION(createTypeError("Some argument names are missing.")); - - // check duplicate names - for (size_t i = 0; i < m_names.size(); i++) - for (size_t j = i + 1; j < m_names.size(); j++) - if (*m_names[i] == *m_names[j]) - BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError("Duplicate named argument.")); - - for (size_t i = 0; i < m_names.size(); i++) { - bool found = false; - for (size_t j = 0; j < parameterNames.size(); j++) { - if (parameterNames[j] == *m_names[i]) { - // check type convertible - if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[j])) - BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in function call.")); - - found = true; - break; - } - } - if (!found) - BOOST_THROW_EXCEPTION(createTypeError("Named argument does not match function declaration.")); - } - } - - // @todo actually the return type should be an anonymous struct, - // but we change it to the type of the first return value until we have structs - if (functionType->getReturnParameterTypes().empty()) - m_type = make_shared(); - else - m_type = functionType->getReturnParameterTypes().front(); - } - else - BOOST_THROW_EXCEPTION(createTypeError("Type is not callable.")); -} - -bool FunctionCall::isTypeConversion() const -{ - return m_expression->getType()->getCategory() == Type::Category::TypeType; -} - -void NewExpression::checkTypeRequirements() -{ - m_contractName->checkTypeRequirements(); - m_contract = dynamic_cast(m_contractName->getReferencedDeclaration()); - if (!m_contract) - BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract.")); - shared_ptr contractType = make_shared(*m_contract); - TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes(); - m_type = make_shared(parameterTypes, TypePointers{contractType}, - FunctionType::Location::Creation); -} - -void MemberAccess::checkTypeRequirements() -{ - m_expression->checkTypeRequirements(); - Type const& type = *m_expression->getType(); - m_type = type.getMemberType(*m_memberName); - if (!m_type) - BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found or not " - "visible in " + type.toString())); - m_isLValue = (type.getCategory() == Type::Category::Struct); -} - -void IndexAccess::checkTypeRequirements() -{ - m_base->checkTypeRequirements(); - if (m_base->getType()->getCategory() != Type::Category::Mapping) - BOOST_THROW_EXCEPTION(m_base->createTypeError("Indexed expression has to be a mapping (is " + - m_base->getType()->toString() + ")")); - MappingType const& type = dynamic_cast(*m_base->getType()); - m_index->expectType(*type.getKeyType()); - m_type = type.getValueType(); - m_isLValue = true; -} - -void Identifier::checkTypeRequirements() -{ - solAssert(m_referencedDeclaration, "Identifier not resolved."); - - m_isLValue = m_referencedDeclaration->isLValue(); - m_type = m_referencedDeclaration->getType(m_currentContract); - if (!m_type) - BOOST_THROW_EXCEPTION(createTypeError("Declaration referenced before type could be determined.")); -} - -void ElementaryTypeNameExpression::checkTypeRequirements() -{ - m_type = make_shared(Type::fromElementaryTypeName(m_typeToken)); -} - -void Literal::checkTypeRequirements() -{ - m_type = Type::forLiteral(*this); - if (!m_type) - BOOST_THROW_EXCEPTION(createTypeError("Invalid literal value.")); -} - -} -} diff --git a/AST.h b/AST.h deleted file mode 100644 index 64dac594d..000000000 --- a/AST.h +++ /dev/null @@ -1,1190 +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 . -*/ -/** - * @author Christian - * @date 2014 - * Solidity abstract syntax tree. - */ - -#pragma once - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace dev -{ -namespace solidity -{ - -class ASTVisitor; -class ASTConstVisitor; - - -/** - * The root (abstract) class of the AST inheritance tree. - * It is possible to traverse all direct and indirect children of an AST node by calling - * accept, providing an ASTVisitor. - */ -class ASTNode: private boost::noncopyable -{ -public: - explicit ASTNode(Location const& _location): m_location(_location) {} - - virtual ~ASTNode() {} - - virtual void accept(ASTVisitor& _visitor) = 0; - virtual void accept(ASTConstVisitor& _visitor) const = 0; - template - static void listAccept(std::vector>& _list, ASTVisitor& _visitor) - { - for (ASTPointer& element: _list) - element->accept(_visitor); - } - template - static void listAccept(std::vector> const& _list, ASTConstVisitor& _visitor) - { - for (ASTPointer const& element: _list) - element->accept(_visitor); - } - - /// Returns the source code location of this node. - Location const& getLocation() const { return m_location; } - - /// Creates a @ref TypeError exception and decorates it with the location of the node and - /// the given description - TypeError createTypeError(std::string const& _description) const; - - ///@{ - ///@name equality operators - /// Equality relies on the fact that nodes cannot be copied. - bool operator==(ASTNode const& _other) const { return this == &_other; } - bool operator!=(ASTNode const& _other) const { return !operator==(_other); } - ///@} - -private: - Location m_location; -}; - -/** - * Source unit containing import directives and contract definitions. - */ -class SourceUnit: public ASTNode -{ -public: - SourceUnit(Location const& _location, std::vector> const& _nodes): - ASTNode(_location), m_nodes(_nodes) {} - - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - - std::vector> getNodes() const { return m_nodes; } - -private: - std::vector> m_nodes; -}; - -/** - * Import directive for referencing other files / source objects. - * Example: import "abc.sol" - * Source objects are identified by a string which can be a file name but does not have to be. - */ -class ImportDirective: public ASTNode -{ -public: - ImportDirective(Location const& _location, ASTPointer const& _identifier): - ASTNode(_location), m_identifier(_identifier) {} - - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - - ASTString const& getIdentifier() const { return *m_identifier; } - -private: - ASTPointer m_identifier; -}; - -/** - * Abstract AST class for a declaration (contract, function, struct, variable). - */ -class Declaration: public ASTNode -{ -public: - /// Visibility ordered from restricted to unrestricted. - enum class Visibility { Default, Private, Inheritable, Public, External }; - - Declaration(Location const& _location, ASTPointer 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 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 const& getLocalVariables() const { return m_localVariables; } - -private: - std::vector m_localVariables; -}; - -/** - * Abstract class that is added to each AST node that can receive documentation. - */ -class Documented -{ -public: - explicit Documented(ASTPointer const& _documentation): m_documentation(_documentation) {} - - /// @return A shared pointer of an ASTString. - /// Can contain a nullptr in which case indicates absence of documentation - ASTPointer const& getDocumentation() const { return m_documentation; } - -protected: - ASTPointer 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, public Documented -{ -public: - ContractDefinition(Location const& _location, - ASTPointer const& _name, - ASTPointer const& _documentation, - std::vector> const& _baseContracts, - std::vector> const& _definedStructs, - std::vector> const& _definedEnums, - std::vector> const& _stateVariables, - std::vector> const& _definedFunctions, - std::vector> const& _functionModifiers, - std::vector> const& _events): - Declaration(_location, _name), Documented(_documentation), - m_baseContracts(_baseContracts), - m_definedStructs(_definedStructs), - m_definedEnums(_definedEnums), - m_stateVariables(_stateVariables), - m_definedFunctions(_definedFunctions), - m_functionModifiers(_functionModifiers), - m_events(_events) - {} - - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - - std::vector> const& getBaseContracts() const { return m_baseContracts; } - std::vector> const& getDefinedStructs() const { return m_definedStructs; } - std::vector> const& getDefinedEnums() const { return m_definedEnums; } - std::vector> const& getStateVariables() const { return m_stateVariables; } - std::vector> const& getFunctionModifiers() const { return m_functionModifiers; } - std::vector> const& getDefinedFunctions() const { return m_definedFunctions; } - std::vector> const& getEvents() const { return m_events; } - std::vector> const& getInterfaceEvents() const; - - virtual TypePointer getType(ContractDefinition const* m_currentContract) const override; - - /// Checks that there are no illegal overrides, that the constructor does not have a "returns" - /// and calls checkTypeRequirements on all its functions. - void checkTypeRequirements(); - - /// @returns a map of canonical function signatures to FunctionDefinitions - /// as intended for use by the ABI. - std::map, FunctionTypePointer> 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 const& getLinearizedBaseContracts() const { return m_linearizedBaseContracts; } - void setLinearizedBaseContracts(std::vector const& _bases) { m_linearizedBaseContracts = _bases; } - - /// 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, FunctionTypePointer>> const& getInterfaceFunctionList() const; - - std::vector> m_baseContracts; - std::vector> m_definedStructs; - std::vector> m_definedEnums; - std::vector> m_stateVariables; - std::vector> m_definedFunctions; - std::vector> m_functionModifiers; - std::vector> m_events; - - std::vector m_linearizedBaseContracts; - mutable std::unique_ptr, FunctionTypePointer>>> m_interfaceFunctionList; - mutable std::unique_ptr>> m_interfaceEvents; -}; - -class InheritanceSpecifier: public ASTNode -{ -public: - InheritanceSpecifier(Location const& _location, ASTPointer const& _baseName, - std::vector> _arguments): - ASTNode(_location), m_baseName(_baseName), m_arguments(_arguments) {} - - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - - ASTPointer const& getName() const { return m_baseName; } - std::vector> const& getArguments() const { return m_arguments; } - - void checkTypeRequirements(); - -private: - ASTPointer m_baseName; - std::vector> m_arguments; -}; - -class StructDefinition: public Declaration -{ -public: - StructDefinition(Location const& _location, - ASTPointer const& _name, - std::vector> const& _members): - Declaration(_location, _name), m_members(_members) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - - std::vector> 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; - -private: - void checkMemberTypes() const; - void checkRecursion() const; - - std::vector> m_members; -}; - -class EnumDefinition: public Declaration -{ -public: - EnumDefinition(Location const& _location, - ASTPointer const& _name, - std::vector> const& _members): - Declaration(_location, _name), m_members(_members) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - - std::vector> const& getMembers() const { return m_members; } - - virtual TypePointer getType(ContractDefinition const*) const override; - -private: - std::vector> m_members; -}; - -/** - * Declaration of an Enum Value - */ -class EnumValue: public Declaration -{ - public: - EnumValue(Location const& _location, - ASTPointer 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 - * inside structs). - */ -class ParameterList: public ASTNode -{ -public: - ParameterList(Location const& _location, - std::vector> const& _parameters): - ASTNode(_location), m_parameters(_parameters) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - - std::vector> const& getParameters() const { return m_parameters; } - -private: - std::vector> m_parameters; -}; - -class FunctionDefinition: public Declaration, public VariableScope, public Documented -{ -public: - FunctionDefinition(Location const& _location, ASTPointer const& _name, - Declaration::Visibility _visibility, bool _isConstructor, - ASTPointer const& _documentation, - ASTPointer const& _parameters, - bool _isDeclaredConst, - std::vector> const& _modifiers, - ASTPointer const& _returnParameters, - ASTPointer const& _body): - Declaration(_location, _name, _visibility), Documented(_documentation), - m_isConstructor(_isConstructor), - m_parameters(_parameters), - m_isDeclaredConst(_isDeclaredConst), - m_functionModifiers(_modifiers), - m_returnParameters(_returnParameters), - m_body(_body) - {} - - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - - bool isConstructor() const { return m_isConstructor; } - bool isDeclaredConst() const { return m_isDeclaredConst; } - std::vector> const& getModifiers() const { return m_functionModifiers; } - std::vector> const& getParameters() const { return m_parameters->getParameters(); } - ParameterList const& getParameterList() const { return *m_parameters; } - std::vector> const& getReturnParameters() const { return m_returnParameters->getParameters(); } - ASTPointer const& getReturnParameterList() const { return m_returnParameters; } - Block const& getBody() const { return *m_body; } - - 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_isConstructor; - ASTPointer m_parameters; - bool m_isDeclaredConst; - std::vector> m_functionModifiers; - ASTPointer m_returnParameters; - ASTPointer m_body; -}; - -/** - * Declaration of a variable. This can be used in various places, e.g. in function parameter - * lists, struct definitions and even function bodys. - */ -class VariableDeclaration: public Declaration -{ -public: - VariableDeclaration(Location const& _location, ASTPointer const& _type, - ASTPointer 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; - - TypeName const* getTypeName() const { return m_typeName.get(); } - - /// 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. - TypePointer getType(ContractDefinition const* = nullptr) const { return m_type; } - void setType(std::shared_ptr const& _type) { m_type = _type; } - - virtual bool isLValue() const override; - bool isLocalVariable() const { return !!dynamic_cast(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 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 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 const& _name, - ASTPointer const& _documentation, - ASTPointer const& _parameters, - ASTPointer 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> 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 m_parameters; - ASTPointer m_body; -}; - -/** - * Invocation/usage of a modifier in a function header. - */ -class ModifierInvocation: public ASTNode -{ -public: - ModifierInvocation(Location const& _location, ASTPointer const& _name, - std::vector> _arguments): - ASTNode(_location), m_modifierName(_name), m_arguments(_arguments) {} - - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - - ASTPointer const& getName() const { return m_modifierName; } - std::vector> const& getArguments() const { return m_arguments; } - - void checkTypeRequirements(); - -private: - ASTPointer m_modifierName; - std::vector> m_arguments; -}; - -/** - * Definition of a (loggable) event. - */ -class EventDefinition: public Declaration, public VariableScope, public Documented -{ -public: - EventDefinition(Location const& _location, - ASTPointer const& _name, - ASTPointer const& _documentation, - ASTPointer 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> 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(*this); - } - - void checkTypeRequirements(); - -private: - ASTPointer 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. - */ -class MagicVariableDeclaration: public Declaration -{ -public: - MagicVariableDeclaration(ASTString const& _name, std::shared_ptr const& _type): - Declaration(Location(), std::make_shared(_name)), m_type(_type) {} - virtual void accept(ASTVisitor&) override { BOOST_THROW_EXCEPTION(InternalCompilerError() - << errinfo_comment("MagicVariableDeclaration used inside real AST.")); } - virtual void accept(ASTConstVisitor&) const override { BOOST_THROW_EXCEPTION(InternalCompilerError() - << errinfo_comment("MagicVariableDeclaration used inside real AST.")); } - - virtual TypePointer getType(ContractDefinition const* = nullptr) const override { return m_type; } - -private: - std::shared_ptr m_type; -}; - -/// Types -/// @{ - -/** - * Abstract base class of a type name, can be any built-in or user-defined type. - */ -class TypeName: public ASTNode -{ -public: - explicit TypeName(Location const& _location): ASTNode(_location) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - - /// Retrieve the element of the type hierarchy this node refers to. Can return an empty shared - /// pointer until the types have been resolved using the @ref NameAndTypeResolver. - /// If it returns an empty shared pointer after that, this indicates that the type was not found. - virtual std::shared_ptr toType() const = 0; -}; - -/** - * Any pre-defined type name represented by a single keyword, i.e. it excludes mappings, - * contracts, functions, etc. - */ -class ElementaryTypeName: public TypeName -{ -public: - explicit ElementaryTypeName(Location const& _location, Token::Value _type): - TypeName(_location), m_type(_type) - { - solAssert(Token::isElementaryTypeName(_type), ""); - } - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - virtual std::shared_ptr toType() const override { return Type::fromElementaryTypeName(m_type); } - - Token::Value getTypeName() const { return m_type; } - -private: - Token::Value m_type; -}; - -/** - * Name referring to a user-defined type (i.e. a struct, contract, etc.). - */ -class UserDefinedTypeName: public TypeName -{ -public: - UserDefinedTypeName(Location const& _location, ASTPointer const& _name): - TypeName(_location), m_name(_name), m_referencedDeclaration(nullptr) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - virtual std::shared_ptr toType() const override { return Type::fromUserDefinedTypeName(*this); } - - ASTString const& getName() const { return *m_name; } - void setReferencedDeclaration(Declaration const& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; } - Declaration const* getReferencedDeclaration() const { return m_referencedDeclaration; } - -private: - ASTPointer m_name; - - Declaration const* m_referencedDeclaration; -}; - -/** - * A mapping type. Its source form is "mapping('keyType' => 'valueType')" - */ -class Mapping: public TypeName -{ -public: - Mapping(Location const& _location, ASTPointer const& _keyType, - ASTPointer const& _valueType): - TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - virtual std::shared_ptr toType() const override { return Type::fromMapping(*this); } - - ElementaryTypeName const& getKeyType() const { return *m_keyType; } - TypeName const& getValueType() const { return *m_valueType; } - -private: - ASTPointer m_keyType; - ASTPointer m_valueType; -}; - -/// @} - -/// Statements -/// @{ - - -/** - * Abstract base class for statements. - */ -class Statement: public ASTNode -{ -public: - explicit Statement(Location const& _location): ASTNode(_location) {} - - /// Check all type requirements, throws exception if some requirement is not met. - /// This includes checking that operators are applicable to their arguments but also that - /// the number of function call arguments matches the number of formal parameters and so forth. - virtual void checkTypeRequirements() = 0; -}; - -/** - * Brace-enclosed block containing zero or more statements. - */ -class Block: public Statement -{ -public: - Block(Location const& _location, std::vector> const& _statements): - Statement(_location), m_statements(_statements) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - - virtual void checkTypeRequirements() override; - -private: - std::vector> m_statements; -}; - -/** - * Special placeholder statement denoted by "_" used in function modifiers. This is replaced by - * the original function when the modifier is applied. - */ -class PlaceholderStatement: public Statement -{ -public: - PlaceholderStatement(Location const& _location): Statement(_location) {} - - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - - virtual void checkTypeRequirements() override { } -}; - -/** - * If-statement with an optional "else" part. Note that "else if" is modeled by having a new - * if-statement as the false (else) body. - */ -class IfStatement: public Statement -{ -public: - IfStatement(Location const& _location, ASTPointer const& _condition, - ASTPointer const& _trueBody, ASTPointer const& _falseBody): - Statement(_location), - m_condition(_condition), m_trueBody(_trueBody), m_falseBody(_falseBody) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; - - 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. - Statement const* getFalseStatement() const { return m_falseBody.get(); } - -private: - ASTPointer m_condition; - ASTPointer m_trueBody; - ASTPointer m_falseBody; ///< "else" part, optional -}; - -/** - * Statement in which a break statement is legal (abstract class). - */ -class BreakableStatement: public Statement -{ -public: - BreakableStatement(Location const& _location): Statement(_location) {} -}; - -class WhileStatement: public BreakableStatement -{ -public: - WhileStatement(Location const& _location, ASTPointer const& _condition, - ASTPointer const& _body): - BreakableStatement(_location), m_condition(_condition), m_body(_body) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; - - Expression const& getCondition() const { return *m_condition; } - Statement const& getBody() const { return *m_body; } - -private: - ASTPointer m_condition; - ASTPointer m_body; -}; - -/** - * For loop statement - */ -class ForStatement: public BreakableStatement -{ -public: - ForStatement(Location const& _location, - ASTPointer const& _initExpression, - ASTPointer const& _conditionExpression, - ASTPointer const& _loopExpression, - ASTPointer const& _body): - BreakableStatement(_location), - m_initExpression(_initExpression), - m_condExpression(_conditionExpression), - m_loopExpression(_loopExpression), - m_body(_body) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; - - Statement const* getInitializationExpression() const { return m_initExpression.get(); } - Expression const* getCondition() const { return m_condExpression.get(); } - ExpressionStatement const* getLoopExpression() const { return m_loopExpression.get(); } - Statement const& getBody() const { return *m_body; } - -private: - /// For statement's initialization expresion. for(XXX; ; ). Can be empty - ASTPointer m_initExpression; - /// For statement's condition expresion. for(; XXX ; ). Can be empty - ASTPointer m_condExpression; - /// For statement's loop expresion. for(;;XXX). Can be empty - ASTPointer m_loopExpression; - /// The body of the loop - ASTPointer m_body; -}; - -class Continue: public Statement -{ -public: - Continue(Location const& _location): Statement(_location) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override {} -}; - -class Break: public Statement -{ -public: - Break(Location const& _location): Statement(_location) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override {} -}; - -class Return: public Statement -{ -public: - Return(Location const& _location, ASTPointer _expression): - Statement(_location), m_expression(_expression), m_returnParameters(nullptr) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; - - 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 m_expression; ///< value to return, optional - - /// Pointer to the parameter list of the function, filled by the @ref NameAndTypeResolver. - ParameterList const* m_returnParameters; -}; - -/** - * Definition of a variable as a statement inside a function. It requires a type name (which can - * also be "var") but the actual assignment can be missing. - * Examples: var a = 2; uint256 a; - */ -class VariableDefinition: public Statement -{ -public: - VariableDefinition(Location const& _location, ASTPointer _variable, - ASTPointer _value): - Statement(_location), m_variable(_variable), m_value(_value) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; - - VariableDeclaration const& getDeclaration() const { return *m_variable; } - Expression const* getExpression() const { return m_value.get(); } - -private: - ASTPointer m_variable; - ASTPointer m_value; ///< the assigned value, can be missing -}; - -/** - * A statement that contains only an expression (i.e. an assignment, function call, ...). - */ -class ExpressionStatement: public Statement -{ -public: - ExpressionStatement(Location const& _location, ASTPointer _expression): - Statement(_location), m_expression(_expression) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; - - Expression const& getExpression() const { return *m_expression; } - -private: - ASTPointer m_expression; -}; - -/// @} - -/// Expressions -/// @{ - -/** - * An expression, i.e. something that has a value (which can also be of type "void" in case - * of some function calls). - * @abstract - */ -class Expression: public ASTNode -{ -public: - Expression(Location const& _location): ASTNode(_location) {} - virtual void checkTypeRequirements() = 0; - - std::shared_ptr const& getType() const { return m_type; } - 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. - void expectType(Type const& _expectedType); - /// Checks that this expression is an lvalue and also registers that an address and - /// not a value is generated during compilation. Can be called after checkTypeRequirements() - /// by an enclosing expression. - void requireLValue(); - /// Returns true if @a requireLValue was previously called on this expression. - bool lvalueRequested() const { return m_lvalueRequested; } - -protected: - //! Inferred type of the expression, only filled after a call to checkTypeRequirements(). - std::shared_ptr m_type; - //! 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 = false; -}; - -/// Assignment, can also be a compound assignment. -/// Examples: (a = 7 + 8) or (a *= 2) -class Assignment: public Expression -{ -public: - Assignment(Location const& _location, ASTPointer const& _leftHandSide, - Token::Value _assignmentOperator, ASTPointer const& _rightHandSide): - Expression(_location), m_leftHandSide(_leftHandSide), - m_assigmentOperator(_assignmentOperator), m_rightHandSide(_rightHandSide) - { - solAssert(Token::isAssignmentOp(_assignmentOperator), ""); - } - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; - - Expression const& getLeftHandSide() const { return *m_leftHandSide; } - Token::Value getAssignmentOperator() const { return m_assigmentOperator; } - Expression const& getRightHandSide() const { return *m_rightHandSide; } - -private: - ASTPointer m_leftHandSide; - Token::Value m_assigmentOperator; - ASTPointer m_rightHandSide; -}; - -/** - * Operation involving a unary operator, pre- or postfix. - * Examples: ++i, delete x or !true - */ -class UnaryOperation: public Expression -{ -public: - UnaryOperation(Location const& _location, Token::Value _operator, - ASTPointer const& _subExpression, bool _isPrefix): - Expression(_location), m_operator(_operator), - m_subExpression(_subExpression), m_isPrefix(_isPrefix) - { - solAssert(Token::isUnaryOp(_operator), ""); - } - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; - - Token::Value getOperator() const { return m_operator; } - bool isPrefixOperation() const { return m_isPrefix; } - Expression const& getSubExpression() const { return *m_subExpression; } - -private: - Token::Value m_operator; - ASTPointer m_subExpression; - bool m_isPrefix; -}; - -/** - * Operation involving a binary operator. - * Examples: 1 + 2, true && false or 1 <= 4 - */ -class BinaryOperation: public Expression -{ -public: - BinaryOperation(Location const& _location, ASTPointer const& _left, - Token::Value _operator, ASTPointer const& _right): - Expression(_location), m_left(_left), m_operator(_operator), m_right(_right) - { - solAssert(Token::isBinaryOp(_operator) || Token::isCompareOp(_operator), ""); - } - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; - - Expression const& getLeftExpression() const { return *m_left; } - Expression const& getRightExpression() const { return *m_right; } - Token::Value getOperator() const { return m_operator; } - Type const& getCommonType() const { return *m_commonType; } - -private: - ASTPointer m_left; - Token::Value m_operator; - ASTPointer m_right; - - /// The common type that is used for the operation, not necessarily the result type (e.g. for - /// comparisons, this is always bool). - std::shared_ptr m_commonType; -}; - -/** - * Can be ordinary function call, type cast or struct construction. - */ -class FunctionCall: public Expression -{ -public: - FunctionCall(Location const& _location, ASTPointer const& _expression, - std::vector> const& _arguments, std::vector> 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> getArguments() const { return {m_arguments.begin(), m_arguments.end()}; } - std::vector> const& getNames() const { return m_names; } - - /// Returns true if this is not an actual function call, but an explicit type conversion - /// or constructor call. - bool isTypeConversion() const; - -private: - ASTPointer m_expression; - std::vector> m_arguments; - std::vector> m_names; -}; - -/** - * 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 const& _contractName): - Expression(_location), m_contractName(_contractName) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; - - /// Returns the referenced contract. Can only be called after type checking. - ContractDefinition const* getContract() const { solAssert(m_contract, ""); return m_contract; } - -private: - ASTPointer m_contractName; - - ContractDefinition const* m_contract = nullptr; -}; - -/** - * Access to a member of an object. Example: x.name - */ -class MemberAccess: public Expression -{ -public: - MemberAccess(Location const& _location, ASTPointer _expression, - ASTPointer const& _memberName): - Expression(_location), m_expression(_expression), m_memberName(_memberName) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - Expression const& getExpression() const { return *m_expression; } - ASTString const& getMemberName() const { return *m_memberName; } - virtual void checkTypeRequirements() override; - -private: - ASTPointer m_expression; - ASTPointer m_memberName; -}; - -/** - * Index access to an array. Example: a[2] - */ -class IndexAccess: public Expression -{ -public: - IndexAccess(Location const& _location, ASTPointer const& _base, - ASTPointer const& _index): - Expression(_location), m_base(_base), m_index(_index) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; - - Expression const& getBaseExpression() const { return *m_base; } - Expression const& getIndexExpression() const { return *m_index; } - -private: - ASTPointer m_base; - ASTPointer m_index; -}; - -/** - * Primary expression, i.e. an expression that cannot be divided any further. Examples are literals - * or variable references. - */ -class PrimaryExpression: public Expression -{ -public: - PrimaryExpression(Location const& _location): Expression(_location) {} -}; - -/** - * An identifier, i.e. a reference to a declaration by name like a variable or function. - */ -class Identifier: public PrimaryExpression -{ -public: - Identifier(Location const& _location, ASTPointer const& _name): - 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, - 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 m_name; - - /// Declaration the name refers to. - 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; -}; - -/** - * An elementary type name expression is used in expressions like "a = uint32(2)" to change the - * type of an expression explicitly. Here, "uint32" is the elementary type name expression and - * "uint32(2)" is a @ref FunctionCall. - */ -class ElementaryTypeNameExpression: public PrimaryExpression -{ -public: - ElementaryTypeNameExpression(Location const& _location, Token::Value _typeToken): - PrimaryExpression(_location), m_typeToken(_typeToken) - { - solAssert(Token::isElementaryTypeName(_typeToken), ""); - } - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; - - Token::Value getTypeToken() const { return m_typeToken; } - -private: - Token::Value m_typeToken; -}; - -/** - * A literal string or number. @see ExpressionCompiler::endVisit() is used to actually parse its value. - */ -class Literal: public PrimaryExpression -{ -public: - 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 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; - - Token::Value getToken() const { return m_token; } - /// @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 m_value; - SubDenomination m_subDenomination; -}; - -/// @} - - -} -} diff --git a/ASTForward.h b/ASTForward.h deleted file mode 100644 index 0b6817e45..000000000 --- a/ASTForward.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @date 2014 - * Forward-declarations of AST classes. - */ - -#pragma once - -#include -#include -#include - -// Forward-declare all AST node types - -namespace dev -{ -namespace solidity -{ - -class ASTNode; -class SourceUnit; -class ImportDirective; -class Declaration; -class ContractDefinition; -class InheritanceSpecifier; -class StructDefinition; -class EnumDefinition; -class EnumValue; -class ParameterList; -class FunctionDefinition; -class VariableDeclaration; -class ModifierDefinition; -class ModifierInvocation; -class EventDefinition; -class MagicVariableDeclaration; -class TypeName; -class ElementaryTypeName; -class UserDefinedTypeName; -class Mapping; -class Statement; -class Block; -class PlaceholderStatement; -class IfStatement; -class BreakableStatement; -class WhileStatement; -class ForStatement; -class Continue; -class Break; -class Return; -class VariableDefinition; -class ExpressionStatement; -class Expression; -class Assignment; -class UnaryOperation; -class BinaryOperation; -class FunctionCall; -class NewExpression; -class MemberAccess; -class IndexAccess; -class PrimaryExpression; -class Identifier; -class ElementaryTypeNameExpression; -class Literal; - -class VariableScope; - -// Used as pointers to AST nodes, to be replaced by more clever pointers, e.g. pointers which do -// not do reference counting but point to a special memory area that is completely released -// explicitly. -template -using ASTPointer = std::shared_ptr; - -using ASTString = std::string; - - -} -} diff --git a/ASTJsonConverter.cpp b/ASTJsonConverter.cpp deleted file mode 100644 index 04feafe2f..000000000 --- a/ASTJsonConverter.cpp +++ /dev/null @@ -1,469 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Lefteris - * @date 2015 - * Converts the AST into json format - */ - -#include -#include - -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> _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(_node.isPublic())), - make_pair("const", boost::lexical_cast(_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(_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(_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(_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 deleted file mode 100644 index 466801e9c..000000000 --- a/ASTJsonConverter.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Lefteris - * @date 2015 - * Converts the AST into json format - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -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> _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 m_jsonNodePtrs; - std::string m_source; - ASTNode const* m_ast; -}; - -} -} diff --git a/ASTPrinter.cpp b/ASTPrinter.cpp deleted file mode 100644 index d380b0029..000000000 --- a/ASTPrinter.cpp +++ /dev/null @@ -1,571 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @date 2014 - * Pretty-printer for the abstract syntax tree (the "pretty" is arguable), used for debugging. - */ - -#include -#include - -using namespace std; - -namespace dev -{ -namespace solidity -{ - -ASTPrinter::ASTPrinter(ASTNode const& _ast, string const& _source): - m_indentation(0), m_source(_source), m_ast(&_ast) -{ -} - -void ASTPrinter::print(ostream& _stream) -{ - m_ostream = &_stream; - m_ast->accept(*this); - m_ostream = nullptr; -} - - -bool ASTPrinter::visit(ImportDirective const& _node) -{ - writeLine("ImportDirective \"" + _node.getIdentifier() + "\""); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(ContractDefinition const& _node) -{ - writeLine("ContractDefinition \"" + _node.getName() + "\""); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(InheritanceSpecifier const& _node) -{ - writeLine("InheritanceSpecifier \"" + _node.getName()->getName() + "\""); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(StructDefinition const& _node) -{ - writeLine("StructDefinition \"" + _node.getName() + "\""); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(EnumDefinition const& _node) -{ - writeLine("EnumDefinition \"" + _node.getName() + "\""); - return goDeeper(); -} - -bool ASTPrinter::visit(EnumValue const& _node) -{ - writeLine("EnumValue \"" + _node.getName() + "\""); - return goDeeper(); -} - -bool ASTPrinter::visit(ParameterList const& _node) -{ - writeLine("ParameterList"); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(FunctionDefinition const& _node) -{ - writeLine("FunctionDefinition \"" + _node.getName() + "\"" + - (_node.isPublic() ? " - public" : "") + - (_node.isDeclaredConst() ? " - const" : "")); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(VariableDeclaration const& _node) -{ - writeLine("VariableDeclaration \"" + _node.getName() + "\""); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(ModifierDefinition const& _node) -{ - writeLine("ModifierDefinition \"" + _node.getName() + "\""); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(ModifierInvocation const& _node) -{ - writeLine("ModifierInvocation \"" + _node.getName()->getName() + "\""); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(EventDefinition const& _node) -{ - writeLine("EventDefinition \"" + _node.getName() + "\""); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(TypeName const& _node) -{ - writeLine("TypeName"); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(ElementaryTypeName const& _node) -{ - writeLine(string("ElementaryTypeName ") + Token::toString(_node.getTypeName())); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(UserDefinedTypeName const& _node) -{ - writeLine("UserDefinedTypeName \"" + _node.getName() + "\""); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(Mapping const& _node) -{ - writeLine("Mapping"); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(Statement const& _node) -{ - writeLine("Statement"); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(Block const& _node) -{ - writeLine("Block"); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(PlaceholderStatement const& _node) -{ - writeLine("PlaceholderStatement"); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(IfStatement const& _node) -{ - writeLine("IfStatement"); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(BreakableStatement const& _node) -{ - writeLine("BreakableStatement"); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(WhileStatement const& _node) -{ - writeLine("WhileStatement"); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(ForStatement const& _node) -{ - writeLine("ForStatement"); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(Continue const& _node) -{ - writeLine("Continue"); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(Break const& _node) -{ - writeLine("Break"); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(Return const& _node) -{ - writeLine("Return"); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(VariableDefinition const& _node) -{ - writeLine("VariableDefinition"); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(ExpressionStatement const& _node) -{ - writeLine("ExpressionStatement"); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(Expression const& _node) -{ - writeLine("Expression"); - printType(_node); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(Assignment const& _node) -{ - writeLine(string("Assignment using operator ") + Token::toString(_node.getAssignmentOperator())); - printType(_node); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(UnaryOperation const& _node) -{ - writeLine(string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") + - ") " + Token::toString(_node.getOperator())); - printType(_node); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(BinaryOperation const& _node) -{ - writeLine(string("BinaryOperation using operator ") + Token::toString(_node.getOperator())); - printType(_node); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(FunctionCall const& _node) -{ - writeLine("FunctionCall"); - printType(_node); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(NewExpression const& _node) -{ - writeLine("NewExpression"); - printType(_node); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(MemberAccess const& _node) -{ - writeLine("MemberAccess to member " + _node.getMemberName()); - printType(_node); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(IndexAccess const& _node) -{ - writeLine("IndexAccess"); - printType(_node); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(PrimaryExpression const& _node) -{ - writeLine("PrimaryExpression"); - printType(_node); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(Identifier const& _node) -{ - writeLine(string("Identifier ") + _node.getName()); - printType(_node); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(ElementaryTypeNameExpression const& _node) -{ - writeLine(string("ElementaryTypeNameExpression ") + Token::toString(_node.getTypeToken())); - printType(_node); - printSourcePart(_node); - return goDeeper(); -} - -bool ASTPrinter::visit(Literal const& _node) -{ - char const* tokenString = Token::toString(_node.getToken()); - if (!tokenString) - tokenString = "[no token]"; - writeLine(string("Literal, token: ") + tokenString + " value: " + _node.getValue()); - printType(_node); - printSourcePart(_node); - return goDeeper(); -} - -void ASTPrinter::endVisit(ImportDirective const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(ContractDefinition const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(InheritanceSpecifier const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(StructDefinition const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(EnumDefinition const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(EnumValue const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(ParameterList const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(FunctionDefinition const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(VariableDeclaration const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(ModifierDefinition const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(ModifierInvocation const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(EventDefinition const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(TypeName const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(ElementaryTypeName const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(UserDefinedTypeName const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(Mapping const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(Statement const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(Block const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(PlaceholderStatement const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(IfStatement const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(BreakableStatement const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(WhileStatement const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(ForStatement const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(Continue const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(Break const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(Return const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(VariableDefinition const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(ExpressionStatement const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(Expression const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(Assignment const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(UnaryOperation const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(BinaryOperation const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(FunctionCall const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(NewExpression const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(MemberAccess const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(IndexAccess const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(PrimaryExpression const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(Identifier const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(ElementaryTypeNameExpression const&) -{ - m_indentation--; -} - -void ASTPrinter::endVisit(Literal const&) -{ - m_indentation--; -} - -void ASTPrinter::printSourcePart(ASTNode const& _node) -{ - if (!m_source.empty()) - { - Location const& location(_node.getLocation()); - *m_ostream << getIndentation() << " Source: " - << escaped(m_source.substr(location.start, location.end - location.start), false) << endl; - } -} - -void ASTPrinter::printType(Expression const& _expression) -{ - if (_expression.getType()) - *m_ostream << getIndentation() << " Type: " << _expression.getType()->toString() << "\n"; - else - *m_ostream << getIndentation() << " Type unknown.\n"; -} - -string ASTPrinter::getIndentation() const -{ - return string(m_indentation * 2, ' '); -} - -void ASTPrinter::writeLine(string const& _line) -{ - *m_ostream << getIndentation() << _line << endl; -} - -} -} diff --git a/ASTPrinter.h b/ASTPrinter.h deleted file mode 100644 index d9072aacc..000000000 --- a/ASTPrinter.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @date 2014 - * Pretty-printer for the abstract syntax tree (the "pretty" is arguable), used for debugging. - */ - -#pragma once - -#include -#include - -namespace dev -{ -namespace solidity -{ - -/** - * Pretty-printer for the abstract syntax tree (the "pretty" is arguable) for debugging purposes. - */ -class ASTPrinter: public ASTConstVisitor -{ -public: - /// Create a printer for the given abstract syntax tree. If the source is specified, - /// the corresponding parts of the source are printed with each node. - ASTPrinter(ASTNode const& _ast, std::string const& _source = std::string()); - /// Output the string representation of the AST to _stream. - void print(std::ostream& _stream); - - bool visit(ImportDirective const& _node) override; - bool visit(ContractDefinition const& _node) override; - bool visit(InheritanceSpecifier const& _node) override; - bool visit(StructDefinition const& _node) override; - bool visit(EnumDefinition const& _node) override; - bool visit(EnumValue const& _node) override; - bool visit(ParameterList const& _node) override; - bool visit(FunctionDefinition const& _node) override; - bool visit(VariableDeclaration const& _node) override; - bool visit(ModifierDefinition const& _node) override; - bool visit(ModifierInvocation const& _node) override; - bool visit(EventDefinition const& _node) override; - bool visit(TypeName const& _node) override; - bool visit(ElementaryTypeName const& _node) override; - bool visit(UserDefinedTypeName const& _node) override; - bool visit(Mapping const& _node) override; - bool visit(Statement const& _node) override; - bool visit(Block const& _node) override; - bool visit(PlaceholderStatement const& _node) override; - bool visit(IfStatement const& _node) override; - bool visit(BreakableStatement const& _node) override; - bool visit(WhileStatement const& _node) override; - bool visit(ForStatement const& _node) override; - bool visit(Continue const& _node) override; - bool visit(Break const& _node) override; - bool visit(Return const& _node) override; - bool visit(VariableDefinition const& _node) override; - bool visit(ExpressionStatement const& _node) override; - bool visit(Expression const& _node) override; - bool visit(Assignment const& _node) override; - bool visit(UnaryOperation const& _node) override; - bool visit(BinaryOperation const& _node) override; - bool visit(FunctionCall const& _node) override; - bool visit(NewExpression const& _node) override; - bool visit(MemberAccess const& _node) override; - bool visit(IndexAccess const& _node) override; - bool visit(PrimaryExpression const& _node) override; - bool visit(Identifier const& _node) override; - bool visit(ElementaryTypeNameExpression const& _node) override; - bool visit(Literal const& _node) override; - - void endVisit(ImportDirective const&) override; - void endVisit(ContractDefinition const&) override; - void endVisit(InheritanceSpecifier const&) override; - void endVisit(StructDefinition const&) override; - void endVisit(EnumDefinition const&) override; - void endVisit(EnumValue const&) override; - void endVisit(ParameterList const&) override; - void endVisit(FunctionDefinition const&) override; - void endVisit(VariableDeclaration const&) override; - void endVisit(ModifierDefinition const&) override; - void endVisit(ModifierInvocation const&) override; - void endVisit(EventDefinition const&) override; - void endVisit(TypeName const&) override; - void endVisit(ElementaryTypeName const&) override; - void endVisit(UserDefinedTypeName const&) override; - void endVisit(Mapping const&) override; - void endVisit(Statement const&) override; - void endVisit(Block const&) override; - void endVisit(PlaceholderStatement const&) override; - void endVisit(IfStatement const&) override; - void endVisit(BreakableStatement const&) override; - void endVisit(WhileStatement const&) override; - void endVisit(ForStatement const&) override; - void endVisit(Continue const&) override; - void endVisit(Break const&) override; - void endVisit(Return const&) override; - void endVisit(VariableDefinition const&) override; - void endVisit(ExpressionStatement const&) override; - void endVisit(Expression const&) override; - void endVisit(Assignment const&) override; - void endVisit(UnaryOperation const&) override; - void endVisit(BinaryOperation const&) override; - void endVisit(FunctionCall const&) override; - void endVisit(NewExpression const&) override; - void endVisit(MemberAccess const&) override; - void endVisit(IndexAccess const&) override; - void endVisit(PrimaryExpression const&) override; - void endVisit(Identifier const&) override; - void endVisit(ElementaryTypeNameExpression const&) override; - void endVisit(Literal const&) override; - -private: - void printSourcePart(ASTNode const& _node); - void printType(Expression const& _expression); - std::string getIndentation() const; - void writeLine(std::string const& _line); - bool goDeeper() { m_indentation++; return true; } - - int m_indentation; - std::string m_source; - ASTNode const* m_ast; - std::ostream* m_ostream; -}; - -} -} diff --git a/ASTVisitor.h b/ASTVisitor.h deleted file mode 100644 index a7fa6b1cf..000000000 --- a/ASTVisitor.h +++ /dev/null @@ -1,222 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @date 2014 - * AST visitor base class. - */ - -#pragma once - -#include -#include - -namespace dev -{ -namespace solidity -{ - -/** - * Visitor interface for the abstract syntax tree. This class is tightly bound to the - * implementation of @ref ASTNode::accept and its overrides. After a call to - * @ref ASTNode::accept, the function visit for the appropriate parameter is called and then - * (if it returns true) this continues recursively for all child nodes in document order - * (there is an exception for contracts). After all child nodes have been visited, endVisit is - * called for the node. - */ -class ASTVisitor -{ -public: - virtual bool visit(ASTNode&) { return true; } - virtual bool visit(SourceUnit&) { return true; } - virtual bool visit(ImportDirective&) { return true; } - virtual bool visit(ContractDefinition&) { return true; } - virtual bool visit(InheritanceSpecifier&) { return true; } - virtual bool visit(StructDefinition&) { return true; } - virtual bool visit(EnumDefinition&) { return true; } - virtual bool visit(EnumValue&) { return true; } - virtual bool visit(ParameterList&) { return true; } - virtual bool visit(FunctionDefinition&) { return true; } - virtual bool visit(VariableDeclaration&) { return true; } - virtual bool visit(ModifierDefinition&) { return true; } - virtual bool visit(ModifierInvocation&) { return true; } - virtual bool visit(EventDefinition&) { return true; } - virtual bool visit(TypeName&) { return true; } - virtual bool visit(ElementaryTypeName&) { return true; } - virtual bool visit(UserDefinedTypeName&) { return true; } - virtual bool visit(Mapping&) { return true; } - virtual bool visit(Statement&) { return true; } - virtual bool visit(Block&) { return true; } - virtual bool visit(PlaceholderStatement&) { return true; } - virtual bool visit(IfStatement&) { return true; } - virtual bool visit(BreakableStatement&) { return true; } - virtual bool visit(WhileStatement&) { return true; } - virtual bool visit(ForStatement&) { return true; } - virtual bool visit(Continue&) { return true; } - virtual bool visit(Break&) { return true; } - virtual bool visit(Return&) { return true; } - virtual bool visit(VariableDefinition&) { return true; } - virtual bool visit(ExpressionStatement&) { return true; } - virtual bool visit(Expression&) { return true; } - virtual bool visit(Assignment&) { return true; } - virtual bool visit(UnaryOperation&) { return true; } - virtual bool visit(BinaryOperation&) { return true; } - virtual bool visit(FunctionCall&) { return true; } - virtual bool visit(NewExpression&) { return true; } - virtual bool visit(MemberAccess&) { return true; } - virtual bool visit(IndexAccess&) { return true; } - virtual bool visit(PrimaryExpression&) { return true; } - virtual bool visit(Identifier&) { return true; } - virtual bool visit(ElementaryTypeNameExpression&) { return true; } - virtual bool visit(Literal&) { return true; } - - virtual void endVisit(ASTNode&) { } - virtual void endVisit(SourceUnit&) { } - virtual void endVisit(ImportDirective&) { } - virtual void endVisit(ContractDefinition&) { } - virtual void endVisit(InheritanceSpecifier&) { } - virtual void endVisit(StructDefinition&) { } - virtual void endVisit(EnumDefinition&) { } - virtual void endVisit(EnumValue&) { } - virtual void endVisit(ParameterList&) { } - virtual void endVisit(FunctionDefinition&) { } - virtual void endVisit(VariableDeclaration&) { } - virtual void endVisit(ModifierDefinition&) { } - virtual void endVisit(ModifierInvocation&) { } - virtual void endVisit(EventDefinition&) { } - virtual void endVisit(TypeName&) { } - virtual void endVisit(ElementaryTypeName&) { } - virtual void endVisit(UserDefinedTypeName&) { } - virtual void endVisit(Mapping&) { } - virtual void endVisit(Statement&) { } - virtual void endVisit(Block&) { } - virtual void endVisit(PlaceholderStatement&) { } - virtual void endVisit(IfStatement&) { } - virtual void endVisit(BreakableStatement&) { } - virtual void endVisit(WhileStatement&) { } - virtual void endVisit(ForStatement&) { } - virtual void endVisit(Continue&) { } - virtual void endVisit(Break&) { } - virtual void endVisit(Return&) { } - virtual void endVisit(VariableDefinition&) { } - virtual void endVisit(ExpressionStatement&) { } - virtual void endVisit(Expression&) { } - virtual void endVisit(Assignment&) { } - virtual void endVisit(UnaryOperation&) { } - virtual void endVisit(BinaryOperation&) { } - virtual void endVisit(FunctionCall&) { } - virtual void endVisit(NewExpression&) { } - virtual void endVisit(MemberAccess&) { } - virtual void endVisit(IndexAccess&) { } - virtual void endVisit(PrimaryExpression&) { } - virtual void endVisit(Identifier&) { } - virtual void endVisit(ElementaryTypeNameExpression&) { } - virtual void endVisit(Literal&) { } -}; - -class ASTConstVisitor -{ -public: - virtual bool visit(ASTNode const&) { return true; } - virtual bool visit(SourceUnit const&) { return true; } - virtual bool visit(ImportDirective const&) { return true; } - virtual bool visit(ContractDefinition const&) { return true; } - virtual bool visit(InheritanceSpecifier const&) { return true; } - virtual bool visit(StructDefinition const&) { return true; } - virtual bool visit(EnumDefinition const&) { return true; } - virtual bool visit(EnumValue const&) { return true; } - virtual bool visit(ParameterList const&) { return true; } - virtual bool visit(FunctionDefinition const&) { return true; } - virtual bool visit(VariableDeclaration const&) { return true; } - virtual bool visit(ModifierDefinition const&) { return true; } - virtual bool visit(ModifierInvocation const&) { return true; } - virtual bool visit(EventDefinition const&) { return true; } - virtual bool visit(TypeName const&) { return true; } - virtual bool visit(ElementaryTypeName const&) { return true; } - virtual bool visit(UserDefinedTypeName const&) { return true; } - virtual bool visit(Mapping const&) { return true; } - virtual bool visit(Statement const&) { return true; } - virtual bool visit(Block const&) { return true; } - virtual bool visit(PlaceholderStatement const&) { return true; } - virtual bool visit(IfStatement const&) { return true; } - virtual bool visit(BreakableStatement const&) { return true; } - virtual bool visit(WhileStatement const&) { return true; } - virtual bool visit(ForStatement const&) { return true; } - virtual bool visit(Continue const&) { return true; } - virtual bool visit(Break const&) { return true; } - virtual bool visit(Return const&) { return true; } - virtual bool visit(VariableDefinition const&) { return true; } - virtual bool visit(ExpressionStatement const&) { return true; } - virtual bool visit(Expression const&) { return true; } - virtual bool visit(Assignment const&) { return true; } - virtual bool visit(UnaryOperation const&) { return true; } - virtual bool visit(BinaryOperation const&) { return true; } - virtual bool visit(FunctionCall const&) { return true; } - virtual bool visit(NewExpression const&) { return true; } - virtual bool visit(MemberAccess const&) { return true; } - virtual bool visit(IndexAccess const&) { return true; } - virtual bool visit(PrimaryExpression const&) { return true; } - virtual bool visit(Identifier const&) { return true; } - virtual bool visit(ElementaryTypeNameExpression const&) { return true; } - virtual bool visit(Literal const&) { return true; } - - virtual void endVisit(ASTNode const&) { } - virtual void endVisit(SourceUnit const&) { } - virtual void endVisit(ImportDirective const&) { } - virtual void endVisit(ContractDefinition const&) { } - virtual void endVisit(InheritanceSpecifier const&) { } - virtual void endVisit(StructDefinition const&) { } - virtual void endVisit(EnumDefinition const&) { } - virtual void endVisit(EnumValue const&) { } - virtual void endVisit(ParameterList const&) { } - virtual void endVisit(FunctionDefinition const&) { } - virtual void endVisit(VariableDeclaration const&) { } - virtual void endVisit(ModifierDefinition const&) { } - virtual void endVisit(ModifierInvocation const&) { } - virtual void endVisit(EventDefinition const&) { } - virtual void endVisit(TypeName const&) { } - virtual void endVisit(ElementaryTypeName const&) { } - virtual void endVisit(UserDefinedTypeName const&) { } - virtual void endVisit(Mapping const&) { } - virtual void endVisit(Statement const&) { } - virtual void endVisit(Block const&) { } - virtual void endVisit(PlaceholderStatement const&) { } - virtual void endVisit(IfStatement const&) { } - virtual void endVisit(BreakableStatement const&) { } - virtual void endVisit(WhileStatement const&) { } - virtual void endVisit(ForStatement const&) { } - virtual void endVisit(Continue const&) { } - virtual void endVisit(Break const&) { } - virtual void endVisit(Return const&) { } - virtual void endVisit(VariableDefinition const&) { } - virtual void endVisit(ExpressionStatement const&) { } - virtual void endVisit(Expression const&) { } - virtual void endVisit(Assignment const&) { } - virtual void endVisit(UnaryOperation const&) { } - virtual void endVisit(BinaryOperation const&) { } - virtual void endVisit(FunctionCall const&) { } - virtual void endVisit(NewExpression const&) { } - virtual void endVisit(MemberAccess const&) { } - virtual void endVisit(IndexAccess const&) { } - virtual void endVisit(PrimaryExpression const&) { } - virtual void endVisit(Identifier const&) { } - virtual void endVisit(ElementaryTypeNameExpression const&) { } - virtual void endVisit(Literal const&) { } -}; - -} -} diff --git a/AST_accept.h b/AST_accept.h deleted file mode 100644 index b71e103df..000000000 --- a/AST_accept.h +++ /dev/null @@ -1,659 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @date 2014 - * Implementation of the accept functions of AST nodes, included by AST.cpp to not clutter that - * file with these mechanical implementations. - */ - -#pragma once - -#include -#include - -namespace dev -{ -namespace solidity -{ - -void SourceUnit::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - listAccept(m_nodes, _visitor); - _visitor.endVisit(*this); -} - -void SourceUnit::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - listAccept(m_nodes, _visitor); - _visitor.endVisit(*this); -} - -void ImportDirective::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void ImportDirective::accept(ASTConstVisitor& _visitor) const -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void ContractDefinition::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - listAccept(m_baseContracts, _visitor); - listAccept(m_definedStructs, _visitor); - listAccept(m_definedEnums, _visitor); - listAccept(m_stateVariables, _visitor); - listAccept(m_events, _visitor); - listAccept(m_functionModifiers, _visitor); - listAccept(m_definedFunctions, _visitor); - } - _visitor.endVisit(*this); -} - -void ContractDefinition::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - listAccept(m_baseContracts, _visitor); - listAccept(m_definedStructs, _visitor); - listAccept(m_definedEnums, _visitor); - listAccept(m_stateVariables, _visitor); - listAccept(m_events, _visitor); - listAccept(m_functionModifiers, _visitor); - listAccept(m_definedFunctions, _visitor); - } - _visitor.endVisit(*this); -} - -void InheritanceSpecifier::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_baseName->accept(_visitor); - listAccept(m_arguments, _visitor); - } - _visitor.endVisit(*this); -} - -void InheritanceSpecifier::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_baseName->accept(_visitor); - listAccept(m_arguments, _visitor); - } - _visitor.endVisit(*this); -} - -void EnumDefinition::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - listAccept(m_members, _visitor); - _visitor.endVisit(*this); -} - -void EnumDefinition::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - listAccept(m_members, _visitor); - _visitor.endVisit(*this); -} - -void EnumValue::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void EnumValue::accept(ASTConstVisitor& _visitor) const -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void StructDefinition::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - listAccept(m_members, _visitor); - _visitor.endVisit(*this); -} - -void StructDefinition::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - listAccept(m_members, _visitor); - _visitor.endVisit(*this); -} - -void StructDefinition::checkValidityOfMembers() const -{ - checkMemberTypes(); - checkRecursion(); -} - -void ParameterList::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - listAccept(m_parameters, _visitor); - _visitor.endVisit(*this); -} - -void ParameterList::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - listAccept(m_parameters, _visitor); - _visitor.endVisit(*this); -} - -void FunctionDefinition::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_parameters->accept(_visitor); - if (m_returnParameters) - m_returnParameters->accept(_visitor); - listAccept(m_functionModifiers, _visitor); - m_body->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void FunctionDefinition::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_parameters->accept(_visitor); - if (m_returnParameters) - m_returnParameters->accept(_visitor); - listAccept(m_functionModifiers, _visitor); - m_body->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void VariableDeclaration::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - if (m_typeName) - m_typeName->accept(_visitor); - _visitor.endVisit(*this); -} - -void VariableDeclaration::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - if (m_typeName) - m_typeName->accept(_visitor); - _visitor.endVisit(*this); -} - -void ModifierDefinition::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_parameters->accept(_visitor); - m_body->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void ModifierDefinition::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_parameters->accept(_visitor); - m_body->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void ModifierInvocation::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_modifierName->accept(_visitor); - listAccept(m_arguments, _visitor); - } - _visitor.endVisit(*this); -} - -void ModifierInvocation::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_modifierName->accept(_visitor); - listAccept(m_arguments, _visitor); - } - _visitor.endVisit(*this); -} - -void EventDefinition::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - m_parameters->accept(_visitor); - _visitor.endVisit(*this); -} - -void EventDefinition::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - m_parameters->accept(_visitor); - _visitor.endVisit(*this); -} - -void TypeName::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void TypeName::accept(ASTConstVisitor& _visitor) const -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void ElementaryTypeName::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void ElementaryTypeName::accept(ASTConstVisitor& _visitor) const -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void UserDefinedTypeName::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void UserDefinedTypeName::accept(ASTConstVisitor& _visitor) const -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void Mapping::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_keyType->accept(_visitor); - m_valueType->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void Mapping::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_keyType->accept(_visitor); - m_valueType->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void Block::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - listAccept(m_statements, _visitor); - _visitor.endVisit(*this); -} - -void Block::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - listAccept(m_statements, _visitor); - _visitor.endVisit(*this); -} - -void PlaceholderStatement::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void PlaceholderStatement::accept(ASTConstVisitor& _visitor) const -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void IfStatement::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_condition->accept(_visitor); - m_trueBody->accept(_visitor); - if (m_falseBody) - m_falseBody->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void IfStatement::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_condition->accept(_visitor); - m_trueBody->accept(_visitor); - if (m_falseBody) - m_falseBody->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void WhileStatement::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_condition->accept(_visitor); - m_body->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void WhileStatement::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_condition->accept(_visitor); - m_body->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void ForStatement::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - if (m_initExpression) - m_initExpression->accept(_visitor); - if (m_condExpression) - m_condExpression->accept(_visitor); - if (m_loopExpression) - m_loopExpression->accept(_visitor); - m_body->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void ForStatement::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - if (m_initExpression) - m_initExpression->accept(_visitor); - if (m_condExpression) - m_condExpression->accept(_visitor); - if (m_loopExpression) - m_loopExpression->accept(_visitor); - m_body->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void Continue::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void Continue::accept(ASTConstVisitor& _visitor) const -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void Break::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void Break::accept(ASTConstVisitor& _visitor) const -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void Return::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - if (m_expression) - m_expression->accept(_visitor); - _visitor.endVisit(*this); -} - -void Return::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - if (m_expression) - m_expression->accept(_visitor); - _visitor.endVisit(*this); -} - -void ExpressionStatement::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - if (m_expression) - m_expression->accept(_visitor); - _visitor.endVisit(*this); -} - -void ExpressionStatement::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - if (m_expression) - m_expression->accept(_visitor); - _visitor.endVisit(*this); -} - -void VariableDefinition::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_variable->accept(_visitor); - if (m_value) - m_value->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void VariableDefinition::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_variable->accept(_visitor); - if (m_value) - m_value->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void Assignment::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_leftHandSide->accept(_visitor); - m_rightHandSide->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void Assignment::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_leftHandSide->accept(_visitor); - m_rightHandSide->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void UnaryOperation::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - m_subExpression->accept(_visitor); - _visitor.endVisit(*this); -} - -void UnaryOperation::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - m_subExpression->accept(_visitor); - _visitor.endVisit(*this); -} - -void BinaryOperation::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_left->accept(_visitor); - m_right->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void BinaryOperation::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_left->accept(_visitor); - m_right->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void FunctionCall::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_expression->accept(_visitor); - listAccept(m_arguments, _visitor); - } - _visitor.endVisit(*this); -} - -void FunctionCall::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_expression->accept(_visitor); - listAccept(m_arguments, _visitor); - } - _visitor.endVisit(*this); -} - -void NewExpression::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - m_contractName->accept(_visitor); - _visitor.endVisit(*this); -} - -void NewExpression::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - m_contractName->accept(_visitor); - _visitor.endVisit(*this); -} - -void MemberAccess::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - m_expression->accept(_visitor); - _visitor.endVisit(*this); -} - -void MemberAccess::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - m_expression->accept(_visitor); - _visitor.endVisit(*this); -} - -void IndexAccess::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_base->accept(_visitor); - m_index->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void IndexAccess::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_base->accept(_visitor); - m_index->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void Identifier::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void Identifier::accept(ASTConstVisitor& _visitor) const -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void ElementaryTypeNameExpression::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void ElementaryTypeNameExpression::accept(ASTConstVisitor& _visitor) const -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void Literal::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void Literal::accept(ASTConstVisitor& _visitor) const -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -} -} diff --git a/BaseTypes.h b/BaseTypes.h deleted file mode 100644 index 057289ef3..000000000 --- a/BaseTypes.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @date 2014 - * Some elementary types for the parser. - */ - -#pragma once - -#include -#include -#include - -namespace dev -{ -namespace solidity -{ - -/** - * Representation of an interval of source positions. - * The interval includes start and excludes end. - */ -struct Location -{ - Location(int _start, int _end, std::shared_ptr _sourceName): - start(_start), end(_end), sourceName(_sourceName) { } - Location(): start(-1), end(-1) { } - - bool isEmpty() const { return start == -1 && end == -1; } - - int start; - int end; - std::shared_ptr sourceName; -}; - -/// Stream output for Location (used e.g. in boost exceptions). -inline std::ostream& operator<<(std::ostream& _out, Location const& _location) -{ - if (_location.isEmpty()) - return _out << "NO_LOCATION_SPECIFIED"; - return _out << *_location.sourceName << "[" << _location.start << "," << _location.end << ")"; -} - -} -} diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 2b1059c69..000000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -cmake_policy(SET CMP0015 NEW) -# this policy was introduced in cmake 3.0 -# remove if, once 3.0 will be used on unix -if (${CMAKE_MAJOR_VERSION} GREATER 2) - # old policy do not use MACOSX_RPATH - cmake_policy(SET CMP0042 OLD) -endif() -set(CMAKE_AUTOMOC OFF) - -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB") - -aux_source_directory(. SRC_LIST) - -include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS}) -include_directories(BEFORE ..) -include_directories(${Boost_INCLUDE_DIRS}) - -set(EXECUTABLE solidity) - -file(GLOB HEADERS "*.h") - -if (ETH_STATIC) - add_library(${EXECUTABLE} STATIC ${SRC_LIST} ${HEADERS}) -else() - add_library(${EXECUTABLE} SHARED ${SRC_LIST} ${HEADERS}) -endif() - -target_link_libraries(${EXECUTABLE} ${JSONCPP_LIBRARIES}) -target_link_libraries(${EXECUTABLE} evmcore) -target_link_libraries(${EXECUTABLE} devcore) -target_link_libraries(${EXECUTABLE} devcrypto) - -install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) -install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) - diff --git a/Compiler.cpp b/Compiler.cpp deleted file mode 100644 index 14acc0113..000000000 --- a/Compiler.cpp +++ /dev/null @@ -1,498 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @date 2014 - * Solidity compiler. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std; - -namespace dev { -namespace solidity { - -void Compiler::compileContract(ContractDefinition const& _contract, - map const& _contracts) -{ - m_context = CompilerContext(); // clear it just in case - initializeContext(_contract, _contracts); - appendFunctionSelector(_contract); - set functions = m_context.getFunctionsWithoutCode(); - while (!functions.empty()) - { - for (Declaration const* function: functions) - function->accept(*this); - functions = m_context.getFunctionsWithoutCode(); - } - - // Swap the runtime context with the creation-time context - swap(m_context, m_runtimeContext); - initializeContext(_contract, _contracts); - packIntoContractCreator(_contract, m_runtimeContext); -} - -void Compiler::initializeContext(ContractDefinition const& _contract, - map const& _contracts) -{ - m_context.setCompiledContracts(_contracts); - m_context.setInheritanceHierarchy(_contract.getLinearizedBaseContracts()); - registerStateVariables(_contract); -} - -void Compiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext) -{ - // arguments for base constructors, filled in derived-to-base order - map> const*> baseArguments; - - // Determine the arguments that are used for the base constructors. - std::vector const& bases = _contract.getLinearizedBaseContracts(); - for (ContractDefinition const* contract: bases) - for (ASTPointer const& base: contract->getBaseContracts()) - { - ContractDefinition const* baseContract = dynamic_cast( - base->getName()->getReferencedDeclaration()); - solAssert(baseContract, ""); - if (baseArguments.count(baseContract) == 0) - baseArguments[baseContract] = &base->getArguments(); - } - - // Call constructors in base-to-derived order. - // The Constructor for the most derived contract is called later. - for (unsigned i = 1; i < bases.size(); i++) - { - ContractDefinition const* base = bases[bases.size() - i]; - solAssert(base, ""); - FunctionDefinition const* baseConstructor = base->getConstructor(); - if (!baseConstructor) - continue; - solAssert(baseArguments[base], ""); - appendBaseConstructorCall(*baseConstructor, *baseArguments[base]); - } - if (_contract.getConstructor()) - appendConstructorCall(*_contract.getConstructor()); - - eth::AssemblyItem sub = m_context.addSubroutine(_runtimeContext.getAssembly()); - // stack contains sub size - m_context << eth::Instruction::DUP1 << sub << u256(0) << eth::Instruction::CODECOPY; - m_context << u256(0) << eth::Instruction::RETURN; - - // note that we have to include the functions again because of absolute jump labels - set 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> const& _arguments) -{ - FunctionType constructorType(_constructor); - eth::AssemblyItem returnLabel = m_context.pushNewTag(); - for (unsigned i = 0; i < _arguments.size(); ++i) - compileExpression(*_arguments[i], constructorType.getParameterTypes()[i]); - m_context.appendJumpTo(m_context.getFunctionEntryLabel(_constructor)); - m_context << returnLabel; -} - -void Compiler::appendConstructorCall(FunctionDefinition const& _constructor) -{ - eth::AssemblyItem returnTag = m_context.pushNewTag(); - // copy constructor arguments from code to memory and then to stack, they are supplied after the actual program - unsigned argumentSize = 0; - for (ASTPointer const& var: _constructor.getParameters()) - argumentSize += CompilerUtils::getPaddedSize(var->getType()->getCalldataEncodedSize()); - - if (argumentSize > 0) - { - m_context << u256(argumentSize); - m_context.appendProgramSize(); - m_context << u256(CompilerUtils::dataStartOffset); // copy it to byte four as expected for ABI calls - m_context << eth::Instruction::CODECOPY; - appendCalldataUnpacker(FunctionType(_constructor).getParameterTypes(), true); - } - m_context.appendJumpTo(m_context.getFunctionEntryLabel(_constructor)); - m_context << returnTag; -} - -void Compiler::appendFunctionSelector(ContractDefinition const& _contract) -{ - map, FunctionTypePointer> interfaceFunctions = _contract.getInterfaceFunctions(); - map, 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 - for (auto const& it: interfaceFunctions) - { - callDataUnpackerEntryPoints.insert(std::make_pair(it.first, m_context.newTag())); - m_context << eth::dupInstruction(1) << u256(FixedHash<4>::Arith(it.first)) << eth::Instruction::EQ; - m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.at(it.first)); - } - if (FunctionDefinition const* fallback = _contract.getFallbackFunction()) - { - eth::AssemblyItem returnTag = m_context.pushNewTag(); - fallback->accept(*this); - m_context << returnTag; - appendReturnValuePacker(FunctionType(*fallback).getReturnParameterTypes()); - } - else - m_context << eth::Instruction::STOP; // function not found - - for (auto const& it: interfaceFunctions) - { - FunctionTypePointer const& functionType = it.second; - m_context << callDataUnpackerEntryPoints.at(it.first); - eth::AssemblyItem returnTag = m_context.pushNewTag(); - appendCalldataUnpacker(functionType->getParameterTypes()); - m_context.appendJumpTo(m_context.getFunctionEntryLabel(it.second->getDeclaration())); - m_context << returnTag; - appendReturnValuePacker(functionType->getReturnParameterTypes()); - } -} - -void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory) -{ - // We do not check the calldata size, everything is zero-padded. - unsigned offset(CompilerUtils::dataStartOffset); - bool const c_padToWords = true; - - unsigned dynamicParameterCount = 0; - for (TypePointer const& type: _typeParameters) - if (type->isDynamicallySized()) - dynamicParameterCount++; - offset += dynamicParameterCount * 32; - unsigned currentDynamicParameter = 0; - for (TypePointer const& type: _typeParameters) - if (type->isDynamicallySized()) - { - // value on stack: [calldata_offset] (only if we are already in dynamic mode) - if (currentDynamicParameter == 0) - // switch from static to dynamic - m_context << u256(offset); - // retrieve length - CompilerUtils(m_context).loadFromMemory( - CompilerUtils::dataStartOffset + currentDynamicParameter * 32, - IntegerType(256), !_fromMemory, c_padToWords); - // stack: offset length - // add 32-byte padding to copy of length - m_context << u256(32) << eth::Instruction::DUP1 << u256(31) - << eth::Instruction::DUP4 << eth::Instruction::ADD - << eth::Instruction::DIV << eth::Instruction::MUL; - // stack: offset length padded_length - m_context << eth::Instruction::DUP3 << eth::Instruction::ADD; - currentDynamicParameter++; - // stack: offset length next_calldata_offset - } - else if (currentDynamicParameter == 0) - // we can still use static load - offset += CompilerUtils(m_context).loadFromMemory(offset, *type, !_fromMemory, c_padToWords); - else - CompilerUtils(m_context).loadFromMemoryDynamic(*type, !_fromMemory, c_padToWords); - if (dynamicParameterCount > 0) - m_context << eth::Instruction::POP; -} - -void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters) -{ - //@todo this can be also done more efficiently - unsigned dataOffset = 0; - unsigned stackDepth = 0; - for (TypePointer const& type: _typeParameters) - stackDepth += type->getSizeOnStack(); - - for (TypePointer const& type: _typeParameters) - { - CompilerUtils(m_context).copyToStackTop(stackDepth, *type); - ExpressionCompiler::appendTypeConversion(m_context, *type, *type, true); - bool const c_padToWords = true; - dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, *type, c_padToWords); - stackDepth -= type->getSizeOnStack(); - } - // note that the stack is not cleaned up here - m_context << u256(dataOffset) << u256(0) << eth::Instruction::RETURN; -} - -void Compiler::registerStateVariables(ContractDefinition const& _contract) -{ - for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.getLinearizedBaseContracts())) - for (ASTPointer const& variable: contract->getStateVariables()) - m_context.addStateVariable(*variable); -} - -bool Compiler::visit(VariableDeclaration const& _variableDeclaration) -{ - solAssert(_variableDeclaration.isStateVariable(), "Compiler visit to non-state variable declaration."); - - m_context.startFunction(_variableDeclaration); - m_breakTags.clear(); - m_continueTags.clear(); - - m_context << m_context.getFunctionEntryLabel(_variableDeclaration); - ExpressionCompiler::appendStateVariableAccessor(m_context, _variableDeclaration); - - return false; -} - -bool Compiler::visit(FunctionDefinition const& _function) -{ - //@todo to simplify this, the calling convention could by changed such that - // caller puts: [retarg0] ... [retargm] [return address] [arg0] ... [argn] - // although note that this reduces the size of the visible stack - - m_context.startFunction(_function); - m_returnTag = m_context.newTag(); - m_breakTags.clear(); - m_continueTags.clear(); - m_stackCleanupForReturn = 0; - m_currentFunction = &_function; - m_modifierDepth = 0; - - // stack upon entry: [return address] [arg0] [arg1] ... [argn] - // reserve additional slots: [retarg0] ... [retargm] [localvar0] ... [localvarp] - - unsigned parametersSize = CompilerUtils::getSizeOnStack(_function.getParameters()); - m_context.adjustStackOffset(parametersSize); - for (ASTPointer const& variable: _function.getParameters()) - { - m_context.addVariable(*variable, parametersSize); - parametersSize -= variable->getType()->getSizeOnStack(); - } - for (ASTPointer const& variable: _function.getReturnParameters()) - m_context.addAndInitializeVariable(*variable); - for (VariableDeclaration const* localVariable: _function.getLocalVariables()) - m_context.addAndInitializeVariable(*localVariable); - - appendModifierOrFunctionCode(); - - m_context << m_returnTag; - - // Now we need to re-shuffle the stack. For this we keep a record of the stack layout - // that shows the target positions of the elements, where "-1" denotes that this element needs - // to be removed from the stack. - // Note that the fact that the return arguments are of increasing index is vital for this - // algorithm to work. - - unsigned const c_argumentsSize = CompilerUtils::getSizeOnStack(_function.getParameters()); - unsigned const c_returnValuesSize = CompilerUtils::getSizeOnStack(_function.getReturnParameters()); - unsigned const c_localVariablesSize = CompilerUtils::getSizeOnStack(_function.getLocalVariables()); - - vector stackLayout; - stackLayout.push_back(c_returnValuesSize); // target of return address - stackLayout += vector(c_argumentsSize, -1); // discard all arguments - for (unsigned i = 0; i < c_returnValuesSize; ++i) - stackLayout.push_back(i); - stackLayout += vector(c_localVariablesSize, -1); - - while (stackLayout.back() != int(stackLayout.size() - 1)) - if (stackLayout.back() < 0) - { - m_context << eth::Instruction::POP; - stackLayout.pop_back(); - } - else - { - m_context << eth::swapInstruction(stackLayout.size() - stackLayout.back() - 1); - swap(stackLayout[stackLayout.back()], stackLayout.back()); - } - //@todo assert that everything is in place now - - m_context << eth::Instruction::JUMP; - - return false; -} - -bool Compiler::visit(IfStatement const& _ifStatement) -{ - compileExpression(_ifStatement.getCondition()); - eth::AssemblyItem trueTag = m_context.appendConditionalJump(); - if (_ifStatement.getFalseStatement()) - _ifStatement.getFalseStatement()->accept(*this); - eth::AssemblyItem endTag = m_context.appendJumpToNew(); - m_context << trueTag; - _ifStatement.getTrueStatement().accept(*this); - m_context << endTag; - return false; -} - -bool Compiler::visit(WhileStatement const& _whileStatement) -{ - eth::AssemblyItem loopStart = m_context.newTag(); - eth::AssemblyItem loopEnd = m_context.newTag(); - m_continueTags.push_back(loopStart); - m_breakTags.push_back(loopEnd); - - m_context << loopStart; - compileExpression(_whileStatement.getCondition()); - m_context << eth::Instruction::ISZERO; - m_context.appendConditionalJumpTo(loopEnd); - - _whileStatement.getBody().accept(*this); - - m_context.appendJumpTo(loopStart); - m_context << loopEnd; - - m_continueTags.pop_back(); - m_breakTags.pop_back(); - return false; -} - -bool Compiler::visit(ForStatement const& _forStatement) -{ - eth::AssemblyItem loopStart = m_context.newTag(); - eth::AssemblyItem loopEnd = m_context.newTag(); - m_continueTags.push_back(loopStart); - m_breakTags.push_back(loopEnd); - - if (_forStatement.getInitializationExpression()) - _forStatement.getInitializationExpression()->accept(*this); - - m_context << loopStart; - - // if there is no terminating condition in for, default is to always be true - if (_forStatement.getCondition()) - { - compileExpression(*_forStatement.getCondition()); - m_context << eth::Instruction::ISZERO; - m_context.appendConditionalJumpTo(loopEnd); - } - - _forStatement.getBody().accept(*this); - - // for's loop expression if existing - if (_forStatement.getLoopExpression()) - _forStatement.getLoopExpression()->accept(*this); - - m_context.appendJumpTo(loopStart); - m_context << loopEnd; - - m_continueTags.pop_back(); - m_breakTags.pop_back(); - return false; -} - -bool Compiler::visit(Continue const&) -{ - if (!m_continueTags.empty()) - m_context.appendJumpTo(m_continueTags.back()); - return false; -} - -bool Compiler::visit(Break const&) -{ - if (!m_breakTags.empty()) - m_context.appendJumpTo(m_breakTags.back()); - return false; -} - -bool Compiler::visit(Return const& _return) -{ - //@todo modifications are needed to make this work with functions returning multiple values - if (Expression const* expression = _return.getExpression()) - { - solAssert(_return.getFunctionReturnParameters(), "Invalid return parameters pointer."); - VariableDeclaration const& firstVariable = *_return.getFunctionReturnParameters()->getParameters().front(); - compileExpression(*expression, firstVariable.getType()); - CompilerUtils(m_context).moveToStackVariable(firstVariable); - } - for (unsigned i = 0; i < m_stackCleanupForReturn; ++i) - m_context << eth::Instruction::POP; - m_context.appendJumpTo(m_returnTag); - m_context.adjustStackOffset(m_stackCleanupForReturn); - return false; -} - -bool Compiler::visit(VariableDefinition const& _variableDefinition) -{ - if (Expression const* expression = _variableDefinition.getExpression()) - { - compileExpression(*expression, _variableDefinition.getDeclaration().getType()); - CompilerUtils(m_context).moveToStackVariable(_variableDefinition.getDeclaration()); - } - return false; -} - -bool Compiler::visit(ExpressionStatement const& _expressionStatement) -{ - Expression const& expression = _expressionStatement.getExpression(); - compileExpression(expression); - CompilerUtils(m_context).popStackElement(*expression.getType()); - return false; -} - -bool Compiler::visit(PlaceholderStatement const&) -{ - ++m_modifierDepth; - appendModifierOrFunctionCode(); - --m_modifierDepth; - return true; -} - -void Compiler::appendModifierOrFunctionCode() -{ - solAssert(m_currentFunction, ""); - if (m_modifierDepth >= m_currentFunction->getModifiers().size()) - m_currentFunction->getBody().accept(*this); - else - { - ASTPointer 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 deleted file mode 100644 index 1aeaee88f..000000000 --- a/Compiler.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @date 2014 - * Solidity AST to EVM bytecode compiler. - */ - -#pragma once - -#include -#include -#include -#include - -namespace dev { -namespace solidity { - -class Compiler: private ASTConstVisitor -{ -public: - explicit Compiler(bool _optimize = false): m_optimize(_optimize), m_context(), - m_returnTag(m_context.newTag()) {} - - void compileContract(ContractDefinition const& _contract, - std::map const& _contracts); - bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); } - bytes getRuntimeBytecode() { return m_runtimeContext.getAssembledBytecode(m_optimize);} - void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); } - -private: - /// Registers the non-function objects inside the contract with the context. - void initializeContext(ContractDefinition const& _contract, - std::map const& _contracts); - /// Adds the code that is run at creation time. Should be run after exchanging the run-time context - /// with a new and initialized context. Adds the constructor code. - void packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext); - void appendBaseConstructorCall(FunctionDefinition const& _constructor, - std::vector> const& _arguments); - void appendConstructorCall(FunctionDefinition const& _constructor); - void appendFunctionSelector(ContractDefinition const& _contract); - /// Creates code that unpacks the arguments for the given function represented by a vector of TypePointers. - /// From memory if @a _fromMemory is true, otherwise from call data. - void appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory = false); - void appendReturnValuePacker(TypePointers const& _typeParameters); - - void registerStateVariables(ContractDefinition const& _contract); - - virtual bool visit(VariableDeclaration const& _variableDeclaration) override; - virtual bool visit(FunctionDefinition const& _function) override; - virtual bool visit(IfStatement const& _ifStatement) override; - virtual bool visit(WhileStatement const& _whileStatement) override; - virtual bool visit(ForStatement const& _forStatement) override; - virtual bool visit(Continue const& _continue) override; - virtual bool visit(Break const& _break) override; - virtual bool visit(Return const& _return) override; - virtual bool visit(VariableDefinition const& _variableDefinition) override; - virtual bool visit(ExpressionStatement const& _expressionStatement) override; - virtual bool visit(PlaceholderStatement const&) override; - - /// Appends one layer of function modifier code of the current function, or the function - /// body itself if the last modifier was reached. - void appendModifierOrFunctionCode(); - - void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer()); - - bool const m_optimize; - CompilerContext m_context; - CompilerContext m_runtimeContext; - std::vector m_breakTags; ///< tag to jump to for a "break" statement - std::vector 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 deleted file mode 100644 index 01a71d7c9..000000000 --- a/CompilerContext.cpp +++ /dev/null @@ -1,165 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @date 2014 - * Utilities for the solidity compiler. - */ - -#include -#include -#include -#include - -using namespace std; - -namespace dev -{ -namespace solidity -{ - -void CompilerContext::addMagicGlobal(MagicVariableDeclaration const& _declaration) -{ - m_magicGlobals.insert(&_declaration); -} - -void CompilerContext::addStateVariable(VariableDeclaration const& _declaration) -{ - m_stateVariables[&_declaration] = m_stateVariablesSize; - m_stateVariablesSize += _declaration.getType()->getStorageSize(); -} - -void CompilerContext::startFunction(Declaration const& _function) -{ - m_functionsWithCode.insert(&_function); - m_localVariables.clear(); - m_asm.setDeposit(0); - *this << getFunctionEntryLabel(_function); -} - -void CompilerContext::addVariable(VariableDeclaration const& _declaration, - unsigned _offsetToCurrent) -{ - solAssert(m_asm.deposit() >= 0 && unsigned(m_asm.deposit()) >= _offsetToCurrent, ""); - m_localVariables[&_declaration] = unsigned(m_asm.deposit()) - _offsetToCurrent; -} - -void CompilerContext::addAndInitializeVariable(VariableDeclaration const& _declaration) -{ - addVariable(_declaration); - - int const size = _declaration.getType()->getSizeOnStack(); - for (int i = 0; i < size; ++i) - *this << u256(0); -} - -bytes const& CompilerContext::getCompiledContract(const ContractDefinition& _contract) const -{ - auto ret = m_compiledContracts.find(&_contract); - solAssert(ret != m_compiledContracts.end(), "Compiled contract not found."); - return *ret->second; -} - -bool CompilerContext::isLocalVariable(Declaration const* _declaration) const -{ - return !!m_localVariables.count(_declaration); -} - -eth::AssemblyItem CompilerContext::getFunctionEntryLabel(Declaration const& _declaration) -{ - auto res = m_functionEntryLabels.find(&_declaration); - if (res == m_functionEntryLabels.end()) - { - eth::AssemblyItem tag(m_asm.newTag()); - m_functionEntryLabels.insert(make_pair(&_declaration, tag)); - return tag.tag(); - } - else - return res->second.tag(); -} - -eth::AssemblyItem CompilerContext::getVirtualFunctionEntryLabel(FunctionDefinition const& _function) -{ - solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); - for (ContractDefinition const* contract: m_inheritanceHierarchy) - for (ASTPointer 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 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 CompilerContext::getFunctionsWithoutCode() -{ - set 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 const& modifier: contract->getFunctionModifiers()) - if (modifier->getName() == _name) - return *modifier.get(); - BOOST_THROW_EXCEPTION(InternalCompilerError() - << errinfo_comment("Function modifier " + _name + " not found.")); -} - -unsigned CompilerContext::getBaseStackOffsetOfVariable(Declaration const& _declaration) const -{ - auto res = m_localVariables.find(&_declaration); - solAssert(res != m_localVariables.end(), "Variable not found on stack."); - return res->second; -} - -unsigned CompilerContext::baseToCurrentStackOffset(unsigned _baseOffset) const -{ - return m_asm.deposit() - _baseOffset - 1; -} - -unsigned CompilerContext::currentToBaseStackOffset(unsigned _offset) const -{ - return m_asm.deposit() - _offset - 1; -} - -u256 CompilerContext::getStorageLocationOfVariable(const Declaration& _declaration) const -{ - auto it = m_stateVariables.find(&_declaration); - solAssert(it != m_stateVariables.end(), "Variable not found in storage."); - return it->second; -} - -} -} diff --git a/CompilerContext.h b/CompilerContext.h deleted file mode 100644 index f202d7f4e..000000000 --- a/CompilerContext.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @date 2014 - * Utilities for the solidity compiler. - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace dev { -namespace solidity { - - -/** - * Context to be shared by all units that compile the same contract. - * It stores the generated bytecode and the position of identifiers in memory and on the stack. - */ -class CompilerContext -{ -public: - void addMagicGlobal(MagicVariableDeclaration const& _declaration); - void addStateVariable(VariableDeclaration const& _declaration); - void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0); - void addAndInitializeVariable(VariableDeclaration const& _declaration); - - void setCompiledContracts(std::map const& _contracts) { m_compiledContracts = _contracts; } - bytes const& getCompiledContract(ContractDefinition const& _contract) const; - - void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); } - unsigned getStackHeight() { solAssert(m_asm.deposit() >= 0, ""); return unsigned(m_asm.deposit()); } - - bool isMagicGlobal(Declaration const* _declaration) const { return m_magicGlobals.count(_declaration) != 0; } - bool isLocalVariable(Declaration const* _declaration) const; - bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration) != 0; } - - eth::AssemblyItem getFunctionEntryLabel(Declaration const& _declaration); - void setInheritanceHierarchy(std::vector 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 getFunctionsWithoutCode(); - /// Resets function specific members, inserts the function entry label and marks the function - /// as "having code". - void startFunction(Declaration const& _function); - - ModifierDefinition const& getFunctionModifier(std::string const& _name) const; - /// Returns the distance of the given local variable from the bottom of the stack (of the current function). - unsigned getBaseStackOffsetOfVariable(Declaration const& _declaration) const; - /// If supplied by a value returned by @ref getBaseStackOffsetOfVariable(variable), returns - /// the distance of that variable from the current top of the stack. - unsigned baseToCurrentStackOffset(unsigned _baseOffset) const; - /// Converts an offset relative to the current stack height to a value that can be used later - /// with baseToCurrentStackOffset to point to the same stack element. - unsigned currentToBaseStackOffset(unsigned _offset) const; - u256 getStorageLocationOfVariable(Declaration const& _declaration) const; - - /// Appends a JUMPI instruction to a new tag and @returns the tag - eth::AssemblyItem appendConditionalJump() { return m_asm.appendJumpI().tag(); } - /// Appends a JUMPI instruction to @a _tag - CompilerContext& appendConditionalJumpTo(eth::AssemblyItem const& _tag) { m_asm.appendJumpI(_tag); return *this; } - /// Appends a JUMP to a new tag and @returns the tag - eth::AssemblyItem appendJumpToNew() { return m_asm.appendJump().tag(); } - /// Appends a JUMP to a tag already on the stack - CompilerContext& appendJump() { return *this << eth::Instruction::JUMP; } - /// Appends a JUMP to a specific tag - CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm.appendJump(_tag); return *this; } - /// Appends pushing of a new tag and @returns the new tag. - eth::AssemblyItem pushNewTag() { return m_asm.append(m_asm.newPushTag()).tag(); } - /// @returns a new tag without pushing any opcodes or data - eth::AssemblyItem newTag() { return m_asm.newTag(); } - /// Adds a subroutine to the code (in the data section) and pushes its size (via a tag) - /// on the stack. @returns the assembly item corresponding to the pushed subroutine, i.e. its offset. - eth::AssemblyItem addSubroutine(eth::Assembly const& _assembly) { return m_asm.appendSubSize(_assembly); } - /// Pushes the size of the final program - void appendProgramSize() { return m_asm.appendProgramSize(); } - /// Adds data to the data section, pushes a reference to the stack - eth::AssemblyItem appendData(bytes const& _data) { return m_asm.append(_data); } - - /// Append elements to the current instruction list and adjust @a m_stackOffset. - CompilerContext& operator<<(eth::AssemblyItem const& _item) { m_asm.append(_item); return *this; } - CompilerContext& operator<<(eth::Instruction _instruction) { m_asm.append(_instruction); return *this; } - CompilerContext& operator<<(u256 const& _value) { m_asm.append(_value); return *this; } - CompilerContext& operator<<(bytes const& _data) { m_asm.append(_data); return *this; } - - eth::Assembly const& getAssembly() const { return m_asm; } - void streamAssembly(std::ostream& _stream) const { _stream << m_asm; } - bytes getAssembledBytecode(bool _optimize = false) { return m_asm.optimise(_optimize).assemble(); } - -private: - eth::Assembly m_asm; - - /// Magic global variables like msg, tx or this, distinguished by type. - std::set m_magicGlobals; - /// Other already compiled contracts to be used in contract creation calls. - std::map m_compiledContracts; - /// Size of the state variables, offset of next variable to be added. - u256 m_stateVariablesSize = 0; - /// Storage offsets of state variables - std::map m_stateVariables; - /// Offsets of local variables on the stack (relative to stack base). - std::map m_localVariables; - /// Labels pointing to the entry points of functions. - std::map m_functionEntryLabels; - /// Set of functions for which we did not yet generate code. - std::set m_functionsWithCode; - /// List of current inheritance hierarchy from derived to base. - std::vector m_inheritanceHierarchy; -}; - -} -} diff --git a/CompilerStack.cpp b/CompilerStack.cpp deleted file mode 100644 index ca9c75bcd..000000000 --- a/CompilerStack.cpp +++ /dev/null @@ -1,370 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @author Gav Wood - * @date 2014 - * Full-stack compiler that converts a source code string to bytecode. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -using namespace std; - -namespace dev -{ -namespace solidity -{ - -const map StandardSources = map{ - {"coin", R"(import "CoinReg";import "Config";import "configUser";contract coin is configUser{function coin(string3 name, uint denom) {CoinReg(Config(configAddr()).lookup(3)).register(name, denom);}})"}, - {"Coin", R"(contract Coin{function isApprovedFor(address _target,address _proxy)constant returns(bool _r){}function isApproved(address _proxy)constant returns(bool _r){}function sendCoinFrom(address _from,uint256 _val,address _to){}function coinBalanceOf(address _a)constant returns(uint256 _r){}function sendCoin(uint256 _val,address _to){}function coinBalance()constant returns(uint256 _r){}function approve(address _a){}})"}, - {"CoinReg", R"(contract CoinReg{function count()constant returns(uint256 r){}function info(uint256 i)constant returns(address addr,string3 name,uint256 denom){}function register(string3 name,uint256 denom){}function unregister(){}})"}, - {"configUser", R"(contract configUser{function configAddr()constant returns(address a){ return 0xc6d9d2cd449a754c494264e1809c50e34d64562b;}})"}, - {"Config", R"(contract Config{function lookup(uint256 service)constant returns(address a){}function kill(){}function unregister(uint256 id){}function register(uint256 id,address service){}})"}, - {"mortal", R"(import "owned";contract mortal is owned {function kill() { if (msg.sender == owner) suicide(owner); }})"}, - {"named", R"(import "Config";import "NameReg";import "configUser";contract named is configUser {function named(string32 name) {NameReg(Config(configAddr()).lookup(1)).register(name);}})"}, - {"NameReg", R"(contract NameReg{function register(string32 name){}function addressOf(string32 name)constant returns(address addr){}function unregister(){}function nameOf(address addr)constant returns(string32 name){}})"}, - {"owned", R"(contract owned{function owned(){owner = msg.sender;}modifier onlyowner(){if(msg.sender==owner)_}address owner;})"}, - {"service", R"(import "Config";import "configUser";contract service is configUser{function service(uint _n){Config(configAddr()).register(_n, this);}})"}, - {"std", R"(import "owned";import "mortal";import "Config";import "configUser";import "NameReg";import "named";)"} -}; - -CompilerStack::CompilerStack(bool _addStandardSources): - m_addStandardSources(_addStandardSources), m_parseSuccessful(false) -{ - if (m_addStandardSources) - addSources(StandardSources); -} - -bool CompilerStack::addSource(string const& _name, string const& _content) -{ - bool existed = m_sources.count(_name) != 0; - reset(true); - m_sources[_name].scanner = make_shared(CharStream(expanded(_content)), _name); - return existed; -} - -void CompilerStack::setSource(string const& _sourceCode) -{ - reset(); - addSource("", expanded(_sourceCode)); -} - -void CompilerStack::parse() -{ - for (auto& sourcePair: m_sources) - { - sourcePair.second.scanner->reset(); - sourcePair.second.ast = Parser().parse(sourcePair.second.scanner); - } - resolveImports(); - - m_globalContext = make_shared(); - NameAndTypeResolver resolver(m_globalContext->getDeclarations()); - for (Source const* source: m_sourceOrder) - resolver.registerDeclarations(*source->ast); - for (Source const* source: m_sourceOrder) - for (ASTPointer const& node: source->ast->getNodes()) - if (ContractDefinition* contract = dynamic_cast(node.get())) - { - m_globalContext->setCurrentContract(*contract); - resolver.updateDeclaration(*m_globalContext->getCurrentThis()); - resolver.updateDeclaration(*m_globalContext->getCurrentSuper()); - resolver.resolveNamesAndTypes(*contract); - m_contracts[contract->getName()].contract = contract; - } - for (Source const* source: m_sourceOrder) - for (ASTPointer const& node: source->ast->getNodes()) - if (ContractDefinition* contract = dynamic_cast(node.get())) - { - m_globalContext->setCurrentContract(*contract); - resolver.updateDeclaration(*m_globalContext->getCurrentThis()); - resolver.checkTypeRequirements(*contract); - m_contracts[contract->getName()].contract = contract; - } - m_parseSuccessful = true; -} - -void CompilerStack::parse(string const& _sourceCode) -{ - setSource(_sourceCode); - parse(); -} - -vector CompilerStack::getContractNames() const -{ - if (!m_parseSuccessful) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); - vector contractNames; - for (auto const& contract: m_contracts) - contractNames.push_back(contract.first); - return contractNames; -} - -////// BEGIN: TEMPORARY ONLY -/// -/// NOTE: THIS INVALIDATES SOURCE POINTERS AND CAN CRASH THE COMPILER -/// -/// remove once import works properly and we have genesis contracts - -string CompilerStack::expanded(string const& _sourceCode) -{ - const map c_standardSources = map{ - { "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 got; - function 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 rs; - boost::split(rs, n, boost::is_any_of(" \t,"), boost::token_compress_on); - for (auto const& r: rs) - if (!got.count(r)) - { - if (c_standardSources.count(r)) - sub.append("\n" + localExpanded(c_standardSources.at(r)) + "\n"); - got.insert(r); - } - } - // TODO: remove once we have genesis contracts. - else if ((p = ret.find("Config()")) != string::npos) - ret.replace(p, 8, "Config(0xc6d9d2cd449a754c494264e1809c50e34d64562b)"); - return ret; - }; - return sub + localExpanded(_sourceCode); -} - -////// END: TEMPORARY ONLY - -void CompilerStack::compile(bool _optimize) -{ - if (!m_parseSuccessful) - parse(); - - map contractBytecode; - for (Source const* source: m_sourceOrder) - for (ASTPointer const& node: source->ast->getNodes()) - if (ContractDefinition* contract = dynamic_cast(node.get())) - { - shared_ptr compiler = make_shared(_optimize); - compiler->compileContract(*contract, contractBytecode); - Contract& compiledContract = m_contracts[contract->getName()]; - compiledContract.bytecode = compiler->getAssembledBytecode(); - compiledContract.runtimeBytecode = compiler->getRuntimeBytecode(); - compiledContract.compiler = move(compiler); - contractBytecode[compiledContract.contract] = &compiledContract.bytecode; - } -} - -bytes const& CompilerStack::compile(string const& _sourceCode, bool _optimize) -{ - parse(_sourceCode); - compile(_optimize); - return getBytecode(); -} - -bytes const& CompilerStack::getBytecode(string const& _contractName) const -{ - return getContract(_contractName).bytecode; -} - -bytes const& CompilerStack::getRuntimeBytecode(string const& _contractName) const -{ - return getContract(_contractName).runtimeBytecode; -} - -dev::h256 CompilerStack::getContractCodeHash(string const& _contractName) const -{ - return dev::sha3(getRuntimeBytecode(_contractName)); -} - -void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName) const -{ - getContract(_contractName).compiler->streamAssembly(_outStream); -} - -string const& CompilerStack::getInterface(string const& _contractName) const -{ - return getMetadata(_contractName, DocumentationType::ABIInterface); -} - -string const& CompilerStack::getSolidityInterface(string const& _contractName) const -{ - return getMetadata(_contractName, DocumentationType::ABISolidityInterface); -} - -string const& CompilerStack::getMetadata(string const& _contractName, DocumentationType _type) const -{ - if (!m_parseSuccessful) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); - - Contract const& contract = getContract(_contractName); - - std::unique_ptr* doc; - switch (_type) - { - case DocumentationType::NatspecUser: - doc = &contract.userDocumentation; - break; - case DocumentationType::NatspecDev: - doc = &contract.devDocumentation; - break; - case DocumentationType::ABIInterface: - doc = &contract.interface; - break; - case DocumentationType::ABISolidityInterface: - doc = &contract.solidityInterface; - break; - default: - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type.")); - } - if (!*doc) - *doc = contract.interfaceHandler->getDocumentation(*contract.contract, _type); - return *(*doc); -} - -Scanner const& CompilerStack::getScanner(string const& _sourceName) const -{ - return *getSource(_sourceName).scanner; -} - -SourceUnit const& CompilerStack::getAST(string const& _sourceName) const -{ - return *getSource(_sourceName).ast; -} - -ContractDefinition const& CompilerStack::getContractDefinition(string const& _contractName) const -{ - return *getContract(_contractName).contract; -} - -bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize) -{ - CompilerStack stack; - return stack.compile(_sourceCode, _optimize); -} - -void CompilerStack::reset(bool _keepSources) -{ - m_parseSuccessful = false; - if (_keepSources) - for (auto sourcePair: m_sources) - sourcePair.second.reset(); - else - { - m_sources.clear(); - if (m_addStandardSources) - addSources(StandardSources); - } - m_globalContext.reset(); - m_sourceOrder.clear(); - m_contracts.clear(); -} - -void CompilerStack::resolveImports() -{ - // topological sorting (depth first search) of the import graph, cutting potential cycles - vector sourceOrder; - set sourcesSeen; - - function toposort = [&](Source const* _source) - { - if (sourcesSeen.count(_source)) - return; - sourcesSeen.insert(_source); - for (ASTPointer const& node: _source->ast->getNodes()) - if (ImportDirective const* import = dynamic_cast(node.get())) - { - string const& id = import->getIdentifier(); - if (!m_sources.count(id)) - BOOST_THROW_EXCEPTION(ParserError() - << errinfo_sourceLocation(import->getLocation()) - << errinfo_comment("Source not found.")); - toposort(&m_sources[id]); - } - sourceOrder.push_back(_source); - }; - - for (auto const& sourcePair: m_sources) - toposort(&sourcePair.second); - - swap(m_sourceOrder, sourceOrder); -} - -std::string CompilerStack::defaultContractName() const -{ - return getContract("").contract->getName(); -} - -CompilerStack::Contract const& CompilerStack::getContract(string const& _contractName) const -{ - if (m_contracts.empty()) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No compiled contracts found.")); - string contractName = _contractName; - if (_contractName.empty()) - // try to find some user-supplied contract - for (auto const& it: m_sources) - if (!StandardSources.count(it.first)) - for (ASTPointer const& node: it.second.ast->getNodes()) - if (auto contract = dynamic_cast(node.get())) - contractName = contract->getName(); - auto it = m_contracts.find(contractName); - if (it == m_contracts.end()) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Contract " + _contractName + " not found.")); - return it->second; -} - -CompilerStack::Source const& CompilerStack::getSource(string const& _sourceName) const -{ - auto it = m_sources.find(_sourceName); - if (it == m_sources.end()) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Given source file not found.")); - return it->second; -} - -CompilerStack::Contract::Contract(): interfaceHandler(make_shared()) {} - -} -} diff --git a/CompilerStack.h b/CompilerStack.h deleted file mode 100644 index cb4770cd3..000000000 --- a/CompilerStack.h +++ /dev/null @@ -1,165 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @author Gav Wood - * @date 2014 - * Full-stack compiler that converts a source code string to bytecode. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace dev { -namespace solidity { - -// forward declarations -class Scanner; -class ContractDefinition; -class SourceUnit; -class Compiler; -class GlobalContext; -class InterfaceHandler; - -enum class DocumentationType: uint8_t -{ - NatspecUser = 1, - NatspecDev, - ABIInterface, - ABISolidityInterface -}; - -extern const std::map StandardSources; - -/** - * Easy to use and self-contained Solidity compiler with as few header dependencies as possible. - * It holds state and can be used to either step through the compilation stages (and abort e.g. - * before compilation to bytecode) or run the whole compilation in one call. - */ -class CompilerStack: boost::noncopyable -{ -public: - /// Creates a new compiler stack. Adds standard sources if @a _addStandardSources. - explicit CompilerStack(bool _addStandardSources = false); - - /// Adds a source object (e.g. file) to the parser. After this, parse has to be called again. - /// @returns true if a source object by the name already existed and was replaced. - void addSources(std::map const& _nameContents) { for (auto const& i: _nameContents) addSource(i.first, i.second); } - bool addSource(std::string const& _name, std::string const& _content); - void setSource(std::string const& _sourceCode); - /// Parses all source units that were added - void parse(); - /// Sets the given source code as the only source unit apart from standard sources and parses it. - void parse(std::string const& _sourceCode); - /// Returns a list of the contract names in the sources. - std::vector getContractNames() const; - std::string defaultContractName() const; - - /// Compiles the source units that were previously added and parsed. - void compile(bool _optimize = false); - /// Parses and compiles the given source code. - /// @returns the compiled bytecode - bytes const& compile(std::string const& _sourceCode, bool _optimize = false); - - /// @returns the assembled bytecode for a contract. - bytes const& getBytecode(std::string const& _contractName = "") const; - /// @returns the runtime bytecode for the contract, i.e. the code that is returned by the constructor. - bytes const& getRuntimeBytecode(std::string const& _contractName = "") const; - /// @returns hash of the runtime bytecode for the contract, i.e. the code that is returned by the constructor. - dev::h256 getContractCodeHash(std::string const& _contractName = "") const; - - /// Streams a verbose version of the assembly to @a _outStream. - /// Prerequisite: Successful compilation. - void streamAssembly(std::ostream& _outStream, std::string const& _contractName = "") const; - - /// Returns a string representing the contract interface in JSON. - /// Prerequisite: Successful call to parse or compile. - std::string const& getInterface(std::string const& _contractName = "") const; - /// Returns a string representing the contract interface in Solidity. - /// Prerequisite: Successful call to parse or compile. - std::string const& getSolidityInterface(std::string const& _contractName = "") const; - /// Returns a string representing the contract's documentation in JSON. - /// Prerequisite: Successful call to parse or compile. - /// @param type The type of the documentation to get. - /// Can be one of 4 types defined at @c DocumentationType - std::string const& getMetadata(std::string const& _contractName, DocumentationType _type) const; - - /// @returns the previously used scanner, useful for counting lines during error reporting. - Scanner const& getScanner(std::string const& _sourceName = "") const; - /// @returns the parsed source unit with the supplied name. - SourceUnit const& getAST(std::string const& _sourceName = "") const; - /// @returns the parsed contract with the supplied name. Throws an exception if the contract - /// does not exist. - ContractDefinition const& getContractDefinition(std::string const& _contractName) const; - - /// Compile the given @a _sourceCode to bytecode. If a scanner is provided, it is used for - /// scanning the source code - this is useful for printing exception information. - static bytes staticCompile(std::string const& _sourceCode, bool _optimize = false); - -private: - /** - * Information pertaining to one source unit, filled gradually during parsing and compilation. - */ - struct Source - { - std::shared_ptr scanner; - std::shared_ptr ast; - std::string interface; - void reset() { scanner.reset(); ast.reset(); interface.clear(); } - }; - - struct Contract - { - ContractDefinition const* contract = nullptr; - std::shared_ptr compiler; - bytes bytecode; - bytes runtimeBytecode; - std::shared_ptr interfaceHandler; - mutable std::unique_ptr interface; - mutable std::unique_ptr solidityInterface; - mutable std::unique_ptr userDocumentation; - mutable std::unique_ptr 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 m_sources; - std::shared_ptr m_globalContext; - std::vector m_sourceOrder; - std::map m_contracts; -}; - -} -} diff --git a/CompilerUtils.cpp b/CompilerUtils.cpp deleted file mode 100644 index 047bc6d62..000000000 --- a/CompilerUtils.cpp +++ /dev/null @@ -1,376 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @date 2014 - * Routines used by both the compiler and the expression compiler. - */ - -#include -#include -#include - -using namespace std; - -namespace dev -{ -namespace solidity -{ - -const unsigned int CompilerUtils::dataStartOffset = 4; - -unsigned CompilerUtils::loadFromMemory(unsigned _offset, Type const& _type, - bool _fromCalldata, bool _padToWordBoundaries) -{ - solAssert(_type.getCategory() != Type::Category::ByteArray, "Unable to statically load dynamic type."); - m_context << u256(_offset); - return loadFromMemoryHelper(_type, _fromCalldata, _padToWordBoundaries); -} - -void CompilerUtils::loadFromMemoryDynamic(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries) -{ - solAssert(_type.getCategory() != Type::Category::ByteArray, "Byte arrays not yet implemented."); - m_context << eth::Instruction::DUP1; - unsigned numBytes = loadFromMemoryHelper(_type, _fromCalldata, _padToWordBoundaries); - // update memory counter - for (unsigned i = 0; i < _type.getSizeOnStack(); ++i) - m_context << eth::swapInstruction(1 + i); - m_context << u256(numBytes) << eth::Instruction::ADD; -} - - -unsigned CompilerUtils::storeInMemory(unsigned _offset, Type const& _type, bool _padToWordBoundaries) -{ - solAssert(_type.getCategory() != Type::Category::ByteArray, "Unable to statically store dynamic type."); - unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries); - if (numBytes > 0) - m_context << u256(_offset) << eth::Instruction::MSTORE; - return numBytes; -} - -void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries) -{ - if (_type.getCategory() == Type::Category::ByteArray) - { - auto const& type = dynamic_cast(_type); - - if (type.getLocation() == ByteArrayType::Location::CallData) - { - // stack: target source_offset source_len - m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::DUP5 - // stack: target source_offset source_len source_len source_offset target - << eth::Instruction::CALLDATACOPY - << eth::Instruction::DUP3 << eth::Instruction::ADD - << eth::Instruction::SWAP2 << eth::Instruction::POP << eth::Instruction::POP; - } - else - { - solAssert(type.getLocation() == ByteArrayType::Location::Storage, "Memory byte arrays not yet implemented."); - m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD; - // stack here: memory_offset storage_offset length_bytes - // jump to end if length is zero - m_context << eth::Instruction::DUP1 << eth::Instruction::ISZERO; - eth::AssemblyItem loopEnd = m_context.newTag(); - m_context.appendConditionalJumpTo(loopEnd); - // compute memory end offset - m_context << eth::Instruction::DUP3 << eth::Instruction::ADD << eth::Instruction::SWAP2; - // actual array data is stored at SHA3(storage_offset) - m_context << eth::Instruction::SWAP1; - CompilerUtils(m_context).computeHashStatic(); - m_context << eth::Instruction::SWAP1; - - // stack here: memory_end_offset storage_data_offset memory_offset - eth::AssemblyItem loopStart = m_context.newTag(); - m_context << loopStart - // load and store - << eth::Instruction::DUP2 << eth::Instruction::SLOAD - << eth::Instruction::DUP2 << eth::Instruction::MSTORE - // increment storage_data_offset by 1 - << eth::Instruction::SWAP1 << u256(1) << eth::Instruction::ADD - // increment memory offset by 32 - << eth::Instruction::SWAP1 << u256(32) << eth::Instruction::ADD - // check for loop condition - << eth::Instruction::DUP1 << eth::Instruction::DUP4 << eth::Instruction::GT; - m_context.appendConditionalJumpTo(loopStart); - m_context << loopEnd << eth::Instruction::POP << eth::Instruction::POP; - } - } - else - { - unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries); - if (numBytes > 0) - { - solAssert(_type.getSizeOnStack() == 1, "Memory store of types with stack size != 1 not implemented."); - m_context << eth::Instruction::DUP2 << eth::Instruction::MSTORE; - m_context << u256(numBytes) << eth::Instruction::ADD; - } - } -} - -void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable) -{ - unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(_variable)); - unsigned const size = _variable.getType()->getSizeOnStack(); - // move variable starting from its top end in the stack - if (stackPosition - size + 1 > 16) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_variable.getLocation()) - << errinfo_comment("Stack too deep.")); - for (unsigned i = 0; i < size; ++i) - m_context << eth::swapInstruction(stackPosition - size + 1) << eth::Instruction::POP; -} - -void CompilerUtils::copyToStackTop(unsigned _stackDepth, Type const& _type) -{ - if (_stackDepth > 16) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Stack too deep.")); - unsigned const size = _type.getSizeOnStack(); - for (unsigned i = 0; i < size; ++i) - m_context << eth::dupInstruction(_stackDepth); -} - -void CompilerUtils::popStackElement(Type const& _type) -{ - unsigned const size = _type.getSizeOnStack(); - for (unsigned i = 0; i < size; ++i) - m_context << eth::Instruction::POP; -} - -unsigned CompilerUtils::getSizeOnStack(vector> const& _variableTypes) -{ - unsigned size = 0; - for (shared_ptr const& type: _variableTypes) - size += type->getSizeOnStack(); - return size; -} - -void CompilerUtils::computeHashStatic(Type const& _type, bool _padToWordBoundaries) -{ - unsigned length = storeInMemory(0, _type, _padToWordBoundaries); - m_context << u256(length) << u256(0) << eth::Instruction::SHA3; -} - -void CompilerUtils::copyByteArrayToStorage(ByteArrayType const& _targetType, - ByteArrayType const& _sourceType) const -{ - // stack layout: [source_ref] target_ref (top) - // need to leave target_ref on the stack at the end - solAssert(_targetType.getLocation() == ByteArrayType::Location::Storage, ""); - - switch (_sourceType.getLocation()) - { - case ByteArrayType::Location::CallData: - { - // This also assumes that after "length" we only have zeros, i.e. it cannot be used to - // slice a byte array from calldata. - - // stack: source_offset source_len target_ref - // fetch old length and convert to words - m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD; - m_context << u256(31) << eth::Instruction::ADD - << u256(32) << eth::Instruction::SWAP1 << eth::Instruction::DIV; - // stack here: source_offset source_len target_ref target_length_words - // actual array data is stored at SHA3(storage_offset) - m_context << eth::Instruction::DUP2; - CompilerUtils(m_context).computeHashStatic(); - // compute target_data_end - m_context << eth::Instruction::DUP1 << eth::Instruction::SWAP2 << eth::Instruction::ADD - << eth::Instruction::SWAP1; - // stack here: source_offset source_len target_ref target_data_end target_data_ref - // store length (in bytes) - m_context << eth::Instruction::DUP4 << eth::Instruction::DUP1 << eth::Instruction::DUP5 - << eth::Instruction::SSTORE; - // jump to end if length is zero - m_context << eth::Instruction::ISZERO; - eth::AssemblyItem copyLoopEnd = m_context.newTag(); - m_context.appendConditionalJumpTo(copyLoopEnd); - // store start offset - m_context << eth::Instruction::DUP5; - // stack now: source_offset source_len target_ref target_data_end target_data_ref calldata_offset - eth::AssemblyItem copyLoopStart = m_context.newTag(); - m_context << copyLoopStart - // copy from calldata and store - << eth::Instruction::DUP1 << eth::Instruction::CALLDATALOAD - << eth::Instruction::DUP3 << eth::Instruction::SSTORE - // increment target_data_ref by 1 - << eth::Instruction::SWAP1 << u256(1) << eth::Instruction::ADD - // increment calldata_offset by 32 - << eth::Instruction::SWAP1 << u256(32) << eth::Instruction::ADD - // check for loop condition - << eth::Instruction::DUP1 << eth::Instruction::DUP6 << eth::Instruction::GT; - m_context.appendConditionalJumpTo(copyLoopStart); - m_context << eth::Instruction::POP; - m_context << copyLoopEnd; - - // now clear leftover bytes of the old value - // stack now: source_offset source_len target_ref target_data_end target_data_ref - clearStorageLoop(); - // stack now: source_offset source_len target_ref target_data_end - - m_context << eth::Instruction::POP << eth::Instruction::SWAP2 - << eth::Instruction::POP << eth::Instruction::POP; - break; - } - case ByteArrayType::Location::Storage: - { - // this copies source to target and also clears target if it was larger - - // stack: source_ref target_ref - // store target_ref - m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2; - // fetch lengthes - m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP2 - << eth::Instruction::DUP1 << eth::Instruction::SLOAD; - // stack: target_ref target_len_bytes target_ref source_ref source_len_bytes - // store new target length - m_context << eth::Instruction::DUP1 << eth::Instruction::DUP4 << eth::Instruction::SSTORE; - // compute hashes (data positions) - m_context << eth::Instruction::SWAP2; - CompilerUtils(m_context).computeHashStatic(); - m_context << eth::Instruction::SWAP1; - CompilerUtils(m_context).computeHashStatic(); - // stack: target_ref target_len_bytes source_len_bytes target_data_pos source_data_pos - // convert lengthes from bytes to storage slots - m_context << u256(31) << u256(32) << eth::Instruction::DUP1 << eth::Instruction::DUP3 - << eth::Instruction::DUP8 << eth::Instruction::ADD << eth::Instruction::DIV - << eth::Instruction::SWAP2 - << eth::Instruction::DUP6 << eth::Instruction::ADD << eth::Instruction::DIV; - // stack: target_ref target_len_bytes source_len_bytes target_data_pos source_data_pos target_len source_len - // @todo we might be able to go without a third counter - m_context << u256(0); - // stack: target_ref target_len_bytes source_len_bytes target_data_pos source_data_pos target_len source_len counter - eth::AssemblyItem copyLoopStart = m_context.newTag(); - m_context << copyLoopStart; - // check for loop condition - m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 - << eth::Instruction::GT << eth::Instruction::ISZERO; - eth::AssemblyItem copyLoopEnd = m_context.newTag(); - m_context.appendConditionalJumpTo(copyLoopEnd); - // copy - m_context << eth::Instruction::DUP4 << eth::Instruction::DUP2 << eth::Instruction::ADD - << eth::Instruction::SLOAD - << eth::Instruction::DUP6 << eth::Instruction::DUP3 << eth::Instruction::ADD - << eth::Instruction::SSTORE; - // increment - m_context << u256(1) << eth::Instruction::ADD; - m_context.appendJumpTo(copyLoopStart); - m_context << copyLoopEnd; - - // zero-out leftovers in target - // stack: target_ref target_len_bytes source_len_bytes target_data_pos source_data_pos target_len source_len counter - // add counter to target_data_pos - m_context << eth::Instruction::DUP5 << eth::Instruction::ADD - << eth::Instruction::SWAP5 << eth::Instruction::POP; - // stack: target_ref target_len_bytes target_data_pos_updated target_data_pos source_data_pos target_len source_len - // add length to target_data_pos to get target_data_end - m_context << eth::Instruction::POP << eth::Instruction::DUP3 << eth::Instruction::ADD - << eth::Instruction::SWAP4 - << eth::Instruction::POP << eth::Instruction::POP << eth::Instruction::POP; - // stack: target_ref target_data_end target_data_pos_updated - clearStorageLoop(); - m_context << eth::Instruction::POP; - break; - } - default: - solAssert(false, "Given byte array location not implemented."); - } -} - -unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries) -{ - unsigned _encodedSize = _type.getCalldataEncodedSize(); - unsigned numBytes = _padToWordBoundaries ? getPaddedSize(_encodedSize) : _encodedSize; - bool leftAligned = _type.getCategory() == Type::Category::String; - if (numBytes == 0) - m_context << eth::Instruction::POP << u256(0); - else - { - solAssert(numBytes <= 32, "Static memory load of more than 32 bytes requested."); - m_context << (_fromCalldata ? eth::Instruction::CALLDATALOAD : eth::Instruction::MLOAD); - if (numBytes != 32) - { - // add leading or trailing zeros by dividing/multiplying depending on alignment - u256 shiftFactor = u256(1) << ((32 - numBytes) * 8); - m_context << shiftFactor << eth::Instruction::SWAP1 << eth::Instruction::DIV; - if (leftAligned) - m_context << shiftFactor << eth::Instruction::MUL; - } - } - - return numBytes; -} - -void CompilerUtils::clearByteArray(ByteArrayType const& _type) const -{ - solAssert(_type.getLocation() == ByteArrayType::Location::Storage, ""); - - // fetch length - m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD; - // set length to zero - m_context << u256(0) << eth::Instruction::DUP3 << eth::Instruction::SSTORE; - // convert length from bytes to storage slots - m_context << u256(31) << eth::Instruction::ADD - << u256(32) << eth::Instruction::SWAP1 << eth::Instruction::DIV; - // compute data positions - m_context << eth::Instruction::SWAP1; - CompilerUtils(m_context).computeHashStatic(); - // stack: len data_pos - m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2 << eth::Instruction::ADD - << eth::Instruction::SWAP1; - clearStorageLoop(); - // cleanup - m_context << eth::Instruction::POP; -} - -unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const -{ - unsigned _encodedSize = _type.getCalldataEncodedSize(); - unsigned numBytes = _padToWordBoundaries ? getPaddedSize(_encodedSize) : _encodedSize; - bool leftAligned = _type.getCategory() == Type::Category::String; - if (numBytes == 0) - m_context << eth::Instruction::POP; - else - { - solAssert(numBytes <= 32, "Memory store of more than 32 bytes requested."); - if (numBytes != 32 && !leftAligned && !_padToWordBoundaries) - // shift the value accordingly before storing - m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL; - } - return numBytes; -} - -void CompilerUtils::clearStorageLoop() const -{ - // stack: end_pos pos - eth::AssemblyItem loopStart = m_context.newTag(); - m_context << loopStart; - // check for loop condition - m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 - << eth::Instruction::GT << eth::Instruction::ISZERO; - eth::AssemblyItem zeroLoopEnd = m_context.newTag(); - m_context.appendConditionalJumpTo(zeroLoopEnd); - // zero out - m_context << u256(0) << eth::Instruction::DUP2 << eth::Instruction::SSTORE; - // increment - m_context << u256(1) << eth::Instruction::ADD; - m_context.appendJumpTo(loopStart); - // cleanup - m_context << zeroLoopEnd; - m_context << eth::Instruction::POP; -} - -} -} diff --git a/CompilerUtils.h b/CompilerUtils.h deleted file mode 100644 index 5369d3bf2..000000000 --- a/CompilerUtils.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @date 2014 - * Routines used by both the compiler and the expression compiler. - */ - -#pragma once - -#include -#include - -namespace dev { -namespace solidity { - -class Type; // forward - -class CompilerUtils -{ -public: - CompilerUtils(CompilerContext& _context): m_context(_context) {} - - /// Loads data from memory to the stack. - /// @param _offset offset in memory (or calldata) - /// @param _type data type to load - /// @param _fromCalldata if true, load from calldata, not from memory - /// @param _padToWordBoundaries if true, assume the data is padded to word (32 byte) boundaries - /// @returns the number of bytes consumed in memory. - unsigned loadFromMemory(unsigned _offset, Type const& _type = IntegerType(256), - bool _fromCalldata = false, bool _padToWordBoundaries = false); - /// Dynamic version of @see loadFromMemory, expects the memory offset on the stack. - /// Stack pre: memory_offset - /// Stack post: value... (memory_offset+length) - void loadFromMemoryDynamic(Type const& _type, bool _fromCalldata = false, bool _padToWordBoundaries = true); - /// Stores data from stack in memory. - /// @param _offset offset in memory - /// @param _type type of the data on the stack - /// @param _padToWordBoundaries if true, pad the data to word (32 byte) boundaries - /// @returns the number of bytes written to memory (can be different from _bytes if - /// _padToWordBoundaries is true) - unsigned storeInMemory(unsigned _offset, Type const& _type = IntegerType(256), bool _padToWordBoundaries = false); - /// Dynamic version of @see storeInMemory, expects the memory offset below the value on the stack - /// and also updates that. - /// Stack pre: memory_offset value... - /// Stack post: (memory_offset+length) - void storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries = true); - /// @returns _size rounded up to the next multiple of 32 (the number of bytes occupied in the - /// padded calldata) - static unsigned getPaddedSize(unsigned _size) { return ((_size + 31) / 32) * 32; } - - /// Moves the value that is at the top of the stack to a stack variable. - void moveToStackVariable(VariableDeclaration const& _variable); - /// Copies a variable of type @a _type from a stack depth of @a _stackDepth to the top of the stack. - void copyToStackTop(unsigned _stackDepth, Type const& _type); - /// Removes the current value from the top of the stack. - void popStackElement(Type const& _type); - - template - static unsigned getSizeOnStack(std::vector const& _variables); - static unsigned getSizeOnStack(std::vector> 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 -unsigned CompilerUtils::getSizeOnStack(std::vector const& _variables) -{ - unsigned size = 0; - for (T const& variable: _variables) - size += variable->getType()->getSizeOnStack(); - return size; -} - -} -} diff --git a/DeclarationContainer.cpp b/DeclarationContainer.cpp deleted file mode 100644 index 2594d4281..000000000 --- a/DeclarationContainer.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @date 2014 - * Scope - object that holds declaration of names. - */ - -#include -#include - -namespace dev -{ -namespace solidity -{ - -bool DeclarationContainer::registerDeclaration(Declaration const& _declaration, bool _invisible, bool _update) -{ - ASTString const& name(_declaration.getName()); - if (name.empty()) - return true; - - if (!_update && (m_declarations.count(name) || m_invisibleDeclarations.count(name))) - return false; - - if (_invisible) - m_invisibleDeclarations.insert(name); - else - m_declarations[name] = &_declaration; - return true; -} - -Declaration const* DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const -{ - solAssert(!_name.empty(), "Attempt to resolve empty name."); - auto result = m_declarations.find(_name); - if (result != m_declarations.end()) - return result->second; - if (_recursive && m_enclosingContainer) - return m_enclosingContainer->resolveName(_name, true); - return nullptr; -} - -} -} diff --git a/DeclarationContainer.h b/DeclarationContainer.h deleted file mode 100644 index f70881f5b..000000000 --- a/DeclarationContainer.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @date 2014 - * Scope - object that holds declaration of names. - */ - -#pragma once - -#include -#include -#include - -#include - -namespace dev -{ -namespace solidity -{ - -/** - * Container that stores mappings betwee names and declarations. It also contains a link to the - * enclosing scope. - */ -class DeclarationContainer -{ -public: - explicit DeclarationContainer(Declaration const* _enclosingDeclaration = nullptr, - DeclarationContainer const* _enclosingContainer = nullptr): - m_enclosingDeclaration(_enclosingDeclaration), m_enclosingContainer(_enclosingContainer) {} - /// Registers the declaration in the scope unless its name is already declared or the name is empty. - /// @param _invisible if true, registers the declaration, reports name clashes but does not return it in @a resolveName - /// @param _update if true, replaces a potential declaration that is already present - /// @returns false if the name was already declared. - bool registerDeclaration(Declaration const& _declaration, bool _invisible = false, bool _update = false); - Declaration const* resolveName(ASTString const& _name, bool _recursive = false) const; - Declaration const* getEnclosingDeclaration() const { return m_enclosingDeclaration; } - std::map const& getDeclarations() const { return m_declarations; } - -private: - Declaration const* m_enclosingDeclaration; - DeclarationContainer const* m_enclosingContainer; - std::map m_declarations; - std::set m_invisibleDeclarations; -}; - -} -} diff --git a/Exceptions.h b/Exceptions.h deleted file mode 100644 index 0b25abee1..000000000 --- a/Exceptions.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @date 2014 - * Solidity exception hierarchy. - */ - -#pragma once - -#include -#include -#include - -namespace dev -{ -namespace solidity -{ - -struct ParserError: virtual Exception {}; -struct TypeError: virtual Exception {}; -struct DeclarationError: virtual Exception {}; -struct CompilerError: virtual Exception {}; -struct InternalCompilerError: virtual Exception {}; -struct DocstringParsingError: virtual Exception {}; - -using errinfo_sourceLocation = boost::error_info; - -} -} diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp deleted file mode 100644 index a8bc53e0f..000000000 --- a/ExpressionCompiler.cpp +++ /dev/null @@ -1,1248 +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 . -*/ -/** - * @author Christian - * @date 2014 - * Solidity AST to EVM bytecode compiler for expressions. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std; - -namespace dev -{ -namespace solidity -{ - -void ExpressionCompiler::compileExpression(CompilerContext& _context, Expression const& _expression, bool _optimize) -{ - ExpressionCompiler compiler(_context, _optimize); - _expression.accept(compiler); -} - -void ExpressionCompiler::appendTypeConversion(CompilerContext& _context, Type const& _typeOnStack, - Type const& _targetType, bool _cleanupNeeded) -{ - ExpressionCompiler compiler(_context); - 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); - 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 - { - 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.getLocation(), true); - appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.getType()); - if (m_currentLValue.storesReferenceOnStack()) - m_context << eth::Instruction::SWAP1; - } - m_currentLValue.storeValue(*_assignment.getRightHandSide().getType(), _assignment.getLocation()); - m_currentLValue.reset(); - - return false; -} - -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: // ! - m_context << eth::Instruction::ISZERO; - break; - case Token::BitNot: // ~ - m_context << eth::Instruction::NOT; - break; - case Token::Delete: // delete - solAssert(m_currentLValue.isValid(), "LValue not retrieved."); - m_currentLValue.setToZero(_unaryOperation.getLocation()); - m_currentLValue.reset(); - break; - case Token::Inc: // ++ (pre- or postfix) - case Token::Dec: // -- (pre- or postfix) - solAssert(m_currentLValue.isValid(), "LValue not retrieved."); - m_currentLValue.retrieveValue(_unaryOperation.getLocation()); - if (!_unaryOperation.isPrefixOperation()) - { - if (m_currentLValue.storesReferenceOnStack()) - m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2; - else - m_context << eth::Instruction::DUP1; - } - m_context << u256(1); - if (_unaryOperation.getOperator() == Token::Inc) - m_context << eth::Instruction::ADD; - else - m_context << eth::Instruction::SWAP1 << eth::Instruction::SUB; // @todo avoid the swap - // Stack for prefix: [ref] (*ref)+-1 - // Stack for postfix: *ref [ref] (*ref)+-1 - if (m_currentLValue.storesReferenceOnStack()) - m_context << eth::Instruction::SWAP1; - m_currentLValue.storeValue(*_unaryOperation.getType(), _unaryOperation.getLocation(), - !_unaryOperation.isPrefixOperation()); - m_currentLValue.reset(); - break; - case Token::Add: // + - // unary add, so basically no-op - break; - 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) -{ - Expression const& leftExpression = _binaryOperation.getLeftExpression(); - Expression const& rightExpression = _binaryOperation.getRightExpression(); - Type const& commonType = _binaryOperation.getCommonType(); - Token::Value const c_op = _binaryOperation.getOperator(); - - 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 = 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 - auto isLiteral = [](Expression const& _e) - { - return dynamic_cast(&_e) || _e.getType()->getCategory() == Type::Category::IntegerConstant; - }; - bool swap = m_optimize && Token::isCommutativeOp(c_op) && isLiteral(rightExpression) && !isLiteral(leftExpression); - if (swap) - { - leftExpression.accept(*this); - appendTypeConversion(*leftExpression.getType(), commonType, cleanupNeeded); - rightExpression.accept(*this); - appendTypeConversion(*rightExpression.getType(), commonType, cleanupNeeded); - } - else - { - rightExpression.accept(*this); - appendTypeConversion(*rightExpression.getType(), commonType, cleanupNeeded); - leftExpression.accept(*this); - appendTypeConversion(*leftExpression.getType(), commonType, cleanupNeeded); - } - if (Token::isCompareOp(c_op)) - appendCompareOperatorCode(c_op, commonType); - else - appendOrdinaryBinaryOperatorCode(c_op, commonType); - } - - // do not visit the child nodes, we already did that explicitly - return false; -} - -bool ExpressionCompiler::visit(FunctionCall const& _functionCall) -{ - using Location = FunctionType::Location; - if (_functionCall.isTypeConversion()) - { - //@todo struct construction - solAssert(_functionCall.getArguments().size() == 1, ""); - solAssert(_functionCall.getNames().empty(), ""); - Expression const& firstArgument = *_functionCall.getArguments().front(); - firstArgument.accept(*this); - appendTypeConversion(*firstArgument.getType(), *_functionCall.getType()); - } - else - { - FunctionType const& function = dynamic_cast(*_functionCall.getExpression().getType()); - TypePointers const& parameterTypes = function.getParameterTypes(); - vector> const& callArguments = _functionCall.getArguments(); - vector> const& callArgumentNames = _functionCall.getNames(); - if (!function.takesArbitraryParameters()) - solAssert(callArguments.size() == parameterTypes.size(), ""); - - vector> 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: - { - // Calling convention: Caller pushes return address and arguments - // Callee removes them and pushes return values - - eth::AssemblyItem returnLabel = m_context.pushNewTag(); - for (unsigned i = 0; i < arguments.size(); ++i) - { - arguments[i]->accept(*this); - appendTypeConversion(*arguments[i]->getType(), *function.getParameterTypes()[i]); - } - _functionCall.getExpression().accept(*this); - - m_context.appendJump(); - m_context << returnLabel; - - unsigned returnParametersSize = CompilerUtils::getSizeOnStack(function.getReturnParameterTypes()); - // callee adds return parameters, but removes arguments and return label - 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 - for (unsigned i = 1; i < function.getReturnParameterTypes().size(); ++i) - CompilerUtils(m_context).popStackElement(*function.getReturnParameterTypes()[i]); - break; - } - case Location::External: - case Location::Bare: - _functionCall.getExpression().accept(*this); - appendExternalFunctionCall(function, arguments, function.getLocation() == Location::Bare); - break; - case Location::Creation: - { - _functionCall.getExpression().accept(*this); - solAssert(!function.gasSet(), "Gas limit set for contract creation."); - solAssert(function.getReturnParameterTypes().size() == 1, ""); - ContractDefinition const& contract = dynamic_cast( - *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::SetGas: - { - // 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::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); - appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true); - m_context << eth::Instruction::SUICIDE; - break; - case Location::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(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::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 contractAddresses{{Location::ECRecover, 1}, - {Location::SHA256, 2}, - {Location::RIPEMD160, 3}}; - m_context << contractAddresses.find(function.getLocation())->second; - appendExternalFunctionCall(function, arguments, true); - break; - } - default: - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid function type.")); - } - } - return false; -} - -bool ExpressionCompiler::visit(NewExpression const&) -{ - // code is created for the function call (CREATION) only - return false; -} - -void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) -{ - ASTString const& member = _memberAccess.getMemberName(); - switch (_memberAccess.getExpression().getType()->getCategory()) - { - case Type::Category::Contract: - { - bool alsoSearchInteger = false; - ContractType const& type = dynamic_cast(*_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); - m_context << eth::Instruction::BALANCE; - } - else if (member == "send" || member.substr(0, min(member.size(), 4)) == "call") - appendTypeConversion(*_memberAccess.getExpression().getType(), - IntegerType(0, IntegerType::Modifier::Address), true); - else - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer.")); - break; - case Type::Category::Function: - solAssert(!!_memberAccess.getExpression().getType()->getMemberType(member), - "Invalid member access to function."); - break; - 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 == "difficulty") - m_context << eth::Instruction::DIFFICULTY; - else if (member == "number") - m_context << eth::Instruction::NUMBER; - else if (member == "gaslimit") - m_context << eth::Instruction::GASLIMIT; - else if (member == "sender") - m_context << eth::Instruction::CALLER; - else if (member == "value") - m_context << eth::Instruction::CALLVALUE; - else if (member == "origin") - m_context << eth::Instruction::ORIGIN; - else if (member == "gas") - 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: - { - StructType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); - m_context << type.getStorageOffsetOfMember(member) << eth::Instruction::ADD; - m_currentLValue = LValue(m_context, LValue::LValueType::Storage, _memberAccess.getType()); - m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess); - break; - } - case Type::Category::Enum: - { - EnumType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); - m_context << type.getMemberValue(_memberAccess.getMemberName()); - break; - } - case Type::Category::TypeType: - { - TypeType const& type = dynamic_cast(*_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(type.getActualType().get())) - { - ContractDefinition const& contract = contractType->getContractDefinition(); - for (ASTPointer 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(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(*_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.")); - } -} - -bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) -{ - _indexAccess.getBaseExpression().accept(*this); - - Type const& baseType = *_indexAccess.getBaseExpression().getType(); - solAssert(baseType.getCategory() == Type::Category::Mapping, ""); - Type const& keyType = *dynamic_cast(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; -} - -void ExpressionCompiler::endVisit(Identifier const& _identifier) -{ - Declaration const* declaration = _identifier.getReferencedDeclaration(); - if (MagicVariableDeclaration const* magicVar = dynamic_cast(declaration)) - { - if (magicVar->getType()->getCategory() == Type::Category::Contract) - // "this" or "super" - if (!dynamic_cast(*magicVar->getType()).isSuper()) - m_context << eth::Instruction::ADDRESS; - } - else if (FunctionDefinition const* functionDef = dynamic_cast(declaration)) - m_context << m_context.getVirtualFunctionEntryLabel(*functionDef).pushTag(); - else if (dynamic_cast(declaration)) - { - m_currentLValue.fromIdentifier(_identifier, *declaration); - m_currentLValue.retrieveValueIfLValueNotRequested(_identifier); - } - else if (dynamic_cast(declaration)) - { - // no-op - } - else if (dynamic_cast(declaration)) - { - // no-op - } - else if (dynamic_cast(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::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.")); - } -} - -void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryOperation) -{ - 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 (c_op == Token::And) - m_context << eth::Instruction::ISZERO; - eth::AssemblyItem endLabel = m_context.appendConditionalJump(); - m_context << eth::Instruction::POP; - _binaryOperation.getRightExpression().accept(*this); - m_context << endLabel; -} - -void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type const& _type) -{ - if (_operator == Token::Equal || _operator == Token::NotEqual) - { - m_context << eth::Instruction::EQ; - if (_operator == Token::NotEqual) - m_context << eth::Instruction::ISZERO; - } - else - { - IntegerType const& type = dynamic_cast(_type); - bool const c_isSigned = type.isSigned(); - - switch (_operator) - { - case Token::GreaterThanOrEqual: - m_context << (c_isSigned ? eth::Instruction::SLT : eth::Instruction::LT) - << eth::Instruction::ISZERO; - break; - case Token::LessThanOrEqual: - m_context << (c_isSigned ? eth::Instruction::SGT : eth::Instruction::GT) - << eth::Instruction::ISZERO; - break; - case Token::GreaterThan: - m_context << (c_isSigned ? eth::Instruction::SGT : eth::Instruction::GT); - break; - case Token::LessThan: - m_context << (c_isSigned ? eth::Instruction::SLT : eth::Instruction::LT); - break; - default: - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown comparison operator.")); - } - } -} - -void ExpressionCompiler::appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type) -{ - if (Token::isArithmeticOp(_operator)) - appendArithmeticOperatorCode(_operator, _type); - else if (Token::isBitOp(_operator)) - appendBitOperatorCode(_operator); - else if (Token::isShiftOp(_operator)) - appendShiftOperatorCode(_operator); - else - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown binary operator.")); -} - -void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Type const& _type) -{ - IntegerType const& type = dynamic_cast(_type); - bool const c_isSigned = type.isSigned(); - - switch (_operator) - { - case Token::Add: - m_context << eth::Instruction::ADD; - break; - case Token::Sub: - m_context << eth::Instruction::SUB; - break; - case Token::Mul: - m_context << eth::Instruction::MUL; - break; - case Token::Div: - m_context << (c_isSigned ? eth::Instruction::SDIV : eth::Instruction::DIV); - break; - 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.")); - } -} - -void ExpressionCompiler::appendBitOperatorCode(Token::Value _operator) -{ - switch (_operator) - { - case Token::BitOr: - m_context << eth::Instruction::OR; - break; - case Token::BitAnd: - m_context << eth::Instruction::AND; - break; - case Token::BitXor: - m_context << eth::Instruction::XOR; - break; - default: - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown bit operator.")); - } -} - -void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator) -{ - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Shift operators not yet implemented.")); - switch (_operator) - { - case Token::SHL: - break; - case Token::SAR: - break; - default: - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown shift operator.")); - } -} - -void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded) -{ - // For a type extension, we need to remove all higher-order bits that we might have ignored in - // previous operations. - // @todo: store in the AST whether the operand might have "dirty" higher order bits - - if (_typeOnStack == _targetType && !_cleanupNeeded) - return; - Type::Category stackTypeCategory = _typeOnStack.getCategory(); - Type::Category targetTypeCategory = _targetType.getCategory(); - - if (stackTypeCategory == Type::Category::String) - { - StaticStringType const& typeOnStack = dynamic_cast(_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(_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(_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) - { - 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(_targetType); - IntegerType const& typeOnStack = dynamic_cast(_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(_targetType) : addressType; - if (stackTypeCategory == Type::Category::IntegerConstant) - { - IntegerConstantType const& constType = dynamic_cast(_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(_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. - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid type conversion requested.")); -} - -void ExpressionCompiler::appendHighBitsCleanup(IntegerType const& _typeOnStack) -{ - if (_typeOnStack.getNumBits() == 256) - return; - else if (_typeOnStack.isSigned()) - m_context << u256(_typeOnStack.getNumBits() / 8 - 1) << eth::Instruction::SIGNEXTEND; - else - m_context << ((u256(1) << _typeOnStack.getNumBits()) - 1) << eth::Instruction::AND; -} - -void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functionType, - vector> const& _arguments, - bool bare) -{ - solAssert(_functionType.takesArbitraryParameters() || - _arguments.size() == _functionType.getParameterTypes().size(), ""); - - // Assumed stack content here: - // - // 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); - - //@todo only return the first return value for now - Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr : - _functionType.getReturnParameterTypes().front().get(); - 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); - 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 (_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> 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) - { - _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); - } -} - -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::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(*returnType).getValueType(); - } - - unsigned retSizeOnStack = 0; - solAssert(accessorType.getReturnParameterTypes().size() >= 1, ""); - if (StructType const* structType = dynamic_cast(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::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::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 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(_location) - << errinfo_comment("Stack too deep.")); - for (unsigned i = 0; i < m_size; ++i) - *m_context << eth::dupInstruction(stackPos + 1); - break; - } - case LValueType::Storage: - retrieveValueFromStorage(_remove); - break; - case LValueType::Memory: - if (!m_dataType->isValueType()) - break; // no distinction between value and reference for non-value types - 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::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 LValueType::Stack: - { - unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)) - m_size + 1; - if (stackDiff > 16) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_location) - << errinfo_comment("Stack too deep.")); - else if (stackDiff > 0) - for (unsigned i = 0; i < m_size; ++i) - *m_context << eth::swapInstruction(stackDiff) << eth::Instruction::POP; - if (!_move) - retrieveValue(_location); - break; - } - case LValueType::Storage: - // stack layout: value value ... value target_ref - if (m_dataType->isValueType()) - { - 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; - } - } - else - { - solAssert(_sourceType.getCategory() == m_dataType->getCategory(), ""); - if (m_dataType->getCategory() == Type::Category::ByteArray) - { - CompilerUtils(*m_context).copyByteArrayToStorage( - dynamic_cast(*m_dataType), - dynamic_cast(_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(*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 - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location) - << errinfo_comment("Invalid non-value type for assignment.")); - } - break; - case LValueType::Memory: - if (!m_dataType->isValueType()) - break; // no distinction between value and reference for non-value types - 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::setToZero(Location const& _location) const -{ - switch (m_type) - { - 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(*m_dataType)); - else if (m_dataType->getCategory() == Type::Category::Struct) - { - // stack layout: ref - auto const& structType = dynamic_cast(*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::retrieveValueIfLValueNotRequested(Expression const& _expression) -{ - if (!_expression.lvalueRequested()) - { - retrieveValue(_expression.getLocation(), true); - reset(); - } -} - -} -} diff --git a/ExpressionCompiler.h b/ExpressionCompiler.h deleted file mode 100644 index 471d81865..000000000 --- a/ExpressionCompiler.h +++ /dev/null @@ -1,180 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @author Gav Wood - * @date 2014 - * Solidity AST to EVM bytecode compiler for expressions. - */ - -#include -#include -#include -#include -#include -#include - -namespace dev { -namespace eth -{ -class AssemblyItem; // forward -} -namespace solidity { - -// forward declarations -class CompilerContext; -class Type; -class IntegerType; -class ByteArrayType; -class StaticStringType; - -/** - * Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream - * of EVM instructions. It needs a compiler context that is the same for the whole compilation - * unit. - */ -class ExpressionCompiler: private ASTConstVisitor -{ -public: - /// Compile the given @a _expression into the @a _context. - static void compileExpression(CompilerContext& _context, Expression const& _expression, bool _optimize = false); - - /// Appends code to remove dirty higher order bits in case of an implicit promotion to a wider type. - static void appendTypeConversion(CompilerContext& _context, Type const& _typeOnStack, - Type const& _targetType, bool _cleanupNeeded = false); - /// Appends code for a State Variable accessor function - static void appendStateVariableAccessor(CompilerContext& _context, VariableDeclaration const& _varDecl, bool _optimize = false); - -private: - explicit ExpressionCompiler(CompilerContext& _compilerContext, bool _optimize = false): - m_optimize(_optimize), m_context(_compilerContext), m_currentLValue(m_context) {} - - virtual bool visit(Assignment const& _assignment) override; - virtual bool visit(UnaryOperation const& _unaryOperation) override; - virtual bool visit(BinaryOperation const& _binaryOperation) override; - virtual bool visit(FunctionCall const& _functionCall) override; - virtual bool visit(NewExpression const& _newExpression) override; - virtual void endVisit(MemberAccess const& _memberAccess) override; - virtual bool visit(IndexAccess const& _indexAccess) override; - virtual void endVisit(Identifier const& _identifier) override; - virtual void endVisit(Literal const& _literal) override; - - ///@{ - ///@name Append code for various operator types - void appendAndOrOperatorCode(BinaryOperation const& _binaryOperation); - void appendCompareOperatorCode(Token::Value _operator, Type const& _type); - void appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type); - - void appendArithmeticOperatorCode(Token::Value _operator, Type const& _type); - void appendBitOperatorCode(Token::Value _operator); - void appendShiftOperatorCode(Token::Value _operator); - /// @} - - /// Appends an implicit or explicit type conversion. For now this comprises only erasing - /// higher-order bits (@see appendHighBitCleanup) when widening integer. - /// If @a _cleanupNeeded, high order bits cleanup is also done if no type conversion would be - /// necessary. - void appendTypeConversion(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded = false); - //// Appends code that cleans higher-order bits for integer types. - void appendHighBitsCleanup(IntegerType const& _typeOnStack); - - /// Appends code to call a function of the given type with the given arguments. - void appendExternalFunctionCall(FunctionType const& _functionType, std::vector> const& _arguments, - bool bare = false); - /// Appends code that evaluates the given arguments and moves the result to memory. The memory offset is - /// expected to be on the stack and is updated by this call. - void appendArgumentsCopyToMemory(std::vector> const& _arguments, - TypePointers const& _types = {}, - bool _padToWordBoundaries = true, - bool _padExceptionIfFourBytes = false); - /// Appends code that moves a stack element of the given type to memory. The memory offset is - /// expected below the stack element and is updated by this call. - void appendTypeMoveToMemory(Type const& _type, bool _padToWordBoundaries = true); - /// Appends code that evaluates a single expression and moves the result to memory. The memory offset is - /// expected to be on the stack and is updated by this call. - void appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression); - - /// Appends code for a State Variable accessor function - void appendStateVariableAccessor(VariableDeclaration const& _varDecl); - - /** - * Helper class to store and retrieve lvalues to and from various locations. - * All types except STACK store a reference in a slot on the stack, STACK just - * stores the base stack offset of the variable in @a m_baseStackOffset. - */ - class LValue - { - public: - enum class LValueType { None, Stack, Memory, Storage }; - - explicit LValue(CompilerContext& _compilerContext): m_context(&_compilerContext) { reset(); } - LValue(CompilerContext& _compilerContext, LValueType _type, - std::shared_ptr const& _dataType, unsigned _baseStackOffset = 0); - - /// Set type according to the declaration and retrieve the reference. - /// @a _expression is the current expression - void fromIdentifier(Identifier const& _identifier, Declaration const& _declaration); - void reset() { m_type = LValueType::None; m_dataType.reset(); m_baseStackOffset = 0; m_size = 0; } - - bool isValid() const { return m_type != LValueType::None; } - bool isInOnStack() const { return m_type == LValueType::Stack; } - bool isInMemory() const { return m_type == LValueType::Memory; } - bool isInStorage() const { return m_type == LValueType::Storage; } - - /// @returns true if this lvalue reference type occupies a slot on the stack. - bool storesReferenceOnStack() const { return m_type == LValueType::Storage || m_type == LValueType::Memory; } - - /// Copies the value of the current lvalue to the top of the stack and, if @a _remove is true, - /// also removes the reference from the stack (note that is does not reset the type to @a NONE). - /// @a _location source location of the current expression, used for error reporting. - void retrieveValue(Location const& _location, bool _remove = false) const; - /// Moves a value from the stack to the lvalue. Removes the value if @a _move is true. - /// @a _location is the source location of the expression that caused this operation. - /// Stack pre: value [lvalue_ref] - /// Stack post if !_move: value_of(lvalue_ref) - void storeValue(Type const& _sourceType, Location const& _location = Location(), bool _move = false) const; - /// Stores zero in the lvalue. - /// @a _location is the source location of the requested operation - void setToZero(Location const& _location = Location()) const; - /// Convenience function to convert the stored reference to a value and reset type to NONE if - /// the reference was not requested by @a _expression. - void retrieveValueIfLValueNotRequested(Expression const& _expression); - - private: - /// Convenience function to retrieve Value from Storage. Specific version of @ref retrieveValue - void retrieveValueFromStorage(bool _remove = false) const; - /// Copies from a byte array to a byte array in storage, both references on the stack. - void copyByteArrayToStorage(ByteArrayType const& _targetType, ByteArrayType const& _sourceType) const; - - CompilerContext* m_context; - LValueType m_type = LValueType::None; - std::shared_ptr m_dataType; - /// If m_type is STACK, this is base stack offset (@see - /// CompilerContext::getBaseStackOffsetOfVariable) of a local variable. - unsigned m_baseStackOffset = 0; - /// Size of the value of this lvalue on the stack or the storage. - unsigned m_size = 0; - }; - - bool m_optimize; - CompilerContext& m_context; - LValue m_currentLValue; -}; - - -} -} diff --git a/GlobalContext.cpp b/GlobalContext.cpp deleted file mode 100644 index 60de5105f..000000000 --- a/GlobalContext.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @author Gav Wood - * @date 2014 - * Container of the (implicit and explicit) global objects. - */ - -#include -#include -#include -#include - -using namespace std; - -namespace dev -{ -namespace solidity -{ - -GlobalContext::GlobalContext(): -m_magicVariables(vector>{make_shared("block", make_shared(MagicType::Kind::Block)), - make_shared("msg", make_shared(MagicType::Kind::Message)), - make_shared("tx", make_shared(MagicType::Kind::Transaction)), - make_shared("suicide", - make_shared(strings{"address"}, strings{}, FunctionType::Location::Suicide)), - make_shared("sha3", - make_shared(strings(), strings{"hash"}, FunctionType::Location::SHA3, true)), - make_shared("log0", - make_shared(strings{"hash"},strings{}, FunctionType::Location::Log0)), - make_shared("log1", - make_shared(strings{"hash", "hash"},strings{}, FunctionType::Location::Log1)), - make_shared("log2", - make_shared(strings{"hash", "hash", "hash"},strings{}, FunctionType::Location::Log2)), - make_shared("log3", - make_shared(strings{"hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::Log3)), - make_shared("log4", - make_shared(strings{"hash", "hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::Log4)), - make_shared("sha256", - make_shared(strings(), strings{"hash"}, FunctionType::Location::SHA256, true)), - make_shared("ecrecover", - make_shared(strings{"hash", "hash8", "hash", "hash"}, strings{"address"}, FunctionType::Location::ECRecover)), - make_shared("ripemd160", - make_shared(strings(), strings{"hash160"}, FunctionType::Location::RIPEMD160, true))}) -{ -} - -void GlobalContext::setCurrentContract(ContractDefinition const& _contract) -{ - m_currentContract = &_contract; -} - -vector GlobalContext::getDeclarations() const -{ - vector declarations; - declarations.reserve(m_magicVariables.size()); - for (ASTPointer const& variable: m_magicVariables) - declarations.push_back(variable.get()); - return declarations; -} - -MagicVariableDeclaration const* GlobalContext::getCurrentThis() const -{ - if (!m_thisPointer[m_currentContract]) - m_thisPointer[m_currentContract] = make_shared( - "this", make_shared(*m_currentContract)); - return m_thisPointer[m_currentContract].get(); - -} - -MagicVariableDeclaration const* GlobalContext::getCurrentSuper() const -{ - if (!m_superPointer[m_currentContract]) - m_superPointer[m_currentContract] = make_shared( - "super", make_shared(*m_currentContract, true)); - return m_superPointer[m_currentContract].get(); -} - -} -} diff --git a/GlobalContext.h b/GlobalContext.h deleted file mode 100644 index f861c67d7..000000000 --- a/GlobalContext.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @date 2014 - * Container of the (implicit and explicit) global objects. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace dev -{ -namespace solidity -{ - -class Type; // forward - -/** - * Container for all global objects which look like AST nodes, but are not part of the AST - * that is currently being compiled. - * @note must not be destroyed or moved during compilation as its objects can be referenced from - * other objects. - */ -class GlobalContext: private boost::noncopyable -{ -public: - GlobalContext(); - void setCurrentContract(ContractDefinition const& _contract); - MagicVariableDeclaration const* getCurrentThis() const; - MagicVariableDeclaration const* getCurrentSuper() const; - - /// @returns a vector of all implicit global declarations excluding "this". - std::vector getDeclarations() const; - -private: - std::vector> m_magicVariables; - ContractDefinition const* m_currentContract = nullptr; - std::map> mutable m_thisPointer; - std::map> mutable m_superPointer; -}; - -} -} diff --git a/InterfaceHandler.cpp b/InterfaceHandler.cpp deleted file mode 100644 index 99a7db96e..000000000 --- a/InterfaceHandler.cpp +++ /dev/null @@ -1,391 +0,0 @@ - -#include -#include -#include -using namespace std; - -namespace dev -{ -namespace solidity -{ - -/* -- public -- */ - -InterfaceHandler::InterfaceHandler() -{ - m_lastTag = DocTagType::None; -} - -std::unique_ptr InterfaceHandler::getDocumentation(ContractDefinition const& _contractDef, - DocumentationType _type) -{ - switch(_type) - { - case DocumentationType::NatspecUser: - return getUserDocumentation(_contractDef); - case DocumentationType::NatspecDev: - return getDevDocumentation(_contractDef); - case DocumentationType::ABIInterface: - return getABIInterface(_contractDef); - case DocumentationType::ABISolidityInterface: - return getABISolidityInterface(_contractDef); - } - - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type")); - return nullptr; -} - -std::unique_ptr InterfaceHandler::getABIInterface(ContractDefinition const& _contractDef) -{ - Json::Value abi(Json::arrayValue); - for (auto const& it: _contractDef.getInterfaceFunctions()) - { - auto populateParameters = [](vector const& _paramNames, vector const& _paramTypes) - { - Json::Value params(Json::arrayValue); - solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match"); - for (unsigned i = 0; i < _paramNames.size(); ++i) - { - Json::Value param; - param["name"] = _paramNames[i]; - param["type"] = _paramTypes[i]; - params.append(param); - } - return params; - }; - - Json::Value method; - method["type"] = "function"; - method["name"] = it.second->getDeclaration().getName(); - method["constant"] = it.second->isConstant(); - method["inputs"] = populateParameters(it.second->getParameterNames(), - it.second->getParameterTypeNames()); - method["outputs"] = populateParameters(it.second->getReturnParameterNames(), - it.second->getReturnParameterTypeNames()); - abi.append(method); - } - - for (auto const& it: _contractDef.getInterfaceEvents()) - { - Json::Value event; - event["type"] = "event"; - event["name"] = it->getName(); - Json::Value params(Json::arrayValue); - for (auto const& p: it->getParameters()) - { - Json::Value input; - input["name"] = p->getName(); - input["type"] = p->getType()->toString(); - input["indexed"] = p->isIndexed(); - params.append(input); - } - event["inputs"] = params; - abi.append(event); - } - return std::unique_ptr(new std::string(m_writer.write(abi))); -} - -unique_ptr InterfaceHandler::getABISolidityInterface(ContractDefinition const& _contractDef) -{ - string ret = "contract " + _contractDef.getName() + "{"; - for (auto const& it: _contractDef.getInterfaceFunctions()) - { - auto populateParameters = [](vector const& _paramNames, - vector 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(new string(ret + "}")); -} - -std::unique_ptr InterfaceHandler::getUserDocumentation(ContractDefinition const& _contractDef) -{ - Json::Value doc; - Json::Value methods(Json::objectValue); - - for (auto const& it: _contractDef.getInterfaceFunctions()) - { - Json::Value user; - auto strPtr = it.second->getDocumentation(); - if (strPtr) - { - resetUser(); - parseDocString(*strPtr, CommentOwner::Function); - if (!m_notice.empty()) - {// since @notice is the only user tag if missing function should not appear - user["notice"] = Json::Value(m_notice); - methods[it.second->getCanonicalSignature()] = user; - } - } - } - doc["methods"] = methods; - - return std::unique_ptr(new std::string(m_writer.write(doc))); -} - -std::unique_ptr InterfaceHandler::getDevDocumentation(ContractDefinition const& _contractDef) -{ - // LTODO: Somewhere in this function warnings for mismatch of param names - // should be thrown - Json::Value doc; - Json::Value methods(Json::objectValue); - - auto contractDoc = _contractDef.getDocumentation(); - if (contractDoc) - { - m_contractAuthor.clear(); - m_title.clear(); - parseDocString(*contractDoc, CommentOwner::Contract); - - if (!m_contractAuthor.empty()) - doc["author"] = m_contractAuthor; - - if (!m_title.empty()) - doc["title"] = m_title; - } - - for (auto const& it: _contractDef.getInterfaceFunctions()) - { - Json::Value method; - auto strPtr = it.second->getDocumentation(); - if (strPtr) - { - resetDev(); - parseDocString(*strPtr, CommentOwner::Function); - - if (!m_dev.empty()) - method["details"] = Json::Value(m_dev); - - if (!m_author.empty()) - method["author"] = m_author; - - Json::Value params(Json::objectValue); - for (auto const& pair: m_params) - params[pair.first] = pair.second; - - if (!m_params.empty()) - method["params"] = params; - - if (!m_return.empty()) - method["return"] = m_return; - - if (!method.empty()) // add the function, only if we have any documentation to add - methods[it.second->getCanonicalSignature()] = method; - } - } - doc["methods"] = methods; - - return std::unique_ptr(new std::string(m_writer.write(doc))); -} - -/* -- private -- */ -void InterfaceHandler::resetUser() -{ - m_notice.clear(); -} - -void InterfaceHandler::resetDev() -{ - m_dev.clear(); - m_author.clear(); - m_return.clear(); - m_params.clear(); -} - -static inline std::string::const_iterator skipLineOrEOS(std::string::const_iterator _nlPos, - std::string::const_iterator _end) -{ - return (_nlPos == _end) ? _end : ++_nlPos; -} - -std::string::const_iterator InterfaceHandler::parseDocTagLine(std::string::const_iterator _pos, - std::string::const_iterator _end, - std::string& _tagString, - DocTagType _tagType, - bool _appending) -{ - auto nlPos = std::find(_pos, _end, '\n'); - if (_appending && _pos < _end && *_pos != ' ') - _tagString += " "; - std::copy(_pos, nlPos, back_inserter(_tagString)); - m_lastTag = _tagType; - return skipLineOrEOS(nlPos, _end); -} - -std::string::const_iterator InterfaceHandler::parseDocTagParam(std::string::const_iterator _pos, - std::string::const_iterator _end) -{ - // find param name - auto currPos = std::find(_pos, _end, ' '); - if (currPos == _end) - BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("End of param name not found" + std::string(_pos, _end))); - - - auto paramName = std::string(_pos, currPos); - - currPos += 1; - auto nlPos = std::find(currPos, _end, '\n'); - auto paramDesc = std::string(currPos, nlPos); - m_params.push_back(std::make_pair(paramName, paramDesc)); - - m_lastTag = DocTagType::Param; - return skipLineOrEOS(nlPos, _end); -} - -std::string::const_iterator InterfaceHandler::appendDocTagParam(std::string::const_iterator _pos, - std::string::const_iterator _end) -{ - // Should never be called with an empty vector - solAssert(!m_params.empty(), "Internal: Tried to append to empty parameter"); - - auto pair = m_params.back(); - if (_pos < _end && *_pos != ' ') - pair.second += " "; - auto nlPos = std::find(_pos, _end, '\n'); - std::copy(_pos, nlPos, back_inserter(pair.second)); - - m_params.at(m_params.size() - 1) = pair; - - return skipLineOrEOS(nlPos, _end); -} - -std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_iterator _pos, - std::string::const_iterator _end, - std::string const& _tag, - CommentOwner _owner) -{ - // LTODO: need to check for @(start of a tag) between here and the end of line - // for all cases. Also somehow automate list of acceptable tags for each - // language construct since current way does not scale well. - if (m_lastTag == DocTagType::None || _tag != "") - { - if (_tag == "dev") - return parseDocTagLine(_pos, _end, m_dev, DocTagType::Dev, false); - else if (_tag == "notice") - return parseDocTagLine(_pos, _end, m_notice, DocTagType::Notice, false); - else if (_tag == "return") - return parseDocTagLine(_pos, _end, m_return, DocTagType::Return, false); - else if (_tag == "author") - { - if (_owner == CommentOwner::Contract) - return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::Author, false); - else if (_owner == CommentOwner::Function) - return parseDocTagLine(_pos, _end, m_author, DocTagType::Author, false); - else - // LTODO: for now this else makes no sense but later comments will go to more language constructs - BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag is legal only for contracts")); - } - else if (_tag == "title") - { - if (_owner == CommentOwner::Contract) - return parseDocTagLine(_pos, _end, m_title, DocTagType::Title, false); - else - // LTODO: Unknown tag, throw some form of warning and not just an exception - BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag is legal only for contracts")); - } - else if (_tag == "param") - return parseDocTagParam(_pos, _end); - else - // LTODO: Unknown tag, throw some form of warning and not just an exception - BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("Unknown tag " + _tag + " encountered")); - } - else - return appendDocTag(_pos, _end, _owner); -} - -std::string::const_iterator InterfaceHandler::appendDocTag(std::string::const_iterator _pos, - std::string::const_iterator _end, - CommentOwner _owner) -{ - switch (m_lastTag) - { - case DocTagType::Dev: - return parseDocTagLine(_pos, _end, m_dev, DocTagType::Dev, true); - case DocTagType::Notice: - return parseDocTagLine(_pos, _end, m_notice, DocTagType::Notice, true); - case DocTagType::Return: - return parseDocTagLine(_pos, _end, m_return, DocTagType::Return, true); - case DocTagType::Author: - if (_owner == CommentOwner::Contract) - return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::Author, true); - else if (_owner == CommentOwner::Function) - return parseDocTagLine(_pos, _end, m_author, DocTagType::Author, true); - else - // LTODO: Unknown tag, throw some form of warning and not just an exception - BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag in illegal comment")); - case DocTagType::Title: - if (_owner == CommentOwner::Contract) - return parseDocTagLine(_pos, _end, m_title, DocTagType::Title, true); - else - // LTODO: Unknown tag, throw some form of warning and not just an exception - BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag in illegal comment")); - case DocTagType::Param: - return appendDocTagParam(_pos, _end); - default: - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Internal: Illegal documentation tag type")); - break; - } -} - -static inline std::string::const_iterator getFirstSpaceOrNl(std::string::const_iterator _pos, - std::string::const_iterator _end) -{ - auto spacePos = std::find(_pos, _end, ' '); - auto nlPos = std::find(_pos, _end, '\n'); - return (spacePos < nlPos) ? spacePos : nlPos; -} - -void InterfaceHandler::parseDocString(std::string const& _string, CommentOwner _owner) -{ - auto currPos = _string.begin(); - auto end = _string.end(); - - while (currPos != end) - { - auto tagPos = std::find(currPos, end, '@'); - auto nlPos = std::find(currPos, end, '\n'); - - if (tagPos != end && tagPos < nlPos) - { - // we found a tag - auto tagNameEndPos = getFirstSpaceOrNl(tagPos, end); - if (tagNameEndPos == end) - BOOST_THROW_EXCEPTION(DocstringParsingError() << - errinfo_comment("End of tag " + std::string(tagPos, tagNameEndPos) + "not found")); - - currPos = parseDocTag(tagNameEndPos + 1, end, std::string(tagPos + 1, tagNameEndPos), _owner); - } - else if (m_lastTag != DocTagType::None) // continuation of the previous tag - currPos = appendDocTag(currPos, end, _owner); - else if (currPos != end) - { - // if it begins without a tag then consider it as @notice - if (currPos == _string.begin()) - { - currPos = parseDocTag(currPos, end, "notice", CommentOwner::Function); - continue; - } - else if (nlPos == end) //end of text - return; - // else skip the line if a newline was found and we get here - currPos = nlPos + 1; - } - } -} - -} //solidity NS -} // dev NS diff --git a/InterfaceHandler.h b/InterfaceHandler.h deleted file mode 100644 index 6aa3f72d6..000000000 --- a/InterfaceHandler.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Lefteris - * @date 2014 - * Takes the parsed AST and produces the Natspec - * documentation and the ABI interface - * https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format - * - * Can generally deal with JSON files - */ - -#pragma once - -#include -#include -#include - -namespace dev -{ -namespace solidity -{ - -// Forward declarations -class ContractDefinition; -enum class DocumentationType: uint8_t; - -enum class DocTagType: uint8_t -{ - None = 0, - Dev, - Notice, - Param, - Return, - Author, - Title -}; - -enum class CommentOwner -{ - Contract, - Function -}; - -class InterfaceHandler -{ -public: - InterfaceHandler(); - - /// Get the given type of documentation - /// @param _contractDef The contract definition - /// @param _type The type of the documentation. Can be one of the - /// types provided by @c DocumentationType - /// @return A unique pointer contained string with the json - /// representation of provided type - std::unique_ptr getDocumentation(ContractDefinition const& _contractDef, - DocumentationType _type); - /// Get the ABI Interface of the contract - /// @param _contractDef The contract definition - /// @return A unique pointer contained string with the json - /// representation of the contract's ABI Interface - std::unique_ptr getABIInterface(ContractDefinition const& _contractDef); - std::unique_ptr getABISolidityInterface(ContractDefinition const& _contractDef); - /// Get the User documentation of the contract - /// @param _contractDef The contract definition - /// @return A unique pointer contained string with the json - /// representation of the contract's user documentation - std::unique_ptr getUserDocumentation(ContractDefinition const& _contractDef); - /// Get the Developer's documentation of the contract - /// @param _contractDef The contract definition - /// @return A unique pointer contained string with the json - /// representation of the contract's developer documentation - std::unique_ptr getDevDocumentation(ContractDefinition const& _contractDef); - -private: - void resetUser(); - void resetDev(); - - std::string::const_iterator parseDocTagLine(std::string::const_iterator _pos, - std::string::const_iterator _end, - std::string& _tagString, - DocTagType _tagType, - bool _appending); - std::string::const_iterator parseDocTagParam(std::string::const_iterator _pos, - std::string::const_iterator _end); - std::string::const_iterator appendDocTagParam(std::string::const_iterator _pos, - std::string::const_iterator _end); - void parseDocString(std::string const& _string, CommentOwner _owner); - std::string::const_iterator appendDocTag(std::string::const_iterator _pos, - std::string::const_iterator _end, - CommentOwner _owner); - std::string::const_iterator parseDocTag(std::string::const_iterator _pos, - std::string::const_iterator _end, - std::string const& _tag, - CommentOwner _owner); - - Json::StyledWriter m_writer; - - // internal state - DocTagType m_lastTag; - std::string m_notice; - std::string m_dev; - std::string m_return; - std::string m_contractAuthor; - std::string m_author; - std::string m_title; - std::vector> m_params; -}; - -} //solidity NS -} // dev NS diff --git a/NameAndTypeResolver.cpp b/NameAndTypeResolver.cpp deleted file mode 100644 index e19b0bf9e..000000000 --- a/NameAndTypeResolver.cpp +++ /dev/null @@ -1,384 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @date 2014 - * Parser part that determines the declarations corresponding to names and the types of expressions. - */ - -#include -#include -#include - -using namespace std; - -namespace dev -{ -namespace solidity -{ - -NameAndTypeResolver::NameAndTypeResolver(std::vector const& _globals) -{ - for (Declaration const* declaration: _globals) - m_scopes[nullptr].registerDeclaration(*declaration); -} - -void NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit) -{ - // The helper registers all declarations in m_scopes as a side-effect of its construction. - DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit); -} - -void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) -{ - m_currentScope = &m_scopes[nullptr]; - - for (ASTPointer 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 const& structDef: _contract.getDefinedStructs()) - ReferencesResolver resolver(*structDef, *this, &_contract, nullptr); - for (ASTPointer const& enumDef: _contract.getDefinedEnums()) - ReferencesResolver resolver(*enumDef, *this, &_contract, nullptr); - for (ASTPointer const& variable: _contract.getStateVariables()) - ReferencesResolver resolver(*variable, *this, &_contract, nullptr); - for (ASTPointer const& event: _contract.getEvents()) - ReferencesResolver resolver(*event, *this, &_contract, nullptr); - for (ASTPointer const& modifier: _contract.getFunctionModifiers()) - { - m_currentScope = &m_scopes[modifier.get()]; - ReferencesResolver resolver(*modifier, *this, &_contract, nullptr); - } - for (ASTPointer const& function: _contract.getDefinedFunctions()) - { - m_currentScope = &m_scopes[function.get()]; - ReferencesResolver referencesResolver(*function, *this, &_contract, - function->getReturnParameterList().get()); - } -} - -void NameAndTypeResolver::checkTypeRequirements(ContractDefinition& _contract) -{ - for (ASTPointer const& structDef: _contract.getDefinedStructs()) - structDef->checkValidityOfMembers(); - _contract.checkTypeRequirements(); -} - -void NameAndTypeResolver::updateDeclaration(Declaration const& _declaration) -{ - m_scopes[nullptr].registerDeclaration(_declaration, false, true); - solAssert(_declaration.getScope() == nullptr, "Updated declaration outside global scope."); -} - -Declaration const* NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const -{ - auto iterator = m_scopes.find(_scope); - if (iterator == end(m_scopes)) - return nullptr; - return iterator->second.resolveName(_name, false); -} - -Declaration const* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive) -{ - return m_currentScope->resolveName(_name, _recursive); -} - -void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base) -{ - auto iterator = m_scopes.find(&_base); - solAssert(iterator != end(m_scopes), ""); - for (auto const& nameAndDeclaration: iterator->second.getDeclarations()) - { - Declaration const* declaration = nameAndDeclaration.second; - // Import if it was declared in the base, is not the constructor and is visible in derived classes - if (declaration->getScope() == &_base && declaration->getName() != _base.getName() && - declaration->isVisibleInDerivedContracts()) - m_currentScope->registerDeclaration(*declaration); - } -} - -void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract) const -{ - // order in the lists is from derived to base - // list of lists to linearize, the last element is the list of direct bases - list> input(1, {}); - for (ASTPointer const& baseSpecifier: _contract.getBaseContracts()) - { - ASTPointer baseName = baseSpecifier->getName(); - ContractDefinition const* base = dynamic_cast( - 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 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(basesBases.begin(), basesBases.end())); - } - input.back().push_front(&_contract); - vector result = cThreeMerge(input); - if (result.empty()) - BOOST_THROW_EXCEPTION(_contract.createTypeError("Linearization of inheritance graph impossible")); - _contract.setLinearizedBaseContracts(result); -} - -template -vector<_T const*> NameAndTypeResolver::cThreeMerge(list>& _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& _scopes, - ASTNode& _astRoot): - m_scopes(_scopes), m_currentScope(nullptr) -{ - _astRoot.accept(*this); -} - -bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract) -{ - registerDeclaration(_contract, true); - return true; -} - -void DeclarationRegistrationHelper::endVisit(ContractDefinition&) -{ - closeCurrentScope(); -} - -bool DeclarationRegistrationHelper::visit(StructDefinition& _struct) -{ - registerDeclaration(_struct, true); - return true; -} - -void DeclarationRegistrationHelper::endVisit(StructDefinition&) -{ - closeCurrentScope(); -} - -bool DeclarationRegistrationHelper::visit(EnumDefinition& _enum) -{ - registerDeclaration(_enum, true); - return true; -} - -void DeclarationRegistrationHelper::endVisit(EnumDefinition&) -{ - closeCurrentScope(); -} - -bool DeclarationRegistrationHelper::visit(EnumValue& _value) -{ - registerDeclaration(_value, false); - return true; -} - -bool DeclarationRegistrationHelper::visit(FunctionDefinition& _function) -{ - registerDeclaration(_function, true); - m_currentFunction = &_function; - return true; -} - -void DeclarationRegistrationHelper::endVisit(FunctionDefinition&) -{ - m_currentFunction = nullptr; - closeCurrentScope(); -} - -bool DeclarationRegistrationHelper::visit(ModifierDefinition& _modifier) -{ - registerDeclaration(_modifier, true); - m_currentFunction = &_modifier; - return true; -} - -void DeclarationRegistrationHelper::endVisit(ModifierDefinition&) -{ - m_currentFunction = nullptr; - closeCurrentScope(); -} - -void DeclarationRegistrationHelper::endVisit(VariableDefinition& _variableDefinition) -{ - // Register the local variables with the function - // This does not fit here perfectly, but it saves us another AST visit. - solAssert(m_currentFunction, "Variable definition without function."); - m_currentFunction->addLocalVariable(_variableDefinition.getDeclaration()); -} - -bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration) -{ - registerDeclaration(_declaration, false); - return true; -} - -bool DeclarationRegistrationHelper::visit(EventDefinition& _event) -{ - registerDeclaration(_event, true); - return true; -} - -void DeclarationRegistrationHelper::endVisit(EventDefinition&) -{ - closeCurrentScope(); -} - -void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declaration) -{ - map::iterator iter; - bool newlyAdded; - tie(iter, newlyAdded) = m_scopes.emplace(&_declaration, DeclarationContainer(m_currentScope, &m_scopes[m_currentScope])); - solAssert(newlyAdded, "Unable to add new scope."); - m_currentScope = &_declaration; -} - -void DeclarationRegistrationHelper::closeCurrentScope() -{ - solAssert(m_currentScope, "Closed non-existing scope."); - m_currentScope = m_scopes[m_currentScope].getEnclosingDeclaration(); -} - -void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope) -{ - if (!m_scopes[m_currentScope].registerDeclaration(_declaration, !_declaration.isVisibleInContract())) - BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_declaration.getLocation()) - << errinfo_comment("Identifier already declared.")); - //@todo the exception should also contain the location of the first declaration - _declaration.setScope(m_currentScope); - if (_opensScope) - enterNewSubScope(_declaration); -} - -ReferencesResolver::ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver, - ContractDefinition const* _currentContract, - ParameterList const* _returnParameters, bool _allowLazyTypes): - m_resolver(_resolver), m_currentContract(_currentContract), - m_returnParameters(_returnParameters), m_allowLazyTypes(_allowLazyTypes) -{ - _root.accept(*this); -} - -void ReferencesResolver::endVisit(VariableDeclaration& _variable) -{ - // endVisit because the internal type needs resolving if it is a user defined type - // or mapping - if (_variable.getTypeName()) - { - TypePointer type = _variable.getTypeName()->toType(); - // All byte array parameter types should point to call data - if (_variable.isExternalFunctionParameter()) - if (auto const* byteArrayType = dynamic_cast(type.get())) - type = byteArrayType->copyForLocation(ByteArrayType::Location::CallData); - _variable.setType(type); - - if (!_variable.getType()) - BOOST_THROW_EXCEPTION(_variable.getTypeName()->createTypeError("Invalid type name")); - } - else if (!m_allowLazyTypes) - BOOST_THROW_EXCEPTION(_variable.createTypeError("Explicit type needed.")); - // otherwise we have a "var"-declaration whose type is resolved by the first assignment -} - -bool ReferencesResolver::visit(Return& _return) -{ - _return.setFunctionReturnParameters(m_returnParameters); - return true; -} - -bool ReferencesResolver::visit(Mapping&) -{ - return true; -} - -bool ReferencesResolver::visit(UserDefinedTypeName& _typeName) -{ - Declaration const* declaration = m_resolver.getNameFromCurrentScope(_typeName.getName()); - if (!declaration) - BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_typeName.getLocation()) - << errinfo_comment("Undeclared identifier.")); - _typeName.setReferencedDeclaration(*declaration); - return false; -} - -bool ReferencesResolver::visit(Identifier& _identifier) -{ - Declaration const* declaration = m_resolver.getNameFromCurrentScope(_identifier.getName()); - if (!declaration) - BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_identifier.getLocation()) - << errinfo_comment("Undeclared identifier.")); - _identifier.setReferencedDeclaration(*declaration, m_currentContract); - return false; -} - - -} -} diff --git a/NameAndTypeResolver.h b/NameAndTypeResolver.h deleted file mode 100644 index d9ac98ce5..000000000 --- a/NameAndTypeResolver.h +++ /dev/null @@ -1,148 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @date 2014 - * Parser part that determines the declarations corresponding to names and the types of expressions. - */ - -#pragma once - -#include -#include -#include - -#include -#include - -namespace dev -{ -namespace solidity -{ - -/** - * Resolves name references, types and checks types of all expressions. - * Specifically, it checks that all operations are valid for the inferred types. - * An exception is throw on the first error. - */ -class NameAndTypeResolver: private boost::noncopyable -{ -public: - explicit NameAndTypeResolver(std::vector const& _globals); - /// Registers all declarations found in the source unit. - void registerDeclarations(SourceUnit& _sourceUnit); - /// Resolves all names and types referenced from the given contract. - void resolveNamesAndTypes(ContractDefinition& _contract); - /// Check all type requirements in the given contract. - void checkTypeRequirements(ContractDefinition& _contract); - /// Updates the given global declaration (used for "this"). Not to be used with declarations - /// that create their own scope. - void updateDeclaration(Declaration const& _declaration); - - /// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted, - /// the global scope is used (i.e. the one containing only the contract). - /// @returns a pointer to the declaration on success or nullptr on failure. - Declaration const* resolveName(ASTString const& _name, Declaration const* _scope = nullptr) const; - - /// Resolves a name in the "current" scope. Should only be called during the initial - /// resolving phase. - Declaration const* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true); - -private: - void reset(); - - /// Imports all members declared directly in the given contract (i.e. does not import inherited - /// members) into the current scope if they are not present already. - void importInheritedScope(ContractDefinition const& _base); - - /// Computes "C3-Linearization" of base contracts and stores it inside the contract. - void linearizeBaseContracts(ContractDefinition& _contract) const; - /// Computes the C3-merge of the given list of lists of bases. - /// @returns the linearized vector or an empty vector if linearization is not possible. - template - static std::vector<_T const*> cThreeMerge(std::list>& _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 m_scopes; - - DeclarationContainer* m_currentScope = nullptr; -}; - -/** - * Traverses the given AST upon construction and fills _scopes with all declarations inside the - * AST. - */ -class DeclarationRegistrationHelper: private ASTVisitor -{ -public: - DeclarationRegistrationHelper(std::map& _scopes, ASTNode& _astRoot); - -private: - bool visit(ContractDefinition& _contract) override; - void endVisit(ContractDefinition& _contract) override; - bool visit(StructDefinition& _struct) override; - void endVisit(StructDefinition& _struct) override; - bool visit(EnumDefinition& _enum) override; - void endVisit(EnumDefinition& _enum) override; - bool visit(EnumValue& _value) override; - bool visit(FunctionDefinition& _function) override; - void endVisit(FunctionDefinition& _function) override; - bool visit(ModifierDefinition& _modifier) override; - void endVisit(ModifierDefinition& _modifier) override; - void endVisit(VariableDefinition& _variableDefinition) override; - bool visit(VariableDeclaration& _declaration) override; - bool visit(EventDefinition& _event) override; - void endVisit(EventDefinition& _event) override; - - void enterNewSubScope(Declaration const& _declaration); - void closeCurrentScope(); - void registerDeclaration(Declaration& _declaration, bool _opensScope); - - std::map& m_scopes; - Declaration const* m_currentScope; - VariableScope* m_currentFunction; -}; - -/** - * Resolves references to declarations (of variables and types) and also establishes the link - * between a return statement and the return parameter list. - */ -class ReferencesResolver: private ASTVisitor -{ -public: - ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver, - ContractDefinition const* _currentContract, - ParameterList const* _returnParameters, - bool _allowLazyTypes = true); - -private: - virtual void endVisit(VariableDeclaration& _variable) override; - virtual bool visit(Identifier& _identifier) override; - virtual bool visit(UserDefinedTypeName& _typeName) override; - virtual bool visit(Mapping&) override; - virtual bool visit(Return& _return) override; - - NameAndTypeResolver& m_resolver; - ContractDefinition const* m_currentContract; - ParameterList const* m_returnParameters; - bool m_allowLazyTypes; -}; - -} -} diff --git a/Parser.cpp b/Parser.cpp deleted file mode 100644 index 72334c6cc..000000000 --- a/Parser.cpp +++ /dev/null @@ -1,883 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @date 2014 - * Solidity parser. - */ - -#include -#include -#include -#include -#include -#include - -using namespace std; - -namespace dev -{ -namespace solidity -{ - -/// AST node factory that also tracks the begin and end position of an AST node -/// while it is being parsed -class Parser::ASTNodeFactory -{ -public: - ASTNodeFactory(Parser const& _parser): - m_parser(_parser), m_location(_parser.getPosition(), -1, _parser.getSourceName()) {} - - void markEndPosition() { m_location.end = m_parser.getEndPosition(); } - void setLocationEmpty() { m_location.end = m_location.start; } - /// Set the end position to the one of the given node. - void setEndPositionFromNode(ASTPointer const& _node) { m_location.end = _node->getLocation().end; } - - template - ASTPointer createNode(Args&& ... _args) - { - if (m_location.end < 0) - markEndPosition(); - return make_shared(m_location, forward(_args)...); - } - -private: - Parser const& m_parser; - Location m_location; -}; - -ASTPointer Parser::parse(shared_ptr const& _scanner) -{ - m_scanner = _scanner; - ASTNodeFactory nodeFactory(*this); - vector> nodes; - while (_scanner->getCurrentToken() != Token::EOS) - { - switch (m_scanner->getCurrentToken()) - { - case Token::Import: - nodes.push_back(parseImportDirective()); - break; - case Token::Contract: - nodes.push_back(parseContractDefinition()); - break; - default: - BOOST_THROW_EXCEPTION(createParserError(std::string("Expected import directive or contract definition."))); - } - } - return nodeFactory.createNode(nodes); -} - -std::shared_ptr const& Parser::getSourceName() const -{ - return m_scanner->getSourceName(); -} - -int Parser::getPosition() const -{ - return m_scanner->getCurrentLocation().start; -} - -int Parser::getEndPosition() const -{ - return m_scanner->getCurrentLocation().end; -} - -ASTPointer Parser::parseImportDirective() -{ - ASTNodeFactory nodeFactory(*this); - expectToken(Token::Import); - if (m_scanner->getCurrentToken() != Token::StringLiteral) - BOOST_THROW_EXCEPTION(createParserError("Expected string literal (URL).")); - ASTPointer url = getLiteralAndAdvance(); - nodeFactory.markEndPosition(); - expectToken(Token::Semicolon); - return nodeFactory.createNode(url); -} - -ASTPointer Parser::parseContractDefinition() -{ - ASTNodeFactory nodeFactory(*this); - ASTPointer docString; - if (m_scanner->getCurrentCommentLiteral() != "") - docString = make_shared(m_scanner->getCurrentCommentLiteral()); - expectToken(Token::Contract); - ASTPointer name = expectIdentifierToken(); - vector> baseContracts; - vector> structs; - vector> enums; - vector> stateVariables; - vector> functions; - vector> modifiers; - vector> events; - if (m_scanner->getCurrentToken() == Token::Is) - do - { - m_scanner->next(); - baseContracts.push_back(parseInheritanceSpecifier()); - } - while (m_scanner->getCurrentToken() == Token::Comma); - expectToken(Token::LBrace); - while (true) - { - Token::Value currentToken = m_scanner->getCurrentToken(); - if (currentToken == Token::RBrace) - break; - else if (currentToken == Token::Function) - functions.push_back(parseFunctionDefinition(name.get())); - else if (currentToken == Token::Struct) - structs.push_back(parseStructDefinition()); - else if (currentToken == Token::Enum) - enums.push_back(parseEnumDefinition()); - else if (currentToken == Token::Identifier || currentToken == Token::Mapping || - Token::isElementaryTypeName(currentToken)) - { - VarDeclParserOptions options; - options.isStateVariable = true; - stateVariables.push_back(parseVariableDeclaration(options)); - expectToken(Token::Semicolon); - } - else if (currentToken == Token::Modifier) - modifiers.push_back(parseModifierDefinition()); - else if (currentToken == Token::Event) - events.push_back(parseEventDefinition()); - else - BOOST_THROW_EXCEPTION(createParserError("Function, variable, struct or modifier declaration expected.")); - } - nodeFactory.markEndPosition(); - expectToken(Token::RBrace); - return nodeFactory.createNode(name, docString, baseContracts, structs, enums, - stateVariables, functions, modifiers, events); -} - -ASTPointer Parser::parseInheritanceSpecifier() -{ - ASTNodeFactory nodeFactory(*this); - ASTPointer name(parseIdentifier()); - vector> arguments; - if (m_scanner->getCurrentToken() == Token::LParen) - { - m_scanner->next(); - arguments = parseFunctionCallListArguments(); - nodeFactory.markEndPosition(); - expectToken(Token::RParen); - } - else - nodeFactory.setEndPositionFromNode(name); - return nodeFactory.createNode(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 Parser::parseFunctionDefinition(ASTString const* _contractName) -{ - ASTNodeFactory nodeFactory(*this); - ASTPointer docstring; - if (m_scanner->getCurrentCommentLiteral() != "") - docstring = make_shared(m_scanner->getCurrentCommentLiteral()); - - expectToken(Token::Function); - ASTPointer name; - if (m_scanner->getCurrentToken() == Token::LParen) - name = make_shared(); // anonymous function - else - name = expectIdentifierToken(); - ASTPointer parameters(parseParameterList()); - bool isDeclaredConst = false; - Declaration::Visibility visibility(Declaration::Visibility::Default); - vector> modifiers; - while (true) - { - Token::Value token = m_scanner->getCurrentToken(); - if (token == Token::Const) - { - isDeclaredConst = true; - m_scanner->next(); - } - else if (token == Token::Identifier) - modifiers.push_back(parseModifierInvocation()); - else if (Token::isVisibilitySpecifier(token)) - { - if (visibility != Declaration::Visibility::Default) - BOOST_THROW_EXCEPTION(createParserError("Multiple visibility specifiers.")); - visibility = parseVisibilitySpecifier(token); - } - else - break; - } - ASTPointer returnParameters; - if (m_scanner->getCurrentToken() == Token::Returns) - { - bool const permitEmptyParameterList = false; - m_scanner->next(); - returnParameters = parseParameterList(permitEmptyParameterList); - } - else - returnParameters = createEmptyParameterList(); - ASTPointer block = parseBlock(); - nodeFactory.setEndPositionFromNode(block); - bool const c_isConstructor = (_contractName && *name == *_contractName); - return nodeFactory.createNode(name, visibility, c_isConstructor, docstring, - parameters, isDeclaredConst, modifiers, - returnParameters, block); -} - -ASTPointer Parser::parseStructDefinition() -{ - ASTNodeFactory nodeFactory(*this); - expectToken(Token::Struct); - ASTPointer name = expectIdentifierToken(); - vector> members; - expectToken(Token::LBrace); - while (m_scanner->getCurrentToken() != Token::RBrace) - { - members.push_back(parseVariableDeclaration()); - expectToken(Token::Semicolon); - } - nodeFactory.markEndPosition(); - expectToken(Token::RBrace); - return nodeFactory.createNode(name, members); -} - -ASTPointer Parser::parseEnumValue() -{ - ASTNodeFactory nodeFactory(*this); - nodeFactory.markEndPosition(); - return nodeFactory.createNode(expectIdentifierToken()); -} - -ASTPointer Parser::parseEnumDefinition() -{ - ASTNodeFactory nodeFactory(*this); - expectToken(Token::Enum); - ASTPointer name = expectIdentifierToken(); - vector> 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(name, members); -} - -ASTPointer Parser::parseVariableDeclaration(VarDeclParserOptions const& _options) -{ - ASTNodeFactory nodeFactory(*this); - ASTPointer type = parseTypeName(_options.allowVar); - if (type != nullptr) - nodeFactory.setEndPositionFromNode(type); - bool isIndexed = false; - ASTPointer 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(""); - solAssert(type != nullptr, ""); - nodeFactory.setEndPositionFromNode(type); - } - else - identifier = expectIdentifierToken(); - return nodeFactory.createNode(type, identifier, - visibility, _options.isStateVariable, - isIndexed); -} - -ASTPointer Parser::parseModifierDefinition() -{ - ScopeGuard resetModifierFlag([this]() { m_insideModifier = false; }); - m_insideModifier = true; - - ASTNodeFactory nodeFactory(*this); - ASTPointer docstring; - if (m_scanner->getCurrentCommentLiteral() != "") - docstring = make_shared(m_scanner->getCurrentCommentLiteral()); - - expectToken(Token::Modifier); - ASTPointer name(expectIdentifierToken()); - ASTPointer parameters; - if (m_scanner->getCurrentToken() == Token::LParen) - parameters = parseParameterList(); - else - parameters = createEmptyParameterList(); - ASTPointer block = parseBlock(); - nodeFactory.setEndPositionFromNode(block); - return nodeFactory.createNode(name, docstring, parameters, block); -} - -ASTPointer Parser::parseEventDefinition() -{ - ASTNodeFactory nodeFactory(*this); - ASTPointer docstring; - if (m_scanner->getCurrentCommentLiteral() != "") - docstring = make_shared(m_scanner->getCurrentCommentLiteral()); - - expectToken(Token::Event); - ASTPointer name(expectIdentifierToken()); - ASTPointer parameters; - if (m_scanner->getCurrentToken() == Token::LParen) - parameters = parseParameterList(true, true); - else - parameters = createEmptyParameterList(); - nodeFactory.markEndPosition(); - expectToken(Token::Semicolon); - return nodeFactory.createNode(name, docstring, parameters); -} - -ASTPointer Parser::parseModifierInvocation() -{ - ASTNodeFactory nodeFactory(*this); - ASTPointer name(parseIdentifier()); - vector> arguments; - if (m_scanner->getCurrentToken() == Token::LParen) - { - m_scanner->next(); - arguments = parseFunctionCallListArguments(); - nodeFactory.markEndPosition(); - expectToken(Token::RParen); - } - else - nodeFactory.setEndPositionFromNode(name); - return nodeFactory.createNode(name, arguments); -} - -ASTPointer Parser::parseIdentifier() -{ - ASTNodeFactory nodeFactory(*this); - nodeFactory.markEndPosition(); - return nodeFactory.createNode(expectIdentifierToken()); -} - -ASTPointer Parser::parseTypeName(bool _allowVar) -{ - ASTPointer type; - Token::Value token = m_scanner->getCurrentToken(); - if (Token::isElementaryTypeName(token)) - { - type = ASTNodeFactory(*this).createNode(token); - m_scanner->next(); - } - else if (token == Token::Var) - { - if (!_allowVar) - BOOST_THROW_EXCEPTION(createParserError("Expected explicit type name.")); - m_scanner->next(); - } - else if (token == Token::Mapping) - { - type = parseMapping(); - } - else if (token == Token::Identifier) - { - ASTNodeFactory nodeFactory(*this); - nodeFactory.markEndPosition(); - type = nodeFactory.createNode(expectIdentifierToken()); - } - else - BOOST_THROW_EXCEPTION(createParserError("Expected type name")); - return type; -} - -ASTPointer Parser::parseMapping() -{ - ASTNodeFactory nodeFactory(*this); - expectToken(Token::Mapping); - expectToken(Token::LParen); - if (!Token::isElementaryTypeName(m_scanner->getCurrentToken())) - BOOST_THROW_EXCEPTION(createParserError("Expected elementary type name for mapping key type")); - ASTPointer keyType; - keyType = ASTNodeFactory(*this).createNode(m_scanner->getCurrentToken()); - m_scanner->next(); - expectToken(Token::Arrow); - bool const allowVar = false; - ASTPointer valueType = parseTypeName(allowVar); - nodeFactory.markEndPosition(); - expectToken(Token::RParen); - return nodeFactory.createNode(keyType, valueType); -} - -ASTPointer Parser::parseParameterList(bool _allowEmpty, bool _allowIndexed) -{ - ASTNodeFactory nodeFactory(*this); - vector> parameters; - VarDeclParserOptions options; - options.allowIndexed = _allowIndexed; - options.allowEmptyName = true; - expectToken(Token::LParen); - if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RParen) - { - parameters.push_back(parseVariableDeclaration(options)); - while (m_scanner->getCurrentToken() != Token::RParen) - { - expectToken(Token::Comma); - parameters.push_back(parseVariableDeclaration(options)); - } - } - nodeFactory.markEndPosition(); - m_scanner->next(); - return nodeFactory.createNode(parameters); -} - -ASTPointer Parser::parseBlock() -{ - ASTNodeFactory nodeFactory(*this); - expectToken(Token::LBrace); - vector> statements; - while (m_scanner->getCurrentToken() != Token::RBrace) - statements.push_back(parseStatement()); - nodeFactory.markEndPosition(); - expectToken(Token::RBrace); - return nodeFactory.createNode(statements); -} - -ASTPointer Parser::parseStatement() -{ - ASTPointer statement; - switch (m_scanner->getCurrentToken()) - { - case Token::If: - return parseIfStatement(); - case Token::While: - return parseWhileStatement(); - case Token::For: - return parseForStatement(); - case Token::LBrace: - return parseBlock(); - // starting from here, all statements must be terminated by a semicolon - case Token::Continue: - statement = ASTNodeFactory(*this).createNode(); - m_scanner->next(); - break; - case Token::Break: - statement = ASTNodeFactory(*this).createNode(); - m_scanner->next(); - break; - case Token::Return: - { - ASTNodeFactory nodeFactory(*this); - ASTPointer expression; - if (m_scanner->next() != Token::Semicolon) - { - expression = parseExpression(); - nodeFactory.setEndPositionFromNode(expression); - } - statement = nodeFactory.createNode(expression); - break; - } - case Token::Identifier: - if (m_insideModifier && m_scanner->getCurrentLiteral() == "_") - { - statement = ASTNodeFactory(*this).createNode(); - m_scanner->next(); - return statement; - } - // fall-through - default: - statement = parseVarDefOrExprStmt(); - } - expectToken(Token::Semicolon); - return statement; -} - -ASTPointer Parser::parseIfStatement() -{ - ASTNodeFactory nodeFactory(*this); - expectToken(Token::If); - expectToken(Token::LParen); - ASTPointer condition = parseExpression(); - expectToken(Token::RParen); - ASTPointer trueBody = parseStatement(); - ASTPointer falseBody; - if (m_scanner->getCurrentToken() == Token::Else) - { - m_scanner->next(); - falseBody = parseStatement(); - nodeFactory.setEndPositionFromNode(falseBody); - } - else - nodeFactory.setEndPositionFromNode(trueBody); - return nodeFactory.createNode(condition, trueBody, falseBody); -} - -ASTPointer Parser::parseWhileStatement() -{ - ASTNodeFactory nodeFactory(*this); - expectToken(Token::While); - expectToken(Token::LParen); - ASTPointer condition = parseExpression(); - expectToken(Token::RParen); - ASTPointer body = parseStatement(); - nodeFactory.setEndPositionFromNode(body); - return nodeFactory.createNode(condition, body); -} - -ASTPointer Parser::parseForStatement() -{ - ASTNodeFactory nodeFactory(*this); - ASTPointer initExpression; - ASTPointer conditionExpression; - ASTPointer loopExpression; - expectToken(Token::For); - expectToken(Token::LParen); - - // LTODO: Maybe here have some predicate like peekExpression() instead of checking for semicolon and RParen? - if (m_scanner->getCurrentToken() != Token::Semicolon) - initExpression = parseVarDefOrExprStmt(); - expectToken(Token::Semicolon); - - if (m_scanner->getCurrentToken() != Token::Semicolon) - conditionExpression = parseExpression(); - expectToken(Token::Semicolon); - - if (m_scanner->getCurrentToken() != Token::RParen) - loopExpression = parseExpressionStatement(); - expectToken(Token::RParen); - - ASTPointer body = parseStatement(); - nodeFactory.setEndPositionFromNode(body); - return nodeFactory.createNode(initExpression, - conditionExpression, - loopExpression, - body); -} - -ASTPointer Parser::parseVarDefOrExprStmt() -{ - if (peekVariableDefinition()) - return parseVariableDefinition(); - else - return parseExpressionStatement(); -} - -ASTPointer Parser::parseVariableDefinition() -{ - ASTNodeFactory nodeFactory(*this); - VarDeclParserOptions options; - options.allowVar = true; - ASTPointer variable = parseVariableDeclaration(options); - ASTPointer value; - if (m_scanner->getCurrentToken() == Token::Assign) - { - m_scanner->next(); - value = parseExpression(); - nodeFactory.setEndPositionFromNode(value); - } - else - nodeFactory.setEndPositionFromNode(variable); - return nodeFactory.createNode(variable, value); -} - -ASTPointer Parser::parseExpressionStatement() -{ - ASTNodeFactory nodeFactory(*this); - ASTPointer expression = parseExpression(); - nodeFactory.setEndPositionFromNode(expression); - return nodeFactory.createNode(expression); -} - -ASTPointer Parser::parseExpression() -{ - ASTNodeFactory nodeFactory(*this); - ASTPointer expression = parseBinaryExpression(); - if (!Token::isAssignmentOp(m_scanner->getCurrentToken())) - return expression; - Token::Value assignmentOperator = expectAssignmentOperator(); - ASTPointer rightHandSide = parseExpression(); - nodeFactory.setEndPositionFromNode(rightHandSide); - return nodeFactory.createNode(expression, assignmentOperator, rightHandSide); -} - -ASTPointer Parser::parseBinaryExpression(int _minPrecedence) -{ - ASTNodeFactory nodeFactory(*this); - ASTPointer expression = parseUnaryExpression(); - int precedence = Token::precedence(m_scanner->getCurrentToken()); - for (; precedence >= _minPrecedence; --precedence) - while (Token::precedence(m_scanner->getCurrentToken()) == precedence) - { - Token::Value op = m_scanner->getCurrentToken(); - m_scanner->next(); - ASTPointer right = parseBinaryExpression(precedence + 1); - nodeFactory.setEndPositionFromNode(right); - expression = nodeFactory.createNode(expression, op, right); - } - return expression; -} - -ASTPointer Parser::parseUnaryExpression() -{ - ASTNodeFactory nodeFactory(*this); - Token::Value token = m_scanner->getCurrentToken(); - if (Token::isUnaryOp(token) || Token::isCountOp(token)) - { - // prefix expression - m_scanner->next(); - ASTPointer subExpression = parseUnaryExpression(); - nodeFactory.setEndPositionFromNode(subExpression); - return nodeFactory.createNode(token, subExpression, true); - } - else - { - // potential postfix expression - ASTPointer subExpression = parseLeftHandSideExpression(); - token = m_scanner->getCurrentToken(); - if (!Token::isCountOp(token)) - return subExpression; - nodeFactory.markEndPosition(); - m_scanner->next(); - return nodeFactory.createNode(token, subExpression, false); - } -} - -ASTPointer Parser::parseLeftHandSideExpression() -{ - ASTNodeFactory nodeFactory(*this); - ASTPointer expression; - if (m_scanner->getCurrentToken() == Token::New) - { - expectToken(Token::New); - ASTPointer contractName(parseIdentifier()); - nodeFactory.setEndPositionFromNode(contractName); - expression = nodeFactory.createNode(contractName); - } - else - expression = parsePrimaryExpression(); - - while (true) - { - switch (m_scanner->getCurrentToken()) - { - case Token::LBrack: - { - m_scanner->next(); - ASTPointer index = parseExpression(); - nodeFactory.markEndPosition(); - expectToken(Token::RBrack); - expression = nodeFactory.createNode(expression, index); - } - break; - case Token::Period: - { - m_scanner->next(); - nodeFactory.markEndPosition(); - expression = nodeFactory.createNode(expression, expectIdentifierToken()); - } - break; - case Token::LParen: - { - m_scanner->next(); - vector> arguments; - vector> names; - std::tie(arguments, names) = parseFunctionCallArguments(); - nodeFactory.markEndPosition(); - expectToken(Token::RParen); - expression = nodeFactory.createNode(expression, arguments, names); - } - break; - default: - return expression; - } - } -} - -ASTPointer Parser::parsePrimaryExpression() -{ - ASTNodeFactory nodeFactory(*this); - Token::Value token = m_scanner->getCurrentToken(); - ASTPointer expression; - switch (token) - { - case Token::TrueLiteral: - case Token::FalseLiteral: - expression = nodeFactory.createNode(token, getLiteralAndAdvance()); - break; - case Token::Number: - if (Token::isEtherSubdenomination(m_scanner->peekNextToken())) - { - ASTPointer literal = getLiteralAndAdvance(); - nodeFactory.markEndPosition(); - Literal::SubDenomination subdenomination = static_cast(m_scanner->getCurrentToken()); - m_scanner->next(); - expression = nodeFactory.createNode(token, literal, subdenomination); - break; - } - // fall-through - case Token::StringLiteral: - nodeFactory.markEndPosition(); - expression = nodeFactory.createNode(token, getLiteralAndAdvance()); - break; - case Token::Identifier: - nodeFactory.markEndPosition(); - expression = nodeFactory.createNode(getLiteralAndAdvance()); - break; - case Token::LParen: - { - m_scanner->next(); - ASTPointer expression = parseExpression(); - expectToken(Token::RParen); - return expression; - } - default: - if (Token::isElementaryTypeName(token)) - { - // used for casts - expression = nodeFactory.createNode(token); - m_scanner->next(); - } - else - { - BOOST_THROW_EXCEPTION(createParserError("Expected primary expression.")); - return ASTPointer(); // this is not reached - } - break; - } - return expression; -} - -vector> Parser::parseFunctionCallListArguments() -{ - vector> arguments; - if (m_scanner->getCurrentToken() != Token::RParen) - { - arguments.push_back(parseExpression()); - while (m_scanner->getCurrentToken() != Token::RParen) - { - expectToken(Token::Comma); - arguments.push_back(parseExpression()); - } - } - return arguments; -} - -pair>, vector>> Parser::parseFunctionCallArguments() -{ - pair>, vector>> ret; - Token::Value token = m_scanner->getCurrentToken(); - if (token == Token::LBrace) - { - // call({arg1 : 1, arg2 : 2 }) - expectToken(Token::LBrace); - while (m_scanner->getCurrentToken() != Token::RBrace) - { - ret.second.push_back(expectIdentifierToken()); - expectToken(Token::Colon); - ret.first.push_back(parseExpression()); - - if (m_scanner->getCurrentToken() == Token::Comma) - expectToken(Token::Comma); - else - break; - } - expectToken(Token::RBrace); - } - else - ret.first = parseFunctionCallListArguments(); - return ret; -} - - -bool Parser::peekVariableDefinition() -{ - // distinguish between variable definition (and potentially assignment) and expression statement - // (which include assignments to other expressions and pre-declared variables) - // We have a variable definition if we get a keyword that specifies a type name, or - // in the case of a user-defined type, we have two identifiers following each other. - return (m_scanner->getCurrentToken() == Token::Mapping || - m_scanner->getCurrentToken() == Token::Var || - ((Token::isElementaryTypeName(m_scanner->getCurrentToken()) || - m_scanner->getCurrentToken() == Token::Identifier) && - m_scanner->peekNextToken() == Token::Identifier)); -} - -void Parser::expectToken(Token::Value _value) -{ - if (m_scanner->getCurrentToken() != _value) - BOOST_THROW_EXCEPTION(createParserError(string("Expected token ") + string(Token::getName(_value)))); - m_scanner->next(); -} - -Token::Value Parser::expectAssignmentOperator() -{ - Token::Value op = m_scanner->getCurrentToken(); - if (!Token::isAssignmentOp(op)) - BOOST_THROW_EXCEPTION(createParserError("Expected assignment operator")); - m_scanner->next(); - return op; -} - -ASTPointer Parser::expectIdentifierToken() -{ - if (m_scanner->getCurrentToken() != Token::Identifier) - BOOST_THROW_EXCEPTION(createParserError("Expected identifier")); - return getLiteralAndAdvance(); -} - -ASTPointer Parser::getLiteralAndAdvance() -{ - ASTPointer identifier = make_shared(m_scanner->getCurrentLiteral()); - m_scanner->next(); - return identifier; -} - -ASTPointer Parser::createEmptyParameterList() -{ - ASTNodeFactory nodeFactory(*this); - nodeFactory.setLocationEmpty(); - return nodeFactory.createNode(vector>()); -} - -ParserError Parser::createParserError(string const& _description) const -{ - return ParserError() << errinfo_sourceLocation(Location(getPosition(), getPosition(), getSourceName())) - << errinfo_comment(_description); -} - - -} -} diff --git a/Parser.h b/Parser.h deleted file mode 100644 index 1bb4ea977..000000000 --- a/Parser.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @date 2014 - * Solidity parser. - */ - -#pragma once - -#include "libsolidity/AST.h" - -namespace dev -{ -namespace solidity -{ - -class Scanner; - -class Parser -{ -public: - ASTPointer parse(std::shared_ptr const& _scanner); - std::shared_ptr const& getSourceName() const; - -private: - class ASTNodeFactory; - - /// Start position of the current token - int getPosition() const; - /// End position of the current token - int getEndPosition() const; - - struct VarDeclParserOptions { - VarDeclParserOptions() {} - bool allowVar = false; - bool isStateVariable = false; - bool allowIndexed = false; - bool allowEmptyName = false; - }; - - ///@{ - ///@name Parsing functions for the AST nodes - ASTPointer parseImportDirective(); - ASTPointer parseContractDefinition(); - ASTPointer parseInheritanceSpecifier(); - Declaration::Visibility parseVisibilitySpecifier(Token::Value _token); - ASTPointer parseFunctionDefinition(ASTString const* _contractName); - ASTPointer parseStructDefinition(); - ASTPointer parseEnumDefinition(); - ASTPointer parseEnumValue(); - ASTPointer parseVariableDeclaration(VarDeclParserOptions const& _options = VarDeclParserOptions()); - ASTPointer parseModifierDefinition(); - ASTPointer parseEventDefinition(); - ASTPointer parseModifierInvocation(); - ASTPointer parseIdentifier(); - ASTPointer parseTypeName(bool _allowVar); - ASTPointer parseMapping(); - ASTPointer parseParameterList(bool _allowEmpty = true, bool _allowIndexed = false); - ASTPointer parseBlock(); - ASTPointer parseStatement(); - ASTPointer parseIfStatement(); - ASTPointer parseWhileStatement(); - ASTPointer parseForStatement(); - ASTPointer parseVarDefOrExprStmt(); - ASTPointer parseVariableDefinition(); - ASTPointer parseExpressionStatement(); - ASTPointer parseExpression(); - ASTPointer parseBinaryExpression(int _minPrecedence = 4); - ASTPointer parseUnaryExpression(); - ASTPointer parseLeftHandSideExpression(); - ASTPointer parsePrimaryExpression(); - std::vector> parseFunctionCallListArguments(); - std::pair>, std::vector>> parseFunctionCallArguments(); - ///@} - - ///@{ - ///@name Helper functions - - /// Peeks ahead in the scanner to determine if a variable definition is going to follow - bool peekVariableDefinition(); - - /// If current token value is not _value, throw exception otherwise advance token. - void expectToken(Token::Value _value); - Token::Value expectAssignmentOperator(); - ASTPointer expectIdentifierToken(); - ASTPointer getLiteralAndAdvance(); - ///@} - - /// Creates an empty ParameterList at the current location (used if parameters can be omitted). - ASTPointer 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 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 deleted file mode 100644 index fbe3ea974..000000000 --- a/Scanner.cpp +++ /dev/null @@ -1,769 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . - - This file is derived from the file "scanner.cc", which was part of the - V8 project. The original copyright header follows: - - Copyright 2006-2012, the V8 project authors. All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ -/** - * @author Christian - * @date 2014 - * Solidity scanner. - */ - -#include -#include -#include -#include - -using namespace std; - -namespace dev -{ -namespace solidity -{ - -namespace -{ -bool isDecimalDigit(char c) -{ - return '0' <= c && c <= '9'; -} -bool isHexDigit(char c) -{ - return isDecimalDigit(c) - || ('a' <= c && c <= 'f') - || ('A' <= c && c <= 'F'); -} -bool isLineTerminator(char c) -{ - return c == '\n'; -} -bool isWhiteSpace(char c) -{ - return c == ' ' || c == '\n' || c == '\t' || c == '\r'; -} -bool isIdentifierStart(char c) -{ - return c == '_' || c == '$' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); -} -bool isIdentifierPart(char c) -{ - return isIdentifierStart(c) || isDecimalDigit(c); -} - -int hexValue(char c) -{ - if (c >= '0' && c <= '9') - return c - '0'; - else if (c >= 'a' && c <= 'f') - return c - 'a' + 10; - else if (c >= 'A' && c <= 'F') - return c - 'A' + 10; - else return -1; -} -} // end anonymous namespace - - - -/// Scoped helper for literal recording. Automatically drops the literal -/// if aborting the scanning before it's complete. -enum LiteralType { - LITERAL_TYPE_STRING, - LITERAL_TYPE_NUMBER, // not really different from string type in behaviour - LITERAL_TYPE_COMMENT -}; - -class LiteralScope -{ -public: - explicit LiteralScope(Scanner* _self, enum LiteralType _type): m_type(_type) - , m_scanner(_self) - , m_complete(false) - { - if (_type == LITERAL_TYPE_COMMENT) - m_scanner->m_nextSkippedComment.literal.clear(); - else - m_scanner->m_nextToken.literal.clear(); - } - ~LiteralScope() - { - if (!m_complete) - { - if (m_type == LITERAL_TYPE_COMMENT) - m_scanner->m_nextSkippedComment.literal.clear(); - else - m_scanner->m_nextToken.literal.clear(); - } - } - void complete() { m_complete = true; } - -private: - enum LiteralType m_type; - Scanner* m_scanner; - bool m_complete; -}; // end of LiteralScope class - - -void Scanner::reset(CharStream const& _source, string const& _sourceName) -{ - m_source = _source; - m_sourceName = make_shared(_sourceName); - reset(); -} - -void Scanner::reset() -{ - m_source.reset(); - m_char = m_source.get(); - skipWhitespace(); - scanToken(); - next(); -} - -bool Scanner::scanHexByte(char& o_scannedByte) -{ - char x = 0; - for (int i = 0; i < 2; i++) - { - int d = hexValue(m_char); - if (d < 0) - { - rollback(i); - return false; - } - x = x * 16 + d; - advance(); - } - o_scannedByte = x; - return true; -} - - -// Ensure that tokens can be stored in a byte. -BOOST_STATIC_ASSERT(Token::NUM_TOKENS <= 0x100); - -Token::Value Scanner::next() -{ - m_currentToken = m_nextToken; - m_skippedComment = m_nextSkippedComment; - scanToken(); - - return m_currentToken.token; -} - -Token::Value Scanner::selectToken(char _next, Token::Value _then, Token::Value _else) -{ - advance(); - if (m_char == _next) - return selectToken(_then); - else - return _else; -} - -bool Scanner::skipWhitespace() -{ - int const startPosition = getSourcePos(); - while (isWhiteSpace(m_char)) - advance(); - // Return whether or not we skipped any characters. - return getSourcePos() != startPosition; -} - -bool Scanner::skipWhitespaceExceptLF() -{ - int const startPosition = getSourcePos(); - while (isWhiteSpace(m_char) && !isLineTerminator(m_char)) - advance(); - // Return whether or not we skipped any characters. - return getSourcePos() != startPosition; -} - -Token::Value Scanner::skipSingleLineComment() -{ - // The line terminator at the end of the line is not considered - // to be part of the single-line comment; it is recognized - // separately by the lexical grammar and becomes part of the - // stream of input elements for the syntactic grammar - while (advance() && !isLineTerminator(m_char)) { }; - return Token::Whitespace; -} - -Token::Value Scanner::scanSingleLineDocComment() -{ - LiteralScope literal(this, LITERAL_TYPE_COMMENT); - advance(); //consume the last '/' at /// - skipWhitespaceExceptLF(); - while (!isSourcePastEndOfInput()) - { - if (isLineTerminator(m_char)) - { - // check if next line is also a documentation comment - skipWhitespace(); - if (!m_source.isPastEndOfInput(3) && - m_source.get(0) == '/' && - m_source.get(1) == '/' && - m_source.get(2) == '/') - { - addCommentLiteralChar('\n'); - m_char = m_source.advanceAndGet(3); - } - else - break; // next line is not a documentation comment, we are done - - } - addCommentLiteralChar(m_char); - advance(); - } - literal.complete(); - return Token::CommentLiteral; -} - -Token::Value Scanner::skipMultiLineComment() -{ - advance(); - while (!isSourcePastEndOfInput()) - { - char ch = m_char; - advance(); - - // If we have reached the end of the multi-line comment, we - // consume the '/' and insert a whitespace. This way all - // multi-line comments are treated as whitespace. - if (ch == '*' && m_char == '/') - { - m_char = ' '; - return Token::Whitespace; - } - } - // Unterminated multi-line comment. - return Token::Illegal; -} - -Token::Value Scanner::scanMultiLineDocComment() -{ - LiteralScope literal(this, LITERAL_TYPE_COMMENT); - bool endFound = false; - bool charsAdded = false; - - while (!isSourcePastEndOfInput()) - { - //handle newlines in multline comments - if (isLineTerminator(m_char)) - { - skipWhitespace(); - if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) != '/') - { // skip first '*' in subsequent lines - if (charsAdded) - addCommentLiteralChar('\n'); - m_char = m_source.advanceAndGet(2); - } - else if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) == '/') - { // if after newline the comment ends, don't insert the newline - m_char = m_source.advanceAndGet(2); - endFound = true; - break; - } - else if (charsAdded) - addCommentLiteralChar('\n'); - } - - if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) == '/') - { - m_char = m_source.advanceAndGet(2); - endFound = true; - break; - } - addCommentLiteralChar(m_char); - charsAdded = true; - advance(); - } - literal.complete(); - if (!endFound) - return Token::Illegal; - else - return Token::CommentLiteral; -} - -Token::Value Scanner::scanSlash() -{ - int firstSlashPosition = getSourcePos(); - advance(); - if (m_char == '/') - { - if (!advance()) /* double slash comment directly before EOS */ - return Token::Whitespace; - else if (m_char == '/') - { - // doxygen style /// comment - Token::Value comment; - m_nextSkippedComment.location.start = firstSlashPosition; - comment = scanSingleLineDocComment(); - m_nextSkippedComment.location.end = getSourcePos(); - m_nextSkippedComment.token = comment; - return Token::Whitespace; - } - else - return skipSingleLineComment(); - } - else if (m_char == '*') - { - // doxygen style /** natspec comment - if (!advance()) /* slash star comment before EOS */ - return Token::Whitespace; - else if (m_char == '*') - { - advance(); //consume the last '*' at /** - skipWhitespaceExceptLF(); - - // special case of a closed normal multiline comment - if (!m_source.isPastEndOfInput() && m_source.get(0) == '/') - advance(); //skip the closing slash - else // we actually have a multiline documentation comment - { - Token::Value comment; - m_nextSkippedComment.location.start = firstSlashPosition; - comment = scanMultiLineDocComment(); - m_nextSkippedComment.location.end = getSourcePos(); - m_nextSkippedComment.token = comment; - } - return Token::Whitespace; - } - else - return skipMultiLineComment(); - } - else if (m_char == '=') - return selectToken(Token::AssignDiv); - else - return Token::Div; -} - -void Scanner::scanToken() -{ - m_nextToken.literal.clear(); - m_nextSkippedComment.literal.clear(); - Token::Value token; - do - { - // Remember the position of the next token - m_nextToken.location.start = getSourcePos(); - switch (m_char) - { - case '\n': // fall-through - case ' ': - case '\t': - token = selectToken(Token::Whitespace); - break; - case '"': - case '\'': - token = scanString(); - break; - case '<': - // < <= << <<= - advance(); - if (m_char == '=') - token = selectToken(Token::LessThanOrEqual); - else if (m_char == '<') - token = selectToken('=', Token::AssignShl, Token::SHL); - else - token = Token::LessThan; - break; - case '>': - // > >= >> >>= >>> >>>= - advance(); - if (m_char == '=') - token = selectToken(Token::GreaterThanOrEqual); - else if (m_char == '>') - { - // >> >>= >>> >>>= - advance(); - if (m_char == '=') - token = selectToken(Token::AssignSar); - else if (m_char == '>') - token = selectToken('=', Token::AssignShr, Token::SHR); - else - token = Token::SAR; - } - else - token = Token::GreaterThan; - break; - case '=': - // = == => - advance(); - if (m_char == '=') - token = selectToken(Token::Equal); - else if (m_char == '>') - token = selectToken(Token::Arrow); - else - token = Token::Assign; - break; - case '!': - // ! != - advance(); - if (m_char == '=') - token = selectToken(Token::NotEqual); - else - token = Token::Not; - break; - case '+': - // + ++ += - advance(); - if (m_char == '+') - token = selectToken(Token::Inc); - else if (m_char == '=') - token = selectToken(Token::AssignAdd); - else - token = Token::Add; - break; - case '-': - // - -- -= - advance(); - if (m_char == '-') - token = selectToken(Token::Dec); - else if (m_char == '=') - token = selectToken(Token::AssignSub); - else - token = Token::Sub; - break; - case '*': - // * ** *= - advance(); - if (m_char == '*') - token = selectToken(Token::Exp); - else if (m_char == '=') - token = selectToken(Token::AssignMul); - else - token = Token::Mul; - break; - case '%': - // % %= - token = selectToken('=', Token::AssignMod, Token::Mod); - break; - case '/': - // / // /* /= - token = scanSlash(); - break; - case '&': - // & && &= - advance(); - if (m_char == '&') - token = selectToken(Token::And); - else if (m_char == '=') - token = selectToken(Token::AssignBitAnd); - else - token = Token::BitAnd; - break; - case '|': - // | || |= - advance(); - if (m_char == '|') - token = selectToken(Token::Or); - else if (m_char == '=') - token = selectToken(Token::AssignBitOr); - else - token = Token::BitOr; - break; - case '^': - // ^ ^= - token = selectToken('=', Token::AssignBitXor, Token::BitXor); - break; - case '.': - // . Number - advance(); - if (isDecimalDigit(m_char)) - token = scanNumber('.'); - else - token = Token::Period; - break; - case ':': - token = selectToken(Token::Colon); - break; - case ';': - token = selectToken(Token::Semicolon); - break; - case ',': - token = selectToken(Token::Comma); - break; - case '(': - token = selectToken(Token::LParen); - break; - case ')': - token = selectToken(Token::RParen); - break; - case '[': - token = selectToken(Token::LBrack); - break; - case ']': - token = selectToken(Token::RBrack); - break; - case '{': - token = selectToken(Token::LBrace); - break; - case '}': - token = selectToken(Token::RBrace); - break; - case '?': - token = selectToken(Token::Conditional); - break; - case '~': - token = selectToken(Token::BitNot); - break; - default: - if (isIdentifierStart(m_char)) - token = scanIdentifierOrKeyword(); - else if (isDecimalDigit(m_char)) - token = scanNumber(); - else if (skipWhitespace()) - token = Token::Whitespace; - else if (isSourcePastEndOfInput()) - token = Token::EOS; - else - token = selectToken(Token::Illegal); - break; - } - // Continue scanning for tokens as long as we're just skipping - // whitespace. - } - while (token == Token::Whitespace); - m_nextToken.location.end = getSourcePos(); - m_nextToken.token = token; -} - -bool Scanner::scanEscape() -{ - char c = m_char; - advance(); - // Skip escaped newlines. - if (isLineTerminator(c)) - return true; - switch (c) - { - case '\'': // fall through - case '"': // fall through - case '\\': - break; - case 'b': - c = '\b'; - break; - case 'f': - c = '\f'; - break; - case 'n': - c = '\n'; - break; - case 'r': - c = '\r'; - break; - case 't': - c = '\t'; - break; - case 'v': - c = '\v'; - break; - case 'x': - if (!scanHexByte(c)) - return false; - break; - } - - addLiteralChar(c); - return true; -} - -Token::Value Scanner::scanString() -{ - char const quote = m_char; - advance(); // consume quote - LiteralScope literal(this, LITERAL_TYPE_STRING); - while (m_char != quote && !isSourcePastEndOfInput() && !isLineTerminator(m_char)) - { - char c = m_char; - advance(); - if (c == '\\') - { - if (isSourcePastEndOfInput() || !scanEscape()) - return Token::Illegal; - } - else - addLiteralChar(c); - } - if (m_char != quote) - return Token::Illegal; - literal.complete(); - advance(); // consume quote - return Token::StringLiteral; -} - -void Scanner::scanDecimalDigits() -{ - while (isDecimalDigit(m_char)) - addLiteralCharAndAdvance(); -} - -Token::Value Scanner::scanNumber(char _charSeen) -{ - enum { DECIMAL, HEX, BINARY } kind = DECIMAL; - LiteralScope literal(this, LITERAL_TYPE_NUMBER); - if (_charSeen == '.') - { - // we have already seen a decimal point of the float - addLiteralChar('.'); - scanDecimalDigits(); // we know we have at least one digit - } - else - { - solAssert(_charSeen == 0, ""); - // if the first character is '0' we must check for octals and hex - if (m_char == '0') - { - addLiteralCharAndAdvance(); - // either 0, 0exxx, 0Exxx, 0.xxx or a hex number - if (m_char == 'x' || m_char == 'X') - { - // hex number - kind = HEX; - addLiteralCharAndAdvance(); - if (!isHexDigit(m_char)) - return Token::Illegal; // we must have at least one hex digit after 'x'/'X' - while (isHexDigit(m_char)) - addLiteralCharAndAdvance(); - } - } - // Parse decimal digits and allow trailing fractional part. - if (kind == DECIMAL) - { - scanDecimalDigits(); // optional - if (m_char == '.') - { - addLiteralCharAndAdvance(); - scanDecimalDigits(); // optional - } - } - } - // scan exponent, if any - if (m_char == 'e' || m_char == 'E') - { - solAssert(kind != HEX, "'e'/'E' must be scanned as part of the hex number"); - if (kind != DECIMAL) - return Token::Illegal; - // scan exponent - addLiteralCharAndAdvance(); - if (m_char == '+' || m_char == '-') - addLiteralCharAndAdvance(); - if (!isDecimalDigit(m_char)) - return Token::Illegal; // we must have at least one decimal digit after 'e'/'E' - scanDecimalDigits(); - } - // The source character immediately following a numeric literal must - // not be an identifier start or a decimal digit; see ECMA-262 - // section 7.8.3, page 17 (note that we read only one decimal digit - // if the value is 0). - if (isDecimalDigit(m_char) || isIdentifierStart(m_char)) - return Token::Illegal; - literal.complete(); - return Token::Number; -} - -Token::Value Scanner::scanIdentifierOrKeyword() -{ - solAssert(isIdentifierStart(m_char), ""); - LiteralScope literal(this, LITERAL_TYPE_STRING); - addLiteralCharAndAdvance(); - // Scan the rest of the identifier characters. - while (isIdentifierPart(m_char)) - addLiteralCharAndAdvance(); - literal.complete(); - return Token::fromIdentifierOrKeyword(m_nextToken.literal); -} - -char CharStream::advanceAndGet(size_t _chars) -{ - if (isPastEndOfInput()) - return 0; - m_pos += _chars; - if (isPastEndOfInput()) - return 0; - return m_source[m_pos]; -} - -char CharStream::rollback(size_t _amount) -{ - solAssert(m_pos >= _amount, ""); - m_pos -= _amount; - return get(); -} - -string CharStream::getLineAtPosition(int _position) const -{ - // if _position points to \n, it returns the line before the \n - using size_type = string::size_type; - size_type searchStart = min(m_source.size(), _position); - if (searchStart > 0) - searchStart--; - size_type lineStart = m_source.rfind('\n', searchStart); - if (lineStart == string::npos) - lineStart = 0; - else - lineStart++; - return m_source.substr(lineStart, min(m_source.find('\n', lineStart), - m_source.size()) - lineStart); -} - -tuple CharStream::translatePositionToLineColumn(int _position) const -{ - using size_type = string::size_type; - size_type searchPosition = min(m_source.size(), _position); - int lineNumber = count(m_source.begin(), m_source.begin() + searchPosition, '\n'); - size_type lineStart; - if (searchPosition == 0) - lineStart = 0; - else - { - lineStart = m_source.rfind('\n', searchPosition - 1); - lineStart = lineStart == string::npos ? 0 : lineStart + 1; - } - return tuple(lineNumber, searchPosition - lineStart); -} - - -} -} diff --git a/Scanner.h b/Scanner.h deleted file mode 100644 index d93b79df0..000000000 --- a/Scanner.h +++ /dev/null @@ -1,224 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . - - This file is derived from the file "scanner.h", which was part of the - V8 project. The original copyright header follows: - - Copyright 2006-2012, the V8 project authors. All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ -/** - * @author Christian - * @date 2014 - * Solidity scanner. - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace dev -{ -namespace solidity -{ - - -class AstRawString; -class AstValueFactory; -class ParserRecorder; - -class CharStream -{ -public: - CharStream(): m_pos(0) {} - explicit CharStream(std::string const& _source): m_source(_source), m_pos(0) {} - int getPos() const { return m_pos; } - bool isPastEndOfInput(size_t _charsForward = 0) const { return (m_pos + _charsForward) >= m_source.size(); } - char get(size_t _charsForward = 0) const { return m_source[m_pos + _charsForward]; } - char advanceAndGet(size_t _chars=1); - char rollback(size_t _amount); - - void reset() { m_pos = 0; } - - ///@{ - ///@name Error printing helper functions - /// Functions that help pretty-printing parse errors - /// Do only use in error cases, they are quite expensive. - std::string getLineAtPosition(int _position) const; - std::tuple translatePositionToLineColumn(int _position) const; - ///@} - -private: - std::string m_source; - size_t m_pos; -}; - - - -class Scanner -{ - friend class LiteralScope; -public: - - explicit Scanner(CharStream const& _source = CharStream(), std::string const& _sourceName = "") { reset(_source, _sourceName); } - - /// Resets the scanner as if newly constructed with _source and _sourceName as input. - void reset(CharStream const& _source, std::string const& _sourceName); - /// Resets scanner to the start of input. - void reset(); - - /// Returns the next token and advances input - Token::Value next(); - - ///@{ - ///@name Information about the current token - - /// Returns the current token - Token::Value getCurrentToken() - { - return m_currentToken.token; - } - - Location getCurrentLocation() const { return m_currentToken.location; } - std::string const& getCurrentLiteral() const { return m_currentToken.literal; } - ///@} - - ///@{ - ///@name Information about the current comment token - - Location getCurrentCommentLocation() const { return m_skippedComment.location; } - std::string const& getCurrentCommentLiteral() const { return m_skippedComment.literal; } - /// Called by the parser during FunctionDefinition parsing to clear the current comment - void clearCurrentCommentLiteral() { m_skippedComment.literal.clear(); } - - ///@} - - ///@{ - ///@name Information about the next token - - /// Returns the next token without advancing input. - Token::Value peekNextToken() const { return m_nextToken.token; } - Location peekLocation() const { return m_nextToken.location; } - std::string const& peekLiteral() const { return m_nextToken.literal; } - ///@} - - std::shared_ptr const& getSourceName() const { return m_sourceName; } - - ///@{ - ///@name Error printing helper functions - /// Functions that help pretty-printing parse errors - /// Do only use in error cases, they are quite expensive. - std::string getLineAtPosition(int _position) const { return m_source.getLineAtPosition(_position); } - std::tuple translatePositionToLineColumn(int _position) const { return m_source.translatePositionToLineColumn(_position); } - ///@} - -private: - /// Used for the current and look-ahead token and comments - struct TokenDesc - { - Token::Value token; - Location location; - std::string literal; - }; - - ///@{ - ///@name Literal buffer support - inline void addLiteralChar(char c) { m_nextToken.literal.push_back(c); } - inline void addCommentLiteralChar(char c) { m_nextSkippedComment.literal.push_back(c); } - inline void addLiteralCharAndAdvance() { addLiteralChar(m_char); advance(); } - ///@} - - bool advance() { m_char = m_source.advanceAndGet(); return !m_source.isPastEndOfInput(); } - void rollback(int _amount) { m_char = m_source.rollback(_amount); } - - inline Token::Value selectToken(Token::Value _tok) { advance(); return _tok; } - /// If the next character is _next, advance and return _then, otherwise return _else. - inline Token::Value selectToken(char _next, Token::Value _then, Token::Value _else); - - bool scanHexByte(char& o_scannedByte); - - /// Scans a single Solidity token. - void scanToken(); - - /// Skips all whitespace and @returns true if something was skipped. - bool skipWhitespace(); - /// Skips all whitespace except Line feeds and returns true if something was skipped - bool skipWhitespaceExceptLF(); - Token::Value skipSingleLineComment(); - Token::Value skipMultiLineComment(); - - void scanDecimalDigits(); - Token::Value scanNumber(char _charSeen = 0); - Token::Value scanIdentifierOrKeyword(); - - Token::Value scanString(); - Token::Value scanSingleLineDocComment(); - Token::Value scanMultiLineDocComment(); - /// Scans a slash '/' and depending on the characters returns the appropriate token - Token::Value scanSlash(); - - /// Scans an escape-sequence which is part of a string and adds the - /// decoded character to the current literal. Returns true if a pattern - /// is scanned. - bool scanEscape(); - - /// Return the current source position. - int getSourcePos() { return m_source.getPos(); } - bool isSourcePastEndOfInput() { return m_source.isPastEndOfInput(); } - - TokenDesc m_skippedComment; // desc for current skipped comment - TokenDesc m_nextSkippedComment; // desc for next skiped comment - - TokenDesc m_currentToken; // desc for current token (as returned by Next()) - TokenDesc m_nextToken; // desc for next token (one token look-ahead) - - CharStream m_source; - std::shared_ptr m_sourceName; - - /// one character look-ahead, equals 0 at end of input - char m_char; -}; - -} -} diff --git a/SourceReferenceFormatter.cpp b/SourceReferenceFormatter.cpp deleted file mode 100644 index c61f9b685..000000000 --- a/SourceReferenceFormatter.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @date 2014 - * Formatting functions for errors referencing positions and locations in the source. - */ - -#include -#include -#include -#include - -using namespace std; - -namespace dev -{ -namespace solidity -{ - -void SourceReferenceFormatter::printSourceLocation(ostream& _stream, - Location const& _location, - Scanner const& _scanner) -{ - int startLine; - int startColumn; - tie(startLine, startColumn) = _scanner.translatePositionToLineColumn(_location.start); - int endLine; - int endColumn; - tie(endLine, endColumn) = _scanner.translatePositionToLineColumn(_location.end); - if (startLine == endLine) - { - _stream << _scanner.getLineAtPosition(_location.start) << endl - << string(startColumn, ' ') << "^"; - if (endColumn > startColumn + 2) - _stream << string(endColumn - startColumn - 2, '-'); - if (endColumn > startColumn + 1) - _stream << "^"; - _stream << endl; - } - else - _stream << _scanner.getLineAtPosition(_location.start) << endl - << string(startColumn, ' ') << "^\n" - << "Spanning multiple lines.\n"; -} - -void SourceReferenceFormatter::printExceptionInformation(ostream& _stream, - Exception const& _exception, - string const& _name, - CompilerStack const& _compiler) -{ - Location const* location = boost::get_error_info(_exception); - Scanner const* scanner; - - if (location) - { - scanner = &_compiler.getScanner(*location->sourceName); - int startLine; - int startColumn; - tie(startLine, startColumn) = scanner->translatePositionToLineColumn(location->start); - _stream << *location->sourceName << ":" << (startLine + 1) << ":" << (startColumn + 1) << ": "; - } - _stream << _name; - if (string const* description = boost::get_error_info(_exception)) - _stream << ": " << *description << endl; - - if (location) - printSourceLocation(_stream, *location, *scanner); -} - -} -} diff --git a/SourceReferenceFormatter.h b/SourceReferenceFormatter.h deleted file mode 100644 index 98f1c745d..000000000 --- a/SourceReferenceFormatter.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @date 2014 - * Formatting functions for errors referencing positions and locations in the source. - */ - -#pragma once - -#include -#include - -namespace dev -{ - -struct Exception; // forward - -namespace solidity -{ - -class Scanner; // forward -class CompilerStack; // forward - -struct SourceReferenceFormatter -{ -public: - static void printSourceLocation(std::ostream& _stream, Location const& _location, Scanner const& _scanner); - static void printExceptionInformation(std::ostream& _stream, Exception const& _exception, - std::string const& _name, CompilerStack const& _compiler); -}; - -} -} diff --git a/Token.cpp b/Token.cpp deleted file mode 100644 index d07d75021..000000000 --- a/Token.cpp +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2006-2012, the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Modifications as part of cpp-ethereum under the following license: -// -// cpp-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// cpp-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with cpp-ethereum. If not, see . - -#include -#include - -using namespace std; - -namespace dev -{ -namespace solidity -{ - -#define T(name, string, precedence) #name, -char const* const Token::m_name[NUM_TOKENS] = -{ - TOKEN_LIST(T, T) -}; -#undef T - - -#define T(name, string, precedence) string, -char const* const Token::m_string[NUM_TOKENS] = -{ - TOKEN_LIST(T, T) -}; -#undef T - - -#define T(name, string, precedence) precedence, -int8_t const Token::m_precedence[NUM_TOKENS] = -{ - TOKEN_LIST(T, T) -}; -#undef T - - -#define KT(a, b, c) 'T', -#define KK(a, b, c) 'K', -char const Token::m_tokenType[] = -{ - TOKEN_LIST(KT, KK) -}; -Token::Value Token::fromIdentifierOrKeyword(const std::string& _name) -{ - // The following macros are used inside TOKEN_LIST and cause non-keyword tokens to be ignored - // and keywords to be put inside the keywords variable. -#define KEYWORD(name, string, precedence) {string, Token::name}, -#define TOKEN(name, string, precedence) - static const map 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 deleted file mode 100644 index 4aa000475..000000000 --- a/Token.h +++ /dev/null @@ -1,413 +0,0 @@ -// Copyright 2006-2012, the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Modifications as part of cpp-ethereum under the following license: -// -// cpp-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// cpp-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with cpp-ethereum. If not, see . - -#pragma once - -#include -#include -#include -#include - -namespace dev -{ -namespace solidity -{ - -// TOKEN_LIST takes a list of 3 macros M, all of which satisfy the -// same signature M(name, string, precedence), where name is the -// symbolic token name, string is the corresponding syntactic symbol -// (or NULL, for literals), and precedence is the precedence (or 0). -// The parameters are invoked for token categories as follows: -// -// T: Non-keyword tokens -// K: Keyword tokens - -// IGNORE_TOKEN is a convenience macro that can be supplied as -// an argument (at any position) for a TOKEN_LIST call. It does -// nothing with tokens belonging to the respective category. - -#define IGNORE_TOKEN(name, string, precedence) - -#define TOKEN_LIST(T, K) \ - /* End of source indicator. */ \ - T(EOS, "EOS", 0) \ - \ - /* Punctuators (ECMA-262, section 7.7, page 15). */ \ - T(LParen, "(", 0) \ - T(RParen, ")", 0) \ - T(LBrack, "[", 0) \ - T(RBrack, "]", 0) \ - T(LBrace, "{", 0) \ - T(RBrace, "}", 0) \ - T(Colon, ":", 0) \ - T(Semicolon, ";", 0) \ - T(Period, ".", 0) \ - T(Conditional, "?", 3) \ - T(Arrow, "=>", 0) \ - \ - /* Assignment operators. */ \ - /* IsAssignmentOp() relies on this block of enum values being */ \ - /* contiguous and sorted in the same order!*/ \ - T(Assign, "=", 2) \ - /* The following have to be in exactly the same order as the simple binary operators*/ \ - T(AssignBitOr, "|=", 2) \ - T(AssignBitXor, "^=", 2) \ - T(AssignBitAnd, "&=", 2) \ - T(AssignShl, "<<=", 2) \ - T(AssignSar, ">>=", 2) \ - T(AssignShr, ">>>=", 2) \ - T(AssignAdd, "+=", 2) \ - T(AssignSub, "-=", 2) \ - T(AssignMul, "*=", 2) \ - T(AssignDiv, "/=", 2) \ - T(AssignMod, "%=", 2) \ - \ - /* Binary operators sorted by precedence. */ \ - /* IsBinaryOp() relies on this block of enum values */ \ - /* being contiguous and sorted in the same order! */ \ - T(Comma, ",", 1) \ - T(Or, "||", 4) \ - T(And, "&&", 5) \ - T(BitOr, "|", 8) \ - T(BitXor, "^", 9) \ - T(BitAnd, "&", 10) \ - T(SHL, "<<", 11) \ - T(SAR, ">>", 11) \ - T(SHR, ">>>", 11) \ - T(Add, "+", 12) \ - T(Sub, "-", 12) \ - T(Mul, "*", 13) \ - T(Div, "/", 13) \ - T(Mod, "%", 13) \ - T(Exp, "**", 14) \ - \ - /* Compare operators sorted by precedence. */ \ - /* IsCompareOp() relies on this block of enum values */ \ - /* being contiguous and sorted in the same order! */ \ - T(Equal, "==", 6) \ - T(NotEqual, "!=", 6) \ - T(LessThan, "<", 7) \ - T(GreaterThan, ">", 7) \ - T(LessThanOrEqual, "<=", 7) \ - T(GreaterThanOrEqual, ">=", 7) \ - K(In, "in", 7) \ - \ - /* Unary operators. */ \ - /* IsUnaryOp() relies on this block of enum values */ \ - /* being contiguous and sorted in the same order! */ \ - T(Not, "!", 0) \ - T(BitNot, "~", 0) \ - T(Inc, "++", 0) \ - T(Dec, "--", 0) \ - K(Delete, "delete", 0) \ - \ - /* Keywords */ \ - K(Break, "break", 0) \ - K(Case, "case", 0) \ - K(Const, "constant", 0) \ - K(Continue, "continue", 0) \ - K(Contract, "contract", 0) \ - K(Default, "default", 0) \ - K(Do, "do", 0) \ - K(Else, "else", 0) \ - K(Event, "event", 0) \ - K(External, "external", 0) \ - K(Is, "is", 0) \ - K(Indexed, "indexed", 0) \ - K(For, "for", 0) \ - K(Function, "function", 0) \ - K(If, "if", 0) \ - K(Import, "import", 0) \ - K(Mapping, "mapping", 0) \ - K(Modifier, "modifier", 0) \ - K(New, "new", 0) \ - K(Public, "public", 0) \ - K(Private, "private", 0) \ - K(Inheritable, "inheritable", 0) \ - K(Return, "return", 0) \ - K(Returns, "returns", 0) \ - K(Struct, "struct", 0) \ - K(Switch, "switch", 0) \ - K(Var, "var", 0) \ - K(While, "while", 0) \ - K(Enum, "enum", 0) \ - \ - /* Ether subdenominations */ \ - K(SubWei, "wei", 0) \ - K(SubSzabo, "szabo", 0) \ - K(SubFinney, "finney", 0) \ - K(SubEther, "ether", 0) \ - /* type keywords, keep them in this order, keep int as first keyword - * the implementation in Types.cpp has to be synced to this here */\ - K(Int, "int", 0) \ - K(Int8, "int8", 0) \ - K(Int16, "int16", 0) \ - K(Int24, "int24", 0) \ - K(Int32, "int32", 0) \ - K(Int40, "int40", 0) \ - K(Int48, "int48", 0) \ - K(Int56, "int56", 0) \ - K(Int64, "int64", 0) \ - K(Int72, "int72", 0) \ - K(Int80, "int80", 0) \ - K(Int88, "int88", 0) \ - K(Int96, "int96", 0) \ - K(Int104, "int104", 0) \ - K(Int112, "int112", 0) \ - K(Int120, "int120", 0) \ - K(Int128, "int128", 0) \ - K(Int136, "int136", 0) \ - K(Int144, "int144", 0) \ - K(Int152, "int152", 0) \ - K(Int160, "int160", 0) \ - K(Int168, "int168", 0) \ - K(Int176, "int178", 0) \ - K(Int184, "int184", 0) \ - K(Int192, "int192", 0) \ - K(Int200, "int200", 0) \ - K(Int208, "int208", 0) \ - K(Int216, "int216", 0) \ - K(Int224, "int224", 0) \ - K(Int232, "int232", 0) \ - K(Int240, "int240", 0) \ - K(Int248, "int248", 0) \ - K(Int256, "int256", 0) \ - K(UInt, "uint", 0) \ - K(UInt8, "uint8", 0) \ - K(UInt16, "uint16", 0) \ - K(UInt24, "uint24", 0) \ - K(UInt32, "uint32", 0) \ - K(UInt40, "uint40", 0) \ - K(UInt48, "uint48", 0) \ - K(UInt56, "uint56", 0) \ - K(UInt64, "uint64", 0) \ - K(UInt72, "uint72", 0) \ - K(UInt80, "uint80", 0) \ - K(UInt88, "uint88", 0) \ - K(UInt96, "uint96", 0) \ - K(UInt104, "uint104", 0) \ - K(UInt112, "uint112", 0) \ - K(UInt120, "uint120", 0) \ - K(UInt128, "uint128", 0) \ - K(UInt136, "uint136", 0) \ - K(UInt144, "uint144", 0) \ - K(UInt152, "uint152", 0) \ - K(UInt160, "uint160", 0) \ - K(UInt168, "uint168", 0) \ - K(UInt176, "uint178", 0) \ - K(UInt184, "uint184", 0) \ - K(UInt192, "uint192", 0) \ - K(UInt200, "uint200", 0) \ - K(UInt208, "uint208", 0) \ - K(UInt216, "uint216", 0) \ - K(UInt224, "uint224", 0) \ - K(UInt232, "uint232", 0) \ - K(UInt240, "uint240", 0) \ - K(UInt248, "uint248", 0) \ - K(UInt256, "uint256", 0) \ - K(Hash, "hash", 0) \ - K(Hash8, "hash8", 0) \ - K(Hash16, "hash16", 0) \ - K(Hash24, "hash24", 0) \ - K(Hash32, "hash32", 0) \ - K(Hash40, "hash40", 0) \ - K(Hash48, "hash48", 0) \ - K(Hash56, "hash56", 0) \ - K(Hash64, "hash64", 0) \ - K(Hash72, "hash72", 0) \ - K(Hash80, "hash80", 0) \ - K(Hash88, "hash88", 0) \ - K(Hash96, "hash96", 0) \ - K(Hash104, "hash104", 0) \ - K(Hash112, "hash112", 0) \ - K(Hash120, "hash120", 0) \ - K(Hash128, "hash128", 0) \ - K(Hash136, "hash136", 0) \ - K(Hash144, "hash144", 0) \ - K(Hash152, "hash152", 0) \ - K(Hash160, "hash160", 0) \ - K(Hash168, "hash168", 0) \ - K(Hash176, "hash178", 0) \ - K(Hash184, "hash184", 0) \ - K(Hash192, "hash192", 0) \ - K(Hash200, "hash200", 0) \ - K(Hash208, "hash208", 0) \ - K(Hash216, "hash216", 0) \ - K(Hash224, "hash224", 0) \ - K(Hash232, "hash232", 0) \ - K(Hash240, "hash240", 0) \ - K(Hash248, "hash248", 0) \ - K(Hash256, "hash256", 0) \ - K(Address, "address", 0) \ - K(Bool, "bool", 0) \ - K(Bytes, "bytes", 0) \ - K(StringType, "string", 0) \ - K(String0, "string0", 0) \ - K(String1, "string1", 0) \ - K(String2, "string2", 0) \ - K(String3, "string3", 0) \ - K(String4, "string4", 0) \ - K(String5, "string5", 0) \ - K(String6, "string6", 0) \ - K(String7, "string7", 0) \ - K(String8, "string8", 0) \ - K(String9, "string9", 0) \ - K(String10, "string10", 0) \ - K(String11, "string11", 0) \ - K(String12, "string12", 0) \ - K(String13, "string13", 0) \ - K(String14, "string14", 0) \ - K(String15, "string15", 0) \ - K(String16, "string16", 0) \ - K(String17, "string17", 0) \ - K(String18, "string18", 0) \ - K(String19, "string19", 0) \ - K(String20, "string20", 0) \ - K(String21, "string21", 0) \ - K(String22, "string22", 0) \ - K(String23, "string23", 0) \ - K(String24, "string24", 0) \ - K(String25, "string25", 0) \ - K(String26, "string26", 0) \ - K(String27, "string27", 0) \ - K(String28, "string28", 0) \ - K(String29, "string29", 0) \ - K(String30, "string30", 0) \ - K(String31, "string31", 0) \ - K(String32, "string32", 0) \ - K(Text, "text", 0) \ - K(Real, "real", 0) \ - K(UReal, "ureal", 0) \ - T(TypesEnd, NULL, 0) /* used as type enum end marker */ \ - \ - /* Literals */ \ - K(NullLiteral, "null", 0) \ - K(TrueLiteral, "true", 0) \ - K(FalseLiteral, "false", 0) \ - T(Number, NULL, 0) \ - T(StringLiteral, NULL, 0) \ - T(CommentLiteral, NULL, 0) \ - \ - /* Identifiers (not keywords or future reserved words). */ \ - T(Identifier, NULL, 0) \ - \ - /* Illegal token - not able to scan. */ \ - T(Illegal, "ILLEGAL", 0) \ - \ - /* Scanner-internal use only. */ \ - T(Whitespace, NULL, 0) - - -class Token -{ -public: - // All token values. - // attention! msvc issue: - // http://stackoverflow.com/questions/9567868/compile-errors-after-adding-v8-to-my-project-c2143-c2059 - // @todo: avoid TOKEN_LIST macro -#define T(name, string, precedence) name, - enum Value - { - TOKEN_LIST(T, T) - NUM_TOKENS - }; -#undef T - - // Returns a string corresponding to the C++ token name - // (e.g. "LT" for the token LT). - static char const* getName(Value tok) - { - solAssert(tok < NUM_TOKENS, ""); - return m_name[tok]; - } - - // Predicates - static bool isElementaryTypeName(Value tok) { return Int <= tok && tok < TypesEnd; } - static bool isAssignmentOp(Value tok) { return Assign <= tok && tok <= AssignMod; } - static bool isBinaryOp(Value op) { return Comma <= op && op <= Exp; } - static bool isCommutativeOp(Value op) { return op == BitOr || op == BitXor || op == BitAnd || - op == Add || op == Mul || op == Equal || op == NotEqual; } - static bool isArithmeticOp(Value op) { return Add <= op && op <= Exp; } - static bool isCompareOp(Value op) { return Equal <= op && op <= In; } - - static Value AssignmentToBinaryOp(Value op) - { - solAssert(isAssignmentOp(op) && op != Assign, ""); - return Value(op + (BitOr - AssignBitOr)); - } - - static bool isBitOp(Value op) { return (BitOr <= op && op <= SHR) || op == BitNot; } - static bool isUnaryOp(Value op) { return (Not <= op && op <= Delete) || op == Add || op == Sub; } - static bool isCountOp(Value op) { return op == Inc || op == Dec; } - static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); } - static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; } - static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Inheritable; } - static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == Token::SubEther; } - - // Returns a string corresponding to the JS token string - // (.e., "<" for the token LT) or NULL if the token doesn't - // have a (unique) string (e.g. an IDENTIFIER). - static char const* toString(Value tok) - { - solAssert(tok < NUM_TOKENS, ""); - return m_string[tok]; - } - - // Returns the precedence > 0 for binary and compare - // operators; returns 0 otherwise. - static int precedence(Value tok) - { - solAssert(tok < NUM_TOKENS, ""); - return m_precedence[tok]; - } - - static Token::Value fromIdentifierOrKeyword(std::string const& _name); - -private: - static char const* const m_name[NUM_TOKENS]; - static char const* const m_string[NUM_TOKENS]; - static int8_t const m_precedence[NUM_TOKENS]; - static char const m_tokenType[NUM_TOKENS]; -}; - -} -} diff --git a/Types.cpp b/Types.cpp deleted file mode 100644 index a9c480176..000000000 --- a/Types.cpp +++ /dev/null @@ -1,1071 +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 . -*/ -/** - * @author Christian - * @date 2014 - * Solidity data types - */ - -#include -#include -#include -#include -#include - -#include - -using namespace std; - -namespace dev -{ -namespace solidity -{ - -TypePointer Type::fromElementaryTypeName(Token::Value _typeToken) -{ - solAssert(Token::isElementaryTypeName(_typeToken), "Elementary type name expected."); - - if (Token::Int <= _typeToken && _typeToken <= Token::Hash256) - { - int offset = _typeToken - Token::Int; - int bytes = offset % 33; - if (bytes == 0) - bytes = 32; - int modifier = offset / 33; - return make_shared(bytes * 8, - modifier == 0 ? IntegerType::Modifier::Signed : - modifier == 1 ? IntegerType::Modifier::Unsigned : - IntegerType::Modifier::Hash); - } - else if (_typeToken == Token::Address) - return make_shared(0, IntegerType::Modifier::Address); - else if (_typeToken == Token::Bool) - return make_shared(); - else if (Token::String0 <= _typeToken && _typeToken <= Token::String32) - return make_shared(int(_typeToken) - int(Token::String0)); - else if (_typeToken == Token::Bytes) - return make_shared(ByteArrayType::Location::Storage); - else - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " + - std::string(Token::toString(_typeToken)) + " to type.")); -} - -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(declaration)) - return make_shared(*structDef); - else if (EnumDefinition const* enumDef = dynamic_cast(declaration)) - return make_shared(*enumDef); - else if (FunctionDefinition const* function = dynamic_cast(declaration)) - return make_shared(*function); - else if (ContractDefinition const* contract = dynamic_cast(declaration)) - return make_shared(*contract); - return TypePointer(); -} - -TypePointer Type::fromMapping(Mapping const& _typeName) -{ - TypePointer keyType = _typeName.getKeyType().toType(); - if (!keyType) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Error resolving type name.")); - TypePointer valueType = _typeName.getValueType().toType(); - if (!valueType) - BOOST_THROW_EXCEPTION(_typeName.getValueType().createTypeError("Invalid type name")); - return make_shared(keyType, valueType); -} - -TypePointer Type::forLiteral(Literal const& _literal) -{ - switch (_literal.getToken()) - { - case Token::TrueLiteral: - case Token::FalseLiteral: - return make_shared(); - case Token::Number: - return make_shared(_literal); - case Token::StringLiteral: - //@todo put larger strings into dynamic strings - return StaticStringType::smallestTypeForLiteral(_literal.getValue()); - default: - return shared_ptr(); - } -} - -TypePointer Type::commonType(TypePointer const& _a, TypePointer const& _b) -{ - 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) -{ - if (isAddress()) - m_bits = 160; - solAssert(m_bits > 0 && m_bits <= 256 && m_bits % 8 == 0, - "Invalid bit number for integer type: " + dev::toString(_bits)); -} - -bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const -{ - if (_convertTo.getCategory() != getCategory()) - return false; - IntegerType const& convertTo = dynamic_cast(_convertTo); - if (convertTo.m_bits < m_bits) - return false; - if (isAddress()) - return convertTo.isAddress(); - else if (isHash()) - return convertTo.isHash(); - else if (isSigned()) - return convertTo.isSigned(); - else - return !convertTo.isSigned() || convertTo.m_bits > m_bits; -} - -bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const -{ - if (_convertTo.getCategory() == Category::String) - { - StaticStringType const& convertTo = dynamic_cast(_convertTo); - return isHash() && (m_bits == convertTo.getNumBytes() * 8); - } - return _convertTo.getCategory() == getCategory() || - _convertTo.getCategory() == Category::Contract || - _convertTo.getCategory() == Category::Enum; -} - -TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const -{ - // "delete" is ok for all integer types - if (_operator == Token::Delete) - return make_shared(); - // 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 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 TypePointer(); -} - -bool IntegerType::operator==(Type const& _other) const -{ - if (_other.getCategory() != getCategory()) - return false; - IntegerType const& other = dynamic_cast(_other); - return other.m_bits == m_bits && other.m_modifier == m_modifier; -} - -string IntegerType::toString() const -{ - if (isAddress()) - return "address"; - string prefix = isHash() ? "hash" : (isSigned() ? "int" : "uint"); - return prefix + dev::toString(m_bits); -} - -TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const -{ - if (_other->getCategory() != Category::IntegerConstant && _other->getCategory() != getCategory()) - return TypePointer(); - auto commonType = dynamic_pointer_cast(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(256)}, - {"call", make_shared(strings(), strings(), FunctionType::Location::Bare, true)}, - {"send", make_shared(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(value); -} - -TypePointer IntegerConstantType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const -{ - if (_other->getCategory() == Category::Integer) - { - shared_ptr integerType = getIntegerType(); - if (!integerType) - return TypePointer(); - return integerType->binaryOperatorResult(_operator, _other); - } - else if (_other->getCategory() != getCategory()) - return TypePointer(); - - IntegerConstantType const& other = dynamic_cast(*_other); - if (Token::isCompareOp(_operator)) - { - shared_ptr thisIntegerType = getIntegerType(); - shared_ptr 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::max()) - return TypePointer(); - else - value = boost::multiprecision::pow(m_value, other.m_value.convert_to()); - break; - default: - return TypePointer(); - } - return make_shared(value); - } -} - -bool IntegerConstantType::operator==(Type const& _other) const -{ - if (_other.getCategory() != getCategory()) - return false; - return m_value == dynamic_cast(_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 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(); - else - return make_shared(max(bytesRequired(value), 1u) * 8, - negative ? IntegerType::Modifier::Signed - : IntegerType::Modifier::Unsigned); -} - -shared_ptr StaticStringType::smallestTypeForLiteral(string const& _literal) -{ - if (_literal.length() <= 32) - return make_shared(_literal.length()); - return shared_ptr(); -} - -StaticStringType::StaticStringType(int _bytes): m_bytes(_bytes) -{ - solAssert(m_bytes >= 0 && m_bytes <= 32, - "Invalid byte number for static string type: " + dev::toString(m_bytes)); -} - -bool StaticStringType::isImplicitlyConvertibleTo(Type const& _convertTo) const -{ - if (_convertTo.getCategory() != getCategory()) - return false; - StaticStringType const& convertTo = dynamic_cast(_convertTo); - 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(_convertTo); - if (convertTo.isHash() && (m_bytes * 8 == convertTo.getNumBits())) - return true; - } - - return false; -} - -bool StaticStringType::operator==(Type const& _other) const -{ - if (_other.getCategory() != getCategory()) - return false; - StaticStringType const& other = dynamic_cast(_other); - return other.m_bytes == m_bytes; -} - -u256 StaticStringType::literalValue(const Literal* _literal) const -{ - solAssert(_literal, ""); - u256 value = 0; - for (char c: _literal->getValue()) - value = (value << 8) | byte(c); - return value << ((32 - _literal->getValue().length()) * 8); -} - -bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const -{ - // conversion to integer is fine, but not to address - // this is an example of explicit conversions being not transitive (though implicit should be) - if (_convertTo.getCategory() == getCategory()) - { - IntegerType const& convertTo = dynamic_cast(_convertTo); - if (!convertTo.isAddress()) - return true; - } - return isImplicitlyConvertibleTo(_convertTo); -} - -u256 BoolType::literalValue(Literal const* _literal) const -{ - solAssert(_literal, ""); - if (_literal->getToken() == Token::TrueLiteral) - return u256(1); - else if (_literal->getToken() == Token::FalseLiteral) - return u256(0); - else - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Bool type constructed from non-boolean literal.")); -} - -TypePointer BoolType::unaryOperatorResult(Token::Value _operator) const -{ - if (_operator == Token::Delete) - return make_shared(); - return (_operator == Token::Not) ? shared_from_this() : TypePointer(); -} - -TypePointer BoolType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const -{ - 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) - return dynamic_cast(_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(_convertTo).getContractDefinition()) != bases.end(); - } - return false; -} - -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() : 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(); - return TypePointer(); -} - -bool ByteArrayType::operator==(Type const& _other) const -{ - if (_other.getCategory() != getCategory()) - return false; - ByteArrayType const& other = dynamic_cast(_other); - return other.m_location == m_location; -} - -unsigned ByteArrayType::getSizeOnStack() const -{ - if (m_location == Location::CallData) - // offset, length (stack top) - return 2; - else - // offset - return 1; -} - -shared_ptr ByteArrayType::copyForLocation(ByteArrayType::Location _location) const -{ - return make_shared(_location); -} - -const MemberList ByteArrayType::s_byteArrayMemberList = MemberList({{"length", make_shared(256)}}); - -bool ContractType::operator==(Type const& _other) const -{ - if (_other.getCategory() != getCategory()) - return false; - ContractType const& other = dynamic_cast(_other); - return other.m_contract == m_contract && other.m_super == m_super; -} - -string ContractType::toString() const -{ - return "contract " + string(m_super ? "super " : "") + m_contract.getName(); -} - -MemberList const& ContractType::getMembers() const -{ - // We need to lazy-initialize it because of recursive references. - if (!m_members) - { - // All address members and all interface functions - vector> members(IntegerType::AddressMemberList.begin(), - IntegerType::AddressMemberList.end()); - if (m_super) - { - for (ContractDefinition const* base: m_contract.getLinearizedBaseContracts()) - for (ASTPointer const& function: base->getDefinedFunctions()) - if (!function->isConstructor() && !function->getName().empty()&& - function->isVisibleInDerivedContracts()) - members.push_back(make_pair(function->getName(), make_shared(*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; -} - -shared_ptr const& ContractType::getConstructorType() const -{ - if (!m_constructorType) - { - FunctionDefinition const* constructor = m_contract.getConstructor(); - if (constructor) - m_constructorType = make_shared(*constructor); - else - m_constructorType = make_shared(TypePointers(), TypePointers()); - } - return m_constructorType; -} - -u256 ContractType::getFunctionIdentifier(string const& _functionName) const -{ - 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() : TypePointer(); -} - -bool StructType::operator==(Type const& _other) const -{ - if (_other.getCategory() != getCategory()) - return false; - StructType const& other = dynamic_cast(_other); - return other.m_struct == m_struct; -} - -u256 StructType::getStorageSize() const -{ - u256 size = 0; - for (pair const& member: getMembers()) - size += member.second->getStorageSize(); - return max(1, size); -} - -bool StructType::canLiveOutsideStorage() const -{ - for (pair const& member: getMembers()) - if (!member.second->canLiveOutsideStorage()) - return false; - return true; -} - -string StructType::toString() const -{ - return string("struct ") + m_struct.getName(); -} - -MemberList const& StructType::getMembers() const -{ - // We need to lazy-initialize it because of recursive references. - if (!m_members) - { - vector> members; - for (ASTPointer const& variable: m_struct.getMembers()) - members.push_back(make_pair(variable->getName(), variable->getType())); - m_members.reset(new MemberList(members)); - } - return *m_members; -} - -u256 StructType::getStorageOffsetOfMember(string const& _name) const -{ - //@todo cache member offset? - u256 offset; - for (ASTPointer const& variable: m_struct.getMembers()) - { - if (variable->getName() == _name) - return offset; - offset += variable->getType()->getStorageSize(); - } - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage offset of non-existing member requested.")); -} - -TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const -{ - return _operator == Token::Delete ? make_shared() : TypePointer(); -} - -bool EnumType::operator==(Type const& _other) const -{ - if (_other.getCategory() != getCategory()) - return false; - EnumType const& other = dynamic_cast(_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 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 paramNames; - TypePointers retParams; - vector retParamNames; - - params.reserve(_function.getParameters().size()); - paramNames.reserve(_function.getParameters().size()); - for (ASTPointer 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 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); - swap(retParamNames, m_returnParameterNames); -} - -FunctionType::FunctionType(VariableDeclaration const& _varDecl): - m_location(Location::External), m_isConstant(true), m_declaration(&_varDecl) -{ - TypePointers params; - vector paramNames; - auto returnType = _varDecl.getType(); - - while (auto mappingType = dynamic_cast(returnType.get())) - { - params.push_back(mappingType->getKeyType()); - paramNames.push_back(""); - returnType = mappingType->getValueType(); - } - - TypePointers retParams; - vector retParamNames; - if (auto structType = dynamic_cast(returnType.get())) - { - for (pair 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 paramNames; - params.reserve(_event.getParameters().size()); - paramNames.reserve(_event.getParameters().size()); - for (ASTPointer 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 -{ - if (_other.getCategory() != getCategory()) - return false; - FunctionType const& other = dynamic_cast(_other); - - 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; - 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; - 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; -} - -string FunctionType::toString() const -{ - string name = "function ("; - for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it) - name += (*it)->toString() + (it + 1 == m_parameterTypes.end() ? "" : ","); - name += ") returns ("; - for (auto it = m_returnParameterTypes.begin(); it != m_returnParameterTypes.end(); ++it) - name += (*it)->toString() + (it + 1 == m_returnParameterTypes.end() ? "" : ","); - return name + ")"; -} - -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::External: - case Location::Creation: - case Location::ECRecover: - case Location::SHA256: - case Location::RIPEMD160: - case Location::Bare: - if (!m_members) - { - vector> members{ - {"value", make_shared(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( - parseElementaryTypeVector({"uint"}), - TypePointers{copyAndSetGasOrValue(true, false)}, - Location::SetGas, false, m_gasSet, m_valueSet))); - m_members.reset(new MemberList(members)); - } - return *m_members; - default: - 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(m_parameterTypes, m_returnParameterTypes, m_location, - m_arbitraryParameters, - m_gasSet || _setGas, m_valueSet || _setValue); -} - -vector const FunctionType::getParameterTypeNames() const -{ - vector names; - for (TypePointer const& t: m_parameterTypes) - names.push_back(t->toString()); - - return names; -} - -vector const FunctionType::getReturnParameterTypeNames() const -{ - vector names; - for (TypePointer const& t: m_returnParameterTypes) - names.push_back(t->toString()); - - return names; -} - -ASTPointer FunctionType::getDocumentation() const -{ - auto function = dynamic_cast(m_declaration); - if (function) - return function->getDocumentation(); - - return ASTPointer(); -} - -bool MappingType::operator==(Type const& _other) const -{ - if (_other.getCategory() != getCategory()) - return false; - MappingType const& other = dynamic_cast(_other); - return *other.m_keyType == *m_keyType && *other.m_valueType == *m_valueType; -} - -string MappingType::toString() const -{ - return "mapping(" + getKeyType()->toString() + " => " + getValueType()->toString() + ")"; -} - -bool TypeType::operator==(Type const& _other) const -{ - if (_other.getCategory() != getCategory()) - return false; - TypeType const& other = dynamic_cast(_other); - return *getActualType() == *other.getActualType(); -} - -MemberList const& TypeType::getMembers() const -{ - // We need to lazy-initialize it because of recursive references. - if (!m_members) - { - vector> members; - if (m_actualType->getCategory() == Category::Contract && m_currentContract != nullptr) - { - ContractDefinition const& contract = dynamic_cast(*m_actualType).getContractDefinition(); - vector 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 const& f: contract.getDefinedFunctions()) - if (!f->isConstructor() && !f->getName().empty() && f->isVisibleInDerivedContracts()) - members.push_back(make_pair(f->getName(), make_shared(*f))); - } - else if (m_actualType->getCategory() == Category::Enum) - { - EnumDefinition const& enumDef = dynamic_cast(*m_actualType).getEnumDefinition(); - auto enumType = make_shared(enumDef); - for (ASTPointer 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 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(_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(0, IntegerType::Modifier::Address)}, - {"timestamp", make_shared(256)}, - {"blockhash", make_shared(strings{"uint"}, strings{"hash"}, FunctionType::Location::BlockHash)}, - {"difficulty", make_shared(256)}, - {"number", make_shared(256)}, - {"gaslimit", make_shared(256)}}); - break; - case Kind::Message: - m_members = MemberList({{"sender", make_shared(0, IntegerType::Modifier::Address)}, - {"gas", make_shared(256)}, - {"value", make_shared(256)}, - {"data", make_shared(ByteArrayType::Location::CallData)}}); - break; - case Kind::Transaction: - m_members = MemberList({{"origin", make_shared(0, IntegerType::Modifier::Address)}, - {"gasprice", make_shared(256)}}); - break; - default: - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of magic.")); - } -} - -bool MagicType::operator==(Type const& _other) const -{ - if (_other.getCategory() != getCategory()) - return false; - MagicType const& other = dynamic_cast(_other); - return other.m_kind == m_kind; -} - -string MagicType::toString() const -{ - switch (m_kind) - { - case Kind::Block: - return "block"; - case Kind::Message: - return "msg"; - case Kind::Transaction: - return "tx"; - default: - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of magic.")); - } -} - -} -} diff --git a/Types.h b/Types.h deleted file mode 100644 index af64f1cb5..000000000 --- a/Types.h +++ /dev/null @@ -1,631 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @date 2014 - * Solidity data types - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace dev -{ -namespace solidity -{ - -// @todo realMxN, dynamic strings, text, arrays - -class Type; // forward -class FunctionType; // forward -using TypePointer = std::shared_ptr; -using FunctionTypePointer = std::shared_ptr; -using TypePointers = std::vector; - -/** - * List of members of a type. - */ -class MemberList -{ -public: - using MemberMap = std::vector>; - - MemberList() {} - explicit MemberList(MemberMap const& _members): m_memberTypes(_members) {} - TypePointer getMemberType(std::string const& _name) const - { - for (auto const& it: m_memberTypes) - if (it.first == _name) - return it.second; - return TypePointer(); - } - - MemberMap::const_iterator begin() const { return m_memberTypes.begin(); } - MemberMap::const_iterator end() const { return m_memberTypes.end(); } - -private: - MemberMap m_memberTypes; -}; - - -/** - * Abstract base class that forms the root of the type hierarchy. - */ -class Type: private boost::noncopyable, public std::enable_shared_from_this -{ -public: - enum class Category - { - Integer, IntegerConstant, Bool, Real, ByteArray, - String, Contract, Struct, Function, Enum, - Mapping, Void, TypeType, Modifier, Magic - }; - - ///@{ - ///@name Factory functions - /// Factory functions that convert an AST @ref TypeName to a Type. - static TypePointer fromElementaryTypeName(Token::Value _typeToken); - static TypePointer fromElementaryTypeName(std::string const& _name); - static TypePointer fromUserDefinedTypeName(UserDefinedTypeName const& _typeName); - static TypePointer fromMapping(Mapping const& _typeName); - static TypePointer fromFunction(FunctionDefinition const& _function); - /// @} - - /// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does - /// not fit any type. - static TypePointer forLiteral(Literal const& _literal); - /// @returns a pointer to _a or _b if the other is implicitly convertible to it or nullptr otherwise - static TypePointer commonType(TypePointer const& _a, TypePointer const& _b); - - virtual Category getCategory() const = 0; - virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; } - virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const - { - return isImplicitlyConvertibleTo(_convertTo); - } - /// @returns the resulting type of applying the given unary operator or an empty pointer if - /// this is not possible. - /// The default implementation does not allow any unary operator. - virtual TypePointer unaryOperatorResult(Token::Value) const { return TypePointer(); } - /// @returns the resulting type of applying the given binary operator or an empty pointer if - /// this is not possible. - /// The default implementation allows comparison operators if a common type exists - virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const - { - return Token::isCompareOp(_operator) ? commonType(shared_from_this(), _other) : TypePointer(); - } - - virtual bool operator==(Type const& _other) const { return getCategory() == _other.getCategory(); } - virtual bool operator!=(Type const& _other) const { return !this->operator ==(_other); } - - /// @returns number of bytes used by this type when encoded for CALL, or 0 if the encoding - /// is not a simple big-endian encoding or the type cannot be stored in calldata. - /// Note that irrespective of this size, each calldata element is padded to a multiple of 32 bytes. - virtual unsigned getCalldataEncodedSize() const { return 0; } - /// @returns true if the type is dynamically encoded in calldata - virtual bool isDynamicallySized() const { return false; } - /// @returns number of bytes required to hold this value in storage. - /// For dynamically "allocated" types, it returns the size of the statically allocated head, - virtual u256 getStorageSize() const { return 1; } - /// Returns true if the type can be stored in storage. - virtual bool canBeStored() const { return true; } - /// Returns false if the type cannot live outside the storage, i.e. if it includes some mapping. - virtual bool canLiveOutsideStorage() const { return true; } - /// Returns true if the type can be stored as a value (as opposed to a reference) on the stack, - /// i.e. it behaves differently in lvalue context and in value context. - virtual bool isValueType() const { return false; } - virtual unsigned getSizeOnStack() const { return 1; } - /// @returns the real type of some types, like e.g: IntegerConstant - virtual TypePointer getRealType() const { return shared_from_this(); } - - /// Returns the list of all members of this type. Default implementation: no members. - virtual MemberList const& getMembers() const { return EmptyMemberList; } - /// Convenience method, returns the type of the given named member or an empty pointer if no such member exists. - TypePointer getMemberType(std::string const& _name) const { return getMembers().getMemberType(_name); } - - virtual std::string toString() const = 0; - virtual u256 literalValue(Literal const*) const - { - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Literal value requested " - "for type without literals.")); - } - -protected: - /// Convenience object used when returning an empty member list. - static const MemberList EmptyMemberList; -}; - -/** - * Any kind of integer type including hash and address. - */ -class IntegerType: public Type -{ -public: - enum class Modifier - { - Unsigned, Signed, Hash, Address - }; - virtual Category getCategory() const override { return Category::Integer; } - - explicit IntegerType(int _bits, Modifier _modifier = Modifier::Unsigned); - - virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; - virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; - - virtual bool operator==(Type const& _other) const override; - - virtual unsigned getCalldataEncodedSize() const override { return m_bits / 8; } - virtual bool isValueType() const override { return true; } - - virtual MemberList const& getMembers() const { return isAddress() ? AddressMemberList : EmptyMemberList; } - - virtual std::string toString() const override; - - int getNumBits() const { return m_bits; } - bool isHash() const { return m_modifier == Modifier::Hash || m_modifier == Modifier::Address; } - bool isAddress() const { return m_modifier == Modifier::Address; } - bool isSigned() const { return m_modifier == Modifier::Signed; } - - static const MemberList AddressMemberList; - -private: - int m_bits; - Modifier m_modifier; -}; - -/** - * Integer constants either literals or computed. Example expressions: 2, 2+10, ~10. - * There is one distinct type per value. - */ -class IntegerConstantType: public Type -{ -public: - virtual Category getCategory() const override { return Category::IntegerConstant; } - - explicit IntegerConstantType(Literal const& _literal); - explicit IntegerConstantType(bigint _value): m_value(_value) {} - - virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; - virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; - - virtual bool operator==(Type const& _other) const override; - - virtual bool canBeStored() const override { return false; } - virtual bool canLiveOutsideStorage() const override { return false; } - virtual unsigned getSizeOnStack() const override { return 1; } - - virtual std::string toString() const override; - virtual u256 literalValue(Literal const* _literal) const override; - virtual TypePointer getRealType() const override; - - /// @returns the smallest integer type that can hold the value or an empty pointer if not possible. - std::shared_ptr getIntegerType() const; - -private: - bigint m_value; -}; - -/** - * String type with fixed length, up to 32 bytes. - */ -class StaticStringType: public Type -{ -public: - virtual Category getCategory() const override { return Category::String; } - - /// @returns the smallest string type for the given literal or an empty pointer - /// if no type fits. - static std::shared_ptr smallestTypeForLiteral(std::string const& _literal); - - explicit StaticStringType(int _bytes); - - virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual bool operator==(Type const& _other) const override; - - virtual unsigned getCalldataEncodedSize() const override { return m_bytes; } - virtual bool isValueType() const override { return true; } - - virtual std::string toString() const override { return "string" + dev::toString(m_bytes); } - virtual u256 literalValue(Literal const* _literal) const override; - - int getNumBytes() const { return m_bytes; } - -private: - int m_bytes; -}; - -/** - * The boolean type. - */ -class BoolType: public Type -{ -public: - BoolType() {} - virtual Category getCategory() const override { return Category::Bool; } - virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; - virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; - - virtual unsigned getCalldataEncodedSize() const { return 1; } - virtual bool isValueType() const override { return true; } - - virtual std::string toString() const override { return "bool"; } - virtual u256 literalValue(Literal const* _literal) const override; -}; - -/** - * The type of a byte array, prototype for a general array. - */ -class ByteArrayType: public Type -{ -public: - enum class Location { Storage, CallData, Memory }; - - virtual Category getCategory() const override { return Category::ByteArray; } - explicit ByteArrayType(Location _location): m_location(_location) {} - virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; - virtual bool operator==(const Type& _other) const override; - virtual bool isDynamicallySized() const { return true; } - virtual unsigned getSizeOnStack() const override; - virtual std::string toString() const override { return "bytes"; } - virtual MemberList const& getMembers() const override { return s_byteArrayMemberList; } - - Location getLocation() const { return m_location; } - - /// @returns a copy of this type with location changed to @a _location - /// @todo this might move as far up as Type later - std::shared_ptr copyForLocation(Location _location) const; - -private: - Location m_location; - static const MemberList s_byteArrayMemberList; -}; - -/** - * The type of a contract instance, there is one distinct type for each contract definition. - */ -class ContractType: public Type -{ -public: - virtual Category getCategory() const override { return Category::Contract; } - explicit ContractType(ContractDefinition const& _contract, bool _super = false): - m_contract(_contract), m_super(_super) {} - /// Contracts can be implicitly converted to super classes and to addresses. - virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; - /// Contracts can be converted to themselves and to integers. - virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; - virtual bool operator==(Type const& _other) const override; - virtual bool isValueType() const override { return true; } - virtual std::string toString() const override; - - virtual MemberList const& getMembers() const override; - - bool isSuper() const { return m_super; } - ContractDefinition const& getContractDefinition() const { return m_contract; } - - /// Returns the function type of the constructor. Note that the location part of the function type - /// is not used, as this type cannot be the type of a variable or expression. - FunctionTypePointer const& getConstructorType() const; - - /// @returns the identifier of the function with the given name or Invalid256 if such a name does - /// not exist. - u256 getFunctionIdentifier(std::string const& _functionName) const; - -private: - ContractDefinition const& m_contract; - /// If true, it is the "super" type of the current contract, i.e. it contains only inherited - /// members. - bool m_super; - /// Type of the constructor, @see getConstructorType. Lazily initialized. - mutable FunctionTypePointer m_constructorType; - /// List of member types, will be lazy-initialized because of recursive references. - mutable std::unique_ptr m_members; -}; - -/** - * The type of a struct instance, there is one distinct type per struct definition. - */ -class StructType: public Type -{ -public: - virtual Category getCategory() const override { return Category::Struct; } - explicit StructType(StructDefinition const& _struct): m_struct(_struct) {} - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; - virtual bool operator==(Type const& _other) const override; - virtual u256 getStorageSize() const override; - virtual bool canLiveOutsideStorage() const override; - virtual unsigned getSizeOnStack() const override { return 1; /*@todo*/ } - virtual std::string toString() const override; - - virtual MemberList const& getMembers() const override; - - u256 getStorageOffsetOfMember(std::string const& _name) const; - -private: - StructDefinition const& m_struct; - /// List of member types, will be lazy-initialized because of recursive references. - mutable std::unique_ptr m_members; -}; - -/** - * The type of an enum instance, there is one distinct type per enum definition. - */ -class EnumType: public Type -{ -public: - virtual Category getCategory() const override { return Category::Enum; } - explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {} - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; - virtual bool operator==(Type const& _other) const override; - virtual unsigned getSizeOnStack() const override { return 1; } - virtual std::string toString() const override; - virtual bool isValueType() const override { return true; } - - virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - - EnumDefinition const& getEnumDefinition() const { return m_enum; } - /// @returns the value that the string has in the Enum - unsigned int getMemberValue(ASTString const& _member) const; - -private: - EnumDefinition const& m_enum; - /// List of member types, will be lazy-initialized because of recursive references. - mutable std::unique_ptr m_members; -}; - -/** - * The type of a function, identified by its (return) parameter types. - * @todo the return parameters should also have names, i.e. return parameters should be a struct - * type. - */ -class FunctionType: public Type -{ -public: - /// The meaning of the value(s) on the stack referencing the function: - /// INTERNAL: jump tag, EXTERNAL: contract address + function identifier, - /// BARE: contract address (non-abi contract call) - /// OTHERS: special virtual function, nothing on the stack - /// @todo This documentation is outdated, and Location should rather be named "Type" - enum class Location { Internal, External, Creation, Send, - SHA3, Suicide, - ECRecover, SHA256, RIPEMD160, - Log0, Log1, Log2, Log3, Log4, Event, - SetGas, SetValue, BlockHash, - Bare }; - - virtual Category getCategory() const override { return Category::Function; } - explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); - explicit FunctionType(VariableDeclaration const& _varDecl); - explicit FunctionType(EventDefinition const& _event); - FunctionType(strings const& _parameterTypes, strings const& _returnParameterTypes, - Location _location = Location::Internal, bool _arbitraryParameters = false): - FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes), - _location, _arbitraryParameters) {} - FunctionType( - TypePointers const& _parameterTypes, - TypePointers const& _returnParameterTypes, - Location _location = Location::Internal, - bool _arbitraryParameters = false, - bool _gasSet = false, - bool _valueSet = false - ): - m_parameterTypes (_parameterTypes), - m_returnParameterTypes (_returnParameterTypes), - m_location (_location), - m_arbitraryParameters (_arbitraryParameters), - m_gasSet (_gasSet), - m_valueSet (_valueSet) - {} - - TypePointers const& getParameterTypes() const { return m_parameterTypes; } - std::vector const& getParameterNames() const { return m_parameterNames; } - std::vector const getParameterTypeNames() const; - TypePointers const& getReturnParameterTypes() const { return m_returnParameterTypes; } - std::vector const& getReturnParameterNames() const { return m_returnParameterNames; } - std::vector const getReturnParameterTypeNames() const; - - virtual bool operator==(Type const& _other) const override; - virtual std::string toString() const override; - virtual bool canBeStored() const override { return false; } - virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable function type requested.")); } - virtual bool canLiveOutsideStorage() const override { return false; } - virtual unsigned getSizeOnStack() const override; - virtual MemberList const& getMembers() const override; - - Location const& getLocation() const { return m_location; } - /// @returns the canonical signature of this function type given the function name - /// If @a _name is not provided (empty string) then the @c m_declaration member of the - /// function type is used - std::string getCanonicalSignature(std::string const& _name = "") const; - Declaration const& getDeclaration() const - { - solAssert(m_declaration, "Requested declaration from a FunctionType that has none"); - return *m_declaration; - } - bool hasDeclaration() const { return !!m_declaration; } - bool isConstant() const { return m_isConstant; } - /// @return A shared pointer of an ASTString. - /// Can contain a nullptr in which case indicates absence of documentation - ASTPointer getDocumentation() const; - - /// true iff arguments are to be padded to multiples of 32 bytes for external calls - bool padArguments() const { return !(m_location == Location::SHA3 || m_location == Location::SHA256 || m_location == Location::RIPEMD160); } - bool takesArbitraryParameters() const { return m_arbitraryParameters; } - bool gasSet() const { return m_gasSet; } - bool valueSet() const { return m_valueSet; } - - /// @returns a copy of this type, where gas or value are set manually. This will never set one - /// of the parameters to fals. - TypePointer copyAndSetGasOrValue(bool _setGas, bool _setValue) const; - -private: - static TypePointers parseElementaryTypeVector(strings const& _types); - - TypePointers m_parameterTypes; - TypePointers m_returnParameterTypes; - std::vector m_parameterNames; - std::vector 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 m_members; - Declaration const* m_declaration = nullptr; -}; - -/** - * The type of a mapping, there is one distinct type per key/value type pair. - */ -class MappingType: public Type -{ -public: - virtual Category getCategory() const override { return Category::Mapping; } - MappingType(TypePointer const& _keyType, TypePointer const& _valueType): - m_keyType(_keyType), m_valueType(_valueType) {} - - virtual bool operator==(Type const& _other) const override; - virtual std::string toString() const override; - virtual bool canLiveOutsideStorage() const override { return false; } - - TypePointer const& getKeyType() const { return m_keyType; } - TypePointer const& getValueType() const { return m_valueType; } - -private: - TypePointer m_keyType; - TypePointer m_valueType; -}; - -/** - * The void type, can only be implicitly used as the type that is returned by functions without - * return parameters. - */ -class VoidType: public Type -{ -public: - virtual Category getCategory() const override { return Category::Void; } - VoidType() {} - - virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } - virtual std::string toString() const override { return "void"; } - virtual bool canBeStored() const override { return false; } - virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable void type requested.")); } - virtual bool canLiveOutsideStorage() const override { return false; } - virtual unsigned getSizeOnStack() const override { return 0; } -}; - -/** - * The type of a type reference. The type of "uint32" when used in "a = uint32(2)" is an example - * of a TypeType. - */ -class TypeType: public Type -{ -public: - virtual Category getCategory() const override { return Category::TypeType; } - explicit TypeType(TypePointer const& _actualType, ContractDefinition const* _currentContract = nullptr): - m_actualType(_actualType), m_currentContract(_currentContract) {} - TypePointer const& getActualType() const { return m_actualType; } - - virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } - virtual bool operator==(Type const& _other) const override; - virtual bool canBeStored() const override { return false; } - virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); } - virtual bool canLiveOutsideStorage() const override { return false; } - virtual unsigned getSizeOnStack() const override { return 0; } - virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; } - virtual MemberList const& getMembers() const override; - -private: - TypePointer m_actualType; - /// Context in which this type is used (influences visibility etc.), can be nullptr. - ContractDefinition const* m_currentContract; - /// List of member types, will be lazy-initialized because of recursive references. - mutable std::unique_ptr m_members; -}; - - -/** - * The type of a function modifier. Not used for anything for now. - */ -class ModifierType: public Type -{ -public: - virtual Category getCategory() const override { return Category::Modifier; } - explicit ModifierType(ModifierDefinition const& _modifier); - - virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } - virtual bool canBeStored() const override { return false; } - virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); } - virtual bool canLiveOutsideStorage() const override { return false; } - virtual unsigned getSizeOnStack() const override { return 0; } - virtual bool operator==(Type const& _other) const override; - virtual std::string toString() const override; - -private: - TypePointers m_parameterTypes; -}; - - -/** - * Special type for magic variables (block, msg, tx), similar to a struct but without any reference - * (it always references a global singleton by name). - */ -class MagicType: public Type -{ -public: - enum class Kind { Block, Message, Transaction }; - virtual Category getCategory() const override { return Category::Magic; } - - explicit MagicType(Kind _kind); - - virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override - { - return TypePointer(); - } - - virtual bool operator==(Type const& _other) const; - virtual bool canBeStored() const override { return false; } - virtual bool canLiveOutsideStorage() const override { return true; } - virtual unsigned getSizeOnStack() const override { return 0; } - virtual MemberList const& getMembers() const override { return m_members; } - - virtual std::string toString() const override; - -private: - Kind m_kind; - - MemberList m_members; -}; - -} -} diff --git a/Utils.h b/Utils.h deleted file mode 100644 index 1411f66b4..000000000 --- a/Utils.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 . -*/ -/** - * @author Christian - * @date 2014 - * Solidity Utilities. - */ - -#pragma once - -#include -#include - -namespace dev -{ -namespace solidity -{ - -/// Assertion that throws an InternalCompilerError containing the given description if it is not met. -#define solAssert(CONDITION, DESCRIPTION) \ - ::dev::solidity::solAssertAux(CONDITION, DESCRIPTION, __LINE__, __FILE__, ETH_FUNC) - -inline void solAssertAux(bool _condition, std::string const& _errorDescription, unsigned _line, - char const* _file, char const* _function) -{ - if (!_condition) - ::boost::throw_exception( InternalCompilerError() - << errinfo_comment(_errorDescription) - << ::boost::throw_function(_function) - << ::boost::throw_file(_file) - << ::boost::throw_line(_line)); -} - -inline void solAssertAux(void const* _pointer, std::string const& _errorDescription, unsigned _line, - char const* _file, char const* _function) -{ - solAssertAux(_pointer != nullptr, _errorDescription, _line, _file, _function); -} - -} -} diff --git a/grammar.txt b/grammar.txt deleted file mode 100644 index a3b246873..000000000 --- a/grammar.txt +++ /dev/null @@ -1,46 +0,0 @@ -ContractDefinition = 'contract' Identifier - ( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )? - '{' ContractPart* '}' -ContractPart = StateVariableDeclaration | StructDefinition | ModifierDefinition | FunctionDefinition | EnumDefinition - -InheritanceSpecifier = Identifier ( '(' Expression ( ',' Expression )* ')' )? -StructDefinition = 'struct' Identifier '{' - ( VariableDeclaration (';' VariableDeclaration)* )? '} -StateVariableDeclaration = TypeName ( 'public' | 'inheritable' | 'private' )? Identifier ';' -ModifierDefinition = 'modifier' Identifier ParameterList? Block -FunctionDefinition = 'function' Identifier ParameterList - ( Identifier | 'constant' | 'external' | 'public' | 'inheritable' | 'private' )* - ( 'returns' ParameterList )? Block - -EnumValue = Identifier -EnumDefinition = 'enum' '{' EnumValue (',' EnumValue)* '}' -ParameterList = '(' ( VariableDeclaration (',' VariableDeclaration)* )? ')' -// semantic restriction: mappings and structs (recursively) containing mappings -// are not allowed in argument lists -VariableDeclaration = TypeName Identifier -TypeName = ElementaryTypeName | Identifier | Mapping -Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')' - -Block = '{' Statement* '}' -Statement = IfStatement | WhileStatement | Block | - ( Continue | Break | Return | VariableDefinition | ExpressionStatement ) ';' - -ExpressionStatement = Expression -IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )? -WhileStatement = 'while' '(' Expression ')' Statement -VardefOrExprStmt = Variabledefinition | ExpressionStatement -ForStatement = 'for' '(' (VardefOrExprStmt)? ';' (Expression)? ';' (ExpressionStatement)? ')' Statement -Continue = 'continue' ';' -Break = 'break' ';' -Return = 'return' Expression? ';' -VariableDefinition = VariableDeclaration ( = Expression )? ';' - -Expression = Assignment | UnaryOperation | BinaryOperation | FunctionCall | NewExpression | IndexAccess | - MemberAccess | PrimaryExpression -// The expression syntax is actually much more complicated -Assignment = Expression (AssignmentOp Expression) -FunctionCall = Expression '(' Expression ( ',' Expression )* ')' -NewExpression = 'new' Identifier -MemberAccess = Expression '.' Identifier -IndexAccess = Expression '[' Expresison ']' -PrimaryExpression = Identifier | NumberLiteral | StringLiteral | ElementaryTypeName | '(' Expression ')' From 94c51caf89a86e29dab21141d76eae0c73292b01 Mon Sep 17 00:00:00 2001 From: chriseth Date: Sat, 7 Mar 2015 23:08:39 +0100 Subject: [PATCH 02/70] Fixed: Some instructions did not have source locations. --- Compiler.cpp | 32 +++++++++++++++++--------------- CompilerContext.cpp | 36 +++++++----------------------------- CompilerContext.h | 19 +++++++++++-------- ExpressionCompiler.cpp | 18 ++++++++++-------- 4 files changed, 45 insertions(+), 60 deletions(-) diff --git a/Compiler.cpp b/Compiler.cpp index 5eeb0c3e2..7ff846bdb 100644 --- a/Compiler.cpp +++ b/Compiler.cpp @@ -132,7 +132,7 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract, Comp void Compiler::appendBaseConstructor(FunctionDefinition const& _constructor) { - CompilerContext::LocationSetter locationSetter(m_context, &_constructor); + CompilerContext::LocationSetter locationSetter(m_context, _constructor); FunctionType constructorType(_constructor); if (!constructorType.getParameterTypes().empty()) { @@ -146,7 +146,7 @@ void Compiler::appendBaseConstructor(FunctionDefinition const& _constructor) void Compiler::appendConstructor(FunctionDefinition const& _constructor) { - CompilerContext::LocationSetter locationSetter(m_context, &_constructor); + CompilerContext::LocationSetter locationSetter(m_context, _constructor); // copy constructor arguments from code to memory and then to stack, they are supplied after the actual program unsigned argumentSize = 0; for (ASTPointer const& var: _constructor.getParameters()) @@ -192,10 +192,12 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract) for (auto const& it: interfaceFunctions) { FunctionTypePointer const& functionType = it.second; + solAssert(functionType->hasDeclaration(), ""); + CompilerContext::LocationSetter locationSetter(m_context, functionType->getDeclaration()); m_context << callDataUnpackerEntryPoints.at(it.first); eth::AssemblyItem returnTag = m_context.pushNewTag(); appendCalldataUnpacker(functionType->getParameterTypes()); - m_context.appendJumpTo(m_context.getFunctionEntryLabel(it.second->getDeclaration())); + m_context.appendJumpTo(m_context.getFunctionEntryLabel(functionType->getDeclaration())); m_context << returnTag; appendReturnValuePacker(functionType->getReturnParameterTypes()); } @@ -286,7 +288,7 @@ void Compiler::initializeStateVariables(ContractDefinition const& _contract) bool Compiler::visit(VariableDeclaration const& _variableDeclaration) { solAssert(_variableDeclaration.isStateVariable(), "Compiler visit to non-state variable declaration."); - CompilerContext::LocationSetter locationSetter(m_context, &_variableDeclaration); + CompilerContext::LocationSetter locationSetter(m_context, _variableDeclaration); m_context.startFunction(_variableDeclaration); m_breakTags.clear(); @@ -300,7 +302,7 @@ bool Compiler::visit(VariableDeclaration const& _variableDeclaration) bool Compiler::visit(FunctionDefinition const& _function) { - CompilerContext::LocationSetter locationSetter(m_context, &_function); + CompilerContext::LocationSetter locationSetter(m_context, _function); //@todo to simplify this, the calling convention could by changed such that // caller puts: [retarg0] ... [retargm] [return address] [arg0] ... [argn] // although note that this reduces the size of the visible stack @@ -384,7 +386,7 @@ bool Compiler::visit(FunctionDefinition const& _function) bool Compiler::visit(IfStatement const& _ifStatement) { StackHeightChecker checker(m_context); - CompilerContext::LocationSetter locationSetter(m_context, &_ifStatement); + CompilerContext::LocationSetter locationSetter(m_context, _ifStatement); compileExpression(_ifStatement.getCondition()); eth::AssemblyItem trueTag = m_context.appendConditionalJump(); if (_ifStatement.getFalseStatement()) @@ -401,7 +403,7 @@ bool Compiler::visit(IfStatement const& _ifStatement) bool Compiler::visit(WhileStatement const& _whileStatement) { StackHeightChecker checker(m_context); - CompilerContext::LocationSetter locationSetter(m_context, &_whileStatement); + CompilerContext::LocationSetter locationSetter(m_context, _whileStatement); eth::AssemblyItem loopStart = m_context.newTag(); eth::AssemblyItem loopEnd = m_context.newTag(); m_continueTags.push_back(loopStart); @@ -427,7 +429,7 @@ bool Compiler::visit(WhileStatement const& _whileStatement) bool Compiler::visit(ForStatement const& _forStatement) { StackHeightChecker checker(m_context); - CompilerContext::LocationSetter locationSetter(m_context, &_forStatement); + CompilerContext::LocationSetter locationSetter(m_context, _forStatement); eth::AssemblyItem loopStart = m_context.newTag(); eth::AssemblyItem loopEnd = m_context.newTag(); m_continueTags.push_back(loopStart); @@ -464,7 +466,7 @@ bool Compiler::visit(ForStatement const& _forStatement) bool Compiler::visit(Continue const& _continueStatement) { - CompilerContext::LocationSetter locationSetter(m_context, &_continueStatement); + CompilerContext::LocationSetter locationSetter(m_context, _continueStatement); if (!m_continueTags.empty()) m_context.appendJumpTo(m_continueTags.back()); return false; @@ -472,7 +474,7 @@ bool Compiler::visit(Continue const& _continueStatement) bool Compiler::visit(Break const& _breakStatement) { - CompilerContext::LocationSetter locationSetter(m_context, &_breakStatement); + CompilerContext::LocationSetter locationSetter(m_context, _breakStatement); if (!m_breakTags.empty()) m_context.appendJumpTo(m_breakTags.back()); return false; @@ -480,7 +482,7 @@ bool Compiler::visit(Break const& _breakStatement) bool Compiler::visit(Return const& _return) { - CompilerContext::LocationSetter locationSetter(m_context, &_return); + CompilerContext::LocationSetter locationSetter(m_context, _return); //@todo modifications are needed to make this work with functions returning multiple values if (Expression const* expression = _return.getExpression()) { @@ -499,7 +501,7 @@ bool Compiler::visit(Return const& _return) bool Compiler::visit(VariableDeclarationStatement const& _variableDeclarationStatement) { StackHeightChecker checker(m_context); - CompilerContext::LocationSetter locationSetter(m_context, &_variableDeclarationStatement); + CompilerContext::LocationSetter locationSetter(m_context, _variableDeclarationStatement); if (Expression const* expression = _variableDeclarationStatement.getExpression()) { compileExpression(*expression, _variableDeclarationStatement.getDeclaration().getType()); @@ -512,7 +514,7 @@ bool Compiler::visit(VariableDeclarationStatement const& _variableDeclarationSta bool Compiler::visit(ExpressionStatement const& _expressionStatement) { StackHeightChecker checker(m_context); - CompilerContext::LocationSetter locationSetter(m_context, &_expressionStatement); + CompilerContext::LocationSetter locationSetter(m_context, _expressionStatement); Expression const& expression = _expressionStatement.getExpression(); compileExpression(expression); CompilerUtils(m_context).popStackElement(*expression.getType()); @@ -523,7 +525,7 @@ bool Compiler::visit(ExpressionStatement const& _expressionStatement) bool Compiler::visit(PlaceholderStatement const& _placeholderStatement) { StackHeightChecker checker(m_context); - CompilerContext::LocationSetter locationSetter(m_context, &_placeholderStatement); + CompilerContext::LocationSetter locationSetter(m_context, _placeholderStatement); ++m_modifierDepth; appendModifierOrFunctionCode(); --m_modifierDepth; @@ -550,7 +552,7 @@ void Compiler::appendModifierOrFunctionCode() } ModifierDefinition const& modifier = m_context.getFunctionModifier(modifierInvocation->getName()->getName()); - CompilerContext::LocationSetter locationSetter(m_context, &modifier); + CompilerContext::LocationSetter locationSetter(m_context, modifier); solAssert(modifier.getParameters().size() == modifierInvocation->getArguments().size(), ""); for (unsigned i = 0; i < modifier.getParameters().size(); ++i) { diff --git a/CompilerContext.cpp b/CompilerContext.cpp index ee8d3e00c..1dea62e93 100644 --- a/CompilerContext.cpp +++ b/CompilerContext.cpp @@ -69,7 +69,7 @@ void CompilerContext::removeVariable(VariableDeclaration const& _declaration) void CompilerContext::addAndInitializeVariable(VariableDeclaration const& _declaration) { - LocationSetter locationSetter(*this, &_declaration); + LocationSetter locationSetter(*this, _declaration); addVariable(_declaration); int const size = _declaration.getType()->getSizeOnStack(); for (int i = 0; i < size; ++i) @@ -182,34 +182,7 @@ void CompilerContext::resetVisitedNodes(ASTNode const* _node) stack newStack; newStack.push(_node); std::swap(m_visitedNodes, newStack); -} - -CompilerContext& CompilerContext::operator<<(eth::AssemblyItem const& _item) -{ - solAssert(!m_visitedNodes.empty(), "No node on the visited stack"); - m_asm.append(_item, m_visitedNodes.top()->getLocation()); - return *this; -} - -CompilerContext& CompilerContext::operator<<(eth::Instruction _instruction) -{ - solAssert(!m_visitedNodes.empty(), "No node on the visited stack"); - m_asm.append(_instruction, m_visitedNodes.top()->getLocation()); - return *this; -} - -CompilerContext& CompilerContext::operator<<(u256 const& _value) -{ - solAssert(!m_visitedNodes.empty(), "No node on the visited stack"); - m_asm.append(_value, m_visitedNodes.top()->getLocation()); - return *this; -} - -CompilerContext& CompilerContext::operator<<(bytes const& _data) -{ - solAssert(!m_visitedNodes.empty(), "No node on the visited stack"); - m_asm.append(_data, m_visitedNodes.top()->getLocation()); - return *this; + updateSourceLocation(); } vector::const_iterator CompilerContext::getSuperContract(ContractDefinition const& _contract) const @@ -220,5 +193,10 @@ vector::const_iterator CompilerContext::getSuperContr return ++it; } +void CompilerContext::updateSourceLocation() +{ + m_asm.setSourceLocation(m_visitedNodes.empty() ? SourceLocation() : m_visitedNodes.top()->getLocation()); +} + } } diff --git a/CompilerContext.h b/CompilerContext.h index e42e7c76c..3f31a1600 100644 --- a/CompilerContext.h +++ b/CompilerContext.h @@ -108,15 +108,15 @@ public: /// Resets the stack of visited nodes with a new stack having only @c _node void resetVisitedNodes(ASTNode const* _node); /// Pops the stack of visited nodes - void popVisitedNodes() { m_visitedNodes.pop(); } + void popVisitedNodes() { m_visitedNodes.pop(); updateSourceLocation(); } /// Pushes an ASTNode to the stack of visited nodes - void pushVisitedNodes(ASTNode const* _node) { m_visitedNodes.push(_node); } + void pushVisitedNodes(ASTNode const* _node) { m_visitedNodes.push(_node); updateSourceLocation(); } /// Append elements to the current instruction list and adjust @a m_stackOffset. - CompilerContext& operator<<(eth::AssemblyItem const& _item); - CompilerContext& operator<<(eth::Instruction _instruction); - CompilerContext& operator<<(u256 const& _value); - CompilerContext& operator<<(bytes const& _data); + CompilerContext& operator<<(eth::AssemblyItem const& _item) { m_asm.append(_item); return *this; } + CompilerContext& operator<<(eth::Instruction _instruction) { m_asm.append(_instruction); return *this; } + CompilerContext& operator<<(u256 const& _value) { m_asm.append(_value); return *this; } + CompilerContext& operator<<(bytes const& _data) { m_asm.append(_data); return *this; } eth::Assembly const& getAssembly() const { return m_asm; } /// @arg _sourceCodes is the map of input files to source code strings @@ -130,12 +130,15 @@ public: class LocationSetter: public ScopeGuard { public: - LocationSetter(CompilerContext& _compilerContext, ASTNode const* _node): - ScopeGuard(std::bind(&CompilerContext::popVisitedNodes, _compilerContext)) { _compilerContext.pushVisitedNodes(_node); } + LocationSetter(CompilerContext& _compilerContext, ASTNode const& _node): + ScopeGuard([&]{ _compilerContext.popVisitedNodes(); }) + { _compilerContext.pushVisitedNodes(&_node); } }; private: std::vector::const_iterator getSuperContract(const ContractDefinition &_contract) const; + /// Updates source location set in the assembly. + void updateSourceLocation(); eth::Assembly m_asm; /// Magic global variables like msg, tx or this, distinguished by type. diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 3d7a25311..b02aecf5f 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -48,7 +48,7 @@ void ExpressionCompiler::appendStateVariableInitialization(VariableDeclaration c if (!_varDecl.getValue()) return; solAssert(!!_varDecl.getValue()->getType(), "Type information not available."); - CompilerContext::LocationSetter locationSetter(m_context, &_varDecl); + CompilerContext::LocationSetter locationSetter(m_context, _varDecl); _varDecl.getValue()->accept(*this); appendTypeConversion(*_varDecl.getValue()->getType(), *_varDecl.getType(), true); @@ -57,7 +57,7 @@ void ExpressionCompiler::appendStateVariableInitialization(VariableDeclaration c void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl) { - CompilerContext::LocationSetter locationSetter(m_context, &_varDecl); + CompilerContext::LocationSetter locationSetter(m_context, _varDecl); FunctionType accessorType(_varDecl); unsigned length = 0; @@ -204,7 +204,7 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con bool ExpressionCompiler::visit(Assignment const& _assignment) { - CompilerContext::LocationSetter locationSetter(m_context, &_assignment); + CompilerContext::LocationSetter locationSetter(m_context, _assignment); _assignment.getRightHandSide().accept(*this); if (_assignment.getType()->isValueType()) appendTypeConversion(*_assignment.getRightHandSide().getType(), *_assignment.getType()); @@ -237,7 +237,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) { - CompilerContext::LocationSetter locationSetter(m_context, &_unaryOperation); + CompilerContext::LocationSetter locationSetter(m_context, _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 @@ -307,7 +307,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) { - CompilerContext::LocationSetter locationSetter(m_context, &_binaryOperation); + CompilerContext::LocationSetter locationSetter(m_context, _binaryOperation); Expression const& leftExpression = _binaryOperation.getLeftExpression(); Expression const& rightExpression = _binaryOperation.getRightExpression(); Type const& commonType = _binaryOperation.getCommonType(); @@ -354,7 +354,7 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) bool ExpressionCompiler::visit(FunctionCall const& _functionCall) { - CompilerContext::LocationSetter locationSetter(m_context, &_functionCall); + CompilerContext::LocationSetter locationSetter(m_context, _functionCall); using Location = FunctionType::Location; if (_functionCall.isTypeConversion()) { @@ -572,7 +572,7 @@ bool ExpressionCompiler::visit(NewExpression const&) void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) { - CompilerContext::LocationSetter locationSetter(m_context, &_memberAccess); + CompilerContext::LocationSetter locationSetter(m_context, _memberAccess); ASTString const& member = _memberAccess.getMemberName(); switch (_memberAccess.getExpression().getType()->getCategory()) { @@ -707,7 +707,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) { - CompilerContext::LocationSetter locationSetter(m_context, &_indexAccess); + CompilerContext::LocationSetter locationSetter(m_context, _indexAccess); _indexAccess.getBaseExpression().accept(*this); Type const& baseType = *_indexAccess.getBaseExpression().getType(); @@ -821,6 +821,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) void ExpressionCompiler::endVisit(Identifier const& _identifier) { + CompilerContext::LocationSetter locationSetter(m_context, _identifier); Declaration const* declaration = _identifier.getReferencedDeclaration(); if (MagicVariableDeclaration const* magicVar = dynamic_cast(declaration)) { @@ -853,6 +854,7 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) void ExpressionCompiler::endVisit(Literal const& _literal) { + CompilerContext::LocationSetter locationSetter(m_context, _literal); switch (_literal.getType()->getCategory()) { case Type::Category::IntegerConstant: From cd8a0ab65d387cd88f3062f937a456b7c0e33b5c Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 9 Mar 2015 13:28:25 +0100 Subject: [PATCH 03/70] Style fixes. --- CompilerContext.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CompilerContext.h b/CompilerContext.h index 3f31a1600..4d63d8ba0 100644 --- a/CompilerContext.h +++ b/CompilerContext.h @@ -131,8 +131,7 @@ public: { public: LocationSetter(CompilerContext& _compilerContext, ASTNode const& _node): - ScopeGuard([&]{ _compilerContext.popVisitedNodes(); }) - { _compilerContext.pushVisitedNodes(&_node); } + ScopeGuard([&]{ _compilerContext.popVisitedNodes(); }) { _compilerContext.pushVisitedNodes(&_node); } }; private: From 9c82cbeddf1b03cc99660a867de8720d1d174b9c Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 9 Mar 2015 19:22:43 +0100 Subject: [PATCH 04/70] Global variable "now" (alias for block.timestamp). --- ExpressionCompiler.cpp | 12 +++++++++++- GlobalContext.cpp | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index b02aecf5f..d2457e676 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -825,10 +825,20 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) Declaration const* declaration = _identifier.getReferencedDeclaration(); if (MagicVariableDeclaration const* magicVar = dynamic_cast(declaration)) { - if (magicVar->getType()->getCategory() == Type::Category::Contract) + switch (magicVar->getType()->getCategory()) + { + case Type::Category::Contract: // "this" or "super" if (!dynamic_cast(*magicVar->getType()).isSuper()) m_context << eth::Instruction::ADDRESS; + break; + case Type::Category::Integer: + // "now" + m_context << eth::Instruction::TIMESTAMP; + break; + default: + break; + } } else if (FunctionDefinition const* functionDef = dynamic_cast(declaration)) m_context << m_context.getVirtualFunctionEntryLabel(*functionDef).pushTag(); diff --git a/GlobalContext.cpp b/GlobalContext.cpp index 60de5105f..411e99abb 100644 --- a/GlobalContext.cpp +++ b/GlobalContext.cpp @@ -37,6 +37,7 @@ GlobalContext::GlobalContext(): m_magicVariables(vector>{make_shared("block", make_shared(MagicType::Kind::Block)), make_shared("msg", make_shared(MagicType::Kind::Message)), make_shared("tx", make_shared(MagicType::Kind::Transaction)), + make_shared("now", make_shared(256)), make_shared("suicide", make_shared(strings{"address"}, strings{}, FunctionType::Location::Suicide)), make_shared("sha3", From 74a01826ee9587ef9413e2ec808fcd53564f0b9d Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Mon, 9 Mar 2015 19:22:24 +0100 Subject: [PATCH 05/70] added information about jump type for jump instructions Conflicts: libevmcore/Assembly.cpp libsolidity/Compiler.cpp --- Compiler.cpp | 11 ++++++++--- CompilerContext.cpp | 7 +++++++ CompilerContext.h | 2 +- ExpressionCompiler.cpp | 5 +++-- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/Compiler.cpp b/Compiler.cpp index 7ff846bdb..46888683e 100644 --- a/Compiler.cpp +++ b/Compiler.cpp @@ -177,7 +177,9 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract) { 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)); + auto assemblyItem = callDataUnpackerEntryPoints.at(it.first); + //assemblyItem.setJumpType(eth::AssemblyItem::JumpType::IntoFunction); + m_context.appendConditionalJumpTo(assemblyItem); } if (FunctionDefinition const* fallback = _contract.getFallbackFunction()) { @@ -197,7 +199,9 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract) m_context << callDataUnpackerEntryPoints.at(it.first); eth::AssemblyItem returnTag = m_context.pushNewTag(); appendCalldataUnpacker(functionType->getParameterTypes()); - m_context.appendJumpTo(m_context.getFunctionEntryLabel(functionType->getDeclaration())); + auto assemblyItem = m_context.getFunctionEntryLabel(functionType->getDeclaration()); + //assemblyItem.setJumpType(eth::AssemblyItem::JumpType::IntoFunction); + m_context.appendJumpTo(assemblyItem); m_context << returnTag; appendReturnValuePacker(functionType->getReturnParameterTypes()); } @@ -378,8 +382,9 @@ bool Compiler::visit(FunctionDefinition const& _function) m_context.removeVariable(*localVariable); m_context.adjustStackOffset(-(int)c_returnValuesSize); + if (!_function.isConstructor()) - m_context << eth::Instruction::JUMP; + m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction); return false; } diff --git a/CompilerContext.cpp b/CompilerContext.cpp index 1dea62e93..f2bb1de20 100644 --- a/CompilerContext.cpp +++ b/CompilerContext.cpp @@ -177,6 +177,13 @@ u256 CompilerContext::getStorageLocationOfVariable(const Declaration& _declarati return it->second; } +CompilerContext& CompilerContext::appendJump(eth::AssemblyItem::JumpType _jumpType) +{ + eth::AssemblyItem item(eth::Instruction::JUMP); + item.setJumpType(_jumpType); + return *this << item; +} + void CompilerContext::resetVisitedNodes(ASTNode const* _node) { stack newStack; diff --git a/CompilerContext.h b/CompilerContext.h index 4d63d8ba0..f468d29c4 100644 --- a/CompilerContext.h +++ b/CompilerContext.h @@ -91,7 +91,7 @@ public: /// Appends a JUMP to a new tag and @returns the tag eth::AssemblyItem appendJumpToNew() { return m_asm.appendJump().tag(); } /// Appends a JUMP to a tag already on the stack - CompilerContext& appendJump() { return *this << eth::Instruction::JUMP; } + CompilerContext& appendJump(eth::AssemblyItem::JumpType _jumpType = eth::AssemblyItem::JumpType::Ordinary); /// Appends a JUMP to a specific tag CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm.appendJump(_tag); return *this; } /// Appends pushing of a new tag and @returns the new tag. diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index d2457e676..129261120 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -108,7 +108,8 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& retSizeOnStack = returnType->getSizeOnStack(); } solAssert(retSizeOnStack <= 15, "Stack too deep."); - m_context << eth::dupInstruction(retSizeOnStack + 1) << eth::Instruction::JUMP; + m_context << eth::dupInstruction(retSizeOnStack + 1); + m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction); } void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded) @@ -405,7 +406,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } _functionCall.getExpression().accept(*this); - m_context.appendJump(); + m_context.appendJump(eth::AssemblyItem::JumpType::IntoFunction); m_context << returnLabel; unsigned returnParametersSize = CompilerUtils::getSizeOnStack(function.getReturnParameterTypes()); From d5cbb2acd2ffd1323524cb1d7e596eb768518e3e Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Tue, 10 Mar 2015 12:00:23 +0100 Subject: [PATCH 06/70] added brackets for printing in/out cleaned up --- Compiler.cpp | 8 ++------ CompilerContext.h | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/Compiler.cpp b/Compiler.cpp index 46888683e..dc6e2c5a8 100644 --- a/Compiler.cpp +++ b/Compiler.cpp @@ -177,9 +177,7 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract) { callDataUnpackerEntryPoints.insert(std::make_pair(it.first, m_context.newTag())); m_context << eth::dupInstruction(1) << u256(FixedHash<4>::Arith(it.first)) << eth::Instruction::EQ; - auto assemblyItem = callDataUnpackerEntryPoints.at(it.first); - //assemblyItem.setJumpType(eth::AssemblyItem::JumpType::IntoFunction); - m_context.appendConditionalJumpTo(assemblyItem); + m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.at(it.first)); } if (FunctionDefinition const* fallback = _contract.getFallbackFunction()) { @@ -199,9 +197,7 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract) m_context << callDataUnpackerEntryPoints.at(it.first); eth::AssemblyItem returnTag = m_context.pushNewTag(); appendCalldataUnpacker(functionType->getParameterTypes()); - auto assemblyItem = m_context.getFunctionEntryLabel(functionType->getDeclaration()); - //assemblyItem.setJumpType(eth::AssemblyItem::JumpType::IntoFunction); - m_context.appendJumpTo(assemblyItem); + m_context.appendJumpTo(m_context.getFunctionEntryLabel(functionType->getDeclaration())); m_context << returnTag; appendReturnValuePacker(functionType->getReturnParameterTypes()); } diff --git a/CompilerContext.h b/CompilerContext.h index f468d29c4..76923a77a 100644 --- a/CompilerContext.h +++ b/CompilerContext.h @@ -120,7 +120,7 @@ public: eth::Assembly const& getAssembly() const { return m_asm; } /// @arg _sourceCodes is the map of input files to source code strings - void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap()) const { m_asm.streamRLP(_stream, "", _sourceCodes); } + void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap()) const { m_asm.stream(_stream, "", _sourceCodes); } bytes getAssembledBytecode(bool _optimize = false) { return m_asm.optimise(_optimize).assemble(); } From d41c51a8106ff20b347d95adf453e58a20d89513 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 10 Mar 2015 15:11:13 +0100 Subject: [PATCH 07/70] Fixed some checker warnings. --- Compiler.h | 4 ++-- Parser.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Compiler.h b/Compiler.h index 76f16f3ab..4b1e1b4d6 100644 --- a/Compiler.h +++ b/Compiler.h @@ -94,8 +94,8 @@ private: std::vector 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 + FunctionDefinition const* m_currentFunction = nullptr; + unsigned m_stackCleanupForReturn = 0; ///< this number of stack elements need to be removed before jump to m_returnTag // arguments for base constructors, filled in derived-to-base order std::map> const*> m_baseArguments; }; diff --git a/Parser.h b/Parser.h index 87eb2f8ff..cc0b2ca17 100644 --- a/Parser.h +++ b/Parser.h @@ -34,6 +34,8 @@ class Scanner; class Parser { public: + Parser() {} + ASTPointer parse(std::shared_ptr const& _scanner); std::shared_ptr const& getSourceName() const; From 36fe571576a7e5af91d9f95ff0a2252394720a34 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 4 Mar 2015 11:41:15 +0100 Subject: [PATCH 08/70] start of cleanup --- Token.h | 99 +++++++++++++++++++------------------------------------ Types.cpp | 2 +- 2 files changed, 34 insertions(+), 67 deletions(-) diff --git a/Token.h b/Token.h index 85979b566..7c1de60bb 100644 --- a/Token.h +++ b/Token.h @@ -253,75 +253,42 @@ namespace solidity 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(Bytes, "bytes", 0) \ + K(Bytes8, "bytes8", 0) \ + K(Bytes16, "bytes16", 0) \ + K(Bytes24, "bytes24", 0) \ + K(Bytes32, "bytes32", 0) \ + K(Bytes40, "bytes40", 0) \ + K(Bytes48, "bytes48", 0) \ + K(Bytes56, "bytes56", 0) \ + K(Bytes64, "bytes64", 0) \ + K(Bytes72, "bytes72", 0) \ + K(Bytes80, "bytes80", 0) \ + K(Bytes88, "bytes88", 0) \ + K(Bytes96, "bytes96", 0) \ + K(Bytes104, "bytes104", 0) \ + K(Bytes112, "bytes112", 0) \ + K(Bytes120, "bytes120", 0) \ + K(Bytes128, "bytes128", 0) \ + K(Bytes136, "bytes136", 0) \ + K(Bytes144, "bytes144", 0) \ + K(Bytes152, "bytes152", 0) \ + K(Bytes160, "bytes160", 0) \ + K(Bytes168, "bytes168", 0) \ + K(Bytes176, "bytes178", 0) \ + K(Bytes184, "bytes184", 0) \ + K(Bytes192, "bytes192", 0) \ + K(Bytes200, "bytes200", 0) \ + K(Bytes208, "bytes208", 0) \ + K(Bytes216, "bytes216", 0) \ + K(Bytes224, "bytes224", 0) \ + K(Bytes232, "bytes232", 0) \ + K(Bytes240, "bytes240", 0) \ + K(Bytes248, "bytes248", 0) \ + K(Bytes256, "bytes256", 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) \ diff --git a/Types.cpp b/Types.cpp index 454d79d9b..73cd09d6f 100644 --- a/Types.cpp +++ b/Types.cpp @@ -39,7 +39,7 @@ TypePointer Type::fromElementaryTypeName(Token::Value _typeToken) { solAssert(Token::isElementaryTypeName(_typeToken), "Elementary type name expected."); - if (Token::Int <= _typeToken && _typeToken <= Token::Hash256) + if (Token::Int <= _typeToken && _typeToken <= Token::Bytes256) { int offset = _typeToken - Token::Int; int bytes = offset % 33; From bede2f2ad7eb023018ab2faac17286fdee82b03c Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 4 Mar 2015 17:43:40 +0100 Subject: [PATCH 09/70] More changes towards getting rid of HashXX --- ExpressionCompiler.cpp | 4 ++-- Token.h | 4 +--- Types.cpp | 29 +++++++++++------------------ Types.h | 4 ++-- 4 files changed, 16 insertions(+), 25 deletions(-) diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 129261120..91a59b2d0 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -131,7 +131,7 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con // 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(_targetType); - solAssert(targetIntegerType.isHash(), "Only conversion between String and Hash is allowed."); + solAssert(targetIntegerType.isBytes(), "Only conversion between String and Bytes 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; } @@ -164,7 +164,7 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con // only to shift left because of opposite alignment StaticStringType const& targetStringType = dynamic_cast(_targetType); IntegerType const& typeOnStack = dynamic_cast(_typeOnStack); - solAssert(typeOnStack.isHash(), "Only conversion between String and Hash is allowed."); + solAssert(typeOnStack.isBytes(), "Only conversion between String and Bytes is allowed."); solAssert(typeOnStack.getNumBits() == targetStringType.getNumBytes() * 8, "The size should be the same."); m_context << (u256(1) << (256 - typeOnStack.getNumBits())) << eth::Instruction::MUL; } diff --git a/Token.h b/Token.h index 7c1de60bb..d7f56aa42 100644 --- a/Token.h +++ b/Token.h @@ -252,8 +252,6 @@ namespace solidity K(UInt240, "uint240", 0) \ K(UInt248, "uint248", 0) \ K(UInt256, "uint256", 0) \ - K(Hash, "hash", 0) \ - K(Bytes, "bytes", 0) \ K(Bytes8, "bytes8", 0) \ K(Bytes16, "bytes16", 0) \ K(Bytes24, "bytes24", 0) \ @@ -286,10 +284,10 @@ namespace solidity K(Bytes240, "bytes240", 0) \ K(Bytes248, "bytes248", 0) \ K(Bytes256, "bytes256", 0) \ + K(Bytes, "bytes", 0) \ K(Address, "address", 0) \ K(Bool, "bool", 0) \ K(StringType, "string", 0) \ - K(Text, "text", 0) \ K(Real, "real", 0) \ K(UReal, "ureal", 0) \ T(TypesEnd, NULL, 0) /* used as type enum end marker */ \ diff --git a/Types.cpp b/Types.cpp index 73cd09d6f..daf7c03e1 100644 --- a/Types.cpp +++ b/Types.cpp @@ -49,14 +49,12 @@ TypePointer Type::fromElementaryTypeName(Token::Value _typeToken) return make_shared(bytes * 8, modifier == 0 ? IntegerType::Modifier::Signed : modifier == 1 ? IntegerType::Modifier::Unsigned : - IntegerType::Modifier::Hash); + IntegerType::Modifier::Bytes); } else if (_typeToken == Token::Address) return make_shared(0, IntegerType::Modifier::Address); else if (_typeToken == Token::Bool) return make_shared(); - else if (Token::String0 <= _typeToken && _typeToken <= Token::String32) - return make_shared(int(_typeToken) - int(Token::String0)); else if (_typeToken == Token::Bytes) return make_shared(ArrayType::Location::Storage); else @@ -159,8 +157,8 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const return false; if (isAddress()) return convertTo.isAddress(); - else if (isHash()) - return convertTo.isHash(); + else if (isBytes()) + return convertTo.isBytes(); else if (isSigned()) return convertTo.isSigned(); else @@ -169,14 +167,9 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - if (_convertTo.getCategory() == Category::String) - { - StaticStringType const& convertTo = dynamic_cast(_convertTo); - return isHash() && (m_bits == convertTo.getNumBytes() * 8); - } return _convertTo.getCategory() == getCategory() || - _convertTo.getCategory() == Category::Contract || - _convertTo.getCategory() == Category::Enum; + _convertTo.getCategory() == Category::Contract || + _convertTo.getCategory() == Category::Enum; } TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const @@ -190,8 +183,8 @@ TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const // "~" is ok for all other types else if (_operator == Token::BitNot) return shared_from_this(); - // nothing else for hashes - else if (isHash()) + // nothing else for bytes + else if (isBytes()) return TypePointer(); // for non-hash integers, we allow +, -, ++ and -- else if (_operator == Token::Add || _operator == Token::Sub || @@ -214,7 +207,7 @@ string IntegerType::toString() const { if (isAddress()) return "address"; - string prefix = isHash() ? "hash" : (isSigned() ? "int" : "uint"); + string prefix = isBytes() ? "bytes" : (isSigned() ? "int" : "uint"); return prefix + dev::toString(m_bits); } @@ -231,10 +224,10 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe if (Token::isCompareOp(_operator)) return commonType; - // Nothing else can be done with addresses, but hashes can receive bit operators + // Nothing else can be done with addresses, but bytes can receive bit operators if (commonType->isAddress()) return TypePointer(); - else if (commonType->isHash() && !Token::isBitOp(_operator)) + else if (commonType->isBytes() && !Token::isBitOp(_operator)) return TypePointer(); else return commonType; @@ -461,7 +454,7 @@ bool StaticStringType::isExplicitlyConvertibleTo(Type const& _convertTo) const if (_convertTo.getCategory() == Category::Integer) { IntegerType const& convertTo = dynamic_cast(_convertTo); - if (convertTo.isHash() && (m_bytes * 8 == convertTo.getNumBits())) + if (convertTo.isBytes() && (m_bytes * 8 == convertTo.getNumBits())) return true; } diff --git a/Types.h b/Types.h index 6cef8d64a..3d67720c7 100644 --- a/Types.h +++ b/Types.h @@ -165,7 +165,7 @@ class IntegerType: public Type public: enum class Modifier { - Unsigned, Signed, Hash, Address + Unsigned, Signed, Bytes, Address }; virtual Category getCategory() const override { return Category::Integer; } @@ -186,7 +186,7 @@ public: virtual std::string toString() const override; int getNumBits() const { return m_bits; } - bool isHash() const { return m_modifier == Modifier::Hash || m_modifier == Modifier::Address; } + bool isBytes() const { return m_modifier == Modifier::Bytes || m_modifier == Modifier::Address; } bool isAddress() const { return m_modifier == Modifier::Address; } bool isSigned() const { return m_modifier == Modifier::Signed; } From 7d7f37bd5e1221243729edbd6ef8d19fd2ce13eb Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 5 Mar 2015 16:54:55 +0100 Subject: [PATCH 10/70] Replacing StaticStringType with FixedBytesType --- CompilerUtils.cpp | 7 ++-- ExpressionCompiler.cpp | 26 ++++++------- Types.cpp | 84 ++++++++++++++++++++++++++---------------- Types.h | 27 +++++++------- 4 files changed, 84 insertions(+), 60 deletions(-) diff --git a/CompilerUtils.cpp b/CompilerUtils.cpp index 8a26b5d17..e11b6b4f6 100644 --- a/CompilerUtils.cpp +++ b/CompilerUtils.cpp @@ -177,8 +177,9 @@ void CompilerUtils::computeHashStatic(Type const& _type, bool _padToWordBoundari unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries) { - unsigned numBytes = _type.getCalldataEncodedSize(_padToWordBoundaries); - bool leftAligned = _type.getCategory() == Type::Category::String; + unsigned _encodedSize = _type.getCalldataEncodedSize(); + unsigned numBytes = _padToWordBoundaries ? getPaddedSize(_encodedSize) : _encodedSize; + bool leftAligned = _type.getCategory() == Type::Category::FixedBytes; if (numBytes == 0) m_context << eth::Instruction::POP << u256(0); else @@ -202,7 +203,7 @@ unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCallda unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const { unsigned numBytes = _type.getCalldataEncodedSize(_padToWordBoundaries); - bool leftAligned = _type.getCategory() == Type::Category::String; + bool leftAligned = _type.getCategory() == Type::Category::FixedBytes; if (numBytes == 0) m_context << eth::Instruction::POP; else diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 91a59b2d0..51e1fd0a9 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -123,23 +123,23 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con Type::Category stackTypeCategory = _typeOnStack.getCategory(); Type::Category targetTypeCategory = _targetType.getCategory(); - if (stackTypeCategory == Type::Category::String) + if (stackTypeCategory == Type::Category::FixedBytes) { - StaticStringType const& typeOnStack = dynamic_cast(_typeOnStack); + FixedBytesType const& typeOnStack = dynamic_cast(_typeOnStack); if (targetTypeCategory == Type::Category::Integer) { - // conversion from string to hash. no need to clean the high bit + // conversion from string to bytes. no need to clean the high bit // only to shift right because of opposite alignment IntegerType const& targetIntegerType = dynamic_cast(_targetType); - solAssert(targetIntegerType.isBytes(), "Only conversion between String and Bytes is allowed."); + solAssert(targetIntegerType.isAddress(), "Only conversion between Address and FixedBytes 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(_targetType); + // clear lower-order bytes for conversion to shorter bytes - we always clean + solAssert(targetTypeCategory == Type::Category::FixedBytes, "Invalid type conversion requested."); + FixedBytesType const& targetType = dynamic_cast(_targetType); if (targetType.getNumBytes() < typeOnStack.getNumBytes()) { if (targetType.getNumBytes() == 0) @@ -158,14 +158,14 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con stackTypeCategory == Type::Category::Contract || stackTypeCategory == Type::Category::IntegerConstant) { - if (targetTypeCategory == Type::Category::String && stackTypeCategory == Type::Category::Integer) + if (targetTypeCategory == Type::Category::FixedBytes && stackTypeCategory == Type::Category::Integer) { - // conversion from hash to string. no need to clean the high bit + // conversion from bytes to string. no need to clean the high bit // only to shift left because of opposite alignment - StaticStringType const& targetStringType = dynamic_cast(_targetType); + FixedBytesType const& targetBytesType = dynamic_cast(_targetType); IntegerType const& typeOnStack = dynamic_cast(_typeOnStack); - solAssert(typeOnStack.isBytes(), "Only conversion between String and Bytes is allowed."); - solAssert(typeOnStack.getNumBits() == targetStringType.getNumBytes() * 8, "The size should be the same."); + solAssert(typeOnStack.isAddress(), "Only conversion between Address and Bytes is allowed."); + solAssert(typeOnStack.getNumBits() == targetBytesType.getNumBytes() * 8, "The size should be the same."); m_context << (u256(1) << (256 - typeOnStack.getNumBits())) << eth::Instruction::MUL; } else if (targetTypeCategory == Type::Category::Enum) @@ -870,7 +870,7 @@ void ExpressionCompiler::endVisit(Literal const& _literal) { case Type::Category::IntegerConstant: case Type::Category::Bool: - case Type::Category::String: + case Type::Category::FixedBytes: m_context << _literal.getType()->literalValue(&_literal); break; default: diff --git a/Types.cpp b/Types.cpp index daf7c03e1..aadd884b5 100644 --- a/Types.cpp +++ b/Types.cpp @@ -46,10 +46,18 @@ TypePointer Type::fromElementaryTypeName(Token::Value _typeToken) if (bytes == 0) bytes = 32; int modifier = offset / 33; - return make_shared(bytes * 8, - modifier == 0 ? IntegerType::Modifier::Signed : - modifier == 1 ? IntegerType::Modifier::Unsigned : - IntegerType::Modifier::Bytes); + switch(modifier) + { + case 0: + return make_shared(bytes * 8, IntegerType::Modifier::Signed); + case 1: + return make_shared(bytes * 8, IntegerType::Modifier::Unsigned); + case 2: + return make_shared(bytes); + default: + solAssert(false, "Unexpected modifier value. Should never happen"); + return TypePointer(); + } } else if (_typeToken == Token::Address) return make_shared(0, IntegerType::Modifier::Address); @@ -121,7 +129,7 @@ TypePointer Type::forLiteral(Literal const& _literal) return make_shared(_literal); case Token::StringLiteral: //@todo put larger strings into dynamic strings - return StaticStringType::smallestTypeForLiteral(_literal.getValue()); + return FixedBytesType::smallestTypeForLiteral(_literal.getValue()); default: return shared_ptr(); } @@ -157,8 +165,6 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const return false; if (isAddress()) return convertTo.isAddress(); - else if (isBytes()) - return convertTo.isBytes(); else if (isSigned()) return convertTo.isSigned(); else @@ -183,10 +189,7 @@ TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const // "~" is ok for all other types else if (_operator == Token::BitNot) return shared_from_this(); - // nothing else for bytes - else if (isBytes()) - return TypePointer(); - // for non-hash integers, we allow +, -, ++ and -- + // for non-address integers, we allow +, -, ++ and -- else if (_operator == Token::Add || _operator == Token::Sub || _operator == Token::Inc || _operator == Token::Dec || _operator == Token::After) @@ -207,7 +210,7 @@ string IntegerType::toString() const { if (isAddress()) return "address"; - string prefix = isBytes() ? "bytes" : (isSigned() ? "int" : "uint"); + string prefix = isSigned() ? "int" : "uint"; return prefix + dev::toString(m_bits); } @@ -224,13 +227,7 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe if (Token::isCompareOp(_operator)) return commonType; - // Nothing else can be done with addresses, but bytes can receive bit operators - if (commonType->isAddress()) - return TypePointer(); - else if (commonType->isBytes() && !Token::isBitOp(_operator)) - return TypePointer(); - else - return commonType; + return TypePointer(); } const MemberList IntegerType::AddressMemberList = @@ -426,50 +423,75 @@ shared_ptr IntegerConstantType::getIntegerType() const : IntegerType::Modifier::Unsigned); } -shared_ptr StaticStringType::smallestTypeForLiteral(string const& _literal) +shared_ptr FixedBytesType::smallestTypeForLiteral(string const& _literal) { if (_literal.length() <= 32) - return make_shared(_literal.length()); - return shared_ptr(); + return make_shared(_literal.length()); + return shared_ptr(); } -StaticStringType::StaticStringType(int _bytes): m_bytes(_bytes) +FixedBytesType::FixedBytesType(int _bytes): m_bytes(_bytes) { solAssert(m_bytes >= 0 && m_bytes <= 32, "Invalid byte number for static string type: " + dev::toString(m_bytes)); } -bool StaticStringType::isImplicitlyConvertibleTo(Type const& _convertTo) const +bool FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const { if (_convertTo.getCategory() != getCategory()) return false; - StaticStringType const& convertTo = dynamic_cast(_convertTo); + FixedBytesType const& convertTo = dynamic_cast(_convertTo); return convertTo.m_bytes >= m_bytes; } -bool StaticStringType::isExplicitlyConvertibleTo(Type const& _convertTo) const +bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const { if (_convertTo.getCategory() == getCategory()) return true; if (_convertTo.getCategory() == Category::Integer) { IntegerType const& convertTo = dynamic_cast(_convertTo); - if (convertTo.isBytes() && (m_bytes * 8 == convertTo.getNumBits())) + if (m_bytes * 8 == convertTo.getNumBits()) return true; } return false; } -bool StaticStringType::operator==(Type const& _other) const +TypePointer FixedBytesType::unaryOperatorResult(Token::Value _operator) const +{ + // "delete" and "~" is okay for FixedBytesType + if (_operator == Token::Delete) + return make_shared(); + else if (_operator == Token::BitNot) + return shared_from_this(); + + return TypePointer(); +} + +TypePointer FixedBytesType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const +{ + auto commonType = dynamic_pointer_cast(Type::commonType(shared_from_this(), _other)); + + if (!commonType) + return TypePointer(); + + // FixedBytes can be compared and have bitwise operators applied to them + if (Token::isCompareOp(_operator) || Token::isBitOp(_operator)) + return commonType; + + return TypePointer(); +} + +bool FixedBytesType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) return false; - StaticStringType const& other = dynamic_cast(_other); + FixedBytesType const& other = dynamic_cast(_other); return other.m_bytes == m_bytes; } -u256 StaticStringType::literalValue(const Literal* _literal) const +u256 FixedBytesType::literalValue(const Literal* _literal) const { solAssert(_literal, ""); u256 value = 0; @@ -1117,7 +1139,7 @@ MagicType::MagicType(MagicType::Kind _kind): case Kind::Block: m_members = MemberList({{"coinbase", make_shared(0, IntegerType::Modifier::Address)}, {"timestamp", make_shared(256)}, - {"blockhash", make_shared(strings{"uint"}, strings{"hash"}, FunctionType::Location::BlockHash)}, + {"blockhash", make_shared(strings{"uint"}, strings{"bytes"}, FunctionType::Location::BlockHash)}, {"difficulty", make_shared(256)}, {"number", make_shared(256)}, {"gaslimit", make_shared(256)}}); diff --git a/Types.h b/Types.h index 3d67720c7..6517cf070 100644 --- a/Types.h +++ b/Types.h @@ -77,12 +77,12 @@ public: enum class Category { Integer, IntegerConstant, Bool, Real, Array, - String, Contract, Struct, Function, Enum, + FixedBytes, Contract, Struct, Function, Enum, Mapping, Void, TypeType, Modifier, Magic }; - ///@{ - ///@name Factory functions + /// @{ + /// @name Factory functions /// Factory functions that convert an AST @ref TypeName to a Type. static TypePointer fromElementaryTypeName(Token::Value _typeToken); static TypePointer fromElementaryTypeName(std::string const& _name); @@ -158,14 +158,14 @@ protected: }; /** - * Any kind of integer type including hash and address. + * Any kind of integer type including address. */ class IntegerType: public Type { public: enum class Modifier { - Unsigned, Signed, Bytes, Address + Unsigned, Signed, Address }; virtual Category getCategory() const override { return Category::Integer; } @@ -186,7 +186,6 @@ public: virtual std::string toString() const override; int getNumBits() const { return m_bits; } - bool isBytes() const { return m_modifier == Modifier::Bytes || m_modifier == Modifier::Address; } bool isAddress() const { return m_modifier == Modifier::Address; } bool isSigned() const { return m_modifier == Modifier::Signed; } @@ -232,27 +231,29 @@ private: }; /** - * String type with fixed length, up to 32 bytes. + * Bytes type with fixed length of up to 32 bytes */ -class StaticStringType: public Type +class FixedBytesType: public Type { public: - virtual Category getCategory() const override { return Category::String; } + virtual Category getCategory() const override { return Category::FixedBytes; } - /// @returns the smallest string type for the given literal or an empty pointer + /// @returns the smallest bytes type for the given literal or an empty pointer /// if no type fits. - static std::shared_ptr smallestTypeForLiteral(std::string const& _literal); + static std::shared_ptr smallestTypeForLiteral(std::string const& _literal); - explicit StaticStringType(int _bytes); + explicit FixedBytesType(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 TypePointer unaryOperatorResult(Token::Value _operator) const override; + virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; virtual unsigned getCalldataEncodedSize(bool _padded) const override { return _padded && m_bytes > 0 ? 32 : m_bytes; } virtual bool isValueType() const override { return true; } - virtual std::string toString() const override { return "string" + dev::toString(m_bytes); } + virtual std::string toString() const override { return "bytes" + dev::toString(m_bytes); } virtual u256 literalValue(Literal const* _literal) const override; int getNumBytes() const { return m_bytes; } From 3ca37cadddf4f58cef5273c6b49e0f55c1162251 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 6 Mar 2015 17:20:00 +0100 Subject: [PATCH 11/70] Fixes after rebasing on top of develop --- AST.cpp | 2 +- CompilerUtils.cpp | 3 +-- LValue.cpp | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/AST.cpp b/AST.cpp index 79b755e97..46c44995b 100644 --- a/AST.cpp +++ b/AST.cpp @@ -671,7 +671,7 @@ void IndexAccess::checkTypeRequirements() BOOST_THROW_EXCEPTION(createTypeError("Index expression cannot be omitted.")); m_index->expectType(IntegerType(256)); if (type.isByteArray()) - m_type = make_shared(8, IntegerType::Modifier::Hash); + m_type = make_shared(1); else m_type = type.getBaseType(); m_isLValue = type.getLocation() != ArrayType::Location::CallData; diff --git a/CompilerUtils.cpp b/CompilerUtils.cpp index e11b6b4f6..7b078e03e 100644 --- a/CompilerUtils.cpp +++ b/CompilerUtils.cpp @@ -177,8 +177,7 @@ void CompilerUtils::computeHashStatic(Type const& _type, bool _padToWordBoundari unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries) { - unsigned _encodedSize = _type.getCalldataEncodedSize(); - unsigned numBytes = _padToWordBoundaries ? getPaddedSize(_encodedSize) : _encodedSize; + unsigned numBytes = _type.getCalldataEncodedSize(_padToWordBoundaries); bool leftAligned = _type.getCategory() == Type::Category::FixedBytes; if (numBytes == 0) m_context << eth::Instruction::POP << u256(0); diff --git a/LValue.cpp b/LValue.cpp index a036be80b..db3cd56be 100644 --- a/LValue.cpp +++ b/LValue.cpp @@ -234,7 +234,7 @@ void StorageItem::setToZero(SourceLocation const&, bool _removeReference) const } /// Used in StorageByteArrayElement -static IntegerType byteType(8, IntegerType::Modifier::Hash); +static FixedBytesType byteType(1); StorageByteArrayElement::StorageByteArrayElement(CompilerContext& _compilerContext): LValue(_compilerContext, byteType) From 2bddebc3d5fc942dc43364a9c4ddfb897768c93d Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 9 Mar 2015 13:49:53 +0100 Subject: [PATCH 12/70] Bytes Tokens properly named and NameAndTypeResolution tests work --- Token.h | 64 +++++++++++++++++++++++++++---------------------------- Types.cpp | 7 ++++-- 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/Token.h b/Token.h index d7f56aa42..2a4d22495 100644 --- a/Token.h +++ b/Token.h @@ -252,39 +252,39 @@ namespace solidity K(UInt240, "uint240", 0) \ K(UInt248, "uint248", 0) \ K(UInt256, "uint256", 0) \ - K(Bytes8, "bytes8", 0) \ - K(Bytes16, "bytes16", 0) \ - K(Bytes24, "bytes24", 0) \ - K(Bytes32, "bytes32", 0) \ - K(Bytes40, "bytes40", 0) \ - K(Bytes48, "bytes48", 0) \ - K(Bytes56, "bytes56", 0) \ - K(Bytes64, "bytes64", 0) \ - K(Bytes72, "bytes72", 0) \ - K(Bytes80, "bytes80", 0) \ - K(Bytes88, "bytes88", 0) \ - K(Bytes96, "bytes96", 0) \ - K(Bytes104, "bytes104", 0) \ - K(Bytes112, "bytes112", 0) \ - K(Bytes120, "bytes120", 0) \ - K(Bytes128, "bytes128", 0) \ - K(Bytes136, "bytes136", 0) \ - K(Bytes144, "bytes144", 0) \ - K(Bytes152, "bytes152", 0) \ - K(Bytes160, "bytes160", 0) \ - K(Bytes168, "bytes168", 0) \ - K(Bytes176, "bytes178", 0) \ - K(Bytes184, "bytes184", 0) \ - K(Bytes192, "bytes192", 0) \ - K(Bytes200, "bytes200", 0) \ - K(Bytes208, "bytes208", 0) \ - K(Bytes216, "bytes216", 0) \ - K(Bytes224, "bytes224", 0) \ - K(Bytes232, "bytes232", 0) \ - K(Bytes240, "bytes240", 0) \ - K(Bytes248, "bytes248", 0) \ - K(Bytes256, "bytes256", 0) \ K(Bytes, "bytes", 0) \ + K(Bytes1, "bytes1", 0) \ + K(Bytes2, "bytes2", 0) \ + K(Bytes3, "bytes3", 0) \ + K(Bytes4, "bytes4", 0) \ + K(Bytes5, "bytes5", 0) \ + K(Bytes6, "bytes6", 0) \ + K(Bytes7, "bytes7", 0) \ + K(Bytes8, "bytes8", 0) \ + K(Bytes9, "bytes9", 0) \ + K(Bytes10, "bytes10", 0) \ + K(Bytes11, "bytes11", 0) \ + K(Bytes12, "bytes12", 0) \ + K(Bytes13, "bytes13", 0) \ + K(Bytes14, "bytes14", 0) \ + K(Bytes15, "bytes15", 0) \ + K(Bytes16, "bytes16", 0) \ + K(Bytes17, "bytes17", 0) \ + K(Bytes18, "bytes18", 0) \ + K(Bytes19, "bytes19", 0) \ + K(Bytes20, "bytes20", 0) \ + K(Bytes21, "bytes21", 0) \ + K(Bytes22, "bytes22", 0) \ + K(Bytes23, "bytes23", 0) \ + K(Bytes24, "bytes24", 0) \ + K(Bytes25, "bytes25", 0) \ + K(Bytes26, "bytes26", 0) \ + K(Bytes27, "bytes27", 0) \ + K(Bytes28, "bytes28", 0) \ + K(Bytes29, "bytes29", 0) \ + K(Bytes30, "bytes30", 0) \ + K(Bytes31, "bytes31", 0) \ + K(Bytes32, "bytes32", 0) \ K(Address, "address", 0) \ K(Bool, "bool", 0) \ K(StringType, "string", 0) \ diff --git a/Types.cpp b/Types.cpp index aadd884b5..9f307cbc6 100644 --- a/Types.cpp +++ b/Types.cpp @@ -39,7 +39,7 @@ TypePointer Type::fromElementaryTypeName(Token::Value _typeToken) { solAssert(Token::isElementaryTypeName(_typeToken), "Elementary type name expected."); - if (Token::Int <= _typeToken && _typeToken <= Token::Bytes256) + if (Token::Int <= _typeToken && _typeToken <= Token::Bytes32) { int offset = _typeToken - Token::Int; int bytes = offset % 33; @@ -226,8 +226,11 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe // 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(); - return TypePointer(); + return commonType; } const MemberList IntegerType::AddressMemberList = From 73ce24ae75554b18a342e1e510f37f99057fdb1d Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 9 Mar 2015 17:48:33 +0100 Subject: [PATCH 13/70] Most EndToEndTests are now compliant with the Bytes renaming --- CompilerStack.cpp | 8 ++++---- ExpressionCompiler.cpp | 2 -- GlobalContext.cpp | 18 +++++++++--------- Token.h | 3 ++- Types.cpp | 13 ++++++++++--- 5 files changed, 25 insertions(+), 19 deletions(-) diff --git a/CompilerStack.cpp b/CompilerStack.cpp index a878bb61a..55ec0cb59 100644 --- a/CompilerStack.cpp +++ b/CompilerStack.cpp @@ -41,14 +41,14 @@ namespace solidity { const map StandardSources = map{ - {"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"(import "CoinReg";import "Config";import "configUser";contract coin is configUser{function coin(bytes3 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(){}})"}, + {"CoinReg", R"(contract CoinReg{function count()constant returns(uint256 r){}function info(uint256 i)constant returns(address addr,bytes3 name,uint256 denom){}function register(bytes3 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){}})"}, + {"named", R"(import "Config";import "NameReg";import "configUser";contract named is configUser {function named(bytes32 name) {NameReg(Config(configAddr()).lookup(1)).register(name);}})"}, + {"NameReg", R"(contract NameReg{function register(bytes32 name){}function addressOf(bytes32 name)constant returns(address addr){}function unregister(){}function nameOf(address addr)constant returns(bytes32 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";)"} diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 51e1fd0a9..089ebc32d 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -131,7 +131,6 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con // conversion from string to bytes. no need to clean the high bit // only to shift right because of opposite alignment IntegerType const& targetIntegerType = dynamic_cast(_targetType); - solAssert(targetIntegerType.isAddress(), "Only conversion between Address and FixedBytes 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; } @@ -164,7 +163,6 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con // only to shift left because of opposite alignment FixedBytesType const& targetBytesType = dynamic_cast(_targetType); IntegerType const& typeOnStack = dynamic_cast(_typeOnStack); - solAssert(typeOnStack.isAddress(), "Only conversion between Address and Bytes is allowed."); solAssert(typeOnStack.getNumBits() == targetBytesType.getNumBytes() * 8, "The size should be the same."); m_context << (u256(1) << (256 - typeOnStack.getNumBits())) << eth::Instruction::MUL; } diff --git a/GlobalContext.cpp b/GlobalContext.cpp index 411e99abb..7bd9c8dfa 100644 --- a/GlobalContext.cpp +++ b/GlobalContext.cpp @@ -41,23 +41,23 @@ m_magicVariables(vector>{make_shared< make_shared("suicide", make_shared(strings{"address"}, strings{}, FunctionType::Location::Suicide)), make_shared("sha3", - make_shared(strings(), strings{"hash"}, FunctionType::Location::SHA3, true)), + make_shared(strings(), strings{"bytes32"}, FunctionType::Location::SHA3, true)), make_shared("log0", - make_shared(strings{"hash"},strings{}, FunctionType::Location::Log0)), + make_shared(strings{"bytes32"}, strings{}, FunctionType::Location::Log0)), make_shared("log1", - make_shared(strings{"hash", "hash"},strings{}, FunctionType::Location::Log1)), + make_shared(strings{"bytes32", "bytes32"}, strings{}, FunctionType::Location::Log1)), make_shared("log2", - make_shared(strings{"hash", "hash", "hash"},strings{}, FunctionType::Location::Log2)), + make_shared(strings{"bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Location::Log2)), make_shared("log3", - make_shared(strings{"hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::Log3)), + make_shared(strings{"bytes32", "bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Location::Log3)), make_shared("log4", - make_shared(strings{"hash", "hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::Log4)), + make_shared(strings{"bytes32", "bytes32", "bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Location::Log4)), make_shared("sha256", - make_shared(strings(), strings{"hash"}, FunctionType::Location::SHA256, true)), + make_shared(strings(), strings{"bytes32"}, FunctionType::Location::SHA256, true)), make_shared("ecrecover", - make_shared(strings{"hash", "hash8", "hash", "hash"}, strings{"address"}, FunctionType::Location::ECRecover)), + make_shared(strings{"bytes32", "bytes1", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Location::ECRecover)), make_shared("ripemd160", - make_shared(strings(), strings{"hash160"}, FunctionType::Location::RIPEMD160, true))}) + make_shared(strings(), strings{"bytes20"}, FunctionType::Location::RIPEMD160, true))}) { } diff --git a/Token.h b/Token.h index 2a4d22495..c439c79ee 100644 --- a/Token.h +++ b/Token.h @@ -252,7 +252,7 @@ namespace solidity K(UInt240, "uint240", 0) \ K(UInt248, "uint248", 0) \ K(UInt256, "uint256", 0) \ - K(Bytes, "bytes", 0) \ + K(Bytes0, "bytes0", 0) \ K(Bytes1, "bytes1", 0) \ K(Bytes2, "bytes2", 0) \ K(Bytes3, "bytes3", 0) \ @@ -285,6 +285,7 @@ namespace solidity K(Bytes30, "bytes30", 0) \ K(Bytes31, "bytes31", 0) \ K(Bytes32, "bytes32", 0) \ + K(Bytes, "bytes", 0) \ K(Address, "address", 0) \ K(Bool, "bool", 0) \ K(StringType, "string", 0) \ diff --git a/Types.cpp b/Types.cpp index 9f307cbc6..6039895af 100644 --- a/Types.cpp +++ b/Types.cpp @@ -37,13 +37,15 @@ namespace solidity TypePointer Type::fromElementaryTypeName(Token::Value _typeToken) { - solAssert(Token::isElementaryTypeName(_typeToken), "Elementary type name expected."); + char const* tokenCstr = Token::toString(_typeToken); + solAssert(Token::isElementaryTypeName(_typeToken), + "Expected an elementary type name but got " + ((tokenCstr) ? std::string(Token::toString(_typeToken)) : "")); if (Token::Int <= _typeToken && _typeToken <= Token::Bytes32) { int offset = _typeToken - Token::Int; int bytes = offset % 33; - if (bytes == 0) + if (bytes == 0 && _typeToken != Token::Bytes0) bytes = 32; int modifier = offset / 33; switch(modifier) @@ -173,6 +175,11 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const { + if (_convertTo.getCategory() == Category::FixedBytes) + { + FixedBytesType const& convertTo = dynamic_cast(_convertTo); + return (m_bits == convertTo.getNumBytes() * 8); + } return _convertTo.getCategory() == getCategory() || _convertTo.getCategory() == Category::Contract || _convertTo.getCategory() == Category::Enum; @@ -436,7 +443,7 @@ shared_ptr FixedBytesType::smallestTypeForLiteral(string const& FixedBytesType::FixedBytesType(int _bytes): m_bytes(_bytes) { solAssert(m_bytes >= 0 && m_bytes <= 32, - "Invalid byte number for static string type: " + dev::toString(m_bytes)); + "Invalid byte number for fixed bytes type: " + dev::toString(m_bytes)); } bool FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const From b2fadf6b933e9034d5edb49608329fb0f7d015dd Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Tue, 10 Mar 2015 18:22:19 +0100 Subject: [PATCH 14/70] Conversion changes after renaming Hash/String to Bytes. - Almost all end to end tests pass. Still needs a little bit of work --- ExpressionCompiler.cpp | 49 +++++++++++++++++++++++++----------------- GlobalContext.cpp | 2 +- LValue.cpp | 8 ++++--- Types.cpp | 20 ++++++++++------- 4 files changed, 47 insertions(+), 32 deletions(-) diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 089ebc32d..f00b2d40c 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -123,16 +123,20 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con Type::Category stackTypeCategory = _typeOnStack.getCategory(); Type::Category targetTypeCategory = _targetType.getCategory(); - if (stackTypeCategory == Type::Category::FixedBytes) + switch (stackTypeCategory) + { + case Type::Category::FixedBytes: { FixedBytesType const& typeOnStack = dynamic_cast(_typeOnStack); if (targetTypeCategory == Type::Category::Integer) { - // conversion from string to bytes. no need to clean the high bit + // conversion from bytes to integer. no need to clean the high bit // only to shift right because of opposite alignment IntegerType const& targetIntegerType = dynamic_cast(_targetType); - solAssert(targetIntegerType.getNumBits() == typeOnStack.getNumBytes() * 8, "The size should be the same."); + // 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; + if (targetIntegerType.getNumBits() < typeOnStack.getNumBytes() * 8) + appendTypeConversion(IntegerType(typeOnStack.getNumBytes() * 8), _targetType, _cleanupNeeded); } else { @@ -150,21 +154,24 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con } } } - 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) - { - if (targetTypeCategory == Type::Category::FixedBytes && stackTypeCategory == Type::Category::Integer) + break; + case Type::Category::Enum: + solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Enum, ""); + break; + case Type::Category::Integer: + case Type::Category::Contract: + case Type::Category::IntegerConstant: + if (targetTypeCategory == Type::Category::FixedBytes) { + solAssert(stackTypeCategory == Type::Category::Integer || stackTypeCategory == Type::Category::IntegerConstant, + "Invalid conversion to FixedBytesType requested."); // conversion from bytes to string. no need to clean the high bit // only to shift left because of opposite alignment FixedBytesType const& targetBytesType = dynamic_cast(_targetType); - IntegerType const& typeOnStack = dynamic_cast(_typeOnStack); - solAssert(typeOnStack.getNumBits() == targetBytesType.getNumBytes() * 8, "The size should be the same."); - m_context << (u256(1) << (256 - typeOnStack.getNumBits())) << eth::Instruction::MUL; + if (auto typeOnStack = dynamic_cast(&_typeOnStack)) + if (targetBytesType.getNumBytes() * 8 > typeOnStack->getNumBits()) + appendHighBitsCleanup(*typeOnStack); + m_context << (u256(1) << (256 - targetBytesType.getNumBytes() * 8)) << eth::Instruction::MUL; } else if (targetTypeCategory == Type::Category::Enum) // just clean @@ -174,7 +181,7 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Contract, ""); IntegerType addressType(0, IntegerType::Modifier::Address); IntegerType const& targetType = targetTypeCategory == Type::Category::Integer - ? dynamic_cast(_targetType) : addressType; + ? dynamic_cast(_targetType) : addressType; if (stackTypeCategory == Type::Category::IntegerConstant) { IntegerConstantType const& constType = dynamic_cast(_typeOnStack); @@ -186,7 +193,7 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con else { IntegerType const& typeOnStack = stackTypeCategory == Type::Category::Integer - ? dynamic_cast(_typeOnStack) : addressType; + ? dynamic_cast(_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()) @@ -195,10 +202,12 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con appendHighBitsCleanup(targetType); } } - } - else if (_typeOnStack != _targetType) + break; + default: // All other types should not be convertible to non-equal types. - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid type conversion requested.")); + solAssert(_typeOnStack == _targetType, "Invalid type conversion requested."); + break; + } } bool ExpressionCompiler::visit(Assignment const& _assignment) @@ -773,7 +782,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) // no lvalue, just retrieve the value m_context << eth::Instruction::ADD << eth::Instruction::CALLDATALOAD - << u256(0) << eth::Instruction::BYTE; + << ((u256(1) << (256 - 8)) - 1) << eth::Instruction::AND; break; case ArrayType::Location::Memory: solAssert(false, "Memory lvalues not yet implemented."); diff --git a/GlobalContext.cpp b/GlobalContext.cpp index 7bd9c8dfa..80cebd760 100644 --- a/GlobalContext.cpp +++ b/GlobalContext.cpp @@ -55,7 +55,7 @@ m_magicVariables(vector>{make_shared< make_shared("sha256", make_shared(strings(), strings{"bytes32"}, FunctionType::Location::SHA256, true)), make_shared("ecrecover", - make_shared(strings{"bytes32", "bytes1", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Location::ECRecover)), + make_shared(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Location::ECRecover)), make_shared("ripemd160", make_shared(strings(), strings{"bytes20"}, FunctionType::Location::RIPEMD160, true))}) { diff --git a/LValue.cpp b/LValue.cpp index db3cd56be..7b8374b81 100644 --- a/LValue.cpp +++ b/LValue.cpp @@ -246,10 +246,11 @@ void StorageByteArrayElement::retrieveValue(SourceLocation const&, bool _remove) // stack: ref byte_number if (_remove) m_context << eth::Instruction::SWAP1 << eth::Instruction::SLOAD - << eth::Instruction::SWAP1 << eth::Instruction::BYTE; + << eth::Instruction::SWAP1 << eth::Instruction::BYTE ; else m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD << eth::Instruction::DUP2 << eth::Instruction::BYTE; + m_context << (u256(1) << (256 - 8)) << eth::Instruction::MUL; } void StorageByteArrayElement::storeValue(Type const&, SourceLocation const&, bool _move) const @@ -265,8 +266,9 @@ void StorageByteArrayElement::storeValue(Type const&, SourceLocation const&, boo m_context << eth::Instruction::DUP2 << u256(0xff) << eth::Instruction::MUL << eth::Instruction::NOT << eth::Instruction::AND; // stack: value ref (1<<(32-byte_number)) old_full_value_with_cleared_byte - m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP4 << eth::Instruction::MUL - << eth::Instruction::OR; + m_context << eth::Instruction::SWAP1; + m_context << (u256(1) << (256 - 8)) << eth::Instruction::DUP5 << eth::Instruction::DIV + << eth::Instruction::MUL << eth::Instruction::OR; // stack: value ref new_full_value m_context << eth::Instruction::SWAP1 << eth::Instruction::SSTORE; if (_move) diff --git a/Types.cpp b/Types.cpp index 6039895af..8524cb8b0 100644 --- a/Types.cpp +++ b/Types.cpp @@ -175,14 +175,10 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - if (_convertTo.getCategory() == Category::FixedBytes) - { - FixedBytesType const& convertTo = dynamic_cast(_convertTo); - return (m_bits == convertTo.getNumBytes() * 8); - } return _convertTo.getCategory() == getCategory() || _convertTo.getCategory() == Category::Contract || - _convertTo.getCategory() == Category::Enum; + _convertTo.getCategory() == Category::Enum || + _convertTo.getCategory() == Category::FixedBytes; } TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const @@ -284,7 +280,15 @@ IntegerConstantType::IntegerConstantType(Literal const& _literal) bool IntegerConstantType::isImplicitlyConvertibleTo(Type const& _convertTo) const { - TypePointer integerType = getIntegerType(); + auto integerType = getIntegerType(); + if (_convertTo.getCategory() == Category::FixedBytes) + { + FixedBytesType const& convertTo = dynamic_cast(_convertTo); + if (convertTo.getNumBytes() * 8 >= integerType->getNumBits()) + return true; + return false; + } + return integerType && integerType->isImplicitlyConvertibleTo(_convertTo); } @@ -461,7 +465,7 @@ bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const if (_convertTo.getCategory() == Category::Integer) { IntegerType const& convertTo = dynamic_cast(_convertTo); - if (m_bytes * 8 == convertTo.getNumBits()) + if (m_bytes * 8 <= convertTo.getNumBits()) return true; } From cd3e8c175645e9a6a81b5b719868b1f74a528c65 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 11 Mar 2015 16:58:25 +0100 Subject: [PATCH 15/70] Fixing byte array index access code generation --- ExpressionCompiler.cpp | 3 +-- LValue.cpp | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index f00b2d40c..3cee40df1 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -133,7 +133,6 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con // conversion from bytes to integer. no need to clean the high bit // only to shift right because of opposite alignment IntegerType const& targetIntegerType = dynamic_cast(_targetType); - // 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; if (targetIntegerType.getNumBits() < typeOnStack.getNumBytes() * 8) appendTypeConversion(IntegerType(typeOnStack.getNumBytes() * 8), _targetType, _cleanupNeeded); @@ -782,7 +781,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) // no lvalue, just retrieve the value m_context << eth::Instruction::ADD << eth::Instruction::CALLDATALOAD - << ((u256(1) << (256 - 8)) - 1) << eth::Instruction::AND; + << ((u256(0xff) << (256 - 8))) << eth::Instruction::AND; break; case ArrayType::Location::Memory: solAssert(false, "Memory lvalues not yet implemented."); diff --git a/LValue.cpp b/LValue.cpp index 7b8374b81..dc07ec311 100644 --- a/LValue.cpp +++ b/LValue.cpp @@ -246,7 +246,7 @@ void StorageByteArrayElement::retrieveValue(SourceLocation const&, bool _remove) // stack: ref byte_number if (_remove) m_context << eth::Instruction::SWAP1 << eth::Instruction::SLOAD - << eth::Instruction::SWAP1 << eth::Instruction::BYTE ; + << eth::Instruction::SWAP1 << eth::Instruction::BYTE; else m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD << eth::Instruction::DUP2 << eth::Instruction::BYTE; From b8cede371dc5b7d5c40188dbb65eff293d7dd6ff Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 11 Mar 2015 17:41:12 +0100 Subject: [PATCH 16/70] byte is now an alias for byte1 --- Token.h | 7 ++++--- Types.cpp | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Token.h b/Token.h index c439c79ee..7b3467908 100644 --- a/Token.h +++ b/Token.h @@ -252,8 +252,8 @@ namespace solidity K(UInt240, "uint240", 0) \ K(UInt248, "uint248", 0) \ K(UInt256, "uint256", 0) \ - K(Bytes0, "bytes0", 0) \ - K(Bytes1, "bytes1", 0) \ + K(Bytes0, "bytes0", 0) \ + K(Bytes1, "bytes1", 0) \ K(Bytes2, "bytes2", 0) \ K(Bytes3, "bytes3", 0) \ K(Bytes4, "bytes4", 0) \ @@ -285,7 +285,8 @@ namespace solidity K(Bytes30, "bytes30", 0) \ K(Bytes31, "bytes31", 0) \ K(Bytes32, "bytes32", 0) \ - K(Bytes, "bytes", 0) \ + K(Bytes, "bytes", 0) \ + K(Byte, "byte", 0) \ K(Address, "address", 0) \ K(Bool, "bool", 0) \ K(StringType, "string", 0) \ diff --git a/Types.cpp b/Types.cpp index 8524cb8b0..aacf56fa3 100644 --- a/Types.cpp +++ b/Types.cpp @@ -61,6 +61,8 @@ TypePointer Type::fromElementaryTypeName(Token::Value _typeToken) return TypePointer(); } } + else if (_typeToken == Token::Byte) + return make_shared(1); else if (_typeToken == Token::Address) return make_shared(0, IntegerType::Modifier::Address); else if (_typeToken == Token::Bool) From c81b4989535cde327b95a65c53fd271dc1e52f82 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 11 Mar 2015 17:52:18 +0100 Subject: [PATCH 17/70] Style fixes in Types[cpp/h] --- Types.cpp | 5 ++--- Types.h | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Types.cpp b/Types.cpp index aacf56fa3..6f8d4b6b1 100644 --- a/Types.cpp +++ b/Types.cpp @@ -178,8 +178,8 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const { return _convertTo.getCategory() == getCategory() || - _convertTo.getCategory() == Category::Contract || - _convertTo.getCategory() == Category::Enum || + _convertTo.getCategory() == Category::Contract || + _convertTo.getCategory() == Category::Enum || _convertTo.getCategory() == Category::FixedBytes; } @@ -488,7 +488,6 @@ TypePointer FixedBytesType::unaryOperatorResult(Token::Value _operator) const TypePointer FixedBytesType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const { auto commonType = dynamic_pointer_cast(Type::commonType(shared_from_this(), _other)); - if (!commonType) return TypePointer(); diff --git a/Types.h b/Types.h index 6517cf070..fd59a37ad 100644 --- a/Types.h +++ b/Types.h @@ -158,7 +158,7 @@ protected: }; /** - * Any kind of integer type including address. + * Any kind of integer type (signed, unsigned, address). */ class IntegerType: public Type { @@ -231,7 +231,7 @@ private: }; /** - * Bytes type with fixed length of up to 32 bytes + * Bytes type with fixed length of up to 32 bytes. */ class FixedBytesType: public Type { From 3b54583d380886de57b0668bab4fff8cfa298b8c Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 12 Mar 2015 12:25:07 +0100 Subject: [PATCH 18/70] Style fixes and some additional hash to bytes32 renaming --- LValue.cpp | 2 +- Token.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/LValue.cpp b/LValue.cpp index dc07ec311..a56ed54c7 100644 --- a/LValue.cpp +++ b/LValue.cpp @@ -268,7 +268,7 @@ void StorageByteArrayElement::storeValue(Type const&, SourceLocation const&, boo // stack: value ref (1<<(32-byte_number)) old_full_value_with_cleared_byte m_context << eth::Instruction::SWAP1; m_context << (u256(1) << (256 - 8)) << eth::Instruction::DUP5 << eth::Instruction::DIV - << eth::Instruction::MUL << eth::Instruction::OR; + << eth::Instruction::MUL << eth::Instruction::OR; // stack: value ref new_full_value m_context << eth::Instruction::SWAP1 << eth::Instruction::SSTORE; if (_move) diff --git a/Token.h b/Token.h index 7b3467908..2d8a49fcd 100644 --- a/Token.h +++ b/Token.h @@ -262,9 +262,9 @@ namespace solidity K(Bytes7, "bytes7", 0) \ K(Bytes8, "bytes8", 0) \ K(Bytes9, "bytes9", 0) \ - K(Bytes10, "bytes10", 0) \ - K(Bytes11, "bytes11", 0) \ - K(Bytes12, "bytes12", 0) \ + K(Bytes10, "bytes10", 0) \ + K(Bytes11, "bytes11", 0) \ + K(Bytes12, "bytes12", 0) \ K(Bytes13, "bytes13", 0) \ K(Bytes14, "bytes14", 0) \ K(Bytes15, "bytes15", 0) \ From a62d3fa24060016f7c1faaa2ab211d692a2b6359 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 12 Mar 2015 13:39:12 +0100 Subject: [PATCH 19/70] Some fixes on Types.cpp for FixedBytesType --- Types.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Types.cpp b/Types.cpp index 6f8d4b6b1..22e9dfb81 100644 --- a/Types.cpp +++ b/Types.cpp @@ -231,7 +231,7 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe // 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 + // Nothing else can be done with addresses if (commonType->isAddress()) return TypePointer(); @@ -282,16 +282,17 @@ IntegerConstantType::IntegerConstantType(Literal const& _literal) bool IntegerConstantType::isImplicitlyConvertibleTo(Type const& _convertTo) const { - auto integerType = getIntegerType(); + shared_ptr integerType = getIntegerType(); + if (!integerType) + return false; + if (_convertTo.getCategory() == Category::FixedBytes) { FixedBytesType const& convertTo = dynamic_cast(_convertTo); - if (convertTo.getNumBytes() * 8 >= integerType->getNumBits()) - return true; - return false; + return convertTo.getNumBytes() * 8 >= integerType->getNumBits(); } - - return integerType && integerType->isImplicitlyConvertibleTo(_convertTo); + + return integerType->isImplicitlyConvertibleTo(_convertTo); } bool IntegerConstantType::isExplicitlyConvertibleTo(Type const& _convertTo) const @@ -462,8 +463,6 @@ bool FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - if (_convertTo.getCategory() == getCategory()) - return true; if (_convertTo.getCategory() == Category::Integer) { IntegerType const& convertTo = dynamic_cast(_convertTo); @@ -471,7 +470,8 @@ bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const return true; } - return false; + return _convertTo.getCategory() == Category::Contract || + _convertTo.getCategory() == getCategory(); } TypePointer FixedBytesType::unaryOperatorResult(Token::Value _operator) const From 039b133c180b15863ee3104637c97822d815d932 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 12 Mar 2015 17:31:39 +0100 Subject: [PATCH 20/70] Small FixedBytes type fixes - Integer Constant is explicitly convertible to FixedBytes, so using that in the tests --- Types.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Types.cpp b/Types.cpp index 22e9dfb81..bd55e2a8b 100644 --- a/Types.cpp +++ b/Types.cpp @@ -191,13 +191,10 @@ TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const // 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(); // for non-address integers, we allow +, -, ++ and -- else if (_operator == Token::Add || _operator == Token::Sub || _operator == Token::Inc || _operator == Token::Dec || - _operator == Token::After) + _operator == Token::After || _operator == Token::BitNot) return shared_from_this(); else return TypePointer(); @@ -1154,7 +1151,7 @@ MagicType::MagicType(MagicType::Kind _kind): case Kind::Block: m_members = MemberList({{"coinbase", make_shared(0, IntegerType::Modifier::Address)}, {"timestamp", make_shared(256)}, - {"blockhash", make_shared(strings{"uint"}, strings{"bytes"}, FunctionType::Location::BlockHash)}, + {"blockhash", make_shared(strings{"uint"}, strings{"bytes32"}, FunctionType::Location::BlockHash)}, {"difficulty", make_shared(256)}, {"number", make_shared(256)}, {"gaslimit", make_shared(256)}}); From a16677dcfbd7fd7d42fbd6166e234b1b7001ec59 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 13 Mar 2015 13:14:51 +0100 Subject: [PATCH 21/70] Fix gas for builtin. Fixes #1300 --- ArrayUtils.cpp | 1 + Compiler.cpp | 2 ++ CompilerUtils.cpp | 4 ++-- ExpressionCompiler.cpp | 6 ++++++ LValue.cpp | 1 + 5 files changed, 12 insertions(+), 2 deletions(-) diff --git a/ArrayUtils.cpp b/ArrayUtils.cpp index f0d7d6a81..a064b2f57 100644 --- a/ArrayUtils.cpp +++ b/ArrayUtils.cpp @@ -125,6 +125,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons CompilerUtils(m_context).loadFromMemoryDynamic(*sourceBaseType, true, true, false); else solAssert(false, "Copying of unknown type requested: " + sourceBaseType->toString()); + solAssert(2 + sourceBaseType->getSizeOnStack() <= 16, "Stack too deep."); m_context << eth::dupInstruction(2 + sourceBaseType->getSizeOnStack()); StorageItem(m_context, *targetBaseType).storeValue(*sourceBaseType, SourceLocation(), true); } diff --git a/Compiler.cpp b/Compiler.cpp index dc6e2c5a8..b8ca03d3e 100644 --- a/Compiler.cpp +++ b/Compiler.cpp @@ -228,6 +228,7 @@ void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool { // Retrieve data start offset by adding length to start offset of previous dynamic type unsigned stackDepth = m_context.getStackHeight() - stackHeightOfPreviousDynamicArgument; + solAssert(stackDepth <= 16, "Stack too deep."); m_context << eth::dupInstruction(stackDepth) << eth::dupInstruction(stackDepth); ArrayUtils(m_context).convertLengthToSize(*previousDynamicType, true); m_context << eth::Instruction::ADD; @@ -359,6 +360,7 @@ bool Compiler::visit(FunctionDefinition const& _function) stackLayout.push_back(i); stackLayout += vector(c_localVariablesSize, -1); + solAssert(stackLayout.size() <= 17, "Stack too deep."); while (stackLayout.back() != int(stackLayout.size() - 1)) if (stackLayout.back() < 0) { diff --git a/CompilerUtils.cpp b/CompilerUtils.cpp index 7b078e03e..e517e384d 100644 --- a/CompilerUtils.cpp +++ b/CompilerUtils.cpp @@ -138,6 +138,7 @@ void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable) { unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(_variable)); unsigned const size = _variable.getType()->getSizeOnStack(); + solAssert(stackPosition >= size, "Variable size and position mismatch."); // move variable starting from its top end in the stack if (stackPosition - size + 1 > 16) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_variable.getLocation()) @@ -148,8 +149,7 @@ void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable) void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize) { - if (_stackDepth > 16) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Stack too deep.")); + solAssert(_stackDepth <= 16, "Stack too deep."); for (unsigned i = 0; i < _itemSize; ++i) m_context << eth::dupInstruction(_stackDepth); } diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 3cee40df1..331979c7d 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -233,9 +233,12 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) m_currentLValue->retrieveValue(_assignment.getLocation(), true); appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.getType()); if (lvalueSize > 0) + { + solAssert(itemSize + lvalueSize <= 16, "Stack too deep."); // value [lvalue_ref] updated_value for (unsigned i = 0; i < itemSize; ++i) m_context << eth::swapInstruction(itemSize + lvalueSize) << eth::Instruction::POP; + } } m_currentLValue->storeValue(*_assignment.getRightHandSide().getType(), _assignment.getLocation()); m_currentLValue.reset(); @@ -557,10 +560,13 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) case Location::SHA256: case Location::RIPEMD160: { + _functionCall.getExpression().accept(*this); static const map contractAddresses{{Location::ECRecover, 1}, {Location::SHA256, 2}, {Location::RIPEMD160, 3}}; m_context << contractAddresses.find(function.getLocation())->second; + for (unsigned i = function.getSizeOnStack(); i > 0; --i) + m_context << eth::swapInstruction(i); appendExternalFunctionCall(function, arguments, true); break; } diff --git a/LValue.cpp b/LValue.cpp index a56ed54c7..68d6797eb 100644 --- a/LValue.cpp +++ b/LValue.cpp @@ -167,6 +167,7 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc // stack: source_ref target_ref member_offset source_member_ref StorageItem(m_context, *memberType).retrieveValue(_location, true); // stack: source_ref target_ref member_offset source_value... + solAssert(2 + memberType->getSizeOnStack() <= 16, "Stack too deep."); 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 From 67cd3a7180023f613efda64f4f4d2b1bc37990c6 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Tue, 3 Mar 2015 12:58:01 +0100 Subject: [PATCH 22/70] added parsing for constant variables --- AST.cpp | 6 +++--- AST.h | 27 +++++++++++++++++++-------- Compiler.cpp | 5 +++-- ExpressionCompiler.cpp | 7 +++++-- Parser.cpp | 11 ++++++++++- Parser.h | 1 + Types.h | 2 +- 7 files changed, 42 insertions(+), 17 deletions(-) diff --git a/AST.cpp b/AST.cpp index 46c44995b..b05602639 100644 --- a/AST.cpp +++ b/AST.cpp @@ -197,7 +197,7 @@ vector, FunctionTypePointer>> const& ContractDefinition::getIn } for (ASTPointer const& v: contract->getStateVariables()) - if (v->isPublic() && functionsSeen.count(v->getName()) == 0) + if (v->isPublic() && functionsSeen.count(v->getName()) == 0 && !v->isConstant()) { FunctionType ftype(*v); functionsSeen.insert(v->getName()); @@ -322,8 +322,8 @@ string FunctionDefinition::getCanonicalSignature() const bool VariableDeclaration::isLValue() const { - // External function parameters are Read-Only - return !isExternalFunctionParameter(); + // External function parameters and constant declared variables are Read-Only + return !isExternalFunctionParameter() && !m_isConstant; } void VariableDeclaration::checkTypeRequirements() diff --git a/AST.h b/AST.h index eab53153f..e959ba248 100644 --- a/AST.h +++ b/AST.h @@ -440,13 +440,22 @@ private: class VariableDeclaration: public Declaration { public: - VariableDeclaration(SourceLocation const& _location, ASTPointer const& _type, - ASTPointer const& _name, ASTPointer _value, - Visibility _visibility, - bool _isStateVar = false, bool _isIndexed = false): - Declaration(_location, _name, _visibility), - m_typeName(_type), m_value(_value), - m_isStateVariable(_isStateVar), m_isIndexed(_isIndexed) {} + VariableDeclaration( + SourceLocation const& _location, + ASTPointer const& _type, + ASTPointer const& _name, + ASTPointer _value, + Visibility _visibility, + bool _isStateVar = false, + bool _isIndexed = false, + bool _isConstant = false): + Declaration(_location, _name, _visibility), + m_typeName(_type), + m_value(_value), + m_isStateVariable(_isStateVar), + m_isIndexed(_isIndexed), + m_isConstant(_isConstant){} + virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; @@ -465,15 +474,17 @@ public: bool isExternalFunctionParameter() const; bool isStateVariable() const { return m_isStateVariable; } bool isIndexed() const { return m_isIndexed; } + bool isConstant() const { return m_isConstant; } protected: Visibility getDefaultVisibility() const override { return Visibility::Internal; } private: ASTPointer m_typeName; ///< can be empty ("var") - ASTPointer m_value; ///< the assigned value, can be missing + ASTPointer m_value; ///< the assigned value, can be missing bool m_isStateVariable; ///< Whether or not this is a contract state variable bool m_isIndexed; ///< Whether this is an indexed variable (used by events). + bool m_isConstant; ///< Whether the variable is a compile-time constant. std::shared_ptr m_type; ///< derived type, initially empty }; diff --git a/Compiler.cpp b/Compiler.cpp index b8ca03d3e..0f4e89de1 100644 --- a/Compiler.cpp +++ b/Compiler.cpp @@ -276,13 +276,14 @@ void Compiler::registerStateVariables(ContractDefinition const& _contract) { for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.getLinearizedBaseContracts())) for (ASTPointer const& variable: contract->getStateVariables()) - m_context.addStateVariable(*variable); + if (!variable->isConstant()) + m_context.addStateVariable(*variable); } void Compiler::initializeStateVariables(ContractDefinition const& _contract) { for (ASTPointer const& variable: _contract.getStateVariables()) - if (variable->getValue()) + if (variable->getValue() && !variable->isConstant()) ExpressionCompiler(m_context, m_optimize).appendStateVariableInitialization(*variable); } diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 331979c7d..ce5f184b4 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -855,8 +855,11 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) } else if (FunctionDefinition const* functionDef = dynamic_cast(declaration)) m_context << m_context.getVirtualFunctionEntryLabel(*functionDef).pushTag(); - else if (dynamic_cast(declaration)) - setLValueFromDeclaration(*declaration, _identifier); + else if (auto variable = dynamic_cast(declaration)) + { + if (!variable->isConstant()) + setLValueFromDeclaration(*declaration, _identifier); + } else if (dynamic_cast(declaration)) { // no-op diff --git a/Parser.cpp b/Parser.cpp index 44d111591..05c05f632 100644 --- a/Parser.cpp +++ b/Parser.cpp @@ -135,6 +135,7 @@ ASTPointer Parser::parseContractDefinition() } while (m_scanner->getCurrentToken() == Token::Comma); expectToken(Token::LBrace); + bool isDeclaredConst = false; while (true) { Token::Value currentToken = m_scanner->getCurrentToken(); @@ -152,13 +153,21 @@ ASTPointer Parser::parseContractDefinition() VarDeclParserOptions options; options.isStateVariable = true; options.allowInitialValue = true; + options.isDeclaredConst = isDeclaredConst; stateVariables.push_back(parseVariableDeclaration(options)); + isDeclaredConst = false; expectToken(Token::Semicolon); } else if (currentToken == Token::Modifier) modifiers.push_back(parseModifierDefinition()); else if (currentToken == Token::Event) events.push_back(parseEventDefinition()); + else if (currentToken == Token::Const) + { + solAssert(Token::isElementaryTypeName(m_scanner->peekNextToken()), ""); + isDeclaredConst = true; + m_scanner->next(); + } else BOOST_THROW_EXCEPTION(createParserError("Function, variable, struct or modifier declaration expected.")); } @@ -348,7 +357,7 @@ ASTPointer Parser::parseVariableDeclaration( } return nodeFactory.createNode(type, identifier, value, visibility, _options.isStateVariable, - isIndexed); + isIndexed, _options.isDeclaredConst); } ASTPointer Parser::parseModifierDefinition() diff --git a/Parser.h b/Parser.h index cc0b2ca17..469e446fd 100644 --- a/Parser.h +++ b/Parser.h @@ -54,6 +54,7 @@ private: bool allowIndexed = false; bool allowEmptyName = false; bool allowInitialValue = false; + bool isDeclaredConst = false; }; ///@{ diff --git a/Types.h b/Types.h index fd59a37ad..853bd6889 100644 --- a/Types.h +++ b/Types.h @@ -516,7 +516,7 @@ private: std::vector m_parameterNames; std::vector m_returnParameterNames; Location const m_location; - /// true iff the function takes an arbitrary number of arguments of arbitrary types + /// true if 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 From 7d6357ae531f604387fc1f91799fca9a9102e856 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Thu, 12 Mar 2015 14:15:04 +0100 Subject: [PATCH 23/70] - added isPartOfExternalInterface to Declaration - changed position for the constant specifier. now it goes after type: = - removed tests for constant functions, checkings for constant function doesn't belong to this story --- AST.cpp | 4 ++-- AST.h | 4 ++++ ExpressionCompiler.cpp | 2 ++ Parser.cpp | 18 ++++++++---------- Parser.h | 4 +--- 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/AST.cpp b/AST.cpp index b05602639..605f53521 100644 --- a/AST.cpp +++ b/AST.cpp @@ -189,7 +189,7 @@ vector, FunctionTypePointer>> const& ContractDefinition::getIn for (ContractDefinition const* contract: getLinearizedBaseContracts()) { for (ASTPointer const& f: contract->getDefinedFunctions()) - if (f->isPublic() && !f->isConstructor() && !f->getName().empty() && functionsSeen.count(f->getName()) == 0) + if (functionsSeen.count(f->getName()) == 0 && f->isPartOfExternalInterface()) { functionsSeen.insert(f->getName()); FixedHash<4> hash(dev::sha3(f->getCanonicalSignature())); @@ -197,7 +197,7 @@ vector, FunctionTypePointer>> const& ContractDefinition::getIn } for (ASTPointer const& v: contract->getStateVariables()) - if (v->isPublic() && functionsSeen.count(v->getName()) == 0 && !v->isConstant()) + if (functionsSeen.count(v->getName()) == 0 && v->isPartOfExternalInterface()) { FunctionType ftype(*v); functionsSeen.insert(v->getName()); diff --git a/AST.h b/AST.h index e959ba248..6a269e154 100644 --- a/AST.h +++ b/AST.h @@ -156,6 +156,7 @@ public: /// contract types. virtual TypePointer getType(ContractDefinition const* m_currentContract = nullptr) const = 0; virtual bool isLValue() const { return false; } + virtual bool isPartOfExternalInterface() const { return false; }; protected: virtual Visibility getDefaultVisibility() const { return Visibility::Public; } @@ -415,6 +416,7 @@ public: getVisibility() >= Visibility::Internal; } virtual TypePointer getType(ContractDefinition const*) const override; + virtual bool isPartOfExternalInterface() const override { return isPublic() && !m_isConstructor && !getName().empty(); } /// Checks that all parameters have allowed types and calls checkTypeRequirements on the body. void checkTypeRequirements(); @@ -468,6 +470,8 @@ public: void setType(std::shared_ptr const& _type) { m_type = _type; } virtual bool isLValue() const override; + virtual bool isPartOfExternalInterface() const override { return isPublic() && !m_isConstant; } + void checkTypeRequirements(); bool isLocalVariable() const { return !!dynamic_cast(getScope()); } diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index ce5f184b4..9391bc2a7 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -859,6 +859,8 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) { if (!variable->isConstant()) setLValueFromDeclaration(*declaration, _identifier); + else + variable->getValue()->accept(*this); } else if (dynamic_cast(declaration)) { diff --git a/Parser.cpp b/Parser.cpp index 05c05f632..9efa30061 100644 --- a/Parser.cpp +++ b/Parser.cpp @@ -135,7 +135,6 @@ ASTPointer Parser::parseContractDefinition() } while (m_scanner->getCurrentToken() == Token::Comma); expectToken(Token::LBrace); - bool isDeclaredConst = false; while (true) { Token::Value currentToken = m_scanner->getCurrentToken(); @@ -153,21 +152,13 @@ ASTPointer Parser::parseContractDefinition() VarDeclParserOptions options; options.isStateVariable = true; options.allowInitialValue = true; - options.isDeclaredConst = isDeclaredConst; stateVariables.push_back(parseVariableDeclaration(options)); - isDeclaredConst = false; expectToken(Token::Semicolon); } else if (currentToken == Token::Modifier) modifiers.push_back(parseModifierDefinition()); else if (currentToken == Token::Event) events.push_back(parseEventDefinition()); - else if (currentToken == Token::Const) - { - solAssert(Token::isElementaryTypeName(m_scanner->peekNextToken()), ""); - isDeclaredConst = true; - m_scanner->next(); - } else BOOST_THROW_EXCEPTION(createParserError("Function, variable, struct or modifier declaration expected.")); } @@ -326,6 +317,7 @@ ASTPointer Parser::parseVariableDeclaration( nodeFactory.setEndPositionFromNode(type); } bool isIndexed = false; + bool isDeclaredConst = false; ASTPointer identifier; Token::Value token = m_scanner->getCurrentToken(); Declaration::Visibility visibility(Declaration::Visibility::Default); @@ -336,7 +328,13 @@ ASTPointer Parser::parseVariableDeclaration( isIndexed = true; m_scanner->next(); } + if (token == Token::Const) + { + m_scanner->next(); + isDeclaredConst = true; + } nodeFactory.markEndPosition(); + if (_options.allowEmptyName && m_scanner->getCurrentToken() != Token::Identifier) { identifier = make_shared(""); @@ -357,7 +355,7 @@ ASTPointer Parser::parseVariableDeclaration( } return nodeFactory.createNode(type, identifier, value, visibility, _options.isStateVariable, - isIndexed, _options.isDeclaredConst); + isIndexed, isDeclaredConst); } ASTPointer Parser::parseModifierDefinition() diff --git a/Parser.h b/Parser.h index 469e446fd..08c47c252 100644 --- a/Parser.h +++ b/Parser.h @@ -54,7 +54,6 @@ private: bool allowIndexed = false; bool allowEmptyName = false; bool allowInitialValue = false; - bool isDeclaredConst = false; }; ///@{ @@ -67,8 +66,7 @@ private: ASTPointer parseStructDefinition(); ASTPointer parseEnumDefinition(); ASTPointer parseEnumValue(); - ASTPointer parseVariableDeclaration( - VarDeclParserOptions const& _options = VarDeclParserOptions(), + ASTPointer parseVariableDeclaration(VarDeclParserOptions const& _options = VarDeclParserOptions(), ASTPointer const& _lookAheadArrayType = ASTPointer()); ASTPointer parseModifierDefinition(); ASTPointer parseEventDefinition(); From ebb4d5e298cbe9d8d6bb6b1fa1d8f6f769d62fba Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Fri, 13 Mar 2015 18:16:04 +0100 Subject: [PATCH 24/70] - added more tests to check constant specifier implementation - deny use of const for local variables - deny unitialized const variables - only int, fixed strings, and enums can be declaired as const --- AST.cpp | 7 +++++++ AST.h | 1 - Parser.cpp | 6 +++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/AST.cpp b/AST.cpp index 605f53521..d05eaf83e 100644 --- a/AST.cpp +++ b/AST.cpp @@ -332,6 +332,13 @@ void VariableDeclaration::checkTypeRequirements() // sets the type. // Note that assignments before the first declaration are legal because of the special scoping // rules inherited from JavaScript. + if (m_isConstant) + { + if (!dynamic_cast(getScope())) + BOOST_THROW_EXCEPTION(createTypeError("Illegal use of \"constant\" specifier.")); + if ((m_type && !m_type->isValueType()) || !m_value) + BOOST_THROW_EXCEPTION(createTypeError("Unitialized \"constant\" variable.")); + } if (!m_value) return; if (m_type) diff --git a/AST.h b/AST.h index 6a269e154..bedba396d 100644 --- a/AST.h +++ b/AST.h @@ -472,7 +472,6 @@ public: virtual bool isLValue() const override; virtual bool isPartOfExternalInterface() const override { return isPublic() && !m_isConstant; } - void checkTypeRequirements(); bool isLocalVariable() const { return !!dynamic_cast(getScope()); } bool isExternalFunctionParameter() const; diff --git a/Parser.cpp b/Parser.cpp index 9efa30061..9e299215c 100644 --- a/Parser.cpp +++ b/Parser.cpp @@ -330,8 +330,11 @@ ASTPointer Parser::parseVariableDeclaration( } if (token == Token::Const) { - m_scanner->next(); + solAssert(_options.isStateVariable, ""); + if (m_scanner->peekNextToken() != Token::Identifier && !Token::isElementaryTypeName(m_scanner->peekNextToken())) + BOOST_THROW_EXCEPTION(createParserError("Invalid use of \"constant\" specifier")); isDeclaredConst = true; + m_scanner->next(); } nodeFactory.markEndPosition(); @@ -920,6 +923,7 @@ Parser::LookAheadInfo Parser::peekStatementType() const // In all other cases, we have an expression statement. Token::Value token(m_scanner->getCurrentToken()); bool mightBeTypeName = (Token::isElementaryTypeName(token) || token == Token::Identifier); + if (token == Token::Mapping || token == Token::Var || (mightBeTypeName && m_scanner->peekNextToken() == Token::Identifier)) return LookAheadInfo::VariableDeclarationStatement; From d7ffba150458f947c03741f1b528d45eb6c8771a Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 13 Mar 2015 17:36:00 +0100 Subject: [PATCH 25/70] Adding msg.sig Solidity Magic type msg.sig will return a bytes4 with the function signature located in CALLDATALOAD --- ExpressionCompiler.cpp | 2 ++ Types.cpp | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 3cee40df1..07f4e94ea 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -646,6 +646,8 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) m_context << eth::Instruction::GASPRICE; else if (member == "data") m_context << u256(0) << eth::Instruction::CALLDATASIZE; + else if (member == "sig") + m_context << u256(0) << eth::Instruction::CALLDATALOAD << (u256(0xffffffff) << (256 - 32))<< eth::Instruction::AND; else BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown magic member.")); break; diff --git a/Types.cpp b/Types.cpp index bd55e2a8b..70cbec5d9 100644 --- a/Types.cpp +++ b/Types.cpp @@ -1160,7 +1160,8 @@ MagicType::MagicType(MagicType::Kind _kind): m_members = MemberList({{"sender", make_shared(0, IntegerType::Modifier::Address)}, {"gas", make_shared(256)}, {"value", make_shared(256)}, - {"data", make_shared(ArrayType::Location::CallData)}}); + {"data", make_shared(ArrayType::Location::CallData)}, + {"sig", make_shared(4)}}); break; case Kind::Transaction: m_members = MemberList({{"origin", make_shared(0, IntegerType::Modifier::Address)}, From 27a89a36e10f260521d20d4e54e6d2f710953776 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Mon, 16 Mar 2015 14:45:11 +0100 Subject: [PATCH 26/70] restyleing removed unnecessary check --- AST.h | 17 +++++++++-------- Parser.cpp | 3 --- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/AST.h b/AST.h index bedba396d..f5f6a3f38 100644 --- a/AST.h +++ b/AST.h @@ -443,14 +443,15 @@ class VariableDeclaration: public Declaration { public: VariableDeclaration( - SourceLocation const& _location, - ASTPointer const& _type, - ASTPointer const& _name, - ASTPointer _value, - Visibility _visibility, - bool _isStateVar = false, - bool _isIndexed = false, - bool _isConstant = false): + SourceLocation const& _location, + ASTPointer const& _type, + ASTPointer const& _name, + ASTPointer _value, + Visibility _visibility, + bool _isStateVar = false, + bool _isIndexed = false, + bool _isConstant = false + ): Declaration(_location, _name, _visibility), m_typeName(_type), m_value(_value), diff --git a/Parser.cpp b/Parser.cpp index 9e299215c..459a34bd0 100644 --- a/Parser.cpp +++ b/Parser.cpp @@ -330,9 +330,6 @@ ASTPointer Parser::parseVariableDeclaration( } if (token == Token::Const) { - solAssert(_options.isStateVariable, ""); - if (m_scanner->peekNextToken() != Token::Identifier && !Token::isElementaryTypeName(m_scanner->peekNextToken())) - BOOST_THROW_EXCEPTION(createParserError("Invalid use of \"constant\" specifier")); isDeclaredConst = true; m_scanner->next(); } From bb5364dd66a85b287b0dc5b71bd810c7af671b0e Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 16 Mar 2015 15:46:04 +0100 Subject: [PATCH 27/70] Additional test for msg.sig --- ExpressionCompiler.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 07f4e94ea..a9cacc65c 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -647,7 +647,8 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) else if (member == "data") m_context << u256(0) << eth::Instruction::CALLDATASIZE; else if (member == "sig") - m_context << u256(0) << eth::Instruction::CALLDATALOAD << (u256(0xffffffff) << (256 - 32))<< eth::Instruction::AND; + m_context << u256(0) << eth::Instruction::CALLDATALOAD + << (u256(0xffffffff) << (256 - 32)) << eth::Instruction::AND; else BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown magic member.")); break; From fff3f98f58345d45dcf518d7dccbfa0d0a4e67b7 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 11 Mar 2015 18:09:35 +0100 Subject: [PATCH 28/70] Enlarge storage references to two stack slots. --- ArrayUtils.cpp | 68 ++++++++++++++++------ ArrayUtils.h | 12 ++-- CompilerUtils.cpp | 1 + ExpressionCompiler.cpp | 72 ++++++++++++++--------- ExpressionCompiler.h | 1 - LValue.cpp | 128 +++++++++++++++++------------------------ LValue.h | 10 ++-- Types.cpp | 3 + Types.h | 7 ++- 9 files changed, 168 insertions(+), 134 deletions(-) diff --git a/ArrayUtils.cpp b/ArrayUtils.cpp index a064b2f57..2596e4afa 100644 --- a/ArrayUtils.cpp +++ b/ArrayUtils.cpp @@ -34,8 +34,10 @@ using namespace solidity; void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType const& _sourceType) const { - // stack layout: [source_ref] target_ref (top) - // need to leave target_ref on the stack at the end + // this copies source to target and also clears target if it was larger + // need to leave "target_ref target_byte_off" on the stack at the end + + // stack layout: [source_ref] [source_byte_off] [source length] target_ref target_byte_off (top) solAssert(_targetType.getLocation() == ArrayType::Location::Storage, ""); solAssert( _sourceType.getLocation() == ArrayType::Location::CallData || @@ -47,14 +49,20 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons Type const* targetBaseType = _targetType.isByteArray() ? &uint256 : &(*_targetType.getBaseType()); Type const* sourceBaseType = _sourceType.isByteArray() ? &uint256 : &(*_sourceType.getBaseType()); - // this copies source to target and also clears target if it was larger - // TODO unroll loop for small sizes - // stack: source_ref [source_length] target_ref + bool sourceIsStorage = _sourceType.getLocation() == ArrayType::Location::Storage; + + // stack: source_ref [source_byte_off] [source_length] target_ref target_byte_off // store target_ref + m_context << eth::Instruction::POP; //@todo for (unsigned i = _sourceType.getSizeOnStack(); i > 0; --i) m_context << eth::swapInstruction(i); + // stack: target_ref source_ref [source_byte_off] [source_length] + if (sourceIsStorage) + m_context << eth::Instruction::POP; //@todo + // stack: target_ref source_ref [source_length] + // retrieve source length if (_sourceType.getLocation() != ArrayType::Location::CallData || !_sourceType.isDynamicallySized()) retrieveLength(_sourceType); // otherwise, length is already there // stack: target_ref source_ref source_length @@ -73,6 +81,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons m_context << eth::Instruction::POP << eth::Instruction::POP << eth::Instruction::POP << eth::Instruction::POP; + m_context << u256(0); //@todo return; } // compute hashes (data positions) @@ -109,32 +118,37 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons // copy if (sourceBaseType->getCategory() == Type::Category::Array) { - m_context << eth::Instruction::DUP3 << eth::Instruction::DUP3; + //@todo + m_context << eth::Instruction::DUP3; + if (sourceIsStorage) + m_context << u256(0); + m_context << eth::dupInstruction(sourceIsStorage ? 4 : 3) << u256(0); copyArrayToStorage( dynamic_cast(*targetBaseType), dynamic_cast(*sourceBaseType) ); - m_context << eth::Instruction::POP; + m_context << eth::Instruction::POP << eth::Instruction::POP; } else { m_context << eth::Instruction::DUP3; if (_sourceType.getLocation() == ArrayType::Location::Storage) + { + m_context << u256(0); StorageItem(m_context, *sourceBaseType).retrieveValue(SourceLocation(), true); + } else if (sourceBaseType->isValueType()) CompilerUtils(m_context).loadFromMemoryDynamic(*sourceBaseType, true, true, false); else solAssert(false, "Copying of unknown type requested: " + sourceBaseType->toString()); solAssert(2 + sourceBaseType->getSizeOnStack() <= 16, "Stack too deep."); - m_context << eth::dupInstruction(2 + sourceBaseType->getSizeOnStack()); + m_context << eth::dupInstruction(2 + sourceBaseType->getSizeOnStack()) << u256(0); StorageItem(m_context, *targetBaseType).storeValue(*sourceBaseType, SourceLocation(), true); } // increment source m_context << eth::Instruction::SWAP2 - << (_sourceType.getLocation() == ArrayType::Location::Storage ? - sourceBaseType->getStorageSize() : - sourceBaseType->getCalldataEncodedSize()) + << (sourceIsStorage ? sourceBaseType->getStorageSize() : sourceBaseType->getCalldataEncodedSize()) << eth::Instruction::ADD << eth::Instruction::SWAP2; // increment target @@ -147,40 +161,48 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons m_context << copyLoopEnd; // zero-out leftovers in target - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end + // stack: target_ref target_data_end source_data_pos target_data_pos_updated source_data_end m_context << eth::Instruction::POP << eth::Instruction::SWAP1 << eth::Instruction::POP; // stack: target_ref target_data_end target_data_pos_updated clearStorageLoop(*targetBaseType); m_context << eth::Instruction::POP; + m_context << u256(0); //@todo } void ArrayUtils::clearArray(ArrayType const& _type) const { + unsigned stackHeightStart = m_context.getStackHeight(); solAssert(_type.getLocation() == ArrayType::Location::Storage, ""); if (_type.isDynamicallySized()) + { + m_context << eth::Instruction::POP; // remove byte offset clearDynamicArray(_type); + } else if (_type.getLength() == 0 || _type.getBaseType()->getCategory() == Type::Category::Mapping) - m_context << eth::Instruction::POP; + m_context << eth::Instruction::POP << eth::Instruction::POP; else if (_type.getLength() < 5) // unroll loop for small arrays @todo choose a good value { solAssert(!_type.isByteArray(), ""); for (unsigned i = 1; i < _type.getLength(); ++i) { StorageItem(m_context, *_type.getBaseType()).setToZero(SourceLocation(), false); + m_context << eth::Instruction::SWAP1; m_context << u256(_type.getBaseType()->getStorageSize()) << eth::Instruction::ADD; + m_context << eth::Instruction::SWAP1; } StorageItem(m_context, *_type.getBaseType()).setToZero(SourceLocation(), true); } else { solAssert(!_type.isByteArray(), ""); - m_context - << eth::Instruction::DUP1 << u256(_type.getLength()) - << u256(_type.getBaseType()->getStorageSize()) - << eth::Instruction::MUL << eth::Instruction::ADD << eth::Instruction::SWAP1; + m_context << eth::Instruction::SWAP1; + m_context << eth::Instruction::DUP1 << _type.getLength(); + convertLengthToSize(_type); + m_context << eth::Instruction::ADD << eth::Instruction::SWAP1; clearStorageLoop(*_type.getBaseType()); - m_context << eth::Instruction::POP; + m_context << eth::Instruction::POP << eth::Instruction::POP; } + solAssert(m_context.getStackHeight() == stackHeightStart - 2, ""); } void ArrayUtils::clearDynamicArray(ArrayType const& _type) const @@ -188,6 +210,7 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const solAssert(_type.getLocation() == ArrayType::Location::Storage, ""); solAssert(_type.isDynamicallySized(), ""); + unsigned stackHeightStart = m_context.getStackHeight(); // fetch length m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD; // set length to zero @@ -197,7 +220,7 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const // compute data positions m_context << eth::Instruction::SWAP1; CompilerUtils(m_context).computeHashStatic(); - // stack: len data_pos (len is in slots for byte array and in items for other arrays) + // stack: len data_pos m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2 << eth::Instruction::ADD << eth::Instruction::SWAP1; // stack: data_pos_end data_pos @@ -207,6 +230,7 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const clearStorageLoop(*_type.getBaseType()); // cleanup m_context << eth::Instruction::POP; + solAssert(m_context.getStackHeight() == stackHeightStart - 1, ""); } void ArrayUtils::resizeDynamicArray(const ArrayType& _type) const @@ -214,6 +238,7 @@ void ArrayUtils::resizeDynamicArray(const ArrayType& _type) const solAssert(_type.getLocation() == ArrayType::Location::Storage, ""); solAssert(_type.isDynamicallySized(), ""); + unsigned stackHeightStart = m_context.getStackHeight(); eth::AssemblyItem resizeEnd = m_context.newTag(); // stack: ref new_length @@ -249,10 +274,12 @@ void ArrayUtils::resizeDynamicArray(const ArrayType& _type) const m_context << resizeEnd; // cleanup m_context << eth::Instruction::POP << eth::Instruction::POP << eth::Instruction::POP; + solAssert(m_context.getStackHeight() == stackHeightStart - 2, ""); } void ArrayUtils::clearStorageLoop(Type const& _type) const { + unsigned stackHeightStart = m_context.getStackHeight(); if (_type.getCategory() == Type::Category::Mapping) { m_context << eth::Instruction::POP; @@ -267,13 +294,16 @@ void ArrayUtils::clearStorageLoop(Type const& _type) const eth::AssemblyItem zeroLoopEnd = m_context.newTag(); m_context.appendConditionalJumpTo(zeroLoopEnd); // delete + m_context << u256(0); //@todo StorageItem(m_context, _type).setToZero(SourceLocation(), false); + m_context << eth::Instruction::POP; // increment m_context << u256(1) << eth::Instruction::ADD; m_context.appendJumpTo(loopStart); // cleanup m_context << zeroLoopEnd; m_context << eth::Instruction::POP; + solAssert(m_context.getStackHeight() == stackHeightStart - 1, ""); } void ArrayUtils::convertLengthToSize(ArrayType const& _arrayType, bool _pad) const diff --git a/ArrayUtils.h b/ArrayUtils.h index 31cca8173..c7b05b6d3 100644 --- a/ArrayUtils.h +++ b/ArrayUtils.h @@ -41,19 +41,19 @@ public: /// Copies an array to an array in storage. The arrays can be of different types only if /// their storage representation is the same. - /// Stack pre: [source_reference] target_reference - /// Stack post: target_reference + /// Stack pre: source_reference [source_byte_offset/source_length] target_reference target_byte_offset + /// Stack post: target_reference target_byte_offset void copyArrayToStorage(ArrayType const& _targetType, ArrayType const& _sourceType) const; /// Clears the given dynamic or static array. - /// Stack pre: reference + /// Stack pre: storage_ref storage_byte_offset /// Stack post: void clearArray(ArrayType const& _type) const; /// Clears the length and data elements of the array referenced on the stack. - /// Stack pre: reference + /// Stack pre: reference (excludes byte offset) /// Stack post: void clearDynamicArray(ArrayType const& _type) const; /// Changes the size of a dynamic array and clears the tail if it is shortened. - /// Stack pre: reference new_length + /// Stack pre: reference (excludes byte offset) new_length /// Stack post: void resizeDynamicArray(ArrayType const& _type) const; /// Appends a loop that clears a sequence of storage slots of the given type (excluding end). @@ -67,7 +67,7 @@ public: void convertLengthToSize(ArrayType const& _arrayType, bool _pad = false) const; /// Retrieves the length (number of elements) of the array ref on the stack. This also /// works for statically-sized arrays. - /// Stack pre: reference + /// Stack pre: reference (excludes byte offset for dynamic storage arrays) /// Stack post: reference length void retrieveLength(ArrayType const& _arrayType) const; diff --git a/CompilerUtils.cpp b/CompilerUtils.cpp index e517e384d..5fc8bd8f2 100644 --- a/CompilerUtils.cpp +++ b/CompilerUtils.cpp @@ -93,6 +93,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound else { solAssert(type.getLocation() == ArrayType::Location::Storage, "Memory arrays not yet implemented."); + m_context << eth::Instruction::POP; //@todo m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD; // stack here: memory_offset storage_offset length_bytes // jump to end if length is zero diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 92fd70437..6f5331294 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -81,6 +81,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& returnType = dynamic_cast(*returnType).getValueType(); } + m_context << u256(0); // @todo unsigned retSizeOnStack = 0; solAssert(accessorType.getReturnParameterTypes().size() >= 1, ""); if (StructType const* structType = dynamic_cast(returnType.get())) @@ -90,15 +91,18 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& // struct for (size_t i = 0; i < names.size(); ++i) { - m_context << eth::Instruction::DUP1 - << structType->getStorageOffsetOfMember(names[i]) - << eth::Instruction::ADD; + if (types[i]->getCategory() == Type::Category::Mapping) + continue; + m_context + << eth::Instruction::DUP2 << structType->getStorageOffsetOfMember(names[i]) + << eth::Instruction::ADD; + m_context << u256(0); //@todo StorageItem(m_context, *types[i]).retrieveValue(SourceLocation(), true); solAssert(types[i]->getSizeOnStack() == 1, "Returning struct elements with stack size != 1 not yet implemented."); - m_context << eth::Instruction::SWAP1; + m_context << eth::Instruction::SWAP2 << eth::Instruction::SWAP1; retSizeOnStack += types[i]->getSizeOnStack(); } - m_context << eth::Instruction::POP; + m_context << eth::Instruction::POP << eth::Instruction::POP; } else { @@ -280,23 +284,24 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) case Token::Dec: // -- (pre- or postfix) solAssert(!!m_currentLValue, "LValue not retrieved."); m_currentLValue->retrieveValue(_unaryOperation.getLocation()); - solAssert(m_currentLValue->sizeOnStack() <= 1, "Not implemented."); if (!_unaryOperation.isPrefixOperation()) { - if (m_currentLValue->sizeOnStack() == 1) - m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2; - else - m_context << eth::Instruction::DUP1; + // store value for later + solAssert(_unaryOperation.getType()->getSizeOnStack() == 1, "Stack size != 1 not implemented."); + m_context << eth::Instruction::DUP1; + if (m_currentLValue->sizeOnStack() > 0) + for (unsigned i = 1 + m_currentLValue->sizeOnStack(); i > 0; --i) + m_context << eth::swapInstruction(i); } m_context << u256(1); if (_unaryOperation.getOperator() == Token::Inc) m_context << eth::Instruction::ADD; else - m_context << eth::Instruction::SWAP1 << eth::Instruction::SUB; // @todo avoid the swap - // Stack for prefix: [ref] (*ref)+-1 - // Stack for postfix: *ref [ref] (*ref)+-1 - if (m_currentLValue->sizeOnStack() == 1) - m_context << eth::Instruction::SWAP1; + m_context << eth::Instruction::SWAP1 << eth::Instruction::SUB; + // Stack for prefix: [ref...] (*ref)+-1 + // Stack for postfix: *ref [ref...] (*ref)+-1 + for (unsigned i = m_currentLValue->sizeOnStack(); i > 0; --i) + m_context << eth::swapInstruction(i); m_currentLValue->storeValue( *_unaryOperation.getType(), _unaryOperation.getLocation(), !_unaryOperation.isPrefixOperation()); @@ -661,7 +666,10 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) case Type::Category::Struct: { StructType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); + m_context << eth::Instruction::POP; //@todo m_context << type.getStorageOffsetOfMember(member) << eth::Instruction::ADD; + //@todo + m_context << u256(0); setLValueToStorageItem(_memberAccess); break; } @@ -729,20 +737,22 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) Type const& baseType = *_indexAccess.getBaseExpression().getType(); if (baseType.getCategory() == Type::Category::Mapping) { + // storage byte offset is ignored for mappings, it should be zero. + m_context << eth::Instruction::POP; + // stack: storage_base_ref Type const& keyType = *dynamic_cast(baseType).getKeyType(); - m_context << u256(0); + m_context << u256(0); // memory position solAssert(_indexAccess.getIndexExpression(), "Index expression expected."); 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_context << u256(0); setLValueToStorageItem( _indexAccess); } else if (baseType.getCategory() == Type::Category::Array) { - // stack layout: [] + // stack layout: [storage_byte_offset] [] ArrayType const& arrayType = dynamic_cast(baseType); solAssert(_indexAccess.getIndexExpression(), "Index expression expected."); ArrayType::Location location = arrayType.getLocation(); @@ -758,9 +768,11 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) else if (location == ArrayType::Location::CallData) // length is stored on the stack m_context << eth::Instruction::SWAP1; + else if (location == ArrayType::Location::Storage) + m_context << eth::Instruction::DUP3 << load; else m_context << eth::Instruction::DUP2 << load; - // stack: + // stack: [storage_byte_offset] // check out-of-bounds access m_context << eth::Instruction::DUP2 << eth::Instruction::LT; eth::AssemblyItem legalAccess = m_context.appendConditionalJump(); @@ -768,7 +780,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) m_context << eth::Instruction::STOP; m_context << legalAccess; - // stack: + // stack: [storage_byte_offset] if (arrayType.isByteArray()) // byte array is packed differently, especially in storage switch (location) @@ -776,14 +788,15 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) case ArrayType::Location::Storage: // byte array index storage lvalue on stack (goal): // = - m_context << u256(32) << eth::Instruction::SWAP2; + m_context << u256(32) << eth::Instruction::SWAP3; CompilerUtils(m_context).computeHashStatic(); - // stack: 32 index data_ref + // stack: 32 storage_byte_offset index data_ref m_context - << eth::Instruction::DUP3 << eth::Instruction::DUP3 + << eth::Instruction::DUP4 << eth::Instruction::DUP3 << eth::Instruction::DIV << eth::Instruction::ADD - // stack: 32 index (data_ref + index / 32) - << eth::Instruction::SWAP2 << eth::Instruction::SWAP1 << eth::Instruction::MOD; + // stack: 32 storage_byte_offset index (data_ref + index / 32) + << eth::Instruction::SWAP3 << eth::Instruction::SWAP2 + << eth::Instruction::POP << eth::Instruction::MOD; setLValue(_indexAccess); break; case ArrayType::Location::CallData: @@ -797,6 +810,10 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) } else { + // stack: [storage_byte_offset] + if (location == ArrayType::Location::Storage) + //@todo use byte offset, remove it for now + m_context << eth::Instruction::SWAP1 << eth::Instruction::POP; u256 elementSize = location == ArrayType::Location::Storage ? arrayType.getBaseType()->getStorageSize() : @@ -822,6 +839,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) CompilerUtils(m_context).loadFromMemoryDynamic(*arrayType.getBaseType(), true, true, false); break; case ArrayType::Location::Storage: + m_context << u256(0); // @todo setLValueToStorageItem(_indexAccess); break; case ArrayType::Location::Memory: @@ -1141,7 +1159,7 @@ void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaratio if (m_context.isLocalVariable(&_declaration)) setLValue(_expression, _declaration); else if (m_context.isStateVariable(&_declaration)) - setLValue(_expression, _declaration); + setLValue(_expression, _declaration); else BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation()) diff --git a/ExpressionCompiler.h b/ExpressionCompiler.h index 9cab757ea..e6caad747 100644 --- a/ExpressionCompiler.h +++ b/ExpressionCompiler.h @@ -140,7 +140,6 @@ void ExpressionCompiler::setLValue(Expression const& _expression, _Arguments con m_currentLValue = move(lvalue); else lvalue->retrieveValue(_expression.getLocation(), true); - } } diff --git a/LValue.cpp b/LValue.cpp index 68d6797eb..8e618e87f 100644 --- a/LValue.cpp +++ b/LValue.cpp @@ -77,7 +77,7 @@ void StackVariable::setToZero(SourceLocation const& _location, bool) const StorageItem::StorageItem(CompilerContext& _compilerContext, Declaration const& _declaration): StorageItem(_compilerContext, *_declaration.getType()) { - m_context << m_context.getStorageLocationOfVariable(_declaration); + m_context << m_context.getStorageLocationOfVariable(_declaration) << u256(0); } StorageItem::StorageItem(CompilerContext& _compilerContext, Type const& _type): @@ -86,62 +86,42 @@ StorageItem::StorageItem(CompilerContext& _compilerContext, Type const& _type): if (m_dataType.isValueType()) { solAssert(m_dataType.getStorageSize() == m_dataType.getSizeOnStack(), ""); - solAssert(m_dataType.getStorageSize() <= numeric_limits::max(), - "The storage size of " + m_dataType.toString() + " should fit in an unsigned"); - m_size = unsigned(m_dataType.getStorageSize()); + //@todo the meaning of getStorageSize() probably changes + solAssert(m_dataType.getStorageSize() == 1, "Invalid storage size."); } - else - m_size = 0; // unused } void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const { + // stack: storage_key storage_offset 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; - } + CompilerUtils(m_context).copyToStackTop(sizeOnStack(), sizeOnStack()); + m_context + << eth::Instruction::SWAP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1 + << u256(2) << eth::Instruction::EXP << eth::Instruction::SWAP1 << eth::Instruction::DIV; + //@todo higher order bits might be dirty. Is this bad? + //@todo this does not work for types that are left-aligned on the stack. + // make those types right-aligned? } void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _location, bool _move) const { - // stack layout: value value ... value target_ref + // stack: value storage_key storage_offset if (m_dataType.isValueType()) { - 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 > 1) // 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; - } + //@todo OR the value into the storage like it is done for ByteArrayElement + m_context + << u256(2) << eth::Instruction::EXP << eth::Instruction::DUP3 << eth::Instruction::MUL + << eth::Instruction::SWAP1 << eth::Instruction::SSTORE; + if (_move) + m_context << eth::Instruction::POP; } else { - solAssert(_sourceType.getCategory() == m_dataType.getCategory(), + solAssert( + _sourceType.getCategory() == m_dataType.getCategory(), "Wrong type conversation for assignment."); if (m_dataType.getCategory() == Type::Category::Array) { @@ -149,40 +129,48 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc dynamic_cast(m_dataType), dynamic_cast(_sourceType)); if (_move) - m_context << eth::Instruction::POP; + CompilerUtils(m_context).popStackElement(_sourceType); } else if (m_dataType.getCategory() == Type::Category::Struct) { - // stack layout: source_ref target_ref + // stack layout: source_ref source_offset target_ref target_offset auto const& structType = dynamic_cast(m_dataType); solAssert(structType == _sourceType, "Struct assignment with conversion."); for (auto const& member: structType.getMembers()) { + //@todo actually use offsets // 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 + << eth::Instruction::DUP5 << eth::Instruction::DUP2 << eth::Instruction::ADD; + m_context << u256(0); // zero offset + // stack: source_ref source_off target_ref target_off member_offset source_member_ref source_member_off StorageItem(m_context, *memberType).retrieveValue(_location, true); - // stack: source_ref target_ref member_offset source_value... + // stack: source_ref source_off target_ref target_off member_offset source_value... solAssert(2 + memberType->getSizeOnStack() <= 16, "Stack too deep."); - m_context << eth::dupInstruction(2 + memberType->getSizeOnStack()) + m_context << eth::dupInstruction(3 + memberType->getSizeOnStack()) << eth::dupInstruction(2 + memberType->getSizeOnStack()) << eth::Instruction::ADD; - // stack: source_ref target_ref member_offset source_value... target_member_ref + // stack: source_ref source_off target_ref target_off member_offset source_value... target_member_ref + m_context << u256(0); // zero offset StorageItem(m_context, *memberType).storeValue(*memberType, _location, true); m_context << eth::Instruction::POP; } if (_move) - m_context << eth::Instruction::POP; + m_context + << eth::Instruction::POP << eth::Instruction::POP + << eth::Instruction::POP << eth::Instruction::POP; else - m_context << eth::Instruction::SWAP1; - m_context << eth::Instruction::POP; + m_context + << eth::Instruction::SWAP2 << eth::Instruction::POP + << eth::Instruction::SWAP2 << eth::Instruction::POP; } else - BOOST_THROW_EXCEPTION(InternalCompilerError() - << errinfo_sourceLocation(_location) << errinfo_comment("Invalid non-value type for assignment.")); + BOOST_THROW_EXCEPTION( + InternalCompilerError() + << errinfo_sourceLocation(_location) + << errinfo_comment("Invalid non-value type for assignment.")); } } @@ -191,12 +179,12 @@ void StorageItem::setToZero(SourceLocation const&, bool _removeReference) const if (m_dataType.getCategory() == Type::Category::Array) { if (!_removeReference) - m_context << eth::Instruction::DUP1; + CompilerUtils(m_context).copyToStackTop(sizeOnStack(), sizeOnStack()); ArrayUtils(m_context).clearArray(dynamic_cast(m_dataType)); } else if (m_dataType.getCategory() == Type::Category::Struct) { - // stack layout: ref + // stack layout: storage_key storage_offset auto const& structType = dynamic_cast(m_dataType); for (auto const& member: structType.getMembers()) { @@ -204,33 +192,23 @@ void StorageItem::setToZero(SourceLocation const&, bool _removeReference) const TypePointer const& memberType = member.second; if (memberType->getCategory() == Type::Category::Mapping) continue; - m_context << structType.getStorageOffsetOfMember(member.first) - << eth::Instruction::DUP2 << eth::Instruction::ADD; + // @todo actually use offset + m_context + << structType.getStorageOffsetOfMember(member.first) + << eth::Instruction::DUP3 << eth::Instruction::ADD; + m_context << u256(0); StorageItem(m_context, *memberType).setToZero(); } if (_removeReference) - m_context << eth::Instruction::POP; + m_context << eth::Instruction::POP << eth::Instruction::POP; } else { solAssert(m_dataType.isValueType(), "Clearing of unsupported type requested: " + m_dataType.toString()); - if (m_size == 0 && _removeReference) - m_context << eth::Instruction::POP; - else if (m_size == 1) - m_context - << u256(0) << (_removeReference ? eth::Instruction::SWAP1 : eth::Instruction::DUP2) - << eth::Instruction::SSTORE; - else - { - if (!_removeReference) - m_context << eth::Instruction::DUP1; - 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; - } + // @todo actually use offset + if (!_removeReference) + CompilerUtils(m_context).copyToStackTop(sizeOnStack(), sizeOnStack()); + m_context << eth::Instruction::POP << u256(0) << eth::Instruction::SWAP1 << eth::Instruction::SSTORE; } } @@ -300,6 +278,8 @@ StorageArrayLength::StorageArrayLength(CompilerContext& _compilerContext, const m_arrayType(_arrayType) { solAssert(m_arrayType.isDynamicallySized(), ""); + // storage byte offset must be zero + m_context << eth::Instruction::POP; } void StorageArrayLength::retrieveValue(SourceLocation const&, bool _remove) const diff --git a/LValue.h b/LValue.h index c57c80e37..ad6225162 100644 --- a/LValue.h +++ b/LValue.h @@ -98,7 +98,9 @@ private: }; /** - * Reference to some item in storage. The (starting) position of the item is stored on the stack. + * Reference to some item in storage. On the stack this is , + * where 0 <= offset_inside_value < 32 and an offset of i means that the value is multiplied + * by 2**i before storing it. */ class StorageItem: public LValue { @@ -107,6 +109,7 @@ public: StorageItem(CompilerContext& _compilerContext, Declaration const& _declaration); /// Constructs the LValue and assumes that the storage reference is already on the stack. StorageItem(CompilerContext& _compilerContext, Type const& _type); + virtual unsigned sizeOnStack() const { return 2; } virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const override; virtual void storeValue( Type const& _sourceType, @@ -117,11 +120,6 @@ public: SourceLocation const& _location = SourceLocation(), bool _removeReference = true ) const override; - -private: - /// Number of stack elements occupied by the value (not the reference). - /// Only used for value types. - unsigned m_size; }; /** diff --git a/Types.cpp b/Types.cpp index 70cbec5d9..2764eb7b1 100644 --- a/Types.cpp +++ b/Types.cpp @@ -644,6 +644,9 @@ unsigned ArrayType::getSizeOnStack() const if (m_location == Location::CallData) // offset [length] (stack top) return 1 + (isDynamicallySized() ? 1 : 0); + else if (m_location == Location::Storage) + // storage_key storage_offset + return 2; else // offset return 1; diff --git a/Types.h b/Types.h index 853bd6889..4b0df694d 100644 --- a/Types.h +++ b/Types.h @@ -284,6 +284,9 @@ public: /** * The type of an array. The flavours are byte array (bytes), statically- ([]) * and dynamically-sized array ([]). + * In storage, all arrays are packed tightly (as long as more than one elementary type fits in + * one slot). Dynamically sized arrays (including byte arrays) start with their size as a uint and + * thus start on their own slot. */ class ArrayType: public Type { @@ -384,7 +387,7 @@ public: virtual bool operator==(Type const& _other) const override; virtual u256 getStorageSize() const override; virtual bool canLiveOutsideStorage() const override; - virtual unsigned getSizeOnStack() const override { return 1; /*@todo*/ } + virtual unsigned getSizeOnStack() const override { return 2; } virtual std::string toString() const override; virtual MemberList const& getMembers() const override; @@ -527,6 +530,7 @@ private: /** * The type of a mapping, there is one distinct type per key/value type pair. + * Mappings always occupy their own storage slot, but do not actually use it. */ class MappingType: public Type { @@ -537,6 +541,7 @@ public: virtual bool operator==(Type const& _other) const override; virtual std::string toString() const override; + virtual unsigned getSizeOnStack() const override { return 2; } virtual bool canLiveOutsideStorage() const override { return false; } TypePointer const& getKeyType() const { return m_keyType; } From 7f64584b7fb151459500ade84612d3cd48f13f18 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 13 Mar 2015 10:52:34 +0100 Subject: [PATCH 29/70] Compute packing offsets. --- Types.cpp | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++---- Types.h | 30 +++++++++++++++--- 2 files changed, 114 insertions(+), 11 deletions(-) diff --git a/Types.cpp b/Types.cpp index 2764eb7b1..500ae9b05 100644 --- a/Types.cpp +++ b/Types.cpp @@ -35,6 +35,56 @@ namespace dev namespace solidity { +std::pair const* MemberList::getMemberStorageOffset(string const& _name) const +{ + if (!m_storageOffsets) + { + bigint slotOffset = 0; + unsigned byteOffset = 0; + map> offsets; + for (auto const& nameAndType: m_memberTypes) + { + TypePointer const& type = nameAndType.second; + if (!type->canBeStored()) + continue; + if (byteOffset + type->getStorageBytes() > 32) + { + // would overflow, go to next slot + ++slotOffset; + byteOffset = 0; + } + if (slotOffset >= bigint(1) << 256) + BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Object too large for storage.")); + offsets[nameAndType.first] = make_pair(u256(slotOffset), byteOffset); + solAssert(type->getStorageSize() >= 1, "Invalid storage size."); + if (type->getStorageSize() == 1 && byteOffset + type->getStorageBytes() <= 32) + byteOffset += type->getStorageBytes(); + else + { + slotOffset += type->getStorageSize(); + byteOffset = 0; + } + } + if (byteOffset > 0) + ++slotOffset; + if (slotOffset >= bigint(1) << 256) + BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Object too large for storage.")); + m_storageSize = u256(slotOffset); + m_storageOffsets.reset(new decltype(offsets)(move(offsets))); + } + if (m_storageOffsets->count(_name)) + return &((*m_storageOffsets)[_name]); + else + return nullptr; +} + +u256 const& MemberList::getStorageSize() const +{ + // trigger lazy computation + getMemberStorageOffset(""); + return m_storageSize; +} + TypePointer Type::fromElementaryTypeName(Token::Value _typeToken) { char const* tokenCstr = Token::toString(_typeToken); @@ -751,12 +801,7 @@ bool StructType::operator==(Type const& _other) const u256 StructType::getStorageSize() const { - bigint size = 0; - for (pair const& member: getMembers()) - size += member.second->getStorageSize(); - if (size >= bigint(1) << 256) - BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Struct too large for storage.")); - return max(1, u256(size)); + return max(1, getMembers().getStorageSize()); } bool StructType::canLiveOutsideStorage() const @@ -787,6 +832,7 @@ MemberList const& StructType::getMembers() const u256 StructType::getStorageOffsetOfMember(string const& _name) const { + //@todo cache member offset? u256 offset; for (ASTPointer const& variable: m_struct.getMembers()) @@ -811,6 +857,15 @@ bool EnumType::operator==(Type const& _other) const return other.m_enum == m_enum; } +unsigned EnumType::getStorageBytes() const +{ + size_t elements = m_enum.getMembers().size(); + if (elements <= 1) + return 1; + else + return dev::bytesRequired(elements - 1); +} + string EnumType::toString() const { return string("enum ") + m_enum.getName(); @@ -955,6 +1010,13 @@ string FunctionType::toString() const return name + ")"; } +u256 FunctionType::getStorageSize() const +{ + BOOST_THROW_EXCEPTION( + InternalCompilerError() + << errinfo_comment("Storage size of non-storable function type requested.")); +} + unsigned FunctionType::getSizeOnStack() const { Location location = m_location; @@ -1077,6 +1139,13 @@ string MappingType::toString() const return "mapping(" + getKeyType()->toString() + " => " + getValueType()->toString() + ")"; } +u256 VoidType::getStorageSize() const +{ + BOOST_THROW_EXCEPTION( + InternalCompilerError() + << errinfo_comment("Storage size of non-storable void type requested.")); +} + bool TypeType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) @@ -1085,6 +1154,13 @@ bool TypeType::operator==(Type const& _other) const return *getActualType() == *other.getActualType(); } +u256 TypeType::getStorageSize() const +{ + BOOST_THROW_EXCEPTION( + InternalCompilerError() + << errinfo_comment("Storage size of non-storable type type requested.")); +} + MemberList const& TypeType::getMembers() const { // We need to lazy-initialize it because of recursive references. @@ -1122,6 +1198,13 @@ ModifierType::ModifierType(const ModifierDefinition& _modifier) swap(params, m_parameterTypes); } +u256 ModifierType::getStorageSize() const +{ + BOOST_THROW_EXCEPTION( + InternalCompilerError() + << errinfo_comment("Storage size of non-storable type type requested.")); +} + bool ModifierType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) diff --git a/Types.h b/Types.h index 4b0df694d..5e4970780 100644 --- a/Types.h +++ b/Types.h @@ -60,12 +60,19 @@ public: return it.second; return TypePointer(); } + /// @returns the offset of the given member in storage slots and bytes inside a slot or + /// a nullptr if the member is not part of storage. + std::pair const* getMemberStorageOffset(std::string const& _name) const; + /// @returns the number of storage slots occupied by the members. + u256 const& getStorageSize() const; MemberMap::const_iterator begin() const { return m_memberTypes.begin(); } MemberMap::const_iterator end() const { return m_memberTypes.end(); } private: MemberMap m_memberTypes; + mutable u256 m_storageSize = 0; + mutable std::unique_ptr>> m_storageOffsets; }; /** @@ -97,6 +104,8 @@ public: /// @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); + /// Calculates the + virtual Category getCategory() const = 0; virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; } virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const @@ -126,9 +135,15 @@ public: unsigned getCalldataEncodedSize() const { return getCalldataEncodedSize(true); } /// @returns true if the type is dynamically encoded in calldata virtual bool isDynamicallySized() const { return false; } - /// @returns number of bytes required to hold this value in storage. + /// @returns the number of storage slots 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; } + /// Multiple small types can be packed into a single storage slot. If such a packing is possible + /// this function @returns the size in bytes smaller than 32. Data is moved to the next slot if + /// it does not fit. + /// In order to avoid computation at runtime of whether such moving is necessary, structs and + /// array data (not each element) always start a new slot. + virtual unsigned getStorageBytes() const { return 32; } /// Returns true if the type can be stored in storage. virtual bool canBeStored() const { return true; } /// Returns false if the type cannot live outside the storage, i.e. if it includes some mapping. @@ -179,6 +194,7 @@ public: virtual bool operator==(Type const& _other) const override; virtual unsigned getCalldataEncodedSize(bool _padded = true) const override { return _padded ? 32 : m_bits / 8; } + virtual unsigned getStorageBytes() const override { return m_bits / 8; } virtual bool isValueType() const override { return true; } virtual MemberList const& getMembers() const { return isAddress() ? AddressMemberList : EmptyMemberList; } @@ -251,6 +267,7 @@ public: virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; virtual unsigned getCalldataEncodedSize(bool _padded) const override { return _padded && m_bytes > 0 ? 32 : m_bytes; } + virtual unsigned getStorageBytes() const override { return m_bytes; } virtual bool isValueType() const override { return true; } virtual std::string toString() const override { return "bytes" + dev::toString(m_bytes); } @@ -275,6 +292,7 @@ public: virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; virtual unsigned getCalldataEncodedSize(bool _padded) const { return _padded ? 32 : 1; } + virtual unsigned getStorageBytes() const override { return 1; } virtual bool isValueType() const override { return true; } virtual std::string toString() const override { return "bool"; } @@ -348,6 +366,7 @@ public: 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 unsigned getStorageBytes() const override { return 20; } virtual bool isValueType() const override { return true; } virtual std::string toString() const override; @@ -411,6 +430,7 @@ public: virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual bool operator==(Type const& _other) const override; virtual unsigned getSizeOnStack() const override { return 1; } + virtual unsigned getStorageBytes() const override; virtual std::string toString() const override; virtual bool isValueType() const override { return true; } @@ -480,7 +500,7 @@ public: virtual bool operator==(Type const& _other) const override; virtual std::string toString() const override; virtual bool canBeStored() const override { return false; } - virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable function type requested.")); } + virtual u256 getStorageSize() const override; virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned getSizeOnStack() const override; virtual MemberList const& getMembers() const override; @@ -565,7 +585,7 @@ public: virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } virtual std::string toString() const override { return "void"; } virtual bool canBeStored() const override { return false; } - virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable void type requested.")); } + virtual u256 getStorageSize() const override; virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned getSizeOnStack() const override { return 0; } }; @@ -585,7 +605,7 @@ public: 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 u256 getStorageSize() const override; 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() + ")"; } @@ -611,7 +631,7 @@ public: 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 u256 getStorageSize() const override; virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned getSizeOnStack() const override { return 0; } virtual bool operator==(Type const& _other) const override; From 02595abf6ac1c30c7c0125c5a705cd2c85974838 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 13 Mar 2015 19:48:24 +0100 Subject: [PATCH 30/70] Fetch and store packed values. --- Compiler.cpp | 11 +++- CompilerContext.cpp | 16 +++--- CompilerContext.h | 9 ++-- CompilerUtils.cpp | 1 - ExpressionCompiler.cpp | 24 ++++----- LValue.cpp | 119 +++++++++++++++++++++++++++++++---------- Types.cpp | 105 ++++++++++++++++++++---------------- Types.h | 25 +++++++-- 8 files changed, 202 insertions(+), 108 deletions(-) diff --git a/Compiler.cpp b/Compiler.cpp index 0f4e89de1..db61cc4a1 100644 --- a/Compiler.cpp +++ b/Compiler.cpp @@ -274,10 +274,19 @@ void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters) void Compiler::registerStateVariables(ContractDefinition const& _contract) { + vector variables; for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.getLinearizedBaseContracts())) for (ASTPointer const& variable: contract->getStateVariables()) if (!variable->isConstant()) - m_context.addStateVariable(*variable); + variables.push_back(variable.get()); + TypePointers types; + for (auto variable: variables) + types.push_back(variable->getType()); + StorageOffsets offsets; + offsets.computeOffsets(types); + for (size_t index = 0; index < variables.size(); ++index) + if (auto const* offset = offsets.getOffset(index)) + m_context.addStateVariable(*variables[index], offset->first, offset->second); } void Compiler::initializeStateVariables(ContractDefinition const& _contract) diff --git a/CompilerContext.cpp b/CompilerContext.cpp index f2bb1de20..7cade367c 100644 --- a/CompilerContext.cpp +++ b/CompilerContext.cpp @@ -37,15 +37,13 @@ void CompilerContext::addMagicGlobal(MagicVariableDeclaration const& _declaratio m_magicGlobals.insert(&_declaration); } -void CompilerContext::addStateVariable(VariableDeclaration const& _declaration) +void CompilerContext::addStateVariable( + VariableDeclaration const& _declaration, + u256 const& _storageOffset, + unsigned _byteOffset +) { - m_stateVariables[&_declaration] = m_stateVariablesSize; - bigint newSize = bigint(m_stateVariablesSize) + _declaration.getType()->getStorageSize(); - if (newSize >= bigint(1) << 256) - BOOST_THROW_EXCEPTION(TypeError() - << errinfo_comment("State variable does not fit in storage.") - << errinfo_sourceLocation(_declaration.getLocation())); - m_stateVariablesSize = u256(newSize); + m_stateVariables[&_declaration] = make_pair(_storageOffset, _byteOffset); } void CompilerContext::startFunction(Declaration const& _function) @@ -170,7 +168,7 @@ unsigned CompilerContext::currentToBaseStackOffset(unsigned _offset) const return m_asm.deposit() - _offset - 1; } -u256 CompilerContext::getStorageLocationOfVariable(const Declaration& _declaration) const +pair CompilerContext::getStorageLocationOfVariable(const Declaration& _declaration) const { auto it = m_stateVariables.find(&_declaration); solAssert(it != m_stateVariables.end(), "Variable not found in storage."); diff --git a/CompilerContext.h b/CompilerContext.h index 76923a77a..87f90d4c4 100644 --- a/CompilerContext.h +++ b/CompilerContext.h @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -42,7 +43,7 @@ class CompilerContext { public: void addMagicGlobal(MagicVariableDeclaration const& _declaration); - void addStateVariable(VariableDeclaration const& _declaration); + void addStateVariable(VariableDeclaration const& _declaration, u256 const& _storageOffset, unsigned _byteOffset); void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0); void removeVariable(VariableDeclaration const& _declaration); void addAndInitializeVariable(VariableDeclaration const& _declaration); @@ -82,7 +83,7 @@ public: /// 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; + std::pair getStorageLocationOfVariable(Declaration const& _declaration) const; /// Appends a JUMPI instruction to a new tag and @returns the tag eth::AssemblyItem appendConditionalJump() { return m_asm.appendJumpI().tag(); } @@ -144,10 +145,8 @@ private: std::set m_magicGlobals; /// Other already compiled contracts to be used in contract creation calls. std::map m_compiledContracts; - /// Size of the state variables, offset of next variable to be added. - u256 m_stateVariablesSize = 0; /// Storage offsets of state variables - std::map m_stateVariables; + std::map> m_stateVariables; /// Offsets of local variables on the stack (relative to stack base). std::map m_localVariables; /// Labels pointing to the entry points of functions. diff --git a/CompilerUtils.cpp b/CompilerUtils.cpp index 5fc8bd8f2..511254fa5 100644 --- a/CompilerUtils.cpp +++ b/CompilerUtils.cpp @@ -199,7 +199,6 @@ unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCallda return numBytes; } - unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const { unsigned numBytes = _type.getCalldataEncodedSize(_padToWordBoundaries); diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 6f5331294..a0a688bbd 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -67,9 +67,10 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& length += CompilerUtils(m_context).storeInMemory(length, *paramType, true); // retrieve the position of the variable - m_context << m_context.getStorageLocationOfVariable(_varDecl); - TypePointer returnType = _varDecl.getType(); + auto const& location = m_context.getStorageLocationOfVariable(_varDecl); + m_context << location.first; + TypePointer returnType = _varDecl.getType(); for (TypePointer const& paramType: paramTypes) { // move offset to memory @@ -81,7 +82,6 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& returnType = dynamic_cast(*returnType).getValueType(); } - m_context << u256(0); // @todo unsigned retSizeOnStack = 0; solAssert(accessorType.getReturnParameterTypes().size() >= 1, ""); if (StructType const* structType = dynamic_cast(returnType.get())) @@ -93,21 +93,20 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& { if (types[i]->getCategory() == Type::Category::Mapping) continue; - m_context - << eth::Instruction::DUP2 << structType->getStorageOffsetOfMember(names[i]) - << eth::Instruction::ADD; - m_context << u256(0); //@todo + pair const& offsets = structType->getStorageOffsetsOfMember(names[i]); + m_context << eth::Instruction::DUP1 << u256(offsets.first) << eth::Instruction::ADD << u256(offsets.second); StorageItem(m_context, *types[i]).retrieveValue(SourceLocation(), true); solAssert(types[i]->getSizeOnStack() == 1, "Returning struct elements with stack size != 1 not yet implemented."); - m_context << eth::Instruction::SWAP2 << eth::Instruction::SWAP1; + m_context << eth::Instruction::SWAP1; retSizeOnStack += types[i]->getSizeOnStack(); } - m_context << eth::Instruction::POP << eth::Instruction::POP; + m_context << eth::Instruction::POP; } else { // simple value solAssert(accessorType.getReturnParameterTypes().size() == 1, ""); + m_context << u256(location.second); StorageItem(m_context, *returnType).retrieveValue(SourceLocation(), true); retSizeOnStack = returnType->getSizeOnStack(); } @@ -666,10 +665,9 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) case Type::Category::Struct: { StructType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); - m_context << eth::Instruction::POP; //@todo - m_context << type.getStorageOffsetOfMember(member) << eth::Instruction::ADD; - //@todo - m_context << u256(0); + m_context << eth::Instruction::POP; // structs always align to new slot + pair const& offsets = type.getStorageOffsetsOfMember(member); + m_context << offsets.first << eth::Instruction::ADD << u256(offsets.second); setLValueToStorageItem(_memberAccess); break; } diff --git a/LValue.cpp b/LValue.cpp index 8e618e87f..234072bce 100644 --- a/LValue.cpp +++ b/LValue.cpp @@ -77,7 +77,8 @@ void StackVariable::setToZero(SourceLocation const& _location, bool) const StorageItem::StorageItem(CompilerContext& _compilerContext, Declaration const& _declaration): StorageItem(_compilerContext, *_declaration.getType()) { - m_context << m_context.getStorageLocationOfVariable(_declaration) << u256(0); + auto const& location = m_context.getStorageLocationOfVariable(_declaration); + m_context << location.first << u256(location.second); } StorageItem::StorageItem(CompilerContext& _compilerContext, Type const& _type): @@ -86,7 +87,6 @@ StorageItem::StorageItem(CompilerContext& _compilerContext, Type const& _type): if (m_dataType.isValueType()) { solAssert(m_dataType.getStorageSize() == m_dataType.getSizeOnStack(), ""); - //@todo the meaning of getStorageSize() probably changes solAssert(m_dataType.getStorageSize() == 1, "Invalid storage size."); } } @@ -98,12 +98,18 @@ void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const return; // no distinction between value and reference for non-value types if (!_remove) CompilerUtils(m_context).copyToStackTop(sizeOnStack(), sizeOnStack()); - m_context - << eth::Instruction::SWAP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1 - << u256(2) << eth::Instruction::EXP << eth::Instruction::SWAP1 << eth::Instruction::DIV; - //@todo higher order bits might be dirty. Is this bad? - //@todo this does not work for types that are left-aligned on the stack. - // make those types right-aligned? + if (m_dataType.getStorageBytes() == 32) + m_context << eth::Instruction::POP << eth::Instruction::SLOAD; + else + { + m_context + << eth::Instruction::SWAP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1 + << u256(0x100) << eth::Instruction::EXP << eth::Instruction::SWAP1 << eth::Instruction::DIV; + if (m_dataType.getCategory() == Type::Category::FixedBytes) + m_context << (u256(0x1) << (256 - 8 * m_dataType.getStorageBytes())) << eth::Instruction::MUL; + else + m_context << ((u256(0x1) << (8 * m_dataType.getStorageBytes())) - 1) << eth::Instruction::AND; + } } void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _location, bool _move) const @@ -111,12 +117,43 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc // stack: value storage_key storage_offset if (m_dataType.isValueType()) { - //@todo OR the value into the storage like it is done for ByteArrayElement - m_context - << u256(2) << eth::Instruction::EXP << eth::Instruction::DUP3 << eth::Instruction::MUL - << eth::Instruction::SWAP1 << eth::Instruction::SSTORE; - if (_move) + solAssert(m_dataType.getStorageBytes() <= 32, "Invalid storage bytes size."); + solAssert(m_dataType.getStorageBytes() > 0, "Invalid storage bytes size."); + if (m_dataType.getStorageBytes() == 32) + { + // offset should be zero m_context << eth::Instruction::POP; + if (!_move) + m_context << eth::Instruction::DUP2 << eth::Instruction::SWAP1; + m_context << eth::Instruction::SSTORE; + } + else + { + // OR the value into the other values in the storage slot + m_context << u256(0x100) << eth::Instruction::EXP; + // stack: value storage_ref multiplier + // fetch old value + m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD; + // stack: value storege_ref multiplier old_full_value + // clear bytes in old value + m_context + << eth::Instruction::DUP2 << ((u256(1) << (8 * m_dataType.getStorageBytes())) - 1) + << eth::Instruction::MUL; + m_context << eth::Instruction::NOT << eth::Instruction::AND; + // stack: value storage_ref multiplier cleared_value + m_context + << eth::Instruction::SWAP1 << eth::Instruction::DUP4; + // stack: value storage_ref cleared_value multiplier value + if (m_dataType.getCategory() == Type::Category::FixedBytes) + m_context + << (u256(0x1) << (256 - 8 * dynamic_cast(m_dataType).getNumBytes())) + << eth::Instruction::SWAP1 << eth::Instruction::DIV; + m_context << eth::Instruction::MUL << eth::Instruction::OR; + // stack: value storage_ref updated_value + m_context << eth::Instruction::SWAP1 << eth::Instruction::SSTORE; + if (_move) + m_context << eth::Instruction::POP; + } } else { @@ -134,28 +171,31 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc else if (m_dataType.getCategory() == Type::Category::Struct) { // stack layout: source_ref source_offset target_ref target_offset + // note that we have structs, so offsets should be zero and are ignored auto const& structType = dynamic_cast(m_dataType); solAssert(structType == _sourceType, "Struct assignment with conversion."); for (auto const& member: structType.getMembers()) { - //@todo actually use offsets // 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::DUP5 << eth::Instruction::DUP2 << eth::Instruction::ADD; - m_context << u256(0); // zero offset - // stack: source_ref source_off target_ref target_off member_offset source_member_ref source_member_off + pair const& offsets = structType.getStorageOffsetsOfMember(member.first); + m_context + << offsets.first << u256(offsets.second) + << eth::Instruction::DUP6 << eth::Instruction::DUP3 + << eth::Instruction::ADD << eth::Instruction::DUP2; + // stack: source_ref source_off target_ref target_off member_slot_offset member_byte_offset source_member_ref source_member_off StorageItem(m_context, *memberType).retrieveValue(_location, true); // stack: source_ref source_off target_ref target_off member_offset source_value... - solAssert(2 + memberType->getSizeOnStack() <= 16, "Stack too deep."); - m_context << eth::dupInstruction(3 + memberType->getSizeOnStack()) - << eth::dupInstruction(2 + memberType->getSizeOnStack()) << eth::Instruction::ADD; - // stack: source_ref source_off target_ref target_off member_offset source_value... target_member_ref - m_context << u256(0); // zero offset + solAssert(4 + memberType->getSizeOnStack() <= 16, "Stack too deep."); + m_context + << eth::dupInstruction(4 + memberType->getSizeOnStack()) + << eth::dupInstruction(3 + memberType->getSizeOnStack()) << eth::Instruction::ADD + << eth::dupInstruction(2 + memberType->getSizeOnStack()); + // stack: source_ref source_off target_ref target_off member_slot_offset member_byte_offset source_value... target_member_ref target_member_byte_off StorageItem(m_context, *memberType).storeValue(*memberType, _location, true); - m_context << eth::Instruction::POP; + m_context << eth::Instruction::POP << eth::Instruction::POP; } if (_move) m_context @@ -185,6 +225,7 @@ void StorageItem::setToZero(SourceLocation const&, bool _removeReference) const else if (m_dataType.getCategory() == Type::Category::Struct) { // stack layout: storage_key storage_offset + // @todo this can be improved for packed types auto const& structType = dynamic_cast(m_dataType); for (auto const& member: structType.getMembers()) { @@ -192,11 +233,10 @@ void StorageItem::setToZero(SourceLocation const&, bool _removeReference) const TypePointer const& memberType = member.second; if (memberType->getCategory() == Type::Category::Mapping) continue; - // @todo actually use offset + pair const& offsets = structType.getStorageOffsetsOfMember(member.first); m_context - << structType.getStorageOffsetOfMember(member.first) - << eth::Instruction::DUP3 << eth::Instruction::ADD; - m_context << u256(0); + << offsets.first << eth::Instruction::DUP3 << eth::Instruction::ADD + << u256(offsets.second); StorageItem(m_context, *memberType).setToZero(); } if (_removeReference) @@ -208,7 +248,28 @@ void StorageItem::setToZero(SourceLocation const&, bool _removeReference) const // @todo actually use offset if (!_removeReference) CompilerUtils(m_context).copyToStackTop(sizeOnStack(), sizeOnStack()); - m_context << eth::Instruction::POP << u256(0) << eth::Instruction::SWAP1 << eth::Instruction::SSTORE; + if (m_dataType.getStorageBytes() == 32) + { + // offset should be zero + m_context + << eth::Instruction::POP << u256(0) + << eth::Instruction::SWAP1 << eth::Instruction::SSTORE; + } + else + { + m_context << u256(0x100) << eth::Instruction::EXP; + // stack: storage_ref multiplier + // fetch old value + m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD; + // stack: storege_ref multiplier old_full_value + // clear bytes in old value + m_context + << eth::Instruction::SWAP1 << ((u256(1) << (8 * m_dataType.getStorageBytes())) - 1) + << eth::Instruction::MUL; + m_context << eth::Instruction::NOT << eth::Instruction::AND; + // stack: storage_ref cleared_value + m_context << eth::Instruction::SWAP1 << eth::Instruction::SSTORE; + } } } diff --git a/Types.cpp b/Types.cpp index 500ae9b05..147eff1ec 100644 --- a/Types.cpp +++ b/Types.cpp @@ -35,54 +35,72 @@ namespace dev namespace solidity { +void StorageOffsets::computeOffsets(TypePointers const& _types) +{ + bigint slotOffset = 0; + unsigned byteOffset = 0; + map> offsets; + for (size_t i = 0; i < _types.size(); ++i) + { + TypePointer const& type = _types[i]; + if (!type->canBeStored()) + continue; + if (byteOffset + type->getStorageBytes() > 32) + { + // would overflow, go to next slot + ++slotOffset; + byteOffset = 0; + } + if (slotOffset >= bigint(1) << 256) + BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Object too large for storage.")); + offsets[i] = make_pair(u256(slotOffset), byteOffset); + solAssert(type->getStorageSize() >= 1, "Invalid storage size."); + if (type->getStorageSize() == 1 && byteOffset + type->getStorageBytes() <= 32) + byteOffset += type->getStorageBytes(); + else + { + slotOffset += type->getStorageSize(); + byteOffset = 0; + } + } + if (byteOffset > 0) + ++slotOffset; + if (slotOffset >= bigint(1) << 256) + BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Object too large for storage.")); + m_storageSize = u256(slotOffset); + swap(m_offsets, offsets); +} + +pair const* StorageOffsets::getOffset(size_t _index) const +{ + if (m_offsets.count(_index)) + return &m_offsets.at(_index); + else + return nullptr; +} + std::pair const* MemberList::getMemberStorageOffset(string const& _name) const { if (!m_storageOffsets) { - bigint slotOffset = 0; - unsigned byteOffset = 0; - map> offsets; + TypePointers memberTypes; + memberTypes.reserve(m_memberTypes.size()); for (auto const& nameAndType: m_memberTypes) - { - TypePointer const& type = nameAndType.second; - if (!type->canBeStored()) - continue; - if (byteOffset + type->getStorageBytes() > 32) - { - // would overflow, go to next slot - ++slotOffset; - byteOffset = 0; - } - if (slotOffset >= bigint(1) << 256) - BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Object too large for storage.")); - offsets[nameAndType.first] = make_pair(u256(slotOffset), byteOffset); - solAssert(type->getStorageSize() >= 1, "Invalid storage size."); - if (type->getStorageSize() == 1 && byteOffset + type->getStorageBytes() <= 32) - byteOffset += type->getStorageBytes(); - else - { - slotOffset += type->getStorageSize(); - byteOffset = 0; - } - } - if (byteOffset > 0) - ++slotOffset; - if (slotOffset >= bigint(1) << 256) - BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Object too large for storage.")); - m_storageSize = u256(slotOffset); - m_storageOffsets.reset(new decltype(offsets)(move(offsets))); + memberTypes.push_back(nameAndType.second); + m_storageOffsets.reset(new StorageOffsets()); + m_storageOffsets->computeOffsets(memberTypes); } - if (m_storageOffsets->count(_name)) - return &((*m_storageOffsets)[_name]); - else - return nullptr; + for (size_t index = 0; index < m_memberTypes.size(); ++index) + if (m_memberTypes[index].first == _name) + return m_storageOffsets->getOffset(index); + return nullptr; } u256 const& MemberList::getStorageSize() const { // trigger lazy computation getMemberStorageOffset(""); - return m_storageSize; + return m_storageOffsets->getStorageSize(); } TypePointer Type::fromElementaryTypeName(Token::Value _typeToken) @@ -830,18 +848,11 @@ MemberList const& StructType::getMembers() const return *m_members; } -u256 StructType::getStorageOffsetOfMember(string const& _name) const +pair const& StructType::getStorageOffsetsOfMember(string const& _name) const { - - //@todo cache member offset? - u256 offset; - for (ASTPointer const& variable: m_struct.getMembers()) - { - if (variable->getName() == _name) - return offset; - offset += variable->getType()->getStorageSize(); - } - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage offset of non-existing member requested.")); + auto const* offsets = getMembers().getMemberStorageOffset(_name); + solAssert(offsets, "Storage offset of non-existing member requested."); + return *offsets; } TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const diff --git a/Types.h b/Types.h index 5e4970780..cc7a0e243 100644 --- a/Types.h +++ b/Types.h @@ -43,6 +43,26 @@ using TypePointer = std::shared_ptr; using FunctionTypePointer = std::shared_ptr; using TypePointers = std::vector; + +/** + * Helper class to compute storage offsets of members of structs and contracts. + */ +class StorageOffsets +{ +public: + /// Resets the StorageOffsets objects and determines the position in storage for each + /// of the elements of @a _types. + void computeOffsets(TypePointers const& _types); + /// @returns the offset of the given member, might be null if the member is not part of storage. + std::pair const* getOffset(size_t _index) const; + /// @returns the total number of slots occupied by all members. + u256 const& getStorageSize() const { return m_storageSize; } + +private: + u256 m_storageSize; + std::map> m_offsets; +}; + /** * List of members of a type. */ @@ -71,8 +91,7 @@ public: private: MemberMap m_memberTypes; - mutable u256 m_storageSize = 0; - mutable std::unique_ptr>> m_storageOffsets; + mutable std::unique_ptr m_storageOffsets; }; /** @@ -411,7 +430,7 @@ public: virtual MemberList const& getMembers() const override; - u256 getStorageOffsetOfMember(std::string const& _name) const; + std::pair const& getStorageOffsetsOfMember(std::string const& _name) const; private: StructDefinition const& m_struct; From 9b2a255c1b0ac1e247a59e4cd8cb3c94b53aca7c Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 16 Mar 2015 18:06:34 +0100 Subject: [PATCH 31/70] Move memberlist to avoid unique_ptr copy. --- Types.cpp | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/Types.cpp b/Types.cpp index 147eff1ec..72f13a01c 100644 --- a/Types.cpp +++ b/Types.cpp @@ -1246,23 +1246,29 @@ MagicType::MagicType(MagicType::Kind _kind): switch (m_kind) { case Kind::Block: - m_members = MemberList({{"coinbase", make_shared(0, IntegerType::Modifier::Address)}, - {"timestamp", make_shared(256)}, - {"blockhash", make_shared(strings{"uint"}, strings{"bytes32"}, FunctionType::Location::BlockHash)}, - {"difficulty", make_shared(256)}, - {"number", make_shared(256)}, - {"gaslimit", make_shared(256)}}); + m_members = move(MemberList({ + {"coinbase", make_shared(0, IntegerType::Modifier::Address)}, + {"timestamp", make_shared(256)}, + {"blockhash", make_shared(strings{"uint"}, strings{"bytes32"}, FunctionType::Location::BlockHash)}, + {"difficulty", make_shared(256)}, + {"number", make_shared(256)}, + {"gaslimit", make_shared(256)} + })); break; case Kind::Message: - m_members = MemberList({{"sender", make_shared(0, IntegerType::Modifier::Address)}, - {"gas", make_shared(256)}, - {"value", make_shared(256)}, - {"data", make_shared(ArrayType::Location::CallData)}, - {"sig", make_shared(4)}}); + m_members = move(MemberList({ + {"sender", make_shared(0, IntegerType::Modifier::Address)}, + {"gas", make_shared(256)}, + {"value", make_shared(256)}, + {"data", make_shared(ArrayType::Location::CallData)}, + {"sig", make_shared(4)} + })); break; case Kind::Transaction: - m_members = MemberList({{"origin", make_shared(0, IntegerType::Modifier::Address)}, - {"gasprice", make_shared(256)}}); + m_members = move(MemberList({ + {"origin", make_shared(0, IntegerType::Modifier::Address)}, + {"gasprice", make_shared(256)} + })); break; default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of magic.")); From c284408e7a31f2e24611cf6360b492e35ab1c0b5 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 16 Mar 2015 19:00:09 +0100 Subject: [PATCH 32/70] Add move assignment operator manually. --- Types.cpp | 6 ++++++ Types.h | 1 + 2 files changed, 7 insertions(+) diff --git a/Types.cpp b/Types.cpp index 72f13a01c..295434836 100644 --- a/Types.cpp +++ b/Types.cpp @@ -79,6 +79,12 @@ pair const* StorageOffsets::getOffset(size_t _index) const return nullptr; } +MemberList& MemberList::operator=(MemberList&& _other) +{ + m_memberTypes = std::move(_other.m_memberTypes); + m_storageOffsets = std::move(_other.m_storageOffsets); +} + std::pair const* MemberList::getMemberStorageOffset(string const& _name) const { if (!m_storageOffsets) diff --git a/Types.h b/Types.h index cc7a0e243..e7601fde8 100644 --- a/Types.h +++ b/Types.h @@ -73,6 +73,7 @@ public: MemberList() {} explicit MemberList(MemberMap const& _members): m_memberTypes(_members) {} + MemberList& operator=(MemberList&& _other); TypePointer getMemberType(std::string const& _name) const { for (auto const& it: m_memberTypes) From fc0bdc3d81189b52314c67e0b1d77c451b7b7091 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 16 Mar 2015 19:27:53 +0100 Subject: [PATCH 33/70] Fix static variables. --- Types.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Types.cpp b/Types.cpp index 295434836..04f86b922 100644 --- a/Types.cpp +++ b/Types.cpp @@ -83,6 +83,7 @@ MemberList& MemberList::operator=(MemberList&& _other) { m_memberTypes = std::move(_other.m_memberTypes); m_storageOffsets = std::move(_other.m_storageOffsets); + return *this; } std::pair const* MemberList::getMemberStorageOffset(string const& _name) const @@ -223,7 +224,7 @@ TypePointer Type::commonType(TypePointer const& _a, TypePointer const& _b) return TypePointer(); } -const MemberList Type::EmptyMemberList = MemberList(); +const MemberList Type::EmptyMemberList; IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier): m_bits(_bits), m_modifier(_modifier) @@ -309,10 +310,11 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe return commonType; } -const MemberList IntegerType::AddressMemberList = - MemberList({{"balance", make_shared(256)}, - {"call", make_shared(strings(), strings(), FunctionType::Location::Bare, true)}, - {"send", make_shared(strings{"uint"}, strings{}, FunctionType::Location::Send)}}); +const MemberList IntegerType::AddressMemberList({ + {"balance", make_shared(256)}, + {"call", make_shared(strings(), strings(), FunctionType::Location::Bare, true)}, + {"send", make_shared(strings{"uint"}, strings{}, FunctionType::Location::Send)} +}); IntegerConstantType::IntegerConstantType(Literal const& _literal) { @@ -749,7 +751,7 @@ shared_ptr ArrayType::copyForLocation(ArrayType::Location _location) return copy; } -const MemberList ArrayType::s_arrayTypeMemberList = MemberList({{"length", make_shared(256)}}); +const MemberList ArrayType::s_arrayTypeMemberList({{"length", make_shared(256)}}); bool ContractType::operator==(Type const& _other) const { From 2986ecbd753ad0e3f76c98280e68f7f9a464518f Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Mon, 16 Mar 2015 19:19:34 +0100 Subject: [PATCH 34/70] Added anonymous flag to event. added test --- AST.h | 7 +++++-- ExpressionCompiler.cpp | 9 ++++++--- InterfaceHandler.cpp | 1 + Parser.cpp | 8 +++++++- Token.h | 1 + 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/AST.h b/AST.h index f5f6a3f38..c0ae3c164 100644 --- a/AST.h +++ b/AST.h @@ -556,14 +556,16 @@ public: EventDefinition(SourceLocation const& _location, ASTPointer const& _name, ASTPointer const& _documentation, - ASTPointer const& _parameters): - Declaration(_location, _name), Documented(_documentation), m_parameters(_parameters) {} + ASTPointer const& _parameters, + bool _anonymous = false): + Declaration(_location, _name), Documented(_documentation), m_parameters(_parameters) , m_anonymous(_anonymous){} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; std::vector> const& getParameters() const { return m_parameters->getParameters(); } ParameterList const& getParameterList() const { return *m_parameters; } + bool IsAnonymous() const { return m_anonymous; } virtual TypePointer getType(ContractDefinition const* = nullptr) const override { @@ -574,6 +576,7 @@ public: private: ASTPointer m_parameters; + bool m_anonymous; }; /** diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index a0a688bbd..99673edde 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -542,9 +542,12 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) 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."); + if (!event.IsAnonymous()) + { + m_context << u256(h256::Arith(dev::sha3(function.getCanonicalSignature(event.getName())))); + ++numIndexed; + } + solAssert(numIndexed <= 4 - (event.IsAnonymous() ? 1 : 0), "Too many indexed arguments."); // Copy all non-indexed arguments to memory (data) m_context << u256(0); for (unsigned arg = 0; arg < arguments.size(); ++arg) diff --git a/InterfaceHandler.cpp b/InterfaceHandler.cpp index 99a7db96e..2b855e243 100644 --- a/InterfaceHandler.cpp +++ b/InterfaceHandler.cpp @@ -70,6 +70,7 @@ std::unique_ptr InterfaceHandler::getABIInterface(ContractDefinitio Json::Value event; event["type"] = "event"; event["name"] = it->getName(); + //todo add anonymous!; Json::Value params(Json::arrayValue); for (auto const& p: it->getParameters()) { diff --git a/Parser.cpp b/Parser.cpp index 459a34bd0..098a38344 100644 --- a/Parser.cpp +++ b/Parser.cpp @@ -388,6 +388,12 @@ ASTPointer Parser::parseEventDefinition() docstring = make_shared(m_scanner->getCurrentCommentLiteral()); expectToken(Token::Event); + bool anonymous = false; + if (m_scanner->getCurrentToken() == Token::Anonymous) + { + anonymous = true; + m_scanner->next(); + } ASTPointer name(expectIdentifierToken()); ASTPointer parameters; if (m_scanner->getCurrentToken() == Token::LParen) @@ -396,7 +402,7 @@ ASTPointer Parser::parseEventDefinition() parameters = createEmptyParameterList(); nodeFactory.markEndPosition(); expectToken(Token::Semicolon); - return nodeFactory.createNode(name, docstring, parameters); + return nodeFactory.createNode(name, docstring, parameters, anonymous); } ASTPointer Parser::parseModifierInvocation() diff --git a/Token.h b/Token.h index 2d8a49fcd..b2951e939 100644 --- a/Token.h +++ b/Token.h @@ -145,6 +145,7 @@ namespace solidity K(Break, "break", 0) \ K(Case, "case", 0) \ K(Const, "constant", 0) \ + K(Anonymous, "anonymous", 0) \ K(Continue, "continue", 0) \ K(Contract, "contract", 0) \ K(Default, "default", 0) \ From 5a5577f5a5e8ef590810651e55a9a3192a0bf31c Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Tue, 17 Mar 2015 10:48:46 +0100 Subject: [PATCH 35/70] changed the position of 'anonymous' keyword: event () anonymous. - style changes --- AST.h | 17 +++++++++++------ ExpressionCompiler.cpp | 2 +- Parser.cpp | 12 ++++++------ 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/AST.h b/AST.h index c0ae3c164..5564d83db 100644 --- a/AST.h +++ b/AST.h @@ -553,12 +553,17 @@ private: class EventDefinition: public Declaration, public VariableScope, public Documented { public: - EventDefinition(SourceLocation const& _location, - ASTPointer const& _name, - ASTPointer const& _documentation, - ASTPointer const& _parameters, - bool _anonymous = false): - Declaration(_location, _name), Documented(_documentation), m_parameters(_parameters) , m_anonymous(_anonymous){} + EventDefinition( + SourceLocation const& _location, + ASTPointer const& _name, + ASTPointer const& _documentation, + ASTPointer const& _parameters, + bool _anonymous = false + ): + Declaration(_location, _name), + Documented(_documentation), + m_parameters(_parameters), + m_anonymous(_anonymous){} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 99673edde..bff27e9c7 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -547,7 +547,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << u256(h256::Arith(dev::sha3(function.getCanonicalSignature(event.getName())))); ++numIndexed; } - solAssert(numIndexed <= 4 - (event.IsAnonymous() ? 1 : 0), "Too many indexed arguments."); + 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) diff --git a/Parser.cpp b/Parser.cpp index 098a38344..393d2734e 100644 --- a/Parser.cpp +++ b/Parser.cpp @@ -388,18 +388,18 @@ ASTPointer Parser::parseEventDefinition() docstring = make_shared(m_scanner->getCurrentCommentLiteral()); expectToken(Token::Event); - bool anonymous = false; - if (m_scanner->getCurrentToken() == Token::Anonymous) - { - anonymous = true; - m_scanner->next(); - } ASTPointer name(expectIdentifierToken()); ASTPointer parameters; if (m_scanner->getCurrentToken() == Token::LParen) parameters = parseParameterList(true, true); else parameters = createEmptyParameterList(); + bool anonymous = false; + if (m_scanner->getCurrentToken() == Token::Anonymous) + { + anonymous = true; + m_scanner->next(); + } nodeFactory.markEndPosition(); expectToken(Token::Semicolon); return nodeFactory.createNode(name, docstring, parameters, anonymous); From 46cbb5525cd8c2f22667c605ee7ef4da34301797 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Tue, 17 Mar 2015 11:34:56 +0100 Subject: [PATCH 36/70] added anonymous to ABI --- AST.h | 2 +- ExpressionCompiler.cpp | 2 +- InterfaceHandler.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/AST.h b/AST.h index 5564d83db..37ddc657e 100644 --- a/AST.h +++ b/AST.h @@ -570,7 +570,7 @@ public: std::vector> const& getParameters() const { return m_parameters->getParameters(); } ParameterList const& getParameterList() const { return *m_parameters; } - bool IsAnonymous() const { return m_anonymous; } + bool isAnonymous() const { return m_anonymous; } virtual TypePointer getType(ContractDefinition const* = nullptr) const override { diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index bff27e9c7..61b17f663 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -542,7 +542,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) appendTypeConversion(*arguments[arg - 1]->getType(), *function.getParameterTypes()[arg - 1], true); } - if (!event.IsAnonymous()) + if (!event.isAnonymous()) { m_context << u256(h256::Arith(dev::sha3(function.getCanonicalSignature(event.getName())))); ++numIndexed; diff --git a/InterfaceHandler.cpp b/InterfaceHandler.cpp index 2b855e243..406d1e24a 100644 --- a/InterfaceHandler.cpp +++ b/InterfaceHandler.cpp @@ -70,7 +70,7 @@ std::unique_ptr InterfaceHandler::getABIInterface(ContractDefinitio Json::Value event; event["type"] = "event"; event["name"] = it->getName(); - //todo add anonymous!; + event["anonymous"] = it->isAnonymous(); Json::Value params(Json::arrayValue); for (auto const& p: it->getParameters()) { From ed757ba5bf1090ba8a9a5e3e6a29da8785afb861 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Tue, 17 Mar 2015 12:28:24 +0100 Subject: [PATCH 37/70] added test to check anonymous events with user specified topics added initial value for m_anonymous of EventDefinition --- AST.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AST.h b/AST.h index 37ddc657e..335b9a880 100644 --- a/AST.h +++ b/AST.h @@ -581,7 +581,7 @@ public: private: ASTPointer m_parameters; - bool m_anonymous; + bool m_anonymous = false; }; /** From 895c08342c4b236c2928a5af9fb23def218eca3d Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 16 Mar 2015 16:15:13 +0100 Subject: [PATCH 38/70] Provide access to storage offsets via contract type. --- Compiler.cpp | 17 +++-------------- Types.cpp | 26 +++++++++++++++++++++++--- Types.h | 4 ++++ 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/Compiler.cpp b/Compiler.cpp index db61cc4a1..8e2634499 100644 --- a/Compiler.cpp +++ b/Compiler.cpp @@ -20,12 +20,12 @@ * Solidity compiler. */ +#include #include #include #include #include #include -#include #include #include @@ -274,19 +274,8 @@ void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters) void Compiler::registerStateVariables(ContractDefinition const& _contract) { - vector variables; - for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.getLinearizedBaseContracts())) - for (ASTPointer const& variable: contract->getStateVariables()) - if (!variable->isConstant()) - variables.push_back(variable.get()); - TypePointers types; - for (auto variable: variables) - types.push_back(variable->getType()); - StorageOffsets offsets; - offsets.computeOffsets(types); - for (size_t index = 0; index < variables.size(); ++index) - if (auto const* offset = offsets.getOffset(index)) - m_context.addStateVariable(*variables[index], offset->first, offset->second); + for (auto const& var: ContractType(_contract).getStateVariables()) + m_context.addStateVariable(*get<0>(var), get<1>(var), get<2>(var)); } void Compiler::initializeStateVariables(ContractDefinition const& _contract) diff --git a/Types.cpp b/Types.cpp index 04f86b922..ed3cb8fd1 100644 --- a/Types.cpp +++ b/Types.cpp @@ -20,14 +20,14 @@ * Solidity data types */ +#include +#include +#include #include #include #include -#include #include -#include - using namespace std; namespace dev @@ -812,6 +812,26 @@ u256 ContractType::getFunctionIdentifier(string const& _functionName) const return Invalid256; } +vector> ContractType::getStateVariables() const +{ + vector variables; + for (ContractDefinition const* contract: boost::adaptors::reverse(m_contract.getLinearizedBaseContracts())) + for (ASTPointer const& variable: contract->getStateVariables()) + if (!variable->isConstant()) + variables.push_back(variable.get()); + TypePointers types; + for (auto variable: variables) + types.push_back(variable->getType()); + StorageOffsets offsets; + offsets.computeOffsets(types); + + vector> variablesAndOffsets; + for (size_t index = 0; index < variables.size(); ++index) + if (auto const* offset = offsets.getOffset(index)) + variablesAndOffsets.push_back(make_tuple(variables[index], offset->first, offset->second)); + return variablesAndOffsets; +} + TypePointer StructType::unaryOperatorResult(Token::Value _operator) const { return _operator == Token::Delete ? make_shared() : TypePointer(); diff --git a/Types.h b/Types.h index e7601fde8..c7dbcdeb9 100644 --- a/Types.h +++ b/Types.h @@ -403,6 +403,10 @@ public: /// not exist. u256 getFunctionIdentifier(std::string const& _functionName) const; + /// @returns a list of all state variables (including inherited) of the contract and their + /// offsets in storage. + std::vector> getStateVariables() const; + private: ContractDefinition const& m_contract; /// If true, it is the "super" type of the current contract, i.e. it contains only inherited From 2cde4f34044b89bb8bced05cab308c0093bd192d Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 16 Mar 2015 17:52:19 +0100 Subject: [PATCH 39/70] Packing for arrays. --- ArrayUtils.cpp | 206 +++++++++++++++++++++++++++++++++-------- ArrayUtils.h | 6 ++ ExpressionCompiler.cpp | 71 ++++++++------ Types.cpp | 18 +++- Types.h | 15 ++- 5 files changed, 240 insertions(+), 76 deletions(-) diff --git a/ArrayUtils.cpp b/ArrayUtils.cpp index 2596e4afa..a46dea3c8 100644 --- a/ArrayUtils.cpp +++ b/ArrayUtils.cpp @@ -52,15 +52,21 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons // TODO unroll loop for small sizes bool sourceIsStorage = _sourceType.getLocation() == ArrayType::Location::Storage; + bool directCopy = sourceIsStorage && sourceBaseType->isValueType() && *sourceBaseType == *targetBaseType; + bool haveByteOffsetSource = !directCopy && sourceIsStorage && sourceBaseType->getStorageBytes() <= 16; + bool haveByteOffsetTarget = !directCopy && targetBaseType->getStorageBytes() <= 16; + unsigned byteOffsetSize = (haveByteOffsetSource ? 1 : 0) + (haveByteOffsetTarget ? 1 : 0); // stack: source_ref [source_byte_off] [source_length] target_ref target_byte_off // store target_ref - m_context << eth::Instruction::POP; //@todo + // arrays always start at zero byte offset, pop offset + m_context << eth::Instruction::POP; for (unsigned i = _sourceType.getSizeOnStack(); i > 0; --i) m_context << eth::swapInstruction(i); // stack: target_ref source_ref [source_byte_off] [source_length] if (sourceIsStorage) - m_context << eth::Instruction::POP; //@todo + // arrays always start at zero byte offset, pop offset + m_context << eth::Instruction::POP; // stack: target_ref source_ref [source_length] // retrieve source length if (_sourceType.getLocation() != ArrayType::Location::CallData || !_sourceType.isDynamicallySized()) @@ -81,7 +87,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons m_context << eth::Instruction::POP << eth::Instruction::POP << eth::Instruction::POP << eth::Instruction::POP; - m_context << u256(0); //@todo + m_context << u256(0); return; } // compute hashes (data positions) @@ -97,8 +103,8 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons // stack: target_ref target_data_end source_length target_data_pos source_ref // skip copying if source length is zero m_context << eth::Instruction::DUP3 << eth::Instruction::ISZERO; - eth::AssemblyItem copyLoopEnd = m_context.newTag(); - m_context.appendConditionalJumpTo(copyLoopEnd); + eth::AssemblyItem copyLoopEndWithoutByteOffset = m_context.newTag(); + m_context.appendConditionalJumpTo(copyLoopEndWithoutByteOffset); if (_sourceType.getLocation() == ArrayType::Location::Storage && _sourceType.isDynamicallySized()) CompilerUtils(m_context).computeHashStatic(); @@ -107,18 +113,24 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons convertLengthToSize(_sourceType); m_context << eth::Instruction::DUP3 << eth::Instruction::ADD; // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end + if (haveByteOffsetTarget) + m_context << u256(0); + if (haveByteOffsetSource) + m_context << u256(0); + // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] eth::AssemblyItem copyLoopStart = m_context.newTag(); m_context << copyLoopStart; // check for loop condition m_context - << eth::Instruction::DUP3 << eth::Instruction::DUP2 + << eth::dupInstruction(3 + byteOffsetSize) << eth::dupInstruction(2 + byteOffsetSize) << eth::Instruction::GT << eth::Instruction::ISZERO; + eth::AssemblyItem copyLoopEnd = m_context.newTag(); m_context.appendConditionalJumpTo(copyLoopEnd); - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end + // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] // copy if (sourceBaseType->getCategory() == Type::Category::Array) { - //@todo + solAssert(byteOffsetSize == 0, "Byte offset for array as base type."); m_context << eth::Instruction::DUP3; if (sourceIsStorage) m_context << u256(0); @@ -129,36 +141,80 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons ); m_context << eth::Instruction::POP << eth::Instruction::POP; } + else if (directCopy) + { + solAssert(byteOffsetSize == 0, "Byte offset for direct copy."); + m_context + << eth::Instruction::DUP3 << eth::Instruction::SLOAD + << eth::Instruction::DUP3 << eth::Instruction::SSTORE; + } else { - m_context << eth::Instruction::DUP3; + // Note that we have to copy each element on its own in case conversion is involved. + // We might copy too much if there is padding at the last element, but this way end + // checking is easier. + // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] + m_context << eth::dupInstruction(3 + byteOffsetSize); if (_sourceType.getLocation() == ArrayType::Location::Storage) { - m_context << u256(0); + if (haveByteOffsetSource) + m_context << eth::Instruction::DUP2; + else + m_context << u256(0); StorageItem(m_context, *sourceBaseType).retrieveValue(SourceLocation(), true); } else if (sourceBaseType->isValueType()) CompilerUtils(m_context).loadFromMemoryDynamic(*sourceBaseType, true, true, false); else solAssert(false, "Copying of unknown type requested: " + sourceBaseType->toString()); - solAssert(2 + sourceBaseType->getSizeOnStack() <= 16, "Stack too deep."); - m_context << eth::dupInstruction(2 + sourceBaseType->getSizeOnStack()) << u256(0); + // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] ... + solAssert(2 + byteOffsetSize + sourceBaseType->getSizeOnStack() <= 16, "Stack too deep."); + // fetch target storage reference + m_context << eth::dupInstruction(2 + byteOffsetSize + sourceBaseType->getSizeOnStack()); + if (haveByteOffsetTarget) + m_context << eth::dupInstruction(1 + byteOffsetSize + sourceBaseType->getSizeOnStack()); + else + m_context << u256(0); StorageItem(m_context, *targetBaseType).storeValue(*sourceBaseType, SourceLocation(), true); } + // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] // increment source - m_context - << eth::Instruction::SWAP2 - << (sourceIsStorage ? sourceBaseType->getStorageSize() : sourceBaseType->getCalldataEncodedSize()) - << eth::Instruction::ADD - << eth::Instruction::SWAP2; + if (haveByteOffsetSource) + incrementByteOffset(sourceBaseType->getStorageBytes(), 1, haveByteOffsetTarget ? 5 : 4); + else + m_context + << eth::swapInstruction(2 + byteOffsetSize) + << (sourceIsStorage ? sourceBaseType->getStorageSize() : sourceBaseType->getCalldataEncodedSize()) + << eth::Instruction::ADD + << eth::swapInstruction(2 + byteOffsetSize); // increment target - m_context - << eth::Instruction::SWAP1 - << targetBaseType->getStorageSize() - << eth::Instruction::ADD - << eth::Instruction::SWAP1; + if (haveByteOffsetTarget) + incrementByteOffset(targetBaseType->getStorageBytes(), byteOffsetSize, byteOffsetSize + 2); + else + m_context + << eth::swapInstruction(1 + byteOffsetSize) + << targetBaseType->getStorageSize() + << eth::Instruction::ADD + << eth::swapInstruction(1 + byteOffsetSize); m_context.appendJumpTo(copyLoopStart); m_context << copyLoopEnd; + if (haveByteOffsetTarget) + { + // clear elements that might be left over in the current slot in target + // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end target_byte_offset [source_byte_offset] + m_context << eth::dupInstruction(byteOffsetSize) << eth::Instruction::ISZERO; + eth::AssemblyItem copyCleanupLoopEnd = m_context.appendConditionalJump(); + m_context << eth::dupInstruction(2 + byteOffsetSize) << eth::dupInstruction(1 + byteOffsetSize); + StorageItem(m_context, *targetBaseType).setToZero(SourceLocation(), true); + incrementByteOffset(targetBaseType->getStorageBytes(), byteOffsetSize, byteOffsetSize + 2); + m_context.appendJumpTo(copyLoopEnd); + + m_context << copyCleanupLoopEnd; + m_context << eth::Instruction::POP; // might pop the source, but then target is popped next + } + if (haveByteOffsetSource) + m_context << eth::Instruction::POP; + m_context << copyLoopEndWithoutByteOffset; // zero-out leftovers in target // stack: target_ref target_data_end source_data_pos target_data_pos_updated source_data_end @@ -166,41 +222,61 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons // stack: target_ref target_data_end target_data_pos_updated clearStorageLoop(*targetBaseType); m_context << eth::Instruction::POP; - m_context << u256(0); //@todo + m_context << u256(0); } void ArrayUtils::clearArray(ArrayType const& _type) const { unsigned stackHeightStart = m_context.getStackHeight(); solAssert(_type.getLocation() == ArrayType::Location::Storage, ""); - if (_type.isDynamicallySized()) + if (_type.getBaseType()->getStorageBytes() < 32) { - m_context << eth::Instruction::POP; // remove byte offset - clearDynamicArray(_type); + solAssert(_type.getBaseType()->isValueType(), "Invalid storage size for non-value type."); + solAssert(_type.getBaseType()->getStorageSize() <= 1, "Invalid storage size for type."); } + if (_type.getBaseType()->isValueType()) + solAssert(_type.getBaseType()->getStorageSize() <= 1, "Invalid size for value type."); + + m_context << eth::Instruction::POP; // remove byte offset + if (_type.isDynamicallySized()) + clearDynamicArray(_type); else if (_type.getLength() == 0 || _type.getBaseType()->getCategory() == Type::Category::Mapping) - m_context << eth::Instruction::POP << eth::Instruction::POP; - else if (_type.getLength() < 5) // unroll loop for small arrays @todo choose a good value + m_context << eth::Instruction::POP; + else if (_type.getBaseType()->isValueType() && _type.getStorageSize() <= 5) { - solAssert(!_type.isByteArray(), ""); + // unroll loop for small arrays @todo choose a good value + // Note that we loop over storage slots here, not elements. + for (unsigned i = 1; i < _type.getStorageSize(); ++i) + m_context + << u256(0) << eth::Instruction::DUP2 << eth::Instruction::SSTORE + << u256(1) << eth::Instruction::ADD; + m_context << u256(0) << eth::Instruction::SWAP1 << eth::Instruction::SSTORE; + } + else if (!_type.getBaseType()->isValueType() && _type.getLength() <= 4) + { + // unroll loop for small arrays @todo choose a good value + solAssert(_type.getBaseType()->getStorageBytes() >= 32, "Invalid storage size."); for (unsigned i = 1; i < _type.getLength(); ++i) { + m_context << u256(0); StorageItem(m_context, *_type.getBaseType()).setToZero(SourceLocation(), false); - m_context << eth::Instruction::SWAP1; - m_context << u256(_type.getBaseType()->getStorageSize()) << eth::Instruction::ADD; - m_context << eth::Instruction::SWAP1; + m_context + << eth::Instruction::POP + << u256(_type.getBaseType()->getStorageSize()) << eth::Instruction::ADD; } + m_context << u256(0); StorageItem(m_context, *_type.getBaseType()).setToZero(SourceLocation(), true); } else { - solAssert(!_type.isByteArray(), ""); - m_context << eth::Instruction::SWAP1; m_context << eth::Instruction::DUP1 << _type.getLength(); convertLengthToSize(_type); m_context << eth::Instruction::ADD << eth::Instruction::SWAP1; - clearStorageLoop(*_type.getBaseType()); - m_context << eth::Instruction::POP << eth::Instruction::POP; + if (_type.getBaseType()->getStorageBytes() < 32) + clearStorageLoop(IntegerType(256)); + else + clearStorageLoop(*_type.getBaseType()); + m_context << eth::Instruction::POP; } solAssert(m_context.getStackHeight() == stackHeightStart - 2, ""); } @@ -224,7 +300,7 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2 << eth::Instruction::ADD << eth::Instruction::SWAP1; // stack: data_pos_end data_pos - if (_type.isByteArray()) + if (_type.isByteArray() || _type.getBaseType()->getStorageBytes() < 32) clearStorageLoop(IntegerType(256)); else clearStorageLoop(*_type.getBaseType()); @@ -237,6 +313,8 @@ void ArrayUtils::resizeDynamicArray(const ArrayType& _type) const { solAssert(_type.getLocation() == ArrayType::Location::Storage, ""); solAssert(_type.isDynamicallySized(), ""); + if (!_type.isByteArray() && _type.getBaseType()->getStorageBytes() < 32) + solAssert(_type.getBaseType()->isValueType(), "Invalid storage size for non-value type."); unsigned stackHeightStart = m_context.getStackHeight(); eth::AssemblyItem resizeEnd = m_context.newTag(); @@ -266,7 +344,7 @@ void ArrayUtils::resizeDynamicArray(const ArrayType& _type) const // stack: ref new_length data_pos new_size delete_end m_context << eth::Instruction::SWAP2 << eth::Instruction::ADD; // stack: ref new_length delete_end delete_start - if (_type.isByteArray()) + if (_type.isByteArray() || _type.getBaseType()->getStorageBytes() < 32) clearStorageLoop(IntegerType(256)); else clearStorageLoop(*_type.getBaseType()); @@ -294,7 +372,7 @@ void ArrayUtils::clearStorageLoop(Type const& _type) const eth::AssemblyItem zeroLoopEnd = m_context.newTag(); m_context.appendConditionalJumpTo(zeroLoopEnd); // delete - m_context << u256(0); //@todo + m_context << u256(0); StorageItem(m_context, _type).setToZero(SourceLocation(), false); m_context << eth::Instruction::POP; // increment @@ -313,7 +391,20 @@ void ArrayUtils::convertLengthToSize(ArrayType const& _arrayType, bool _pad) con if (_arrayType.isByteArray()) m_context << u256(31) << eth::Instruction::ADD << u256(32) << eth::Instruction::SWAP1 << eth::Instruction::DIV; - else if (_arrayType.getBaseType()->getStorageSize() > 1) + else if (_arrayType.getBaseType()->getStorageSize() <= 1) + { + unsigned baseBytes = _arrayType.getBaseType()->getStorageBytes(); + if (baseBytes == 0) + m_context << eth::Instruction::POP << u256(1); + else if (baseBytes <= 16) + { + unsigned itemsPerSlot = 32 / baseBytes; + m_context + << u256(itemsPerSlot - 1) << eth::Instruction::ADD + << u256(itemsPerSlot) << eth::Instruction::SWAP1 << eth::Instruction::DIV; + } + } + else m_context << _arrayType.getBaseType()->getStorageSize() << eth::Instruction::MUL; } else @@ -349,3 +440,38 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType) const } } +void ArrayUtils::incrementByteOffset(unsigned byteSize, unsigned byteOffsetPosition, unsigned storageOffsetPosition) const +{ + solAssert(byteSize < 32, ""); + // We do the following, but avoiding jumps: + // byteOffset += byteSize + // if (byteOffset + byteSize > 32) + // { + // storageOffset++; + // byteOffset = 0; + // } + if (byteOffsetPosition > 1) + m_context << eth::swapInstruction(byteOffsetPosition - 1); + m_context << u256(byteSize) << eth::Instruction::ADD; + if (byteOffsetPosition > 1) + m_context << eth::swapInstruction(byteOffsetPosition - 1); + // compute, X := (byteOffset + byteSize - 1) / 32, should be 1 iff byteOffset + bytesize > 32 + m_context + << u256(32) << eth::dupInstruction(1 + byteOffsetPosition) << u256(byteSize - 1) + << eth::Instruction::ADD << eth::Instruction::DIV; + // increment storage offset if X == 1 (just add X to it) + // stack: X + m_context + << eth::swapInstruction(storageOffsetPosition) << eth::dupInstruction(storageOffsetPosition + 1) + << eth::Instruction::ADD << eth::swapInstruction(storageOffsetPosition); + // stack: X + // set source_byte_offset to zero if X == 1 (using source_byte_offset *= 1 - X) + m_context << u256(1) << eth::Instruction::SUB; + // stack: 1 - X + if (byteOffsetPosition == 1) + m_context << eth::Instruction::MUL; + else + m_context + << eth::dupInstruction(byteOffsetPosition + 1) << eth::Instruction::MUL + << eth::swapInstruction(byteOffsetPosition) << eth::Instruction::POP; +} diff --git a/ArrayUtils.h b/ArrayUtils.h index c7b05b6d3..6e4ceec1c 100644 --- a/ArrayUtils.h +++ b/ArrayUtils.h @@ -72,6 +72,12 @@ public: void retrieveLength(ArrayType const& _arrayType) const; private: + /// Adds the given number of bytes to a storage byte offset counter and also increments + /// the storage offset if adding this number again would increase the counter over 32. + /// @param byteOffsetPosition the stack offset of the storage byte offset + /// @param storageOffsetPosition the stack offset of the storage slot offset + void incrementByteOffset(unsigned byteSize, unsigned byteOffsetPosition, unsigned storageOffsetPosition) const; + CompilerContext& m_context; }; diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 61b17f663..144827d44 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -753,7 +753,6 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) } else if (baseType.getCategory() == Type::Category::Array) { - // stack layout: [storage_byte_offset] [] ArrayType const& arrayType = dynamic_cast(baseType); solAssert(_indexAccess.getIndexExpression(), "Index expression expected."); ArrayType::Location location = arrayType.getLocation(); @@ -762,6 +761,11 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) location == ArrayType::Location::Memory ? eth::Instruction::MLOAD : eth::Instruction::CALLDATALOAD; + // remove storage byte offset + if (location == ArrayType::Location::Storage) + m_context << eth::Instruction::POP; + + // stack layout: [] _indexAccess.getIndexExpression()->accept(*this); // retrieve length if (!arrayType.isDynamicallySized()) @@ -769,11 +773,9 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) else if (location == ArrayType::Location::CallData) // length is stored on the stack m_context << eth::Instruction::SWAP1; - else if (location == ArrayType::Location::Storage) - m_context << eth::Instruction::DUP3 << load; else m_context << eth::Instruction::DUP2 << load; - // stack: [storage_byte_offset] + // stack: // check out-of-bounds access m_context << eth::Instruction::DUP2 << eth::Instruction::LT; eth::AssemblyItem legalAccess = m_context.appendConditionalJump(); @@ -781,23 +783,22 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) m_context << eth::Instruction::STOP; m_context << legalAccess; - // stack: [storage_byte_offset] + // stack: if (arrayType.isByteArray()) - // byte array is packed differently, especially in storage switch (location) { case ArrayType::Location::Storage: // byte array index storage lvalue on stack (goal): // = - m_context << u256(32) << eth::Instruction::SWAP3; + m_context << u256(32) << eth::Instruction::SWAP2; CompilerUtils(m_context).computeHashStatic(); - // stack: 32 storage_byte_offset index data_ref + // stack: 32 index data_ref m_context - << eth::Instruction::DUP4 << eth::Instruction::DUP3 + << eth::Instruction::DUP3 << eth::Instruction::DUP3 << eth::Instruction::DIV << eth::Instruction::ADD - // stack: 32 storage_byte_offset index (data_ref + index / 32) - << eth::Instruction::SWAP3 << eth::Instruction::SWAP2 - << eth::Instruction::POP << eth::Instruction::MOD; + // stack: 32 index (data_ref + index / 32) + << eth::Instruction::SWAP2 << eth::Instruction::SWAP1 + << eth::Instruction::MOD; setLValue(_indexAccess); break; case ArrayType::Location::CallData: @@ -811,36 +812,50 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) } else { - // stack: [storage_byte_offset] - if (location == ArrayType::Location::Storage) - //@todo use byte offset, remove it for now - m_context << eth::Instruction::SWAP1 << eth::Instruction::POP; - u256 elementSize = - location == ArrayType::Location::Storage ? - arrayType.getBaseType()->getStorageSize() : - arrayType.getBaseType()->getCalldataEncodedSize(); - solAssert(elementSize != 0, "Invalid element size."); - if (elementSize > 1) - m_context << elementSize << eth::Instruction::MUL; + // stack: + m_context << eth::Instruction::SWAP1; if (arrayType.isDynamicallySized()) { if (location == ArrayType::Location::Storage) - { - m_context << eth::Instruction::SWAP1; CompilerUtils(m_context).computeHashStatic(); - } else if (location == ArrayType::Location::Memory) m_context << u256(32) << eth::Instruction::ADD; } - m_context << eth::Instruction::ADD; + // stack: switch (location) { case ArrayType::Location::CallData: + m_context + << eth::Instruction::SWAP1 << arrayType.getBaseType()->getCalldataEncodedSize() + << eth::Instruction::MUL << eth::Instruction::ADD; if (arrayType.getBaseType()->isValueType()) CompilerUtils(m_context).loadFromMemoryDynamic(*arrayType.getBaseType(), true, true, false); break; case ArrayType::Location::Storage: - m_context << u256(0); // @todo + m_context << eth::Instruction::SWAP1; + if (arrayType.getBaseType()->getStorageBytes() <= 16) + { + // stack: + // goal: + // = <(index % itemsPerSlot) * byteSize> + unsigned byteSize = arrayType.getBaseType()->getStorageBytes(); + unsigned itemsPerSlot = 32 / byteSize; + m_context << u256(itemsPerSlot) << eth::Instruction::SWAP2; + // stack: itemsPerSlot index data_ref + m_context + << eth::Instruction::DUP3 << eth::Instruction::DUP3 + << eth::Instruction::DIV << eth::Instruction::ADD + // stack: itemsPerSlot index (data_ref + index / itemsPerSlot) + << eth::Instruction::SWAP2 << eth::Instruction::SWAP1 + << eth::Instruction::MOD + << u256(byteSize) << eth::Instruction::MUL; + } + else + { + if (arrayType.getBaseType()->getStorageSize() != 1) + m_context << arrayType.getBaseType()->getStorageSize() << eth::Instruction::MUL; + m_context << eth::Instruction::ADD << u256(0); + } setLValueToStorageItem(_indexAccess); break; case ArrayType::Location::Memory: diff --git a/Types.cpp b/Types.cpp index ed3cb8fd1..14ea4c73c 100644 --- a/Types.cpp +++ b/Types.cpp @@ -706,13 +706,21 @@ u256 ArrayType::getStorageSize() const { if (isDynamicallySized()) return 1; - else + + bigint size; + unsigned baseBytes = getBaseType()->getStorageBytes(); + if (baseBytes == 0) + size = 1; + else if (baseBytes < 32) { - bigint size = bigint(getLength()) * getBaseType()->getStorageSize(); - if (size >= bigint(1) << 256) - BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Array too large for storage.")); - return max(1, u256(size)); + unsigned itemsPerSlot = 32 / baseBytes; + size = (bigint(getLength()) + (itemsPerSlot - 1)) / itemsPerSlot; } + else + size = bigint(getLength()) * getBaseType()->getStorageSize(); + if (size >= bigint(1) << 256) + BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Array too large for storage.")); + return max(1, u256(size)); } unsigned ArrayType::getSizeOnStack() const diff --git a/Types.h b/Types.h index c7dbcdeb9..c90aabda1 100644 --- a/Types.h +++ b/Types.h @@ -335,13 +335,22 @@ public: /// Constructor for a byte array ("bytes") explicit ArrayType(Location _location): - m_location(_location), m_isByteArray(true), m_baseType(std::make_shared(8)) {} + m_location(_location), + m_isByteArray(true), + m_baseType(std::make_shared(8)) + {} /// Constructor for a dynamically sized array type ("type[]") ArrayType(Location _location, const TypePointer &_baseType): - m_location(_location), m_baseType(_baseType) {} + m_location(_location), + m_baseType(_baseType) + {} /// Constructor for a fixed-size array type ("type[20]") ArrayType(Location _location, const TypePointer &_baseType, u256 const& _length): - m_location(_location), m_baseType(_baseType), m_hasDynamicLength(false), m_length(_length) {} + m_location(_location), + m_baseType(_baseType), + m_hasDynamicLength(false), + m_length(_length) + {} virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; From 90c519d08f9de494369d122296ae6a9ca0445dad Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 17 Mar 2015 18:34:57 +0100 Subject: [PATCH 40/70] Disallowed special case of bytes0 arrays. --- Types.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Types.cpp b/Types.cpp index 14ea4c73c..a718ee4f9 100644 --- a/Types.cpp +++ b/Types.cpp @@ -184,6 +184,8 @@ TypePointer Type::fromArrayTypeName(TypeName& _baseTypeName, Expression* _length TypePointer baseType = _baseTypeName.toType(); if (!baseType) BOOST_THROW_EXCEPTION(_baseTypeName.createTypeError("Invalid type name.")); + if (baseType->getStorageBytes() == 0) + BOOST_THROW_EXCEPTION(_baseTypeName.createTypeError("Illegal base type of storage size zero for array.")); if (_length) { if (!_length->getType()) From 8e0f4c7db972950215d807bd0e17b7998e22b0db Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Tue, 17 Mar 2015 17:35:18 +0100 Subject: [PATCH 41/70] Explicit conversion between bytes and int of any size - Allowing aforementioned conversion and adding tests for it --- Types.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Types.cpp b/Types.cpp index 04f86b922..4b5dddacd 100644 --- a/Types.cpp +++ b/Types.cpp @@ -536,14 +536,8 @@ bool FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - if (_convertTo.getCategory() == Category::Integer) - { - IntegerType const& convertTo = dynamic_cast(_convertTo); - if (m_bytes * 8 <= convertTo.getNumBits()) - return true; - } - - return _convertTo.getCategory() == Category::Contract || + return _convertTo.getCategory() == Category::Integer || + _convertTo.getCategory() == Category::Contract || _convertTo.getCategory() == getCategory(); } From d1f9e14476e0a05cb5ddd2b10f8fd5e6f618996a Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 18 Mar 2015 15:31:16 +0100 Subject: [PATCH 42/70] Exception-throwing assert. --- Utils.h | 29 ++--------------------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/Utils.h b/Utils.h index 1411f66b4..05c5fa6f0 100644 --- a/Utils.h +++ b/Utils.h @@ -22,34 +22,9 @@ #pragma once -#include -#include - -namespace dev -{ -namespace solidity -{ +#include /// Assertion that throws an InternalCompilerError containing the given description if it is not met. #define solAssert(CONDITION, DESCRIPTION) \ - ::dev::solidity::solAssertAux(CONDITION, DESCRIPTION, __LINE__, __FILE__, ETH_FUNC) + assertThrow(CONDITION, ::dev::solidity::InternalCompilerError, DESCRIPTION) -inline void solAssertAux(bool _condition, std::string const& _errorDescription, unsigned _line, - char const* _file, char const* _function) -{ - if (!_condition) - ::boost::throw_exception( InternalCompilerError() - << errinfo_comment(_errorDescription) - << ::boost::throw_function(_function) - << ::boost::throw_file(_file) - << ::boost::throw_line(_line)); -} - -inline void solAssertAux(void const* _pointer, std::string const& _errorDescription, unsigned _line, - char const* _file, char const* _function) -{ - solAssertAux(_pointer != nullptr, _errorDescription, _line, _file, _function); -} - -} -} From dba9dd1169d0b8a02287434c0499ccc4a16d97bc Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 19 Mar 2015 18:15:16 +0100 Subject: [PATCH 43/70] Byte size checked for zero; coding style. --- ArrayUtils.cpp | 27 ++++++++++++++------------- ArrayUtils.h | 2 +- ExpressionCompiler.cpp | 1 + 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/ArrayUtils.cpp b/ArrayUtils.cpp index a46dea3c8..bbf7f985a 100644 --- a/ArrayUtils.cpp +++ b/ArrayUtils.cpp @@ -440,9 +440,10 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType) const } } -void ArrayUtils::incrementByteOffset(unsigned byteSize, unsigned byteOffsetPosition, unsigned storageOffsetPosition) const +void ArrayUtils::incrementByteOffset(unsigned _byteSize, unsigned _byteOffsetPosition, unsigned _storageOffsetPosition) const { - solAssert(byteSize < 32, ""); + solAssert(_byteSize < 32, ""); + solAssert(_byteSize != 0, ""); // We do the following, but avoiding jumps: // byteOffset += byteSize // if (byteOffset + byteSize > 32) @@ -450,28 +451,28 @@ void ArrayUtils::incrementByteOffset(unsigned byteSize, unsigned byteOffsetPosit // storageOffset++; // byteOffset = 0; // } - if (byteOffsetPosition > 1) - m_context << eth::swapInstruction(byteOffsetPosition - 1); - m_context << u256(byteSize) << eth::Instruction::ADD; - if (byteOffsetPosition > 1) - m_context << eth::swapInstruction(byteOffsetPosition - 1); + if (_byteOffsetPosition > 1) + m_context << eth::swapInstruction(_byteOffsetPosition - 1); + m_context << u256(_byteSize) << eth::Instruction::ADD; + if (_byteOffsetPosition > 1) + m_context << eth::swapInstruction(_byteOffsetPosition - 1); // compute, X := (byteOffset + byteSize - 1) / 32, should be 1 iff byteOffset + bytesize > 32 m_context - << u256(32) << eth::dupInstruction(1 + byteOffsetPosition) << u256(byteSize - 1) + << u256(32) << eth::dupInstruction(1 + _byteOffsetPosition) << u256(_byteSize - 1) << eth::Instruction::ADD << eth::Instruction::DIV; // increment storage offset if X == 1 (just add X to it) // stack: X m_context - << eth::swapInstruction(storageOffsetPosition) << eth::dupInstruction(storageOffsetPosition + 1) - << eth::Instruction::ADD << eth::swapInstruction(storageOffsetPosition); + << eth::swapInstruction(_storageOffsetPosition) << eth::dupInstruction(_storageOffsetPosition + 1) + << eth::Instruction::ADD << eth::swapInstruction(_storageOffsetPosition); // stack: X // set source_byte_offset to zero if X == 1 (using source_byte_offset *= 1 - X) m_context << u256(1) << eth::Instruction::SUB; // stack: 1 - X - if (byteOffsetPosition == 1) + if (_byteOffsetPosition == 1) m_context << eth::Instruction::MUL; else m_context - << eth::dupInstruction(byteOffsetPosition + 1) << eth::Instruction::MUL - << eth::swapInstruction(byteOffsetPosition) << eth::Instruction::POP; + << eth::dupInstruction(_byteOffsetPosition + 1) << eth::Instruction::MUL + << eth::swapInstruction(_byteOffsetPosition) << eth::Instruction::POP; } diff --git a/ArrayUtils.h b/ArrayUtils.h index 6e4ceec1c..22c0646a5 100644 --- a/ArrayUtils.h +++ b/ArrayUtils.h @@ -76,7 +76,7 @@ private: /// the storage offset if adding this number again would increase the counter over 32. /// @param byteOffsetPosition the stack offset of the storage byte offset /// @param storageOffsetPosition the stack offset of the storage slot offset - void incrementByteOffset(unsigned byteSize, unsigned byteOffsetPosition, unsigned storageOffsetPosition) const; + void incrementByteOffset(unsigned _byteSize, unsigned _byteOffsetPosition, unsigned _storageOffsetPosition) const; CompilerContext& m_context; }; diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 144827d44..da762147a 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -839,6 +839,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) // goal: // = <(index % itemsPerSlot) * byteSize> unsigned byteSize = arrayType.getBaseType()->getStorageBytes(); + solAssert(byteSize != 0, ""); unsigned itemsPerSlot = 32 / byteSize; m_context << u256(itemsPerSlot) << eth::Instruction::SWAP2; // stack: itemsPerSlot index data_ref From 139dd3b1d2d8aa3de845cb0bfe04c4ff2bfc113d Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 23 Mar 2015 12:07:57 +0100 Subject: [PATCH 44/70] Removing string as a token. - The string keyword is reserved for future use but should not be a token in the code since it can cause trigger internal compiler assertions. - fixes #1384 --- ExpressionCompiler.h | 1 - Token.h | 1 - 2 files changed, 2 deletions(-) diff --git a/ExpressionCompiler.h b/ExpressionCompiler.h index e6caad747..2577d21b5 100644 --- a/ExpressionCompiler.h +++ b/ExpressionCompiler.h @@ -42,7 +42,6 @@ class CompilerContext; class Type; class IntegerType; class ArrayType; -class StaticStringType; /** * Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream diff --git a/Token.h b/Token.h index b2951e939..6ffbe2194 100644 --- a/Token.h +++ b/Token.h @@ -290,7 +290,6 @@ namespace solidity K(Byte, "byte", 0) \ K(Address, "address", 0) \ K(Bool, "bool", 0) \ - K(StringType, "string", 0) \ K(Real, "real", 0) \ K(UReal, "ureal", 0) \ T(TypesEnd, NULL, 0) /* used as type enum end marker */ \ From 677cefc0e3f89ca94b0712ab1b078f9fc6c412d4 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 23 Mar 2015 13:09:15 +0100 Subject: [PATCH 45/70] Adding keywords for future use section in Token.h --- Token.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Token.h b/Token.h index 6ffbe2194..57d71c1fe 100644 --- a/Token.h +++ b/Token.h @@ -143,7 +143,6 @@ namespace solidity \ /* Keywords */ \ K(Break, "break", 0) \ - K(Case, "case", 0) \ K(Const, "constant", 0) \ K(Anonymous, "anonymous", 0) \ K(Continue, "continue", 0) \ @@ -168,7 +167,6 @@ namespace solidity 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) \ @@ -305,6 +303,13 @@ namespace solidity /* Identifiers (not keywords or future reserved words). */ \ T(Identifier, NULL, 0) \ \ + /* Keywords reserved for future. use*/ \ + T(String, "string", 0) \ + K(Case, "case", 0) \ + K(Switch, "switch", 0) \ + K(Throw, "throw", 0) \ + K(Try, "try", 0) \ + K(Catch, "catch", 0) \ /* Illegal token - not able to scan. */ \ T(Illegal, "ILLEGAL", 0) \ \ From ee1de5d02e60d2422b7b98d2ea82a2d59b1b86fa Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Tue, 24 Mar 2015 19:04:41 +0100 Subject: [PATCH 46/70] Adding some more reserved keywords --- Token.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Token.h b/Token.h index 57d71c1fe..1435dcc57 100644 --- a/Token.h +++ b/Token.h @@ -310,6 +310,9 @@ namespace solidity K(Throw, "throw", 0) \ K(Try, "try", 0) \ K(Catch, "catch", 0) \ + K(Using, "using", 0) \ + K(Type, "type", 0) \ + K(TypeOf, "typeof", 0) \ /* Illegal token - not able to scan. */ \ T(Illegal, "ILLEGAL", 0) \ \ From a69d59ae717f5bef1995c20e46ea94b8acff18c2 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Tue, 17 Mar 2015 14:21:22 +0100 Subject: [PATCH 47/70] added getABIType() to types --- Types.h | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Types.h b/Types.h index c90aabda1..3d3a263d5 100644 --- a/Types.h +++ b/Types.h @@ -187,6 +187,9 @@ public: "for type without literals.")); } + /// Returns address of type for ABI interface + virtual TypePointer ABIType() const { return TypePointer(); } + protected: /// Convenience object used when returning an empty member list. static const MemberList EmptyMemberList; @@ -217,10 +220,12 @@ public: virtual unsigned getStorageBytes() const override { return m_bits / 8; } virtual bool isValueType() const override { return true; } - virtual MemberList const& getMembers() const { return isAddress() ? AddressMemberList : EmptyMemberList; } + virtual MemberList const& getMembers() const override { return isAddress() ? AddressMemberList : EmptyMemberList; } virtual std::string toString() const override; + virtual TypePointer ABIType() const override { return shared_from_this(); } + int getNumBits() const { return m_bits; } bool isAddress() const { return m_modifier == Modifier::Address; } bool isSigned() const { return m_modifier == Modifier::Signed; } @@ -258,6 +263,7 @@ public: virtual std::string toString() const override; virtual u256 literalValue(Literal const* _literal) const override; virtual TypePointer getRealType() const override; + virtual TypePointer ABIType() const override { return shared_from_this(); } /// @returns the smallest integer type that can hold the value or an empty pointer if not possible. std::shared_ptr getIntegerType() const; @@ -311,7 +317,7 @@ public: virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; - virtual unsigned getCalldataEncodedSize(bool _padded) const { return _padded ? 32 : 1; } + virtual unsigned getCalldataEncodedSize(bool _padded) const override{ return _padded ? 32 : 1; } virtual unsigned getStorageBytes() const override { return 1; } virtual bool isValueType() const override { return true; } @@ -361,7 +367,7 @@ public: virtual unsigned getSizeOnStack() const override; virtual std::string toString() const override; virtual MemberList const& getMembers() const override { return s_arrayTypeMemberList; } - + virtual TypePointer ABIType() const override { return (m_baseType->ABIType() ? m_baseType->ABIType() : ABIType()); } Location getLocation() const { return m_location; } bool isByteArray() const { return m_isByteArray; } TypePointer const& getBaseType() const { solAssert(!!m_baseType, ""); return m_baseType;} @@ -400,6 +406,7 @@ public: virtual std::string toString() const override; virtual MemberList const& getMembers() const override; + virtual TypePointer ABIType() const override { return std::make_shared(160, IntegerType::Modifier::Address); } bool isSuper() const { return m_super; } ContractDefinition const& getContractDefinition() const { return m_contract; } @@ -468,6 +475,7 @@ public: virtual bool isValueType() const override { return true; } virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual TypePointer ABIType() const override { return std::make_shared(8 * int(getStorageBytes())); } EnumDefinition const& getEnumDefinition() const { return m_enum; } /// @returns the value that the string has in the Enum From 607f3972349a17b9d06aca0ce733789f16f573c1 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Thu, 19 Mar 2015 16:48:54 +0100 Subject: [PATCH 48/70] added externalType for ArrayType --- Types.cpp | 17 +++++++++++++++++ Types.h | 13 +++++++------ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/Types.cpp b/Types.cpp index 016f0b236..6fe8a6410 100644 --- a/Types.cpp +++ b/Types.cpp @@ -742,6 +742,23 @@ string ArrayType::toString() const return ret + "]"; } +TypePointer ArrayType::externalType() const +{ + if (m_location != Location::CallData) + return TypePointer(); + if (m_isByteArray) + return shared_from_this(); + if (!(m_baseType->externalType())) + return TypePointer(); + if (dynamic_cast(m_baseType.get()) && m_baseType->isDynamicallySized()) + return TypePointer(); + + if (m_baseType->isDynamicallySized()) + return std::make_shared(Location::CallData, m_baseType->externalType()); + else + return std::make_shared(Location::CallData, m_baseType->externalType(), m_length); +} + shared_ptr ArrayType::copyForLocation(ArrayType::Location _location) const { auto copy = make_shared(_location); diff --git a/Types.h b/Types.h index 3d3a263d5..013c65893 100644 --- a/Types.h +++ b/Types.h @@ -188,7 +188,7 @@ public: } /// Returns address of type for ABI interface - virtual TypePointer ABIType() const { return TypePointer(); } + virtual TypePointer externalType() const { return TypePointer(); } protected: /// Convenience object used when returning an empty member list. @@ -224,7 +224,7 @@ public: virtual std::string toString() const override; - virtual TypePointer ABIType() const override { return shared_from_this(); } + virtual TypePointer externalType() const override { return shared_from_this(); } int getNumBits() const { return m_bits; } bool isAddress() const { return m_modifier == Modifier::Address; } @@ -263,7 +263,7 @@ public: virtual std::string toString() const override; virtual u256 literalValue(Literal const* _literal) const override; virtual TypePointer getRealType() const override; - virtual TypePointer ABIType() const override { return shared_from_this(); } + virtual TypePointer externalType() const override { return shared_from_this(); } /// @returns the smallest integer type that can hold the value or an empty pointer if not possible. std::shared_ptr getIntegerType() const; @@ -367,7 +367,8 @@ public: virtual unsigned getSizeOnStack() const override; virtual std::string toString() const override; virtual MemberList const& getMembers() const override { return s_arrayTypeMemberList; } - virtual TypePointer ABIType() const override { return (m_baseType->ABIType() ? m_baseType->ABIType() : ABIType()); } + virtual TypePointer externalType() const override; + Location getLocation() const { return m_location; } bool isByteArray() const { return m_isByteArray; } TypePointer const& getBaseType() const { solAssert(!!m_baseType, ""); return m_baseType;} @@ -406,7 +407,7 @@ public: virtual std::string toString() const override; virtual MemberList const& getMembers() const override; - virtual TypePointer ABIType() const override { return std::make_shared(160, IntegerType::Modifier::Address); } + virtual TypePointer externalType() const override { return std::make_shared(160, IntegerType::Modifier::Address); } bool isSuper() const { return m_super; } ContractDefinition const& getContractDefinition() const { return m_contract; } @@ -475,7 +476,7 @@ public: virtual bool isValueType() const override { return true; } virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer ABIType() const override { return std::make_shared(8 * int(getStorageBytes())); } + virtual TypePointer externalType() const override { return std::make_shared(8 * int(getStorageBytes())); } EnumDefinition const& getEnumDefinition() const { return m_enum; } /// @returns the value that the string has in the Enum From 60204d593e48d2f4395a34c3e7bb010d4f40e513 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Thu, 19 Mar 2015 17:33:10 +0100 Subject: [PATCH 49/70] added check for valid externalType to checkTypeRequirements for function --- AST.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/AST.cpp b/AST.cpp index d05eaf83e..3ecf79eb6 100644 --- a/AST.cpp +++ b/AST.cpp @@ -305,8 +305,12 @@ TypePointer FunctionDefinition::getType(ContractDefinition const*) const void FunctionDefinition::checkTypeRequirements() { for (ASTPointer const& var: getParameters() + getReturnParameters()) + { if (!var->getType()->canLiveOutsideStorage()) BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); + if (!var->getType()->externalType() && getVisibility() >= Visibility::Internal) + BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to have an external address.")); + } for (ASTPointer const& modifier: m_functionModifiers) modifier->checkTypeRequirements(isConstructor() ? dynamic_cast(*getScope()).getBaseContracts() : @@ -653,6 +657,10 @@ void MemberAccess::checkTypeRequirements() if (!m_type) BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found or not " "visible in " + type.toString())); + //todo check for visibility +// else if (!m_type->externalType()) +// BOOST_THROW_EXCEPTION(createTypeError("Type is required to have an external address.")); + // This should probably move somewhere else. if (type.getCategory() == Type::Category::Struct) m_isLValue = true; From 011d95e7e348908b1027dea922a429517cdebf07 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Fri, 20 Mar 2015 13:30:00 +0100 Subject: [PATCH 50/70] - added externalType to BooleanType. - fixed the error message --- AST.cpp | 6 +++--- Types.cpp | 2 ++ Types.h | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/AST.cpp b/AST.cpp index 3ecf79eb6..0827f7941 100644 --- a/AST.cpp +++ b/AST.cpp @@ -308,8 +308,8 @@ void FunctionDefinition::checkTypeRequirements() { if (!var->getType()->canLiveOutsideStorage()) BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); - if (!var->getType()->externalType() && getVisibility() >= Visibility::Internal) - BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to have an external address.")); + if (!var->getType()->externalType() && getVisibility() >= Visibility::Public) + BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for function with external visibility")); } for (ASTPointer const& modifier: m_functionModifiers) modifier->checkTypeRequirements(isConstructor() ? @@ -659,7 +659,7 @@ void MemberAccess::checkTypeRequirements() "visible in " + type.toString())); //todo check for visibility // else if (!m_type->externalType()) -// BOOST_THROW_EXCEPTION(createTypeError("Type is required to have an external address.")); +// BOOST_THROW_EXCEPTION(createTypeError("Internal type not allowed for function with external visibility")); // This should probably move somewhere else. if (type.getCategory() == Type::Category::Struct) diff --git a/Types.cpp b/Types.cpp index 6fe8a6410..82c28a394 100644 --- a/Types.cpp +++ b/Types.cpp @@ -749,7 +749,9 @@ TypePointer ArrayType::externalType() const if (m_isByteArray) return shared_from_this(); if (!(m_baseType->externalType())) + { return TypePointer(); + } if (dynamic_cast(m_baseType.get()) && m_baseType->isDynamicallySized()) return TypePointer(); diff --git a/Types.h b/Types.h index 013c65893..5941646aa 100644 --- a/Types.h +++ b/Types.h @@ -263,7 +263,6 @@ public: virtual std::string toString() const override; virtual u256 literalValue(Literal const* _literal) const override; virtual TypePointer getRealType() const override; - virtual TypePointer externalType() const override { return shared_from_this(); } /// @returns the smallest integer type that can hold the value or an empty pointer if not possible. std::shared_ptr getIntegerType() const; @@ -298,6 +297,7 @@ public: virtual std::string toString() const override { return "bytes" + dev::toString(m_bytes); } virtual u256 literalValue(Literal const* _literal) const override; + virtual TypePointer externalType() const override { return shared_from_this(); } int getNumBytes() const { return m_bytes; } @@ -323,6 +323,7 @@ public: virtual std::string toString() const override { return "bool"; } virtual u256 literalValue(Literal const* _literal) const override; + virtual TypePointer externalType() const override { return shared_from_this(); } }; /** From e3ea90e997caf1d316252ad16ecb14c4d6163da5 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Fri, 20 Mar 2015 17:02:24 +0100 Subject: [PATCH 51/70] added check for events and stat variables --- AST.cpp | 13 +++++++++---- Types.cpp | 8 +++----- Types.h | 3 ++- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/AST.cpp b/AST.cpp index 0827f7941..63f3d772b 100644 --- a/AST.cpp +++ b/AST.cpp @@ -346,8 +346,11 @@ void VariableDeclaration::checkTypeRequirements() if (!m_value) return; if (m_type) + { m_value->expectType(*m_type); - else + if (m_isStateVariable && !m_type->externalType() && getVisibility() >= Visibility::Public) + BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for state variables.")); + } else { // no type declared and no previous assignment, infer the type m_value->checkTypeRequirements(); @@ -426,6 +429,8 @@ void EventDefinition::checkTypeRequirements() numIndexed++; if (!var->getType()->canLiveOutsideStorage()) BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); + if (!var->getType()->externalType() && getVisibility() >= Visibility::Public) + BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for Events")); } if (numIndexed > 3) BOOST_THROW_EXCEPTION(createTypeError("More than 3 indexed arguments for event.")); @@ -657,9 +662,9 @@ void MemberAccess::checkTypeRequirements() if (!m_type) BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found or not " "visible in " + type.toString())); - //todo check for visibility -// else if (!m_type->externalType()) -// BOOST_THROW_EXCEPTION(createTypeError("Internal type not allowed for function with external visibility")); +// if (auto f = dynamic_cast(m_expression->getType().get())) +// if (f->getLocation() == FunctionType::Location::External && !m_type->externalType()) +// BOOST_THROW_EXCEPTION(createTypeError(*m_memberName + " member has an internal type.")); // This should probably move somewhere else. if (type.getCategory() == Type::Category::Struct) diff --git a/Types.cpp b/Types.cpp index 82c28a394..7b4d1de26 100644 --- a/Types.cpp +++ b/Types.cpp @@ -748,14 +748,12 @@ TypePointer ArrayType::externalType() const return TypePointer(); if (m_isByteArray) return shared_from_this(); - if (!(m_baseType->externalType())) - { + if (!m_baseType->externalType()) return TypePointer(); - } - if (dynamic_cast(m_baseType.get()) && m_baseType->isDynamicallySized()) + if (m_baseType->getCategory() == Category::Array && m_baseType->isDynamicallySized()) return TypePointer(); - if (m_baseType->isDynamicallySized()) + if (isDynamicallySized()) return std::make_shared(Location::CallData, m_baseType->externalType()); else return std::make_shared(Location::CallData, m_baseType->externalType(), m_length); diff --git a/Types.h b/Types.h index 5941646aa..17264dc59 100644 --- a/Types.h +++ b/Types.h @@ -187,7 +187,8 @@ public: "for type without literals.")); } - /// Returns address of type for ABI interface + /// @returns a type suitable for outside of Solidity, i.e. for contract types it returns address. + /// If there is no such type, returns an empty shared pointer. virtual TypePointer externalType() const { return TypePointer(); } protected: From 701b34fbeb1bd08445aad55f82672d35343f5a44 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Mon, 23 Mar 2015 18:08:45 +0100 Subject: [PATCH 52/70] renamed getCanonicalSignature added externalTypes instead of types for interface functions added simple test todo testing --- AST.cpp | 11 ++++++----- AST.h | 2 +- ExpressionCompiler.cpp | 2 +- InterfaceHandler.cpp | 4 ++-- Types.cpp | 8 +++++--- Types.h | 4 ++-- 6 files changed, 17 insertions(+), 14 deletions(-) diff --git a/AST.cpp b/AST.cpp index 63f3d772b..2e24d4f9d 100644 --- a/AST.cpp +++ b/AST.cpp @@ -88,7 +88,7 @@ void ContractDefinition::checkTypeRequirements() if (hashes.count(hash)) BOOST_THROW_EXCEPTION(createTypeError( std::string("Function signature hash collision for ") + - it.second->getCanonicalSignature())); + it.second->externalTypes())); hashes.insert(hash); } } @@ -192,7 +192,7 @@ vector, FunctionTypePointer>> const& ContractDefinition::getIn if (functionsSeen.count(f->getName()) == 0 && f->isPartOfExternalInterface()) { functionsSeen.insert(f->getName()); - FixedHash<4> hash(dev::sha3(f->getCanonicalSignature())); + FixedHash<4> hash(dev::sha3(f->externalTypes())); m_interfaceFunctionList->push_back(make_pair(hash, make_shared(*f, false))); } @@ -200,8 +200,9 @@ vector, FunctionTypePointer>> const& ContractDefinition::getIn if (functionsSeen.count(v->getName()) == 0 && v->isPartOfExternalInterface()) { FunctionType ftype(*v); + solAssert(v->getType().get(), ""); functionsSeen.insert(v->getName()); - FixedHash<4> hash(dev::sha3(ftype.getCanonicalSignature(v->getName()))); + FixedHash<4> hash(dev::sha3(ftype.externalTypes(v->getName()))); m_interfaceFunctionList->push_back(make_pair(hash, make_shared(*v))); } } @@ -319,9 +320,9 @@ void FunctionDefinition::checkTypeRequirements() m_body->checkTypeRequirements(); } -string FunctionDefinition::getCanonicalSignature() const +string FunctionDefinition::externalTypes() const { - return FunctionType(*this).getCanonicalSignature(getName()); + return FunctionType(*this).externalTypes(getName()); } bool VariableDeclaration::isLValue() const diff --git a/AST.h b/AST.h index 335b9a880..c5cd2e5b1 100644 --- a/AST.h +++ b/AST.h @@ -424,7 +424,7 @@ public: /// @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; + std::string externalTypes() const; private: bool m_isConstructor; diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index da762147a..288af3983 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -544,7 +544,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } if (!event.isAnonymous()) { - m_context << u256(h256::Arith(dev::sha3(function.getCanonicalSignature(event.getName())))); + m_context << u256(h256::Arith(dev::sha3(function.externalTypes(event.getName())))); ++numIndexed; } solAssert(numIndexed <= 4, "Too many indexed arguments."); diff --git a/InterfaceHandler.cpp b/InterfaceHandler.cpp index 406d1e24a..e09e55cbc 100644 --- a/InterfaceHandler.cpp +++ b/InterfaceHandler.cpp @@ -129,7 +129,7 @@ std::unique_ptr InterfaceHandler::getUserDocumentation(ContractDefi if (!m_notice.empty()) {// since @notice is the only user tag if missing function should not appear user["notice"] = Json::Value(m_notice); - methods[it.second->getCanonicalSignature()] = user; + methods[it.second->externalTypes()] = user; } } } @@ -185,7 +185,7 @@ std::unique_ptr InterfaceHandler::getDevDocumentation(ContractDefin method["return"] = m_return; if (!method.empty()) // add the function, only if we have any documentation to add - methods[it.second->getCanonicalSignature()] = method; + methods[it.second->externalTypes()] = method; } } doc["methods"] = methods; diff --git a/Types.cpp b/Types.cpp index 7b4d1de26..28a3af33a 100644 --- a/Types.cpp +++ b/Types.cpp @@ -1127,7 +1127,7 @@ MemberList const& FunctionType::getMembers() const } } -string FunctionType::getCanonicalSignature(std::string const& _name) const +string FunctionType::externalTypes(std::string const& _name) const { std::string funcName = _name; if (_name == "") @@ -1138,8 +1138,10 @@ string FunctionType::getCanonicalSignature(std::string const& _name) const string ret = funcName + "("; for (auto it = m_parameterTypes.cbegin(); it != m_parameterTypes.cend(); ++it) - ret += (*it)->toString() + (it + 1 == m_parameterTypes.cend() ? "" : ","); - + { + solAssert(!!(*it)->externalType(), "Parameter should have external type"); + ret += (*it)->externalType()->toString() + (it + 1 == m_parameterTypes.cend() ? "" : ","); + } return ret + ")"; } diff --git a/Types.h b/Types.h index 17264dc59..599d80cc6 100644 --- a/Types.h +++ b/Types.h @@ -550,10 +550,10 @@ public: 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 + /// @returns the external type 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; + std::string externalTypes(std::string const& _name = "") const; Declaration const& getDeclaration() const { solAssert(m_declaration, "Requested declaration from a FunctionType that has none"); From 9986b072ad471908df4402ef860e58e331035948 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Tue, 24 Mar 2015 11:11:27 +0100 Subject: [PATCH 53/70] renamed externalTypes to externalSignature --- AST.cpp | 12 ++++++------ AST.h | 6 +++--- ExpressionCompiler.cpp | 2 +- InterfaceHandler.cpp | 4 ++-- Types.cpp | 2 +- Types.h | 4 ++-- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/AST.cpp b/AST.cpp index 2e24d4f9d..9a2c1be07 100644 --- a/AST.cpp +++ b/AST.cpp @@ -88,7 +88,7 @@ void ContractDefinition::checkTypeRequirements() if (hashes.count(hash)) BOOST_THROW_EXCEPTION(createTypeError( std::string("Function signature hash collision for ") + - it.second->externalTypes())); + it.second->externalSignature())); hashes.insert(hash); } } @@ -192,7 +192,7 @@ vector, FunctionTypePointer>> const& ContractDefinition::getIn if (functionsSeen.count(f->getName()) == 0 && f->isPartOfExternalInterface()) { functionsSeen.insert(f->getName()); - FixedHash<4> hash(dev::sha3(f->externalTypes())); + FixedHash<4> hash(dev::sha3(f->externalSignature())); m_interfaceFunctionList->push_back(make_pair(hash, make_shared(*f, false))); } @@ -202,7 +202,7 @@ vector, FunctionTypePointer>> const& ContractDefinition::getIn FunctionType ftype(*v); solAssert(v->getType().get(), ""); functionsSeen.insert(v->getName()); - FixedHash<4> hash(dev::sha3(ftype.externalTypes(v->getName()))); + FixedHash<4> hash(dev::sha3(ftype.externalSignature(v->getName()))); m_interfaceFunctionList->push_back(make_pair(hash, make_shared(*v))); } } @@ -320,9 +320,9 @@ void FunctionDefinition::checkTypeRequirements() m_body->checkTypeRequirements(); } -string FunctionDefinition::externalTypes() const +string FunctionDefinition::externalSignature() const { - return FunctionType(*this).externalTypes(getName()); + return FunctionType(*this).externalSignature(getName()); } bool VariableDeclaration::isLValue() const @@ -430,7 +430,7 @@ void EventDefinition::checkTypeRequirements() numIndexed++; if (!var->getType()->canLiveOutsideStorage()) BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); - if (!var->getType()->externalType() && getVisibility() >= Visibility::Public) + if (!var->getType()->externalType()) BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for Events")); } if (numIndexed > 3) diff --git a/AST.h b/AST.h index c5cd2e5b1..937c2ceab 100644 --- a/AST.h +++ b/AST.h @@ -421,10 +421,10 @@ public: /// 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 + /// @returns the external signature of the function + /// That consists of the name of the function followed by the external types of the /// arguments separated by commas all enclosed in parentheses without any spaces. - std::string externalTypes() const; + std::string externalSignature() const; private: bool m_isConstructor; diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 288af3983..90568767b 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -544,7 +544,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } if (!event.isAnonymous()) { - m_context << u256(h256::Arith(dev::sha3(function.externalTypes(event.getName())))); + m_context << u256(h256::Arith(dev::sha3(function.externalSignature(event.getName())))); ++numIndexed; } solAssert(numIndexed <= 4, "Too many indexed arguments."); diff --git a/InterfaceHandler.cpp b/InterfaceHandler.cpp index e09e55cbc..2f35a96f6 100644 --- a/InterfaceHandler.cpp +++ b/InterfaceHandler.cpp @@ -129,7 +129,7 @@ std::unique_ptr InterfaceHandler::getUserDocumentation(ContractDefi if (!m_notice.empty()) {// since @notice is the only user tag if missing function should not appear user["notice"] = Json::Value(m_notice); - methods[it.second->externalTypes()] = user; + methods[it.second->externalSignature()] = user; } } } @@ -185,7 +185,7 @@ std::unique_ptr InterfaceHandler::getDevDocumentation(ContractDefin method["return"] = m_return; if (!method.empty()) // add the function, only if we have any documentation to add - methods[it.second->externalTypes()] = method; + methods[it.second->externalSignature()] = method; } } doc["methods"] = methods; diff --git a/Types.cpp b/Types.cpp index 28a3af33a..6344d128d 100644 --- a/Types.cpp +++ b/Types.cpp @@ -1127,7 +1127,7 @@ MemberList const& FunctionType::getMembers() const } } -string FunctionType::externalTypes(std::string const& _name) const +string FunctionType::externalSignature(std::string const& _name) const { std::string funcName = _name; if (_name == "") diff --git a/Types.h b/Types.h index 599d80cc6..e00e6b983 100644 --- a/Types.h +++ b/Types.h @@ -550,10 +550,10 @@ public: virtual MemberList const& getMembers() const override; Location const& getLocation() const { return m_location; } - /// @returns the external type of this function type given the function name + /// @returns the external 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 externalTypes(std::string const& _name = "") const; + std::string externalSignature(std::string const& _name = "") const; Declaration const& getDeclaration() const { solAssert(m_declaration, "Requested declaration from a FunctionType that has none"); From 8f747aab0f977e62c344f57fe922ba6b0fda3a64 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Wed, 25 Mar 2015 13:58:15 +0100 Subject: [PATCH 54/70] tests for external types --- AST.cpp | 14 +++++--------- AST.h | 4 ++-- ExpressionCompiler.cpp | 2 +- Types.cpp | 7 ++++--- Types.h | 3 ++- 5 files changed, 14 insertions(+), 16 deletions(-) diff --git a/AST.cpp b/AST.cpp index 9a2c1be07..9e81c2cb2 100644 --- a/AST.cpp +++ b/AST.cpp @@ -192,7 +192,7 @@ vector, FunctionTypePointer>> const& ContractDefinition::getIn if (functionsSeen.count(f->getName()) == 0 && f->isPartOfExternalInterface()) { functionsSeen.insert(f->getName()); - FixedHash<4> hash(dev::sha3(f->externalSignature())); + FixedHash<4> hash(dev::sha3(f->externalSignature(true))); m_interfaceFunctionList->push_back(make_pair(hash, make_shared(*f, false))); } @@ -202,7 +202,7 @@ vector, FunctionTypePointer>> const& ContractDefinition::getIn FunctionType ftype(*v); solAssert(v->getType().get(), ""); functionsSeen.insert(v->getName()); - FixedHash<4> hash(dev::sha3(ftype.externalSignature(v->getName()))); + FixedHash<4> hash(dev::sha3(ftype.externalSignature(true, v->getName()))); m_interfaceFunctionList->push_back(make_pair(hash, make_shared(*v))); } } @@ -309,7 +309,7 @@ void FunctionDefinition::checkTypeRequirements() { if (!var->getType()->canLiveOutsideStorage()) BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); - if (!var->getType()->externalType() && getVisibility() >= Visibility::Public) + if (!(var->getType()->externalType()) && getVisibility() >= Visibility::Public) BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for function with external visibility")); } for (ASTPointer const& modifier: m_functionModifiers) @@ -320,9 +320,9 @@ void FunctionDefinition::checkTypeRequirements() m_body->checkTypeRequirements(); } -string FunctionDefinition::externalSignature() const +string FunctionDefinition::externalSignature(bool isExternalCall) const { - return FunctionType(*this).externalSignature(getName()); + return FunctionType(*this).externalSignature(isExternalCall, getName()); } bool VariableDeclaration::isLValue() const @@ -663,10 +663,6 @@ void MemberAccess::checkTypeRequirements() if (!m_type) BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found or not " "visible in " + type.toString())); -// if (auto f = dynamic_cast(m_expression->getType().get())) -// if (f->getLocation() == FunctionType::Location::External && !m_type->externalType()) -// BOOST_THROW_EXCEPTION(createTypeError(*m_memberName + " member has an internal type.")); - // This should probably move somewhere else. if (type.getCategory() == Type::Category::Struct) m_isLValue = true; diff --git a/AST.h b/AST.h index 937c2ceab..0d3ef857a 100644 --- a/AST.h +++ b/AST.h @@ -422,9 +422,9 @@ public: void checkTypeRequirements(); /// @returns the external signature of the function - /// That consists of the name of the function followed by the external types of the + /// That consists of the name of the function followed by the types(external types if isExternalCall) of the /// arguments separated by commas all enclosed in parentheses without any spaces. - std::string externalSignature() const; + std::string externalSignature(bool isExternalCall = false) const; private: bool m_isConstructor; diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 90568767b..ddc347e68 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -544,7 +544,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } if (!event.isAnonymous()) { - m_context << u256(h256::Arith(dev::sha3(function.externalSignature(event.getName())))); + m_context << u256(h256::Arith(dev::sha3(function.externalSignature(false, event.getName())))); ++numIndexed; } solAssert(numIndexed <= 4, "Too many indexed arguments."); diff --git a/Types.cpp b/Types.cpp index 6344d128d..1776413a0 100644 --- a/Types.cpp +++ b/Types.cpp @@ -1127,7 +1127,7 @@ MemberList const& FunctionType::getMembers() const } } -string FunctionType::externalSignature(std::string const& _name) const +string FunctionType::externalSignature(bool isExternalCall, std::string const& _name) const { std::string funcName = _name; if (_name == "") @@ -1139,8 +1139,9 @@ string FunctionType::externalSignature(std::string const& _name) const for (auto it = m_parameterTypes.cbegin(); it != m_parameterTypes.cend(); ++it) { - solAssert(!!(*it)->externalType(), "Parameter should have external type"); - ret += (*it)->externalType()->toString() + (it + 1 == m_parameterTypes.cend() ? "" : ","); + if (isExternalCall) + solAssert(!!(*it)->externalType(), "Parameter should have external type"); + ret += (isExternalCall ? (*it)->externalType()->toString() : (*it)->toString()) + (it + 1 == m_parameterTypes.cend() ? "" : ","); } return ret + ")"; } diff --git a/Types.h b/Types.h index e00e6b983..c075ff07f 100644 --- a/Types.h +++ b/Types.h @@ -553,7 +553,8 @@ public: /// @returns the external 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 externalSignature(std::string const& _name = "") const; + /// @a isExternalCall shows if it is external function call + std::string externalSignature(bool isExternalCall = false, std::string const& _name = "") const; Declaration const& getDeclaration() const { solAssert(m_declaration, "Requested declaration from a FunctionType that has none"); From 9d6d7ccab86da43da03b79f7b74bf7146c0c0b14 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 20 Mar 2015 17:50:26 +0100 Subject: [PATCH 55/70] Parsing of not fully implemented functions - Adding the possibility of omitting a function body by simply ending a function definition with a semicolon - Such a function is marked as not fully implemented and any contract that contains such a function is considered a not fully implemented contract --- AST.cpp | 4 +-- AST.h | 85 +++++++++++++++++++++++++++++++++------------------- AST_accept.h | 6 ++-- Parser.cpp | 33 ++++++++++++++++---- 4 files changed, 89 insertions(+), 39 deletions(-) diff --git a/AST.cpp b/AST.cpp index d05eaf83e..71af3286f 100644 --- a/AST.cpp +++ b/AST.cpp @@ -311,8 +311,8 @@ void FunctionDefinition::checkTypeRequirements() modifier->checkTypeRequirements(isConstructor() ? dynamic_cast(*getScope()).getBaseContracts() : vector>()); - - m_body->checkTypeRequirements(); + if (m_body) + m_body->checkTypeRequirements(); } string FunctionDefinition::getCanonicalSignature() const diff --git a/AST.h b/AST.h index 335b9a880..fa0172b10 100644 --- a/AST.h +++ b/AST.h @@ -196,6 +196,21 @@ protected: ASTPointer m_documentation; }; +/** + * Abstract class that is added to AST nodes that can be marked as not being fully implemented + */ +class ImplementationOptional +{ +public: + explicit ImplementationOptional(bool _implemented): m_implemented(_implemented) {} + + /// @return whether this node is fully implemented or not + bool isFullyImplemented() const { return m_implemented; } + +protected: + bool m_implemented; +}; + /// @} /** @@ -203,20 +218,25 @@ protected: * document order. It first visits all struct declarations, then all variable declarations and * finally all function declarations. */ -class ContractDefinition: public Declaration, public Documented +class ContractDefinition: public Declaration, public Documented, public ImplementationOptional { public: - ContractDefinition(SourceLocation const& _location, - ASTPointer const& _name, - ASTPointer const& _documentation, - std::vector> const& _baseContracts, - std::vector> const& _definedStructs, - std::vector> const& _definedEnums, - std::vector> const& _stateVariables, - std::vector> const& _definedFunctions, - std::vector> const& _functionModifiers, - std::vector> const& _events): - Declaration(_location, _name), Documented(_documentation), + ContractDefinition( + SourceLocation const& _location, + ASTPointer const& _name, + ASTPointer const& _documentation, + std::vector> const& _baseContracts, + std::vector> const& _definedStructs, + std::vector> const& _definedEnums, + std::vector> const& _stateVariables, + std::vector> const& _definedFunctions, + std::vector> const& _functionModifiers, + std::vector> const& _events, + bool _isFullyImplemented + ): + Declaration(_location, _name), + Documented(_documentation), + ImplementationOptional(_isFullyImplemented), m_baseContracts(_baseContracts), m_definedStructs(_definedStructs), m_definedEnums(_definedEnums), @@ -224,7 +244,7 @@ public: m_definedFunctions(_definedFunctions), m_functionModifiers(_functionModifiers), m_events(_events) - {} + {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; @@ -378,25 +398,30 @@ private: std::vector> m_parameters; }; -class FunctionDefinition: public Declaration, public VariableScope, public Documented +class FunctionDefinition: public Declaration, public VariableScope, public Documented, public ImplementationOptional { public: - FunctionDefinition(SourceLocation const& _location, ASTPointer const& _name, - Declaration::Visibility _visibility, bool _isConstructor, - ASTPointer const& _documentation, - ASTPointer const& _parameters, - bool _isDeclaredConst, - std::vector> const& _modifiers, - ASTPointer const& _returnParameters, - ASTPointer const& _body): - Declaration(_location, _name, _visibility), Documented(_documentation), - m_isConstructor(_isConstructor), - m_parameters(_parameters), - m_isDeclaredConst(_isDeclaredConst), - m_functionModifiers(_modifiers), - m_returnParameters(_returnParameters), - m_body(_body) - {} + FunctionDefinition( + SourceLocation const& _location, + ASTPointer const& _name, + Declaration::Visibility _visibility, bool _isConstructor, + ASTPointer const& _documentation, + ASTPointer const& _parameters, + bool _isDeclaredConst, + std::vector> const& _modifiers, + ASTPointer const& _returnParameters, + ASTPointer const& _body + ): + Declaration(_location, _name, _visibility), + Documented(_documentation), + ImplementationOptional(_body != nullptr), + m_isConstructor(_isConstructor), + m_parameters(_parameters), + m_isDeclaredConst(_isDeclaredConst), + m_functionModifiers(_modifiers), + m_returnParameters(_returnParameters), + m_body(_body) + {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; diff --git a/AST_accept.h b/AST_accept.h index 81ede8fc9..3557f8779 100644 --- a/AST_accept.h +++ b/AST_accept.h @@ -175,7 +175,8 @@ void FunctionDefinition::accept(ASTVisitor& _visitor) if (m_returnParameters) m_returnParameters->accept(_visitor); listAccept(m_functionModifiers, _visitor); - m_body->accept(_visitor); + if (m_body) + m_body->accept(_visitor); } _visitor.endVisit(*this); } @@ -188,7 +189,8 @@ void FunctionDefinition::accept(ASTConstVisitor& _visitor) const if (m_returnParameters) m_returnParameters->accept(_visitor); listAccept(m_functionModifiers, _visitor); - m_body->accept(_visitor); + if (m_body) + m_body->accept(_visitor); } _visitor.endVisit(*this); } diff --git a/Parser.cpp b/Parser.cpp index 393d2734e..3dfedf6d2 100644 --- a/Parser.cpp +++ b/Parser.cpp @@ -116,6 +116,7 @@ ASTPointer Parser::parseContractDefinition() { ASTNodeFactory nodeFactory(*this); ASTPointer docString; + bool contractFullyImplemented = true; if (m_scanner->getCurrentCommentLiteral() != "") docString = make_shared(m_scanner->getCurrentCommentLiteral()); expectToken(Token::Contract); @@ -141,7 +142,12 @@ ASTPointer Parser::parseContractDefinition() if (currentToken == Token::RBrace) break; else if (currentToken == Token::Function) - functions.push_back(parseFunctionDefinition(name.get())); + { + ASTPointer func = parseFunctionDefinition(name.get()); + functions.push_back(func); + if (!func->isFullyImplemented()) + contractFullyImplemented = false; + } else if (currentToken == Token::Struct) structs.push_back(parseStructDefinition()); else if (currentToken == Token::Enum) @@ -164,8 +170,18 @@ ASTPointer Parser::parseContractDefinition() } nodeFactory.markEndPosition(); expectToken(Token::RBrace); - return nodeFactory.createNode(name, docString, baseContracts, structs, enums, - stateVariables, functions, modifiers, events); + return nodeFactory.createNode( + name, + docString, + baseContracts, + structs, + enums, + stateVariables, + functions, + modifiers, + events, + contractFullyImplemented + ); } ASTPointer Parser::parseInheritanceSpecifier() @@ -247,8 +263,15 @@ ASTPointer Parser::parseFunctionDefinition(ASTString const* } else returnParameters = createEmptyParameterList(); - ASTPointer block = parseBlock(); - nodeFactory.setEndPositionFromNode(block); + ASTPointer block = ASTPointer(); + nodeFactory.markEndPosition(); + if (m_scanner->getCurrentToken() != Token::Semicolon) + { + block = parseBlock(); + nodeFactory.setEndPositionFromNode(block); + } + else + m_scanner->next(); // just consume the ';' bool const c_isConstructor = (_contractName && *name == *_contractName); return nodeFactory.createNode(name, visibility, c_isConstructor, docstring, parameters, isDeclaredConst, modifiers, From 6b50386763142c0619e58e511a57bbaab0867bdc Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 25 Mar 2015 18:33:20 +0100 Subject: [PATCH 56/70] Clean up some code redundancy. Merge branch 'develop' of github.com:ethereum/cpp-ethereum into develop --- ExpressionCompiler.h | 1 - Token.h | 13 ++++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/ExpressionCompiler.h b/ExpressionCompiler.h index e6caad747..2577d21b5 100644 --- a/ExpressionCompiler.h +++ b/ExpressionCompiler.h @@ -42,7 +42,6 @@ class CompilerContext; class Type; class IntegerType; class ArrayType; -class StaticStringType; /** * Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream diff --git a/Token.h b/Token.h index b2951e939..1435dcc57 100644 --- a/Token.h +++ b/Token.h @@ -143,7 +143,6 @@ namespace solidity \ /* Keywords */ \ K(Break, "break", 0) \ - K(Case, "case", 0) \ K(Const, "constant", 0) \ K(Anonymous, "anonymous", 0) \ K(Continue, "continue", 0) \ @@ -168,7 +167,6 @@ namespace solidity 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) \ @@ -290,7 +288,6 @@ namespace solidity K(Byte, "byte", 0) \ K(Address, "address", 0) \ K(Bool, "bool", 0) \ - K(StringType, "string", 0) \ K(Real, "real", 0) \ K(UReal, "ureal", 0) \ T(TypesEnd, NULL, 0) /* used as type enum end marker */ \ @@ -306,6 +303,16 @@ namespace solidity /* Identifiers (not keywords or future reserved words). */ \ T(Identifier, NULL, 0) \ \ + /* Keywords reserved for future. use*/ \ + T(String, "string", 0) \ + K(Case, "case", 0) \ + K(Switch, "switch", 0) \ + K(Throw, "throw", 0) \ + K(Try, "try", 0) \ + K(Catch, "catch", 0) \ + K(Using, "using", 0) \ + K(Type, "type", 0) \ + K(TypeOf, "typeof", 0) \ /* Illegal token - not able to scan. */ \ T(Illegal, "ILLEGAL", 0) \ \ From b1ca27ea93907dc500d0b84298f5d63a9d4a7c1d Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Thu, 26 Mar 2015 14:11:24 +0100 Subject: [PATCH 57/70] two more tests style fixes --- AST.cpp | 2 +- AST.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AST.cpp b/AST.cpp index 9e81c2cb2..461c3d0ca 100644 --- a/AST.cpp +++ b/AST.cpp @@ -431,7 +431,7 @@ void EventDefinition::checkTypeRequirements() if (!var->getType()->canLiveOutsideStorage()) BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); if (!var->getType()->externalType()) - BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for Events")); + BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed as event parameter type.")); } if (numIndexed > 3) BOOST_THROW_EXCEPTION(createTypeError("More than 3 indexed arguments for event.")); diff --git a/AST.h b/AST.h index 0d3ef857a..abcae83c5 100644 --- a/AST.h +++ b/AST.h @@ -422,7 +422,7 @@ public: void checkTypeRequirements(); /// @returns the external signature of the function - /// That consists of the name of the function followed by the types(external types if isExternalCall) of the + /// That consists of the name of the function followed by the types (external types if isExternalCall) of the /// arguments separated by commas all enclosed in parentheses without any spaces. std::string externalSignature(bool isExternalCall = false) const; From a7e78fadf5d8fd4d1d4300a4d8064d9bf51af687 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 27 Mar 2015 11:53:17 +0100 Subject: [PATCH 58/70] Moving contract fullyImplemented check to TypeRequirements --- AST.cpp | 4 ++++ AST.h | 10 +++++----- Parser.cpp | 6 +----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/AST.cpp b/AST.cpp index 71af3286f..52c58170d 100644 --- a/AST.cpp +++ b/AST.cpp @@ -60,6 +60,7 @@ void ContractDefinition::checkTypeRequirements() FunctionDefinition const* fallbackFunction = nullptr; for (ASTPointer const& function: getDefinedFunctions()) + { if (function->getName().empty()) { if (fallbackFunction) @@ -71,6 +72,9 @@ void ContractDefinition::checkTypeRequirements() BOOST_THROW_EXCEPTION(fallbackFunction->getParameterList().createTypeError("Fallback function cannot take parameters.")); } } + if (!function->isFullyImplemented()) + setFullyImplemented(false); + } for (ASTPointer const& modifier: getFunctionModifiers()) modifier->checkTypeRequirements(); diff --git a/AST.h b/AST.h index fa0172b10..c1da90cdb 100644 --- a/AST.h +++ b/AST.h @@ -206,6 +206,7 @@ public: /// @return whether this node is fully implemented or not bool isFullyImplemented() const { return m_implemented; } + void setFullyImplemented(bool _implemented) { m_implemented = _implemented; } protected: bool m_implemented; @@ -231,12 +232,11 @@ public: std::vector> const& _stateVariables, std::vector> const& _definedFunctions, std::vector> const& _functionModifiers, - std::vector> const& _events, - bool _isFullyImplemented + std::vector> const& _events ): Declaration(_location, _name), Documented(_documentation), - ImplementationOptional(_isFullyImplemented), + ImplementationOptional(true), m_baseContracts(_baseContracts), m_definedStructs(_definedStructs), m_definedEnums(_definedEnums), @@ -244,7 +244,7 @@ public: m_definedFunctions(_definedFunctions), m_functionModifiers(_functionModifiers), m_events(_events) - {} + {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; @@ -421,7 +421,7 @@ public: m_functionModifiers(_modifiers), m_returnParameters(_returnParameters), m_body(_body) - {} + {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; diff --git a/Parser.cpp b/Parser.cpp index 3dfedf6d2..0f1b34069 100644 --- a/Parser.cpp +++ b/Parser.cpp @@ -116,7 +116,6 @@ ASTPointer Parser::parseContractDefinition() { ASTNodeFactory nodeFactory(*this); ASTPointer docString; - bool contractFullyImplemented = true; if (m_scanner->getCurrentCommentLiteral() != "") docString = make_shared(m_scanner->getCurrentCommentLiteral()); expectToken(Token::Contract); @@ -145,8 +144,6 @@ ASTPointer Parser::parseContractDefinition() { ASTPointer func = parseFunctionDefinition(name.get()); functions.push_back(func); - if (!func->isFullyImplemented()) - contractFullyImplemented = false; } else if (currentToken == Token::Struct) structs.push_back(parseStructDefinition()); @@ -179,8 +176,7 @@ ASTPointer Parser::parseContractDefinition() stateVariables, functions, modifiers, - events, - contractFullyImplemented + events ); } From a3d829d074859d748f1fd392acb8b5883f646e61 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Fri, 27 Mar 2015 13:28:32 +0100 Subject: [PATCH 59/70] added externalTypes function to functionType removed flag for externalSigniture --- AST.cpp | 8 ++++---- AST.h | 4 ++-- ExpressionCompiler.cpp | 2 +- Types.cpp | 24 +++++++++++++++++++----- Types.h | 6 ++++-- 5 files changed, 30 insertions(+), 14 deletions(-) diff --git a/AST.cpp b/AST.cpp index 461c3d0ca..1736469eb 100644 --- a/AST.cpp +++ b/AST.cpp @@ -192,7 +192,7 @@ vector, FunctionTypePointer>> const& ContractDefinition::getIn if (functionsSeen.count(f->getName()) == 0 && f->isPartOfExternalInterface()) { functionsSeen.insert(f->getName()); - FixedHash<4> hash(dev::sha3(f->externalSignature(true))); + FixedHash<4> hash(dev::sha3(f->externalSignature())); m_interfaceFunctionList->push_back(make_pair(hash, make_shared(*f, false))); } @@ -202,7 +202,7 @@ vector, FunctionTypePointer>> const& ContractDefinition::getIn FunctionType ftype(*v); solAssert(v->getType().get(), ""); functionsSeen.insert(v->getName()); - FixedHash<4> hash(dev::sha3(ftype.externalSignature(true, v->getName()))); + FixedHash<4> hash(dev::sha3(ftype.externalSignature(v->getName()))); m_interfaceFunctionList->push_back(make_pair(hash, make_shared(*v))); } } @@ -320,9 +320,9 @@ void FunctionDefinition::checkTypeRequirements() m_body->checkTypeRequirements(); } -string FunctionDefinition::externalSignature(bool isExternalCall) const +string FunctionDefinition::externalSignature() const { - return FunctionType(*this).externalSignature(isExternalCall, getName()); + return FunctionType(*this).externalSignature(getName()); } bool VariableDeclaration::isLValue() const diff --git a/AST.h b/AST.h index abcae83c5..00ad33073 100644 --- a/AST.h +++ b/AST.h @@ -422,9 +422,9 @@ public: void checkTypeRequirements(); /// @returns the external signature of the function - /// That consists of the name of the function followed by the types (external types if isExternalCall) of the + /// 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 externalSignature(bool isExternalCall = false) const; + std::string externalSignature() const; private: bool m_isConstructor; diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index ddc347e68..90568767b 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -544,7 +544,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } if (!event.isAnonymous()) { - m_context << u256(h256::Arith(dev::sha3(function.externalSignature(false, event.getName())))); + m_context << u256(h256::Arith(dev::sha3(function.externalSignature(event.getName())))); ++numIndexed; } solAssert(numIndexed <= 4, "Too many indexed arguments."); diff --git a/Types.cpp b/Types.cpp index 1776413a0..5fd7d24a6 100644 --- a/Types.cpp +++ b/Types.cpp @@ -1098,6 +1098,19 @@ unsigned FunctionType::getSizeOnStack() const return size; } +TypePointer FunctionType::externalType() const +{ + TypePointers paramTypes; + TypePointers retParamTypes; + + for (auto it = m_parameterTypes.cbegin(); it != m_parameterTypes.cend(); ++it) + paramTypes.push_back((*it)->externalType()); + for (auto it = m_returnParameterTypes.cbegin(); it != m_returnParameterTypes.cend(); ++it) + retParamTypes.push_back((*it)->externalType()); + + return make_shared(paramTypes, retParamTypes, m_location, m_arbitraryParameters); +} + MemberList const& FunctionType::getMembers() const { switch (m_location) @@ -1127,7 +1140,7 @@ MemberList const& FunctionType::getMembers() const } } -string FunctionType::externalSignature(bool isExternalCall, std::string const& _name) const +string FunctionType::externalSignature(std::string const& _name) const { std::string funcName = _name; if (_name == "") @@ -1137,12 +1150,13 @@ string FunctionType::externalSignature(bool isExternalCall, std::string const& _ } string ret = funcName + "("; - for (auto it = m_parameterTypes.cbegin(); it != m_parameterTypes.cend(); ++it) + TypePointers externalParameterTypes = dynamic_cast(*externalType()).getParameterTypes(); + for (auto it = externalParameterTypes.cbegin(); it != externalParameterTypes.cend(); ++it) { - if (isExternalCall) - solAssert(!!(*it)->externalType(), "Parameter should have external type"); - ret += (isExternalCall ? (*it)->externalType()->toString() : (*it)->toString()) + (it + 1 == m_parameterTypes.cend() ? "" : ","); + solAssert(!!(*it), "Parameter should have external type"); + ret += (*it)->toString() + (it + 1 == externalParameterTypes.cend() ? "" : ","); } + return ret + ")"; } diff --git a/Types.h b/Types.h index c075ff07f..99fd878f0 100644 --- a/Types.h +++ b/Types.h @@ -511,6 +511,9 @@ public: Bare }; virtual Category getCategory() const override { return Category::Function; } + + virtual TypePointer externalType() const override; + explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); explicit FunctionType(VariableDeclaration const& _varDecl); explicit FunctionType(EventDefinition const& _event); @@ -553,8 +556,7 @@ public: /// @returns the external 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 - /// @a isExternalCall shows if it is external function call - std::string externalSignature(bool isExternalCall = false, std::string const& _name = "") const; + std::string externalSignature(std::string const& _name = "") const; Declaration const& getDeclaration() const { solAssert(m_declaration, "Requested declaration from a FunctionType that has none"); From 85bb056993ccb111b7e32e28475b906f75b77aa6 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 27 Mar 2015 15:15:34 +0100 Subject: [PATCH 60/70] Abstract contract and inheritance - Checking the linearized base contracts for abstract functions and handle their existence appropriately - If a contract is abstract it can't be created with new - An abstract contract is not compiled (no backend code is generated) - Of course tests --- AST.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ AST.h | 1 + CompilerStack.cpp | 2 ++ Parser.cpp | 5 +---- 4 files changed, 44 insertions(+), 4 deletions(-) diff --git a/AST.cpp b/AST.cpp index 52c58170d..2ada73627 100644 --- a/AST.cpp +++ b/AST.cpp @@ -21,6 +21,7 @@ */ #include +#include #include #include #include @@ -52,6 +53,7 @@ void ContractDefinition::checkTypeRequirements() baseSpecifier->checkTypeRequirements(); checkIllegalOverrides(); + checkAbstractFunctions(); FunctionDefinition const* constructor = getConstructor(); if (constructor && !constructor->getReturnParameters().empty()) @@ -128,6 +130,42 @@ FunctionDefinition const* ContractDefinition::getFallbackFunction() const return nullptr; } +void ContractDefinition::checkAbstractFunctions() +{ + map functions; + + // Search from base to derived + for (ContractDefinition const* contract: boost::adaptors::reverse(getLinearizedBaseContracts())) + { + for (ASTPointer const& function: contract->getDefinedFunctions()) + { + string const& name = function->getName(); + if (!function->isFullyImplemented() && functions.count(name) && functions[name]) + BOOST_THROW_EXCEPTION(function->createTypeError("Redeclaring an already implemented function as abstract")); + // if (functions.count(name) && !functions[name] && function->isFullyImplemented()) + // functions.insert(make_pair(name, true); + + // if (functions.count(name) && !functions[name] && function->isFullyImplemented) + // functions.insert(make_pair(name, true)); + + // functions.insert(make_pair(name, function->isFullyImplemented())); + functions[name] = function->isFullyImplemented(); + + // if (function->isFullyImplemented()) + // full_functions.insert(make_pair(name, function.get())); + // else + // abs_functions.insert(make_pair(name, function.get())); + } + } + for (auto const& it: functions) + if (!it.second) + { + setFullyImplemented(false); + break; + } + +} + void ContractDefinition::checkIllegalOverrides() const { // TODO unify this at a later point. for this we need to put the constness and the access specifier @@ -643,6 +681,8 @@ void NewExpression::checkTypeRequirements() m_contract = dynamic_cast(m_contractName->getReferencedDeclaration()); if (!m_contract) BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract.")); + if (!m_contract->isFullyImplemented()) + BOOST_THROW_EXCEPTION(m_contract->createTypeError("Trying to create an object of an abstract contract.")); shared_ptr contractType = make_shared(*m_contract); TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes(); m_type = make_shared(parameterTypes, TypePointers{contractType}, diff --git a/AST.h b/AST.h index c1da90cdb..8570e7bd9 100644 --- a/AST.h +++ b/AST.h @@ -283,6 +283,7 @@ public: private: void checkIllegalOverrides() const; + void checkAbstractFunctions(); std::vector, FunctionTypePointer>> const& getInterfaceFunctionList() const; diff --git a/CompilerStack.cpp b/CompilerStack.cpp index 55ec0cb59..1301bfa5d 100644 --- a/CompilerStack.cpp +++ b/CompilerStack.cpp @@ -138,6 +138,8 @@ void CompilerStack::compile(bool _optimize) for (ASTPointer const& node: source->ast->getNodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { + if (!contract->isFullyImplemented()) + continue; shared_ptr compiler = make_shared(_optimize); compiler->compileContract(*contract, contractBytecode); Contract& compiledContract = m_contracts[contract->getName()]; diff --git a/Parser.cpp b/Parser.cpp index 0f1b34069..5c7676df5 100644 --- a/Parser.cpp +++ b/Parser.cpp @@ -141,10 +141,7 @@ ASTPointer Parser::parseContractDefinition() if (currentToken == Token::RBrace) break; else if (currentToken == Token::Function) - { - ASTPointer func = parseFunctionDefinition(name.get()); - functions.push_back(func); - } + functions.push_back(parseFunctionDefinition(name.get())); else if (currentToken == Token::Struct) structs.push_back(parseStructDefinition()); else if (currentToken == Token::Enum) From 819d9fd5a665d33f5c3b08ddf50eca3426c28e01 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 30 Mar 2015 15:14:59 +0200 Subject: [PATCH 61/70] Detect if non-existant parameter is documented with natspec --- InterfaceHandler.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/InterfaceHandler.cpp b/InterfaceHandler.cpp index 2f35a96f6..aacbbfd72 100644 --- a/InterfaceHandler.cpp +++ b/InterfaceHandler.cpp @@ -175,8 +175,17 @@ std::unique_ptr InterfaceHandler::getDevDocumentation(ContractDefin method["author"] = m_author; Json::Value params(Json::objectValue); + std::vector paramNames = it.second->getParameterNames(); for (auto const& pair: m_params) + { + if (find(paramNames.begin(), paramNames.end(), pair.first) == paramNames.end()) + // LTODO: mismatching parameter name, throw some form of warning and not just an exception + BOOST_THROW_EXCEPTION( + DocstringParsingError() << + errinfo_comment("documented parameter \"" + pair.first + "\" not found found in the function") + ); params[pair.first] = pair.second; + } if (!m_params.empty()) method["params"] = params; From c6781ed38dd7c634ef8dbbbebcac582451c04ddd Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 30 Mar 2015 17:18:38 +0200 Subject: [PATCH 62/70] Fix some styling issues and remove comments --- AST.cpp | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/AST.cpp b/AST.cpp index 2ada73627..db72ea65a 100644 --- a/AST.cpp +++ b/AST.cpp @@ -136,34 +136,20 @@ void ContractDefinition::checkAbstractFunctions() // Search from base to derived for (ContractDefinition const* contract: boost::adaptors::reverse(getLinearizedBaseContracts())) - { for (ASTPointer const& function: contract->getDefinedFunctions()) { string const& name = function->getName(); if (!function->isFullyImplemented() && functions.count(name) && functions[name]) BOOST_THROW_EXCEPTION(function->createTypeError("Redeclaring an already implemented function as abstract")); - // if (functions.count(name) && !functions[name] && function->isFullyImplemented()) - // functions.insert(make_pair(name, true); - - // if (functions.count(name) && !functions[name] && function->isFullyImplemented) - // functions.insert(make_pair(name, true)); - - // functions.insert(make_pair(name, function->isFullyImplemented())); functions[name] = function->isFullyImplemented(); - - // if (function->isFullyImplemented()) - // full_functions.insert(make_pair(name, function.get())); - // else - // abs_functions.insert(make_pair(name, function.get())); } - } + for (auto const& it: functions) if (!it.second) { setFullyImplemented(false); break; } - } void ContractDefinition::checkIllegalOverrides() const @@ -682,7 +668,7 @@ void NewExpression::checkTypeRequirements() if (!m_contract) BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract.")); if (!m_contract->isFullyImplemented()) - BOOST_THROW_EXCEPTION(m_contract->createTypeError("Trying to create an object of an abstract contract.")); + BOOST_THROW_EXCEPTION(m_contract->createTypeError("Trying to create an instance of an abstract contract.")); shared_ptr contractType = make_shared(*m_contract); TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes(); m_type = make_shared(parameterTypes, TypePointers{contractType}, From d0ede6fafdd2a2e6f2498e5c9e939052a058f7c8 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 30 Mar 2015 19:31:57 +0200 Subject: [PATCH 63/70] Refactor: Pull out array index access. --- ArrayUtils.cpp | 104 +++++++++++++++++++++++++++++++++++++++ ArrayUtils.h | 6 +++ ExpressionCompiler.cpp | 107 +++-------------------------------------- 3 files changed, 117 insertions(+), 100 deletions(-) diff --git a/ArrayUtils.cpp b/ArrayUtils.cpp index bbf7f985a..58031390f 100644 --- a/ArrayUtils.cpp +++ b/ArrayUtils.cpp @@ -440,6 +440,110 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType) const } } +void ArrayUtils::accessIndex(ArrayType const& _arrayType) const +{ + ArrayType::Location location = _arrayType.getLocation(); + eth::Instruction load = + location == ArrayType::Location::Storage ? eth::Instruction::SLOAD : + location == ArrayType::Location::Memory ? eth::Instruction::MLOAD : + eth::Instruction::CALLDATALOAD; + + // retrieve length + if (!_arrayType.isDynamicallySized()) + m_context << _arrayType.getLength(); + else if (location == ArrayType::Location::CallData) + // length is stored on the stack + m_context << eth::Instruction::SWAP1; + else + m_context << eth::Instruction::DUP2 << load; + // stack: + // check out-of-bounds access + m_context << eth::Instruction::DUP2 << eth::Instruction::LT; + eth::AssemblyItem legalAccess = m_context.appendConditionalJump(); + // out-of-bounds access throws exception (just STOP for now) + m_context << eth::Instruction::STOP; + + m_context << legalAccess; + // stack: + if (_arrayType.isByteArray()) + switch (location) + { + case ArrayType::Location::Storage: + // byte array index storage lvalue on stack (goal): + // = + m_context << u256(32) << eth::Instruction::SWAP2; + CompilerUtils(m_context).computeHashStatic(); + // stack: 32 index data_ref + m_context + << eth::Instruction::DUP3 << eth::Instruction::DUP3 + << eth::Instruction::DIV << eth::Instruction::ADD + // stack: 32 index (data_ref + index / 32) + << eth::Instruction::SWAP2 << eth::Instruction::SWAP1 + << eth::Instruction::MOD; + break; + case ArrayType::Location::CallData: + // no lvalue, just retrieve the value + m_context + << eth::Instruction::ADD << eth::Instruction::CALLDATALOAD + << ((u256(0xff) << (256 - 8))) << eth::Instruction::AND; + break; + case ArrayType::Location::Memory: + solAssert(false, "Memory lvalues not yet implemented."); + } + else + { + // stack: + m_context << eth::Instruction::SWAP1; + if (_arrayType.isDynamicallySized()) + { + if (location == ArrayType::Location::Storage) + CompilerUtils(m_context).computeHashStatic(); + else if (location == ArrayType::Location::Memory) + m_context << u256(32) << eth::Instruction::ADD; + } + // stack: + switch (location) + { + case ArrayType::Location::CallData: + m_context + << eth::Instruction::SWAP1 << _arrayType.getBaseType()->getCalldataEncodedSize() + << eth::Instruction::MUL << eth::Instruction::ADD; + if (_arrayType.getBaseType()->isValueType()) + CompilerUtils(m_context).loadFromMemoryDynamic(*_arrayType.getBaseType(), true, true, false); + break; + case ArrayType::Location::Storage: + m_context << eth::Instruction::SWAP1; + if (_arrayType.getBaseType()->getStorageBytes() <= 16) + { + // stack: + // goal: + // = <(index % itemsPerSlot) * byteSize> + unsigned byteSize = _arrayType.getBaseType()->getStorageBytes(); + solAssert(byteSize != 0, ""); + unsigned itemsPerSlot = 32 / byteSize; + m_context << u256(itemsPerSlot) << eth::Instruction::SWAP2; + // stack: itemsPerSlot index data_ref + m_context + << eth::Instruction::DUP3 << eth::Instruction::DUP3 + << eth::Instruction::DIV << eth::Instruction::ADD + // stack: itemsPerSlot index (data_ref + index / itemsPerSlot) + << eth::Instruction::SWAP2 << eth::Instruction::SWAP1 + << eth::Instruction::MOD + << u256(byteSize) << eth::Instruction::MUL; + } + else + { + if (_arrayType.getBaseType()->getStorageSize() != 1) + m_context << _arrayType.getBaseType()->getStorageSize() << eth::Instruction::MUL; + m_context << eth::Instruction::ADD << u256(0); + } + break; + case ArrayType::Location::Memory: + solAssert(false, "Memory lvalues not yet implemented."); + } + } +} + void ArrayUtils::incrementByteOffset(unsigned _byteSize, unsigned _byteOffsetPosition, unsigned _storageOffsetPosition) const { solAssert(_byteSize < 32, ""); diff --git a/ArrayUtils.h b/ArrayUtils.h index 22c0646a5..dab40e2d6 100644 --- a/ArrayUtils.h +++ b/ArrayUtils.h @@ -70,6 +70,12 @@ public: /// Stack pre: reference (excludes byte offset for dynamic storage arrays) /// Stack post: reference length void retrieveLength(ArrayType const& _arrayType) const; + /// Retrieves the value at a specific index. If the location is storage, only retrieves the + /// position. + /// Stack pre: reference [length] index + /// Stack post for storage: slot byte_offset + /// Stack post for calldata: value + void accessIndex(ArrayType const& _arrayType) const; private: /// Adds the given number of bytes to a storage byte offset counter and also increments diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 90568767b..daea21623 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -755,113 +755,20 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) { ArrayType const& arrayType = dynamic_cast(baseType); solAssert(_indexAccess.getIndexExpression(), "Index expression expected."); - ArrayType::Location location = arrayType.getLocation(); - eth::Instruction load = - location == ArrayType::Location::Storage ? eth::Instruction::SLOAD : - location == ArrayType::Location::Memory ? eth::Instruction::MLOAD : - eth::Instruction::CALLDATALOAD; // remove storage byte offset - if (location == ArrayType::Location::Storage) + if (arrayType.getLocation() == ArrayType::Location::Storage) m_context << eth::Instruction::POP; - // stack layout: [] _indexAccess.getIndexExpression()->accept(*this); - // retrieve length - if (!arrayType.isDynamicallySized()) - m_context << arrayType.getLength(); - else if (location == ArrayType::Location::CallData) - // length is stored on the stack - m_context << eth::Instruction::SWAP1; - else - m_context << eth::Instruction::DUP2 << load; - // stack: - // check out-of-bounds access - m_context << eth::Instruction::DUP2 << eth::Instruction::LT; - eth::AssemblyItem legalAccess = m_context.appendConditionalJump(); - // out-of-bounds access throws exception (just STOP for now) - m_context << eth::Instruction::STOP; - - m_context << legalAccess; - // stack: - if (arrayType.isByteArray()) - switch (location) - { - case ArrayType::Location::Storage: - // byte array index storage lvalue on stack (goal): - // = - m_context << u256(32) << eth::Instruction::SWAP2; - CompilerUtils(m_context).computeHashStatic(); - // stack: 32 index data_ref - m_context - << eth::Instruction::DUP3 << eth::Instruction::DUP3 - << eth::Instruction::DIV << eth::Instruction::ADD - // stack: 32 index (data_ref + index / 32) - << eth::Instruction::SWAP2 << eth::Instruction::SWAP1 - << eth::Instruction::MOD; - setLValue(_indexAccess); - break; - case ArrayType::Location::CallData: - // no lvalue, just retrieve the value - m_context - << eth::Instruction::ADD << eth::Instruction::CALLDATALOAD - << ((u256(0xff) << (256 - 8))) << eth::Instruction::AND; - break; - case ArrayType::Location::Memory: - solAssert(false, "Memory lvalues not yet implemented."); - } - else + // stack layout: [] + ArrayUtils(m_context).accessIndex(arrayType); + if (arrayType.getLocation() == ArrayType::Location::Storage) { - // stack: - m_context << eth::Instruction::SWAP1; - if (arrayType.isDynamicallySized()) - { - if (location == ArrayType::Location::Storage) - CompilerUtils(m_context).computeHashStatic(); - else if (location == ArrayType::Location::Memory) - m_context << u256(32) << eth::Instruction::ADD; - } - // stack: - switch (location) - { - case ArrayType::Location::CallData: - m_context - << eth::Instruction::SWAP1 << arrayType.getBaseType()->getCalldataEncodedSize() - << eth::Instruction::MUL << eth::Instruction::ADD; - if (arrayType.getBaseType()->isValueType()) - CompilerUtils(m_context).loadFromMemoryDynamic(*arrayType.getBaseType(), true, true, false); - break; - case ArrayType::Location::Storage: - m_context << eth::Instruction::SWAP1; - if (arrayType.getBaseType()->getStorageBytes() <= 16) - { - // stack: - // goal: - // = <(index % itemsPerSlot) * byteSize> - unsigned byteSize = arrayType.getBaseType()->getStorageBytes(); - solAssert(byteSize != 0, ""); - unsigned itemsPerSlot = 32 / byteSize; - m_context << u256(itemsPerSlot) << eth::Instruction::SWAP2; - // stack: itemsPerSlot index data_ref - m_context - << eth::Instruction::DUP3 << eth::Instruction::DUP3 - << eth::Instruction::DIV << eth::Instruction::ADD - // stack: itemsPerSlot index (data_ref + index / itemsPerSlot) - << eth::Instruction::SWAP2 << eth::Instruction::SWAP1 - << eth::Instruction::MOD - << u256(byteSize) << eth::Instruction::MUL; - } - else - { - if (arrayType.getBaseType()->getStorageSize() != 1) - m_context << arrayType.getBaseType()->getStorageSize() << eth::Instruction::MUL; - m_context << eth::Instruction::ADD << u256(0); - } + if (arrayType.isByteArray()) + setLValue(_indexAccess); + else setLValueToStorageItem(_indexAccess); - break; - case ArrayType::Location::Memory: - solAssert(false, "Memory lvalues not yet implemented."); - } } } else From 37a0234c4a673f130f28a3241cb72589b0aa1f47 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Fri, 27 Mar 2015 17:07:32 +0100 Subject: [PATCH 64/70] style fixes --- AST.cpp | 2 +- Types.cpp | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/AST.cpp b/AST.cpp index e3c9bc964..7fe3a9825 100644 --- a/AST.cpp +++ b/AST.cpp @@ -337,7 +337,7 @@ void FunctionDefinition::checkTypeRequirements() { if (!var->getType()->canLiveOutsideStorage()) BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); - if (!(var->getType()->externalType()) && getVisibility() >= Visibility::Public) + if (getVisibility() >= Visibility::Public && !(var->getType()->externalType())) BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for function with external visibility")); } for (ASTPointer const& modifier: m_functionModifiers) diff --git a/Types.cpp b/Types.cpp index 5fd7d24a6..e784c6b8c 100644 --- a/Types.cpp +++ b/Types.cpp @@ -1103,11 +1103,16 @@ TypePointer FunctionType::externalType() const TypePointers paramTypes; TypePointers retParamTypes; - for (auto it = m_parameterTypes.cbegin(); it != m_parameterTypes.cend(); ++it) - paramTypes.push_back((*it)->externalType()); - for (auto it = m_returnParameterTypes.cbegin(); it != m_returnParameterTypes.cend(); ++it) - retParamTypes.push_back((*it)->externalType()); - + for(auto type: m_parameterTypes) + { + solAssert(!!type->externalType(), "To be included in external type of the function, the argument should have external type."); + paramTypes.push_back(type->externalType()); + } + for(auto param: m_returnParameterTypes) + { + solAssert(!!param->externalType(), "To be included in external type of the function, the argument should have external type."); + retParamTypes.push_back(param->externalType()); + } return make_shared(paramTypes, retParamTypes, m_location, m_arbitraryParameters); } From 06dea23331698800c07c3e4a7645ad8cfbb7d11c Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Mon, 30 Mar 2015 14:34:38 +0200 Subject: [PATCH 65/70] changed checking for external type in VariableDeclaration::checkTypeRequirements() changed error msg --- AST.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/AST.cpp b/AST.cpp index 7fe3a9825..a111a6f88 100644 --- a/AST.cpp +++ b/AST.cpp @@ -338,7 +338,14 @@ void FunctionDefinition::checkTypeRequirements() if (!var->getType()->canLiveOutsideStorage()) BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); if (getVisibility() >= Visibility::Public && !(var->getType()->externalType())) - BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for function with external visibility")); + { + // todo delete when arrays as parameter type in internal functions will be implemented + ArrayType const* type = dynamic_cast(var->getType().get()); + if (getVisibility() == Visibility::Public && type) + BOOST_THROW_EXCEPTION(var->createTypeError("Array type is not allowed as parameter for internal functions.")); + else + BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for function with external visibility.")); + } } for (ASTPointer const& modifier: m_functionModifiers) modifier->checkTypeRequirements(isConstructor() ? @@ -379,6 +386,14 @@ void VariableDeclaration::checkTypeRequirements() m_value->expectType(*m_type); if (m_isStateVariable && !m_type->externalType() && getVisibility() >= Visibility::Public) BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for state variables.")); + + auto sharedToExternalTypes = FunctionType(*this).externalType(); // do not distroy the shared pointer. + auto externalFunctionTypes = dynamic_cast(sharedToExternalTypes.get()); + TypePointers retParamTypes = externalFunctionTypes->getReturnParameterTypes(); + TypePointers parameterTypes = externalFunctionTypes->getParameterTypes(); + for (auto parameter: parameterTypes + retParamTypes) + if (!parameter && !(parameter->externalType())) + BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for state variables.")); } else { // no type declared and no previous assignment, infer the type From d7d5b8200a617e5faa3a3d7510aa408ff73e3237 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Wed, 1 Apr 2015 15:19:33 +0200 Subject: [PATCH 66/70] miner changes in the implementation of the externalTypes function of FunctionType. better error messages for exeptions style fixes after review --- AST.cpp | 18 ++++++++---------- Types.cpp | 12 +++++++----- Types.h | 2 ++ 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/AST.cpp b/AST.cpp index a111a6f88..d489a4489 100644 --- a/AST.cpp +++ b/AST.cpp @@ -339,12 +339,11 @@ void FunctionDefinition::checkTypeRequirements() BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); if (getVisibility() >= Visibility::Public && !(var->getType()->externalType())) { - // todo delete when arrays as parameter type in internal functions will be implemented - ArrayType const* type = dynamic_cast(var->getType().get()); - if (getVisibility() == Visibility::Public && type) + // todo delete when will be implemented arrays as parameter type in internal functions + if (getVisibility() == Visibility::Public && var->getType()->getCategory() == Type::Category::Array) BOOST_THROW_EXCEPTION(var->createTypeError("Array type is not allowed as parameter for internal functions.")); else - BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for function with external visibility.")); + BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for public and external functions.")); } } for (ASTPointer const& modifier: m_functionModifiers) @@ -389,12 +388,11 @@ void VariableDeclaration::checkTypeRequirements() auto sharedToExternalTypes = FunctionType(*this).externalType(); // do not distroy the shared pointer. auto externalFunctionTypes = dynamic_cast(sharedToExternalTypes.get()); - TypePointers retParamTypes = externalFunctionTypes->getReturnParameterTypes(); - TypePointers parameterTypes = externalFunctionTypes->getParameterTypes(); - for (auto parameter: parameterTypes + retParamTypes) - if (!parameter && !(parameter->externalType())) - BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for state variables.")); - } else + for (auto parameter: externalFunctionTypes->getParameterTypes() + externalFunctionTypes->getReturnParameterTypes()) + if (!parameter || !(parameter->externalType())) + BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for public state variables.")); + } + else { // no type declared and no previous assignment, infer the type m_value->checkTypeRequirements(); diff --git a/Types.cpp b/Types.cpp index e784c6b8c..86b740262 100644 --- a/Types.cpp +++ b/Types.cpp @@ -1103,15 +1103,17 @@ TypePointer FunctionType::externalType() const TypePointers paramTypes; TypePointers retParamTypes; - for(auto type: m_parameterTypes) + for (auto type: m_parameterTypes) { - solAssert(!!type->externalType(), "To be included in external type of the function, the argument should have external type."); + if(!type->externalType()) + return TypePointer(); paramTypes.push_back(type->externalType()); } - for(auto param: m_returnParameterTypes) + for (auto type: m_returnParameterTypes) { - solAssert(!!param->externalType(), "To be included in external type of the function, the argument should have external type."); - retParamTypes.push_back(param->externalType()); + if(!type->externalType()) + return TypePointer(); + retParamTypes.push_back(type->externalType()); } return make_shared(paramTypes, retParamTypes, m_location, m_arbitraryParameters); } diff --git a/Types.h b/Types.h index 99fd878f0..cea711991 100644 --- a/Types.h +++ b/Types.h @@ -512,6 +512,8 @@ public: virtual Category getCategory() const override { return Category::Function; } + /// @returns TypePointer of a new FunctionType object. All input/return parameters are an appropriate external types of input/return parameters of current function. + /// Returns an empty shared pointer if one of input/return parameters does not have an externaltype. virtual TypePointer externalType() const override; explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); From f730931fc71aa3b1a7985e597ae2d33470b27827 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Wed, 1 Apr 2015 15:26:37 +0200 Subject: [PATCH 67/70] Update Types.h --- Types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Types.h b/Types.h index cea711991..e1852bc7f 100644 --- a/Types.h +++ b/Types.h @@ -513,7 +513,7 @@ public: virtual Category getCategory() const override { return Category::Function; } /// @returns TypePointer of a new FunctionType object. All input/return parameters are an appropriate external types of input/return parameters of current function. - /// Returns an empty shared pointer if one of input/return parameters does not have an externaltype. + /// Returns an empty shared pointer if one of the input/return parameters does not have an externaltype. virtual TypePointer externalType() const override; explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); From e3853ec6d14119052a168c4b5031277c866fd0e0 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Wed, 1 Apr 2015 15:41:24 +0200 Subject: [PATCH 68/70] Update AST.cpp --- AST.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AST.cpp b/AST.cpp index d489a4489..fec123f75 100644 --- a/AST.cpp +++ b/AST.cpp @@ -341,7 +341,7 @@ void FunctionDefinition::checkTypeRequirements() { // todo delete when will be implemented arrays as parameter type in internal functions if (getVisibility() == Visibility::Public && var->getType()->getCategory() == Type::Category::Array) - BOOST_THROW_EXCEPTION(var->createTypeError("Array type is not allowed as parameter for internal functions.")); + BOOST_THROW_EXCEPTION(var->createTypeError("Arrays only implemented for external functions.")); else BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for public and external functions.")); } @@ -386,7 +386,7 @@ void VariableDeclaration::checkTypeRequirements() if (m_isStateVariable && !m_type->externalType() && getVisibility() >= Visibility::Public) BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for state variables.")); - auto sharedToExternalTypes = FunctionType(*this).externalType(); // do not distroy the shared pointer. + auto sharedToExternalTypes = FunctionType(*this).externalType(); // do not destroy the shared pointer. auto externalFunctionTypes = dynamic_cast(sharedToExternalTypes.get()); for (auto parameter: externalFunctionTypes->getParameterTypes() + externalFunctionTypes->getReturnParameterTypes()) if (!parameter || !(parameter->externalType())) From eac47b13280f7edf2431f25d61d5dc1c3425f206 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Wed, 1 Apr 2015 15:42:30 +0200 Subject: [PATCH 69/70] Update Types.cpp --- Types.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Types.cpp b/Types.cpp index 86b740262..78649cc95 100644 --- a/Types.cpp +++ b/Types.cpp @@ -1105,13 +1105,13 @@ TypePointer FunctionType::externalType() const for (auto type: m_parameterTypes) { - if(!type->externalType()) + if (!type->externalType()) return TypePointer(); paramTypes.push_back(type->externalType()); } for (auto type: m_returnParameterTypes) { - if(!type->externalType()) + if (!type->externalType()) return TypePointer(); retParamTypes.push_back(type->externalType()); } From 8e19eea7d5d6cd4bffa03f8617023a74268de608 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Wed, 1 Apr 2015 15:57:39 +0200 Subject: [PATCH 70/70] VariableDeclaration::checkTypeRequirements() refactoring --- AST.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/AST.cpp b/AST.cpp index fec123f75..095ba7bf1 100644 --- a/AST.cpp +++ b/AST.cpp @@ -386,11 +386,8 @@ void VariableDeclaration::checkTypeRequirements() if (m_isStateVariable && !m_type->externalType() && getVisibility() >= Visibility::Public) BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for state variables.")); - auto sharedToExternalTypes = FunctionType(*this).externalType(); // do not destroy the shared pointer. - auto externalFunctionTypes = dynamic_cast(sharedToExternalTypes.get()); - for (auto parameter: externalFunctionTypes->getParameterTypes() + externalFunctionTypes->getReturnParameterTypes()) - if (!parameter || !(parameter->externalType())) - BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for public state variables.")); + if (!FunctionType(*this).externalType()) + BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for public state variables.")); } else {