diff --git a/AST.cpp b/AST.cpp index ea8ecdb75..7b6335645 100644 --- a/AST.cpp +++ b/AST.cpp @@ -331,20 +331,13 @@ bool FunctionCall::isTypeConversion() const void NewExpression::checkTypeRequirements() { m_contractName->checkTypeRequirements(); - for (ASTPointer const& argument: m_arguments) - argument->checkTypeRequirements(); - m_contract = dynamic_cast(m_contractName->getReferencedDeclaration()); if (!m_contract) BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract.")); - shared_ptr type = make_shared(*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 = make_shared(*m_contract); + TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes(); + m_type = make_shared(parameterTypes, TypePointers{contractType}, + FunctionType::Location::CREATION); } void MemberAccess::checkTypeRequirements() diff --git a/AST.h b/AST.h index 28fb7f0a5..409aed443 100755 --- a/AST.h +++ b/AST.h @@ -792,26 +792,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 const& _contractName, - std::vector> const& _arguments): - Expression(_location), m_contractName(_contractName), m_arguments(_arguments) {} + 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; - std::vector> 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 m_contractName; - std::vector> m_arguments; ContractDefinition const* m_contract = nullptr; }; diff --git a/AST_accept.h b/AST_accept.h index 0e5a71b62..7f3db85a1 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/CompilerContext.cpp b/CompilerContext.cpp index 5d10a5f95..29e98eabf 100644 --- a/CompilerContext.cpp +++ b/CompilerContext.cpp @@ -95,6 +95,11 @@ unsigned CompilerContext::baseToCurrentStackOffset(unsigned _baseOffset) const return _baseOffset + m_asm.deposit(); } +unsigned CompilerContext::currentToBaseStackOffset(unsigned _offset) const +{ + return -baseToCurrentStackOffset(-_offset); +} + u256 CompilerContext::getStorageLocationOfVariable(const Declaration& _declaration) const { auto it = m_stateVariables.find(&_declaration); diff --git a/CompilerContext.h b/CompilerContext.h index 14672c956..cf505d654 100644 --- a/CompilerContext.h +++ b/CompilerContext.h @@ -62,6 +62,9 @@ public: /// 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 diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 1c02f4f32..bcb577374 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -232,27 +232,71 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } case Location::EXTERNAL: case Location::BARE: + _functionCall.getExpression().accept(*this); + appendExternalFunctionCall(function, arguments, function.getLocation() == Location::BARE); + break; + case Location::CREATION: { - FunctionCallOptions options; - options.bare = function.getLocation() == Location::BARE; - options.obtainAddress = [&]() { _functionCall.getExpression().accept(*this); }; - appendExternalFunctionCall(function, arguments, options); + _functionCall.getExpression().accept(*this); + solAssert(!function.gasSet(), "Gas limit set for contract creation."); + solAssert(function.getReturnParameterTypes().size() == 1, ""); + ContractDefinition const& contract = dynamic_cast( + *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; break; } + case Location::SET_GAS: + { + // 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::SET_VALUE: + // 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: - { - FunctionCallOptions options; - options.bare = true; - options.obtainAddress = [&]() { _functionCall.getExpression().accept(*this); }; - options.obtainValue = [&]() - { - arguments.front()->accept(*this); - appendTypeConversion(*arguments.front()->getType(), - *function.getParameterTypes().front(), true); - }; - appendExternalFunctionCall(FunctionType(TypePointers{}, TypePointers{}, Location::EXTERNAL), {}, options); + _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, true, true), {}, true); break; - } case Location::SUICIDE: arguments.front()->accept(*this); appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true); @@ -289,11 +333,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) static const map contractAddresses{{Location::ECRECOVER, 1}, {Location::SHA256, 2}, {Location::RIPEMD160, 3}}; - u256 contractAddress = contractAddresses.find(function.getLocation())->second; - FunctionCallOptions options; - options.bare = true; - options.obtainAddress = [&]() { m_context << contractAddress; }; - appendExternalFunctionCall(function, arguments, options); + m_context << contractAddresses.find(function.getLocation())->second; + appendExternalFunctionCall(function, arguments, true); break; } default: @@ -305,38 +346,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) bool ExpressionCompiler::visit(NewExpression const& _newExpression) { - ContractType const* type = dynamic_cast(_newExpression.getType().get()); - solAssert(type, ""); - TypePointers const& types = type->getConstructorType()->getParameterTypes(); - vector> 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; } @@ -370,6 +380,10 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) 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") @@ -646,46 +660,60 @@ void ExpressionCompiler::appendHighBitsCleanup(IntegerType const& _typeOnStack) void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functionType, vector> const& _arguments, - FunctionCallOptions const& _options) + bool bare) { solAssert(_arguments.size() == _functionType.getParameterTypes().size(), ""); - _options.obtainAddress(); - if (!_options.bare) - CompilerUtils(m_context).storeInMemory(0, CompilerUtils::dataStartOffset); + // Assumed stack content here: + // + // value [if _functionType.valueSet()] + // gas [if _functionType.gasSet()] + // function identifier [unless bare] + // contract address - unsigned dataOffset = _options.bare ? 0 : CompilerUtils::dataStartOffset; // reserve 4 bytes for the function's hash identifier - for (unsigned i = 0; i < _arguments.size(); ++i) + 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); + + if (!bare) { - _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); + // copy function identifier + m_context << eth::dupInstruction(gasValueSize + 1); + CompilerUtils(m_context).storeInMemory(0, CompilerUtils::dataStartOffset); } + + // reserve space for the function identifier + unsigned dataOffset = bare ? 0 : CompilerUtils::dataStartOffset; + 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(); unsigned retSize = firstType ? CompilerUtils::getPaddedSize(firstType->getCalldataEncodedSize()) : 0; // CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top) m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0); - if (_options.obtainValue) - _options.obtainValue(); + if (_functionType.valueSet()) + m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos)); else m_context << u256(0); - m_context << eth::dupInstruction(6); //copy contract address + m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(contractStackPos)); - m_context << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB - << eth::Instruction::CALL - << eth::Instruction::POP // @todo do not ignore failure indicator - << eth::Instruction::POP; // pop contract address + 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 (retSize > 0) { @@ -694,6 +722,28 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio } } +unsigned ExpressionCompiler::appendArgumentCopyToMemory(TypePointers const& _types, + vector> 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 98f58c854..4a696c39d 100644 --- a/ExpressionCompiler.h +++ b/ExpressionCompiler.h @@ -87,21 +87,13 @@ private: //// Appends code that cleans higher-order bits for integer types. void appendHighBitsCleanup(IntegerType const& _typeOnStack); - /// Additional options used in appendExternalFunctionCall. - struct FunctionCallOptions - { - FunctionCallOptions() {} - /// Invoked to copy the address to the stack - std::function obtainAddress; - /// Invoked to copy the ethe value to the stack (if not specified, value is 0). - std::function obtainValue; - /// If true, do not prepend function index to call data - bool bare = false; - }; - /// Appends code to call a function of the given type with the given arguments. void appendExternalFunctionCall(FunctionType const& _functionType, std::vector> const& _arguments, - FunctionCallOptions const& _options = FunctionCallOptions()); + 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> const& _arguments, + unsigned _memoryOffset = 0); /** * Helper class to store and retrieve lvalues to and from various locations. diff --git a/Parser.cpp b/Parser.cpp index e287319d2..ebff3ba40 100644 --- a/Parser.cpp +++ b/Parser.cpp @@ -466,17 +466,7 @@ ASTPointer Parser::parseUnaryExpression() { ASTNodeFactory nodeFactory(*this); Token::Value token = m_scanner->getCurrentToken(); - if (token == Token::NEW) - { - expectToken(Token::NEW); - ASTPointer contractName = ASTNodeFactory(*this).createNode(expectIdentifierToken()); - expectToken(Token::LPAREN); - vector> arguments(parseFunctionCallArguments()); - expectToken(Token::RPAREN); - nodeFactory.markEndPosition(); - return nodeFactory.createNode(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 Parser::parseUnaryExpression() ASTPointer Parser::parseLeftHandSideExpression() { ASTNodeFactory nodeFactory(*this); - ASTPointer expression = parsePrimaryExpression(); + ASTPointer expression; + if (m_scanner->getCurrentToken() == Token::NEW) + { + expectToken(Token::NEW); + ASTPointer contractName = ASTNodeFactory(*this).createNode(expectIdentifierToken()); + nodeFactory.markEndPosition(); + expression = nodeFactory.createNode(contractName); + } + else + expression = parsePrimaryExpression(); + while (true) { switch (m_scanner->getCurrentToken()) diff --git a/Types.cpp b/Types.cpp index 7ca1dc6d5..6a1b120c9 100644 --- a/Types.cpp +++ b/Types.cpp @@ -546,7 +546,8 @@ u256 StructType::getStorageOffsetOfMember(string const& _name) const BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage offset of non-existing member requested.")); } -FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal) +FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal): + m_location(_isInternal ? Location::INTERNAL : Location::EXTERNAL) { TypePointers params; TypePointers retParams; @@ -558,7 +559,6 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal retParams.push_back(var->getType()); swap(params, m_parameterTypes); swap(retParams, m_returnParameterTypes); - m_location = _isInternal ? Location::INTERNAL : Location::EXTERNAL; } bool FunctionType::operator==(Type const& _other) const @@ -580,6 +580,9 @@ bool FunctionType::operator==(Type const& _other) const if (!equal(m_returnParameterTypes.cbegin(), m_returnParameterTypes.cend(), other.m_returnParameterTypes.cbegin(), typeCompare)) return false; + //@todo this is ugly, but cannot be prevented right now + if (m_gasSet != other.m_gasSet || m_valueSet != other.m_valueSet) + return false; return true; } @@ -595,17 +598,45 @@ string FunctionType::toString() const } unsigned FunctionType::getSizeOnStack() const +{ + unsigned size = 0; + if (m_location == Location::EXTERNAL) + size = 2; + else if (m_location == Location::INTERNAL || m_location == Location::BARE) + size = 1; + if (m_gasSet) + size++; + if (m_valueSet) + size++; + return size; +} + +MemberList const& FunctionType::getMembers() const { switch (m_location) { - case Location::INTERNAL: - return 1; case Location::EXTERNAL: - return 2; + case Location::CREATION: + case Location::ECRECOVER: + case Location::SHA256: + case Location::RIPEMD160: case Location::BARE: - return 1; + if (!m_members) + { + map members{ + {"gas", make_shared(parseElementaryTypeVector({"uint"}), + TypePointers{copyAndSetGasOrValue(true, false)}, + Location::SET_GAS, m_gasSet, m_valueSet)}, + {"value", make_shared(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; default: - return 0; + return EmptyMemberList; } } @@ -628,6 +659,12 @@ TypePointers FunctionType::parseElementaryTypeVector(strings const& _types) return pointers; } +TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) const +{ + return make_shared(m_parameterTypes, m_returnParameterTypes, m_location, + m_gasSet || _setGas, m_valueSet || _setValue); +} + bool MappingType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) diff --git a/Types.h b/Types.h index 1ccdd706a..158d58eb9 100644 --- a/Types.h +++ b/Types.h @@ -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 const& getConstructorType() const; @@ -345,10 +347,15 @@ class FunctionType: public Type { public: /// The meaning of the value(s) on the stack referencing the function: - /// INTERNAL: jump tag, EXTERNAL: contract address + function index, + /// INTERNAL: jump tag, EXTERNAL: contract address + function identifier, /// BARE: contract address (non-abi contract call) /// OTHERS: special virtual function, nothing on the stack - enum class Location { INTERNAL, EXTERNAL, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160, LOG0, LOG1, LOG2, LOG3, LOG4, BARE }; + enum class Location { INTERNAL, EXTERNAL, CREATION, SEND, + SHA3, SUICIDE, + ECRECOVER, SHA256, RIPEMD160, + LOG0, LOG1, LOG2, LOG3, LOG4, + SET_GAS, SET_VALUE, + BARE }; virtual Category getCategory() const override { return Category::FUNCTION; } explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); @@ -357,9 +364,10 @@ public: FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes), _location) {} FunctionType(TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes, - Location _location = Location::INTERNAL): + Location _location = Location::INTERNAL, + bool _gasSet = false, bool _valueSet = false): m_parameterTypes(_parameterTypes), m_returnParameterTypes(_returnParameterTypes), - m_location(_location) {} + m_location(_location), m_gasSet(_gasSet), m_valueSet(_valueSet) {} TypePointers const& getParameterTypes() const { return m_parameterTypes; } TypePointers const& getReturnParameterTypes() const { return m_returnParameterTypes; } @@ -370,16 +378,27 @@ public: virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable function type requested.")); } virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned getSizeOnStack() const override; + virtual MemberList const& getMembers() const override; Location const& getLocation() const { return m_location; } std::string getCanonicalSignature() const; + bool gasSet() const { return m_gasSet; } + bool valueSet() const { return m_valueSet; } + + /// @returns a copy of this type, where gas or value are set manually. This will never set one + /// of the parameters to fals. + TypePointer copyAndSetGasOrValue(bool _setGas, bool _setValue) const; + private: static TypePointers parseElementaryTypeVector(strings const& _types); TypePointers m_parameterTypes; TypePointers m_returnParameterTypes; - Location m_location; + Location const m_location; + 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 + mutable std::unique_ptr m_members; }; /** diff --git a/grammar.txt b/grammar.txt index 8c34997b9..f06d4def2 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 ')'