diff options
-rw-r--r-- | AST.cpp | 15 | ||||
-rwxr-xr-x | AST.h | 10 | ||||
-rw-r--r-- | AST_accept.h | 6 | ||||
-rw-r--r-- | ExpressionCompiler.cpp | 102 | ||||
-rw-r--r-- | ExpressionCompiler.h | 4 | ||||
-rw-r--r-- | Parser.cpp | 24 | ||||
-rw-r--r-- | Types.cpp | 3 | ||||
-rw-r--r-- | Types.h | 4 | ||||
-rw-r--r-- | grammar.txt | 2 |
9 files changed, 85 insertions, 85 deletions
@@ -309,20 +309,13 @@ bool FunctionCall::isTypeConversion() const void NewExpression::checkTypeRequirements() { m_contractName->checkTypeRequirements(); - for (ASTPointer<Expression> const& argument: m_arguments) - argument->checkTypeRequirements(); - m_contract = dynamic_cast<ContractDefinition const*>(m_contractName->getReferencedDeclaration()); if (!m_contract) BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract.")); - shared_ptr<ContractType const> type = make_shared<ContractType>(*m_contract); - m_type = type; - TypePointers const& parameterTypes = type->getConstructorType()->getParameterTypes(); - if (parameterTypes.size() != m_arguments.size()) - BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for constructor call.")); - for (size_t i = 0; i < m_arguments.size(); ++i) - if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i])) - BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in constructor call.")); + shared_ptr<ContractType const> contractType = make_shared<ContractType>(*m_contract); + TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes(); + m_type = make_shared<FunctionType>(parameterTypes, TypePointers{contractType}, + FunctionType::Location::CREATION); } void MemberAccess::checkTypeRequirements() @@ -790,26 +790,22 @@ private: }; /** - * Expression that creates a new contract, e.g. "new SomeContract(1, 2)". + * Expression that creates a new contract, e.g. the "new SomeContract" part in "new SomeContract(1, 2)". */ class NewExpression: public Expression { public: - NewExpression(Location const& _location, ASTPointer<Identifier> const& _contractName, - std::vector<ASTPointer<Expression>> const& _arguments): - Expression(_location), m_contractName(_contractName), m_arguments(_arguments) {} + NewExpression(Location const& _location, ASTPointer<Identifier> const& _contractName): + Expression(_location), m_contractName(_contractName) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; virtual void checkTypeRequirements() override; - std::vector<ASTPointer<Expression const>> getArguments() const { return {m_arguments.begin(), m_arguments.end()}; } - /// Returns the referenced contract. Can only be called after type checking. ContractDefinition const* getContract() const { solAssert(m_contract, ""); return m_contract; } private: ASTPointer<Identifier> m_contractName; - std::vector<ASTPointer<Expression>> m_arguments; ContractDefinition const* m_contract = nullptr; }; diff --git a/AST_accept.h b/AST_accept.h index 0e5a71b6..7f3db85a 100644 --- a/AST_accept.h +++ b/AST_accept.h @@ -452,20 +452,14 @@ void FunctionCall::accept(ASTConstVisitor& _visitor) const void NewExpression::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) - { m_contractName->accept(_visitor); - listAccept(m_arguments, _visitor); - } _visitor.endVisit(*this); } void NewExpression::accept(ASTConstVisitor& _visitor) const { if (_visitor.visit(*this)) - { m_contractName->accept(_visitor); - listAccept(m_arguments, _visitor); - } _visitor.endVisit(*this); } diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 8cecbb1b..ff08a326 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -235,6 +235,36 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) _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<ContractType const&>( + *function.getReturnParameterTypes().front()).getContractDefinition(); + // copy the contract's code into memory + bytes const& bytecode = m_context.getCompiledContract(contract); + m_context << u256(bytecode.size()); + //@todo could be done by actually appending the Assembly, but then we probably need to compile + // multiple times. Will revisit once external fuctions are inlined. + m_context.appendData(bytecode); + //@todo copy to memory position 0, shift as soon as we use memory + m_context << u256(0) << eth::Instruction::CODECOPY; + + unsigned length = bytecode.size(); + length += appendArgumentCopyToMemory(function.getParameterTypes(), arguments, length); + // size, offset, endowment + m_context << u256(length) << 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; + return false; + break; + } case Location::SET_GAS: { // stack layout: contract_address function_id [gas] [value] @@ -316,38 +346,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) bool ExpressionCompiler::visit(NewExpression const& _newExpression) { - ContractType const* type = dynamic_cast<ContractType const*>(_newExpression.getType().get()); - solAssert(type, ""); - TypePointers const& types = type->getConstructorType()->getParameterTypes(); - vector<ASTPointer<Expression const>> arguments = _newExpression.getArguments(); - solAssert(arguments.size() == types.size(), ""); - - // copy the contracts code into memory - bytes const& bytecode = m_context.getCompiledContract(*_newExpression.getContract()); - m_context << u256(bytecode.size()); - //@todo could be done by actually appending the Assembly, but then we probably need to compile - // multiple times. Will revisit once external fuctions are inlined. - m_context.appendData(bytecode); - //@todo copy to memory position 0, shift as soon as we use memory - m_context << u256(0) << eth::Instruction::CODECOPY; - - unsigned dataOffset = bytecode.size(); - for (unsigned i = 0; i < arguments.size(); ++i) - { - arguments[i]->accept(*this); - appendTypeConversion(*arguments[i]->getType(), *types[i], true); - unsigned const c_numBytes = types[i]->getCalldataEncodedSize(); - if (c_numBytes > 32) - BOOST_THROW_EXCEPTION(CompilerError() - << errinfo_sourceLocation(arguments[i]->getLocation()) - << errinfo_comment("Type " + types[i]->toString() + " not yet supported.")); - bool const c_leftAligned = types[i]->getCategory() == Type::Category::STRING; - bool const c_padToWords = true; - dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, c_numBytes, - c_leftAligned, c_padToWords); - } - // size, offset, endowment - m_context << u256(dataOffset) << u256(0) << u256(0) << eth::Instruction::CREATE; + // code is created for the function call (CREATION) only return false; } @@ -680,21 +679,8 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio } unsigned dataOffset = bare ? 0 : CompilerUtils::dataStartOffset; // reserve 4 bytes for the function's hash identifier - for (unsigned i = 0; i < _arguments.size(); ++i) - { - _arguments[i]->accept(*this); - Type const& type = *_functionType.getParameterTypes()[i]; - appendTypeConversion(*_arguments[i]->getType(), type, true); - unsigned const c_numBytes = type.getCalldataEncodedSize(); - if (c_numBytes == 0 || c_numBytes > 32) - BOOST_THROW_EXCEPTION(CompilerError() - << errinfo_sourceLocation(_arguments[i]->getLocation()) - << errinfo_comment("Type " + type.toString() + " not yet supported.")); - bool const c_leftAligned = type.getCategory() == Type::Category::STRING; - bool const c_padToWords = true; - dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, c_numBytes, - c_leftAligned, c_padToWords); - } + dataOffset += appendArgumentCopyToMemory(_functionType.getParameterTypes(), _arguments, dataOffset); + //@todo only return the first return value for now Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr : _functionType.getReturnParameterTypes().front().get(); @@ -728,6 +714,28 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio } } +unsigned ExpressionCompiler::appendArgumentCopyToMemory(TypePointers const& _types, + vector<ASTPointer<Expression const>> const& _arguments, + unsigned _memoryOffset) +{ + unsigned length = 0; + for (unsigned i = 0; i < _arguments.size(); ++i) + { + _arguments[i]->accept(*this); + appendTypeConversion(*_arguments[i]->getType(), *_types[i], true); + unsigned const c_numBytes = _types[i]->getCalldataEncodedSize(); + if (c_numBytes == 0 || c_numBytes > 32) + BOOST_THROW_EXCEPTION(CompilerError() + << errinfo_sourceLocation(_arguments[i]->getLocation()) + << errinfo_comment("Type " + _types[i]->toString() + " not yet supported.")); + bool const c_leftAligned = _types[i]->getCategory() == Type::Category::STRING; + bool const c_padToWords = true; + length += CompilerUtils(m_context).storeInMemory(_memoryOffset + length, c_numBytes, + c_leftAligned, c_padToWords); + } + return length; +} + ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType, unsigned _baseStackOffset): m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset), diff --git a/ExpressionCompiler.h b/ExpressionCompiler.h index 024c4644..4a696c39 100644 --- a/ExpressionCompiler.h +++ b/ExpressionCompiler.h @@ -90,6 +90,10 @@ private: /// Appends code to call a function of the given type with the given arguments. void appendExternalFunctionCall(FunctionType const& _functionType, std::vector<ASTPointer<Expression const>> const& _arguments, bool bare = false); + /// Appends code that copies the given arguments to memory (with optional offset). + /// @returns the number of bytes copied to memory + unsigned appendArgumentCopyToMemory(TypePointers const& _functionType, std::vector<ASTPointer<Expression const>> const& _arguments, + unsigned _memoryOffset = 0); /** * Helper class to store and retrieve lvalues to and from various locations. @@ -466,17 +466,7 @@ ASTPointer<Expression> Parser::parseUnaryExpression() { ASTNodeFactory nodeFactory(*this); Token::Value token = m_scanner->getCurrentToken(); - if (token == Token::NEW) - { - expectToken(Token::NEW); - ASTPointer<Identifier> contractName = ASTNodeFactory(*this).createNode<Identifier>(expectIdentifierToken()); - expectToken(Token::LPAREN); - vector<ASTPointer<Expression>> arguments(parseFunctionCallArguments()); - expectToken(Token::RPAREN); - nodeFactory.markEndPosition(); - return nodeFactory.createNode<NewExpression>(contractName, arguments); - } - else if (Token::isUnaryOp(token) || Token::isCountOp(token)) + if (Token::isUnaryOp(token) || Token::isCountOp(token)) { // prefix expression m_scanner->next(); @@ -500,7 +490,17 @@ ASTPointer<Expression> Parser::parseUnaryExpression() ASTPointer<Expression> Parser::parseLeftHandSideExpression() { ASTNodeFactory nodeFactory(*this); - ASTPointer<Expression> expression = parsePrimaryExpression(); + ASTPointer<Expression> expression; + if (m_scanner->getCurrentToken() == Token::NEW) + { + expectToken(Token::NEW); + ASTPointer<Identifier> contractName = ASTNodeFactory(*this).createNode<Identifier>(expectIdentifierToken()); + nodeFactory.markEndPosition(); + expression = nodeFactory.createNode<NewExpression>(contractName); + } + else + expression = parsePrimaryExpression(); + while (true) { switch (m_scanner->getCurrentToken()) @@ -631,6 +631,7 @@ MemberList const& FunctionType::getMembers() const switch (m_location) { case Location::EXTERNAL: + case Location::CREATION: case Location::ECRECOVER: case Location::SHA256: case Location::RIPEMD160: @@ -644,6 +645,8 @@ MemberList const& FunctionType::getMembers() const {"value", make_shared<FunctionType>(parseElementaryTypeVector({"uint"}), TypePointers{copyAndSetGasOrValue(false, true)}, Location::SET_VALUE, m_gasSet, m_valueSet)}}; + if (m_location == Location::CREATION) + members.erase("gas"); m_members.reset(new MemberList(members)); } return *m_members; @@ -291,6 +291,8 @@ public: virtual MemberList const& getMembers() const override; + ContractDefinition const& getContractDefinition() const { return m_contract; } + /// Returns the function type of the constructor. Note that the location part of the function type /// is not used, as this type cannot be the type of a variable or expression. std::shared_ptr<FunctionType const> const& getConstructorType() const; @@ -348,7 +350,7 @@ public: /// INTERNAL: jump tag, EXTERNAL: contract address + function identifier, /// BARE: contract address (non-abi contract call) /// OTHERS: special virtual function, nothing on the stack - enum class Location { INTERNAL, EXTERNAL, SEND, + enum class Location { INTERNAL, EXTERNAL, CREATION, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160, LOG0, LOG1, LOG2, LOG3, LOG4, diff --git a/grammar.txt b/grammar.txt index 8c34997b..f06d4def 100644 --- a/grammar.txt +++ b/grammar.txt @@ -33,7 +33,7 @@ Expression = Assignment | UnaryOperation | BinaryOperation | FunctionCall | NewE // The expression syntax is actually much more complicated Assignment = Expression (AssignmentOp Expression) FunctionCall = Expression '(' Expression ( ',' Expression )* ')' -NewExpression = 'new' Identifier '(' ( Expression ( ',' Expression )* ) ')' +NewExpression = 'new' Identifier MemberAccess = Expression '.' Identifier IndexAccess = Expression '[' Expresison ']' PrimaryExpression = Identifier | NumberLiteral | StringLiteral | ElementaryTypeName | '(' Expression ')' |