diff options
-rw-r--r-- | AST.cpp | 38 | ||||
-rw-r--r-- | AST.h | 28 | ||||
-rw-r--r-- | ASTForward.h | 1 | ||||
-rw-r--r-- | ASTPrinter.cpp | 13 | ||||
-rw-r--r-- | ASTPrinter.h | 2 | ||||
-rw-r--r-- | ASTVisitor.h | 4 | ||||
-rw-r--r-- | AST_accept.h | 20 | ||||
-rw-r--r-- | Compiler.cpp | 17 | ||||
-rw-r--r-- | Compiler.h | 6 | ||||
-rw-r--r-- | CompilerContext.cpp | 8 | ||||
-rw-r--r-- | CompilerContext.h | 12 | ||||
-rw-r--r-- | CompilerStack.cpp | 6 | ||||
-rw-r--r-- | CompilerStack.h | 2 | ||||
-rw-r--r-- | ExpressionCompiler.cpp | 38 | ||||
-rw-r--r-- | ExpressionCompiler.h | 1 | ||||
-rw-r--r-- | InterfaceHandler.cpp | 8 | ||||
-rw-r--r-- | InterfaceHandler.h | 8 | ||||
-rw-r--r-- | Parser.cpp | 12 | ||||
-rw-r--r-- | Types.cpp | 13 | ||||
-rw-r--r-- | Types.h | 11 | ||||
-rw-r--r-- | grammar.txt | 3 |
21 files changed, 221 insertions, 30 deletions
@@ -54,6 +54,14 @@ vector<FunctionDefinition const*> ContractDefinition::getInterfaceFunctions() co return exportedFunctions; } +FunctionDefinition const* ContractDefinition::getConstructor() const +{ + for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions) + if (f->getName() == getName()) + return f.get(); + return nullptr; +} + void StructDefinition::checkMemberTypes() const { for (ASTPointer<VariableDeclaration> const& member: getMembers()) @@ -235,13 +243,12 @@ void FunctionCall::checkTypeRequirements() BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed.")); m_type = type.getActualType(); } - else + else if (FunctionType const* functionType = dynamic_cast<FunctionType const*>(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 - FunctionType const& functionType = dynamic_cast<FunctionType const&>(*expressionType); - TypePointers const& parameterTypes = functionType.getParameterTypes(); + TypePointers const& parameterTypes = functionType->getParameterTypes(); if (parameterTypes.size() != m_arguments.size()) BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call.")); for (size_t i = 0; i < m_arguments.size(); ++i) @@ -249,11 +256,13 @@ void FunctionCall::checkTypeRequirements() BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in function call.")); // @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()) + if (functionType->getReturnParameterTypes().empty()) m_type = make_shared<VoidType>(); else - m_type = functionType.getReturnParameterTypes().front(); + m_type = functionType->getReturnParameterTypes().front(); } + else + BOOST_THROW_EXCEPTION(createTypeError("Type is not callable.")); } bool FunctionCall::isTypeConversion() const @@ -261,6 +270,25 @@ bool FunctionCall::isTypeConversion() const return m_expression->getType()->getCategory() == Type::Category::TYPE; } +void NewExpression::checkTypeRequirements() +{ + m_contractName->checkTypeRequirements(); + for (ASTPointer<Expression> const& argument: m_arguments) + argument->checkTypeRequirements(); + + m_contract = dynamic_cast<ContractDefinition const*>(m_contractName->getReferencedDeclaration()); + if (!m_contract) + BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract.")); + shared_ptr<ContractType const> type = make_shared<ContractType const>(*m_contract); + m_type = type; + TypePointers const& parameterTypes = type->getConstructorType()->getParameterTypes(); + if (parameterTypes.size() != m_arguments.size()) + BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for constructor call.")); + for (size_t i = 0; i < m_arguments.size(); ++i) + if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i])) + BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in constructor call.")); +} + void MemberAccess::checkTypeRequirements() { m_expression->checkTypeRequirements(); @@ -181,6 +181,9 @@ public: /// Returns the functions that make up the calling interface in the intended order. std::vector<FunctionDefinition const*> getInterfaceFunctions() const; + /// Returns the constructor or nullptr if no constructor was specified + FunctionDefinition const* getConstructor() const; + private: std::vector<ASTPointer<StructDefinition>> m_definedStructs; std::vector<ASTPointer<VariableDeclaration>> m_stateVariables; @@ -741,6 +744,31 @@ private: }; /** + * Expression that creates a new contract, e.g. "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) {} + 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 { return m_contract; } + +private: + ASTPointer<Identifier> m_contractName; + std::vector<ASTPointer<Expression>> m_arguments; + + ContractDefinition const* m_contract = nullptr; +}; + +/** * Access to a member of an object. Example: x.name */ class MemberAccess: public Expression diff --git a/ASTForward.h b/ASTForward.h index 8b4bac1c..d6c4c9f4 100644 --- a/ASTForward.h +++ b/ASTForward.h @@ -62,6 +62,7 @@ class Assignment; class UnaryOperation; class BinaryOperation; class FunctionCall; +class NewExpression; class MemberAccess; class IndexAccess; class PrimaryExpression; diff --git a/ASTPrinter.cpp b/ASTPrinter.cpp index eddb5134..970822a9 100644 --- a/ASTPrinter.cpp +++ b/ASTPrinter.cpp @@ -226,6 +226,14 @@ bool ASTPrinter::visit(FunctionCall const& _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()); @@ -402,6 +410,11 @@ void ASTPrinter::endVisit(FunctionCall const&) m_indentation--; } +void ASTPrinter::endVisit(NewExpression const&) +{ + m_indentation--; +} + void ASTPrinter::endVisit(MemberAccess const&) { m_indentation--; diff --git a/ASTPrinter.h b/ASTPrinter.h index 5a918715..15b65e3f 100644 --- a/ASTPrinter.h +++ b/ASTPrinter.h @@ -67,6 +67,7 @@ public: 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; @@ -99,6 +100,7 @@ public: 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; diff --git a/ASTVisitor.h b/ASTVisitor.h index 4e1a4945..5728cd3d 100644 --- a/ASTVisitor.h +++ b/ASTVisitor.h @@ -68,6 +68,7 @@ public: 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; } @@ -102,6 +103,7 @@ public: 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&) { } @@ -140,6 +142,7 @@ public: 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; } @@ -174,6 +177,7 @@ public: 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&) { } diff --git a/AST_accept.h b/AST_accept.h index 173273c6..e0454d33 100644 --- a/AST_accept.h +++ b/AST_accept.h @@ -419,6 +419,26 @@ void FunctionCall::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } +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); +} + void MemberAccess::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) diff --git a/Compiler.cpp b/Compiler.cpp index 25833691..48d8be88 100644 --- a/Compiler.cpp +++ b/Compiler.cpp @@ -33,9 +33,11 @@ using namespace std; namespace dev { namespace solidity { -void Compiler::compileContract(ContractDefinition const& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals) +void Compiler::compileContract(ContractDefinition const& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals, + map<ContractDefinition const*, bytes const*> const& _contracts) { m_context = CompilerContext(); // clear it just in case + m_context.setCompiledContracts(_contracts); for (MagicVariableDeclaration const* variable: _magicGlobals) m_context.addMagicGlobal(*variable); @@ -50,12 +52,14 @@ void Compiler::compileContract(ContractDefinition const& _contract, vector<Magic if (function->getName() != _contract.getName()) // don't add the constructor here function->accept(*this); - packIntoContractCreator(_contract); + packIntoContractCreator(_contract, _contracts); } -void Compiler::packIntoContractCreator(ContractDefinition const& _contract) +void Compiler::packIntoContractCreator(ContractDefinition const& _contract, + map<ContractDefinition const*, bytes const*> const& _contracts) { CompilerContext runtimeContext; + runtimeContext.setCompiledContracts(_contracts); swap(m_context, runtimeContext); registerStateVariables(_contract); @@ -67,11 +71,14 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract) constructor = function.get(); break; } + eth::AssemblyItem sub = m_context.addSubroutine(runtimeContext.getAssembly()); + // stack contains sub size if (constructor) { eth::AssemblyItem returnTag = m_context.pushNewTag(); m_context.addFunction(*constructor); // note that it cannot be called due to syntactic reasons - //@todo copy constructor arguments from calldata to memory prior to this + // copy constructor arguments + //@todo ask assembly for the size of the current program //@todo calling other functions inside the constructor should either trigger a parse error //or we should copy them here (register them above and call "accept") - detecting which // functions are referenced / called needs to be done in a recursive way. @@ -81,8 +88,6 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract) m_context << returnTag; } - 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; } @@ -32,14 +32,16 @@ class Compiler: private ASTConstVisitor public: explicit Compiler(bool _optimize = false): m_optimize(_optimize), m_returnTag(m_context.newTag()) {} - void compileContract(ContractDefinition const& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals); + void compileContract(ContractDefinition const& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals, + std::map<ContractDefinition const*, bytes const*> const& _contracts); bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); } void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); } private: /// Creates a new compiler context / assembly, packs the current code into the data part and /// adds the constructor code. - void packIntoContractCreator(ContractDefinition const& _contract); + void packIntoContractCreator(ContractDefinition const& _contract, + std::map<ContractDefinition const*, bytes const*> const& _contracts); void appendFunctionSelector(ContractDefinition const& _contract); /// Creates code that unpacks the arguments for the given function, from memory if /// @a _fromMemory is true, otherwise from call data. @returns the size of the data in bytes. diff --git a/CompilerContext.cpp b/CompilerContext.cpp index cd22c4e8..47401436 100644 --- a/CompilerContext.cpp +++ b/CompilerContext.cpp @@ -62,6 +62,14 @@ void CompilerContext::addFunction(FunctionDefinition const& _function) m_functionEntryLabels.insert(std::make_pair(&_function, m_asm.newTag())); } +bytes const& CompilerContext::getCompiledContract(const ContractDefinition& _contract) const +{ + auto ret = m_compiledContracts.find(&_contract); + if (asserts(ret != m_compiledContracts.end())) + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Compiled contract not found.")); + return *ret->second; +} + bool CompilerContext::isLocalVariable(Declaration const* _declaration) const { return m_localVariables.count(_declaration) > 0; diff --git a/CompilerContext.h b/CompilerContext.h index 652e65a6..99f5ae94 100644 --- a/CompilerContext.h +++ b/CompilerContext.h @@ -39,8 +39,6 @@ namespace solidity { class CompilerContext { public: - CompilerContext(): m_stateVariablesSize(0) {} - void addMagicGlobal(MagicVariableDeclaration const& _declaration); void addStateVariable(VariableDeclaration const& _declaration); void startNewFunction() { m_localVariables.clear(); m_asm.setDeposit(0); } @@ -48,6 +46,9 @@ public: void addAndInitializeVariable(VariableDeclaration const& _declaration); void addFunction(FunctionDefinition const& _function); + void setCompiledContracts(std::map<ContractDefinition const*, bytes const*> const& _contracts) { m_compiledContracts = _contracts; } + bytes const& getCompiledContract(ContractDefinition const& _contract) const; + void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); } bool isMagicGlobal(Declaration const* _declaration) const { return m_magicGlobals.count(_declaration); } @@ -80,6 +81,8 @@ public: /// 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); } + /// 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; } @@ -90,13 +93,16 @@ public: 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<Declaration const*> m_magicGlobals; + /// Other already compiled contracts to be used in contract creation calls. + std::map<ContractDefinition const*, bytes const*> m_compiledContracts; /// Size of the state variables, offset of next variable to be added. - u256 m_stateVariablesSize; + u256 m_stateVariablesSize = 0; /// Storage offsets of state variables std::map<Declaration const*, u256> m_stateVariables; /// Offsets of local variables on the stack (relative to stack base). diff --git a/CompilerStack.cpp b/CompilerStack.cpp index 7aaa79b1..23f5fd68 100644 --- a/CompilerStack.cpp +++ b/CompilerStack.cpp @@ -96,16 +96,20 @@ void CompilerStack::compile(bool _optimize) { if (!m_parseSuccessful) parse(); + + map<ContractDefinition const*, bytes const*> contractBytecode; for (Source const* source: m_sourceOrder) for (ASTPointer<ASTNode> const& node: source->ast->getNodes()) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) { m_globalContext->setCurrentContract(*contract); shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize); - compiler->compileContract(*contract, m_globalContext->getMagicVariables()); + compiler->compileContract(*contract, m_globalContext->getMagicVariables(), + contractBytecode); Contract& compiledContract = m_contracts[contract->getName()]; compiledContract.bytecode = compiler->getAssembledBytecode(); compiledContract.compiler = move(compiler); + contractBytecode[compiledContract.contract] = &compiledContract.bytecode; } } diff --git a/CompilerStack.h b/CompilerStack.h index b6c34f1a..5ad6f0a6 100644 --- a/CompilerStack.h +++ b/CompilerStack.h @@ -108,7 +108,7 @@ private: struct Contract { - ContractDefinition* contract; + ContractDefinition const* contract; std::shared_ptr<Compiler> compiler; bytes bytecode; std::shared_ptr<InterfaceHandler> interfaceHandler; diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index f872e058..743503bb 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -279,6 +279,44 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) return false; } +bool ExpressionCompiler::visit(NewExpression const& _newExpression) +{ + ContractType const* type = dynamic_cast<ContractType const*>(_newExpression.getType().get()); + if (asserts(type)) + BOOST_THROW_EXCEPTION(InternalCompilerError()); + TypePointers const& types = type->getConstructorType()->getParameterTypes(); + vector<ASTPointer<Expression const>> arguments = _newExpression.getArguments(); + if (asserts(arguments.size() == types.size())) + BOOST_THROW_EXCEPTION(InternalCompilerError()); + + // copy the contracts code into memory + bytes const& bytecode = m_context.getCompiledContract(*_newExpression.getContract()); + m_context << u256(bytecode.size()); + //@todo could be done by actually appending the Assembly, but then we probably need to compile + // multiple times. Will revisit once external fuctions are inlined. + m_context.appendData(bytecode); + //@todo copy to memory position 0, shift as soon as we use memory + m_context << u256(0) << eth::Instruction::CODECOPY; + + unsigned dataOffset = bytecode.size(); + for (unsigned i = 0; i < arguments.size(); ++i) + { + arguments[i]->accept(*this); + appendTypeConversion(*arguments[i]->getType(), *types[i]); + unsigned const numBytes = types[i]->getCalldataEncodedSize(); + if (numBytes > 32) + BOOST_THROW_EXCEPTION(CompilerError() + << errinfo_sourceLocation(arguments[i]->getLocation()) + << errinfo_comment("Type " + types[i]->toString() + " not yet supported.")); + bool const leftAligned = types[i]->getCategory() == Type::Category::STRING; + CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned); + dataOffset += numBytes; + } + // size, offset, endowment + m_context << u256(dataOffset) << u256(0) << u256(0) << eth::Instruction::CREATE; + return false; +} + void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) { ASTString const& member = _memberAccess.getMemberName(); diff --git a/ExpressionCompiler.h b/ExpressionCompiler.h index 0bba211c..67b16aac 100644 --- a/ExpressionCompiler.h +++ b/ExpressionCompiler.h @@ -60,6 +60,7 @@ private: virtual void endVisit(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; diff --git a/InterfaceHandler.cpp b/InterfaceHandler.cpp index f26088af..1310f504 100644 --- a/InterfaceHandler.cpp +++ b/InterfaceHandler.cpp @@ -15,7 +15,7 @@ InterfaceHandler::InterfaceHandler() m_lastTag = DocTagType::NONE; } -std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefinition& _contractDef, +std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefinition const& _contractDef, DocumentationType _type) { switch(_type) @@ -32,7 +32,7 @@ std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefiniti return nullptr; } -std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinition& _contractDef) +std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinition const& _contractDef) { Json::Value methods(Json::arrayValue); @@ -63,7 +63,7 @@ std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinitio return std::unique_ptr<std::string>(new std::string(m_writer.write(methods))); } -std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefinition& _contractDef) +std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefinition const& _contractDef) { Json::Value doc; Json::Value methods(Json::objectValue); @@ -88,7 +88,7 @@ std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefi return std::unique_ptr<std::string>(new std::string(m_writer.write(doc))); } -std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefinition& _contractDef) +std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefinition const& _contractDef) { // LTODO: Somewhere in this function warnings for mismatch of param names // should be thrown diff --git a/InterfaceHandler.h b/InterfaceHandler.h index b1cd4b56..d271a669 100644 --- a/InterfaceHandler.h +++ b/InterfaceHandler.h @@ -67,23 +67,23 @@ public: /// types provided by @c DocumentationType /// @return A unique pointer contained string with the json /// representation of provided type - std::unique_ptr<std::string> getDocumentation(ContractDefinition& _contractDef, + std::unique_ptr<std::string> 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<std::string> getABIInterface(ContractDefinition& _contractDef); + std::unique_ptr<std::string> getABIInterface(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<std::string> getUserDocumentation(ContractDefinition& _contractDef); + std::unique_ptr<std::string> 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<std::string> getDevDocumentation(ContractDefinition& _contractDef); + std::unique_ptr<std::string> getDevDocumentation(ContractDefinition const& _contractDef); private: void resetUser(); @@ -437,7 +437,17 @@ ASTPointer<Expression> Parser::parseUnaryExpression() { ASTNodeFactory nodeFactory(*this); Token::Value token = m_scanner->getCurrentToken(); - if (Token::isUnaryOp(token) || Token::isCountOp(token)) + 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)) { // prefix expression m_scanner->next(); @@ -310,6 +310,19 @@ MemberList const& ContractType::getMembers() const return *m_members; } +shared_ptr<FunctionType const> const& ContractType::getConstructorType() const +{ + if (!m_constructorType) + { + FunctionDefinition const* constr = m_contract.getConstructor(); + if (constr) + m_constructorType = make_shared<FunctionType const>(*constr); + else + m_constructorType = make_shared<FunctionType const>(TypePointers(), TypePointers()); + } + return m_constructorType; +} + unsigned ContractType::getFunctionIndex(string const& _functionName) const { unsigned index = 0; @@ -39,6 +39,7 @@ namespace solidity // @todo realMxN, dynamic strings, text, arrays class Type; // forward +class FunctionType; // forward using TypePointer = std::shared_ptr<Type const>; using TypePointers = std::vector<TypePointer>; @@ -249,10 +250,16 @@ public: virtual MemberList const& getMembers() const override; + /// 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; + unsigned getFunctionIndex(std::string const& _functionName) const; private: ContractDefinition const& m_contract; + /// Type of the constructor, @see getConstructorType. Lazily initialized. + mutable std::shared_ptr<FunctionType const> m_constructorType; /// List of member types, will be lazy-initialized because of recursive references. mutable std::unique_ptr<MemberList> m_members; }; @@ -339,8 +346,8 @@ public: virtual std::string toString() const override; virtual bool canLiveOutsideStorage() const override { return false; } - TypePointer getKeyType() const { return m_keyType; } - TypePointer getValueType() const { return m_valueType; } + TypePointer const& getKeyType() const { return m_keyType; } + TypePointer const& getValueType() const { return m_valueType; } private: TypePointer m_keyType; diff --git a/grammar.txt b/grammar.txt index c0ab0607..793e9188 100644 --- a/grammar.txt +++ b/grammar.txt @@ -25,11 +25,12 @@ Break = 'break' ';' Return = 'return' Expression? ';' VariableDefinition = VariableDeclaration ( = Expression )? ';' -Expression = Assignment | UnaryOperation | BinaryOperation | FunctionCall | IndexAccess | +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 '(' ( Expression ( ',' Expression )* ) ')' MemberAccess = Expression '.' Identifier IndexAccess = Expression '[' Expresison ']' PrimaryExpression = Identifier | NumberLiteral | StringLiteral | ElementaryTypeName | '(' Expression ')' |