From 5026c5855ded326a5ea7e675d8c084ccda88c8ee Mon Sep 17 00:00:00 2001 From: Christian Date: Fri, 12 Dec 2014 16:49:26 +0100 Subject: [PATCH 01/33] Create contracts. --- libsolidity/AST.cpp | 38 ++++++++++++++++++++++++++---- libsolidity/AST.h | 28 ++++++++++++++++++++++ libsolidity/ASTForward.h | 1 + libsolidity/ASTPrinter.cpp | 13 ++++++++++ libsolidity/ASTPrinter.h | 2 ++ libsolidity/ASTVisitor.h | 4 ++++ libsolidity/AST_accept.h | 20 ++++++++++++++++ libsolidity/Compiler.cpp | 17 ++++++++----- libsolidity/Compiler.h | 6 +++-- libsolidity/CompilerContext.cpp | 8 +++++++ libsolidity/CompilerContext.h | 12 +++++++--- libsolidity/CompilerStack.cpp | 6 ++++- libsolidity/CompilerStack.h | 2 +- libsolidity/ExpressionCompiler.cpp | 38 ++++++++++++++++++++++++++++++ libsolidity/ExpressionCompiler.h | 1 + libsolidity/InterfaceHandler.cpp | 8 +++---- libsolidity/InterfaceHandler.h | 8 +++---- libsolidity/Parser.cpp | 12 +++++++++- libsolidity/Types.cpp | 13 ++++++++++ libsolidity/Types.h | 11 +++++++-- libsolidity/grammar.txt | 3 ++- test/solidityCompiler.cpp | 2 +- 22 files changed, 222 insertions(+), 31 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 8174d138a..3fcf8ba0b 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -54,6 +54,14 @@ vector ContractDefinition::getInterfaceFunctions() co return exportedFunctions; } +FunctionDefinition const* ContractDefinition::getConstructor() const +{ + for (ASTPointer const& f: m_definedFunctions) + if (f->getName() == getName()) + return f.get(); + return nullptr; +} + void StructDefinition::checkMemberTypes() const { for (ASTPointer 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(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(*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(); 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 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.")); +} + void MemberAccess::checkTypeRequirements() { m_expression->checkTypeRequirements(); diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 19c4b4275..3a15bbebe 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -181,6 +181,9 @@ public: /// Returns the functions that make up the calling interface in the intended order. std::vector getInterfaceFunctions() const; + /// Returns the constructor or nullptr if no constructor was specified + FunctionDefinition const* getConstructor() const; + private: std::vector> m_definedStructs; std::vector> m_stateVariables; @@ -740,6 +743,31 @@ private: std::vector> m_arguments; }; +/** + * Expression that creates a new contract, e.g. "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) {} + 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 { return m_contract; } + +private: + ASTPointer m_contractName; + std::vector> m_arguments; + + ContractDefinition const* m_contract = nullptr; +}; + /** * Access to a member of an object. Example: x.name */ diff --git a/libsolidity/ASTForward.h b/libsolidity/ASTForward.h index 8b4bac1ce..d6c4c9f44 100644 --- a/libsolidity/ASTForward.h +++ b/libsolidity/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/libsolidity/ASTPrinter.cpp b/libsolidity/ASTPrinter.cpp index eddb51340..970822a9a 100644 --- a/libsolidity/ASTPrinter.cpp +++ b/libsolidity/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/libsolidity/ASTPrinter.h b/libsolidity/ASTPrinter.h index 5a9187154..15b65e3f0 100644 --- a/libsolidity/ASTPrinter.h +++ b/libsolidity/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/libsolidity/ASTVisitor.h b/libsolidity/ASTVisitor.h index 4e1a49458..5728cd3d0 100644 --- a/libsolidity/ASTVisitor.h +++ b/libsolidity/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/libsolidity/AST_accept.h b/libsolidity/AST_accept.h index 173273c6d..e0454d33e 100644 --- a/libsolidity/AST_accept.h +++ b/libsolidity/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/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 258336917..48d8be88a 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -33,9 +33,11 @@ using namespace std; namespace dev { namespace solidity { -void Compiler::compileContract(ContractDefinition const& _contract, vector const& _magicGlobals) +void Compiler::compileContract(ContractDefinition const& _contract, vector const& _magicGlobals, + map 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, vectorgetName() != _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 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; } diff --git a/libsolidity/Compiler.h b/libsolidity/Compiler.h index 639e98410..57e40cdae 100644 --- a/libsolidity/Compiler.h +++ b/libsolidity/Compiler.h @@ -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 const& _magicGlobals); + void compileContract(ContractDefinition const& _contract, std::vector const& _magicGlobals, + std::map 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 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/libsolidity/CompilerContext.cpp b/libsolidity/CompilerContext.cpp index cd22c4e8b..47401436a 100644 --- a/libsolidity/CompilerContext.cpp +++ b/libsolidity/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/libsolidity/CompilerContext.h b/libsolidity/CompilerContext.h index 652e65a63..99f5ae94c 100644 --- a/libsolidity/CompilerContext.h +++ b/libsolidity/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 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 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; + u256 m_stateVariablesSize = 0; /// Storage offsets of state variables std::map m_stateVariables; /// Offsets of local variables on the stack (relative to stack base). diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index 7aaa79b1c..23f5fd68f 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -96,16 +96,20 @@ 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())) { m_globalContext->setCurrentContract(*contract); shared_ptr compiler = make_shared(_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/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index b6c34f1ac..5ad6f0a60 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -108,7 +108,7 @@ private: struct Contract { - ContractDefinition* contract; + ContractDefinition const* contract; std::shared_ptr compiler; bytes bytecode; std::shared_ptr interfaceHandler; diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index f872e0581..743503bbf 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/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(_newExpression.getType().get()); + if (asserts(type)) + BOOST_THROW_EXCEPTION(InternalCompilerError()); + TypePointers const& types = type->getConstructorType()->getParameterTypes(); + vector> 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/libsolidity/ExpressionCompiler.h b/libsolidity/ExpressionCompiler.h index 0bba211ce..67b16aac0 100644 --- a/libsolidity/ExpressionCompiler.h +++ b/libsolidity/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/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index f26088afa..1310f504a 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -15,7 +15,7 @@ InterfaceHandler::InterfaceHandler() m_lastTag = DocTagType::NONE; } -std::unique_ptr InterfaceHandler::getDocumentation(ContractDefinition& _contractDef, +std::unique_ptr InterfaceHandler::getDocumentation(ContractDefinition const& _contractDef, DocumentationType _type) { switch(_type) @@ -32,7 +32,7 @@ std::unique_ptr InterfaceHandler::getDocumentation(ContractDefiniti return nullptr; } -std::unique_ptr InterfaceHandler::getABIInterface(ContractDefinition& _contractDef) +std::unique_ptr InterfaceHandler::getABIInterface(ContractDefinition const& _contractDef) { Json::Value methods(Json::arrayValue); @@ -63,7 +63,7 @@ std::unique_ptr InterfaceHandler::getABIInterface(ContractDefinitio return std::unique_ptr(new std::string(m_writer.write(methods))); } -std::unique_ptr InterfaceHandler::getUserDocumentation(ContractDefinition& _contractDef) +std::unique_ptr InterfaceHandler::getUserDocumentation(ContractDefinition const& _contractDef) { Json::Value doc; Json::Value methods(Json::objectValue); @@ -88,7 +88,7 @@ std::unique_ptr InterfaceHandler::getUserDocumentation(ContractDefi return std::unique_ptr(new std::string(m_writer.write(doc))); } -std::unique_ptr InterfaceHandler::getDevDocumentation(ContractDefinition& _contractDef) +std::unique_ptr InterfaceHandler::getDevDocumentation(ContractDefinition const& _contractDef) { // LTODO: Somewhere in this function warnings for mismatch of param names // should be thrown diff --git a/libsolidity/InterfaceHandler.h b/libsolidity/InterfaceHandler.h index b1cd4b562..d271a6697 100644 --- a/libsolidity/InterfaceHandler.h +++ b/libsolidity/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 getDocumentation(ContractDefinition& _contractDef, + 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& _contractDef); + std::unique_ptr 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 getUserDocumentation(ContractDefinition& _contractDef); + 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& _contractDef); + std::unique_ptr getDevDocumentation(ContractDefinition const& _contractDef); private: void resetUser(); diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index b678b2fc0..21651eb14 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -437,7 +437,17 @@ ASTPointer 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 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)) { // prefix expression m_scanner->next(); diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index c2d488418..f1cd7c228 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -310,6 +310,19 @@ MemberList const& ContractType::getMembers() const return *m_members; } +shared_ptr const& ContractType::getConstructorType() const +{ + if (!m_constructorType) + { + FunctionDefinition const* constr = m_contract.getConstructor(); + if (constr) + m_constructorType = make_shared(*constr); + else + m_constructorType = make_shared(TypePointers(), TypePointers()); + } + return m_constructorType; +} + unsigned ContractType::getFunctionIndex(string const& _functionName) const { unsigned index = 0; diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 807e60c74..48539a1d7 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -39,6 +39,7 @@ namespace solidity // @todo realMxN, dynamic strings, text, arrays class Type; // forward +class FunctionType; // forward using TypePointer = std::shared_ptr; using TypePointers = std::vector; @@ -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 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 m_constructorType; /// List of member types, will be lazy-initialized because of recursive references. mutable std::unique_ptr 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/libsolidity/grammar.txt b/libsolidity/grammar.txt index c0ab06074..793e91882 100644 --- a/libsolidity/grammar.txt +++ b/libsolidity/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 ')' diff --git a/test/solidityCompiler.cpp b/test/solidityCompiler.cpp index eae8f3142..29f61454a 100644 --- a/test/solidityCompiler.cpp +++ b/test/solidityCompiler.cpp @@ -56,7 +56,7 @@ bytes compileContract(const string& _sourceCode) BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); Compiler compiler; - compiler.compileContract(*contract, {}); + compiler.compileContract(*contract, {}, {}); // debug //compiler.streamAssembly(cout); return compiler.getAssembledBytecode(); From 7801b87ddb2b21f32af1495e367f11bc99c88766 Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 15 Dec 2014 12:59:17 +0100 Subject: [PATCH 02/33] Packing and unpacking of constructor arguments. --- libevmcore/Assembly.cpp | 21 ++++++++++++++++++++- libevmcore/Assembly.h | 5 ++++- libsolidity/Compiler.cpp | 16 ++++++++++++---- libsolidity/CompilerContext.h | 2 ++ test/solidityEndToEndTest.cpp | 26 ++++++++++++++++++++++++++ 5 files changed, 64 insertions(+), 6 deletions(-) diff --git a/libevmcore/Assembly.cpp b/libevmcore/Assembly.cpp index 059810af8..b78f03035 100644 --- a/libevmcore/Assembly.cpp +++ b/libevmcore/Assembly.cpp @@ -39,6 +39,7 @@ unsigned AssemblyItem::bytesRequired(unsigned _addressLength) const case Push: return 1 + max(1, dev::bytesRequired(m_data)); case PushSubSize: + case PushProgramSize: return 4; // worst case: a 16MB program case PushTag: case PushData: @@ -59,7 +60,7 @@ int AssemblyItem::deposit() const { case Operation: return instructionInfo((Instruction)(byte)m_data).ret - instructionInfo((Instruction)(byte)m_data).args; - case Push: case PushString: case PushTag: case PushData: case PushSub: case PushSubSize: + case Push: case PushString: case PushTag: case PushData: case PushSub: case PushSubSize: case PushProgramSize: return 1; case Tag: return 0; @@ -146,6 +147,9 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItemsConstRef _i) case PushSubSize: _out << " PUSHss[" << hex << h256(i.data()).abridged() << "]"; break; + case PushProgramSize: + _out << " PUSHSIZE"; + break; case NoOptimizeBegin: _out << " DoNotOptimze{{"; break; @@ -185,6 +189,9 @@ ostream& Assembly::streamRLP(ostream& _out, string const& _prefix) const case PushSubSize: _out << _prefix << " PUSH #[$" << h256(i.m_data).abridged() << "]" << endl; break; + case PushProgramSize: + _out << _prefix << " PUSHSIZE" << endl; + break; case Tag: _out << _prefix << "tag" << i.m_data << ": " << endl << _prefix << " JUMPDEST" << endl; break; @@ -303,6 +310,7 @@ Assembly& Assembly::optimise(bool _enable) { { PushString, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, { { PushSub, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, { { PushSubSize, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, + { { PushProgramSize, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, { { Push, PushTag, Instruction::JUMPI }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].data()) return { m[1], Instruction::JUMP }; else return {}; } }, { { Instruction::ISZERO, Instruction::ISZERO }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, }; @@ -468,6 +476,7 @@ bytes Assembly::assemble() const vector tagPos(m_usedTags); map tagRef; multimap dataRef; + vector sizeRef; ///< Pointers to code locations where the size of the program is inserted unsigned bytesPerTag = dev::bytesRequired(totalBytes); byte tagPush = (byte)Instruction::PUSH1 - 1 + bytesPerTag; @@ -526,6 +535,11 @@ bytes Assembly::assemble() const toBigEndian(s, byr); break; } + case PushProgramSize: + ret.push_back(tagPush); + sizeRef.push_back(ret.size()); + ret.resize(ret.size() + bytesPerTag); + break; case Tag: tagPos[(unsigned)i.m_data] = ret.size(); ret.push_back((byte)Instruction::JUMPDEST); @@ -561,5 +575,10 @@ bytes Assembly::assemble() const } } } + for (unsigned pos: sizeRef) + { + bytesRef r(ret.data() + pos, bytesPerTag); + toBigEndian(ret.size(), r); + } return ret; } diff --git a/libevmcore/Assembly.h b/libevmcore/Assembly.h index 5523710d4..81e4a9ff7 100644 --- a/libevmcore/Assembly.h +++ b/libevmcore/Assembly.h @@ -32,7 +32,7 @@ namespace dev namespace eth { -enum AssemblyItemType { UndefinedItem, Operation, Push, PushString, PushTag, PushSub, PushSubSize, Tag, PushData, NoOptimizeBegin, NoOptimizeEnd }; +enum AssemblyItemType { UndefinedItem, Operation, Push, PushString, PushTag, PushSub, PushSubSize, PushProgramSize, Tag, PushData, NoOptimizeBegin, NoOptimizeEnd }; class Assembly; @@ -86,6 +86,9 @@ public: AssemblyItem const& append(std::string const& _data) { return append(newPushString(_data)); } AssemblyItem const& append(bytes const& _data) { return append(newData(_data)); } AssemblyItem appendSubSize(Assembly const& _a) { auto ret = newSub(_a); append(newPushSubSize(ret.data())); return ret; } + /// Pushes the final size of the current assembly itself. Use this when the code is modified + /// after compilation and CODESIZE is not an option. + void appendProgramSize() { append(AssemblyItem(PushProgramSize)); } AssemblyItem appendJump() { auto ret = append(newPushTag()); append(Instruction::JUMP); return ret; } AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(Instruction::JUMPI); return ret; } diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 48d8be88a..f2877c1ce 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -77,12 +77,21 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract, { eth::AssemblyItem returnTag = m_context.pushNewTag(); m_context.addFunction(*constructor); // note that it cannot be called due to syntactic reasons - // copy constructor arguments - //@todo ask assembly for the size of the current program + // 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 += var->getType()->getCalldataEncodedSize(); + if (argumentSize > 0) + { + m_context << u256(argumentSize); + m_context.appendProgramSize(); + m_context << u256(1); // copy it to byte one as expected for ABI calls + m_context << eth::Instruction::CODECOPY; + appendCalldataUnpacker(*constructor, true); + } //@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. - appendCalldataUnpacker(*constructor, true); m_context.appendJumpTo(m_context.getFunctionEntryLabel(*constructor)); constructor->accept(*this); m_context << returnTag; @@ -135,7 +144,6 @@ unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, b { // We do not check the calldata size, everything is zero-padded. unsigned dataOffset = 1; - //@todo this can be done more efficiently, saving some CALLDATALOAD calls for (ASTPointer const& var: _function.getParameters()) { diff --git a/libsolidity/CompilerContext.h b/libsolidity/CompilerContext.h index 99f5ae94c..795f447ab 100644 --- a/libsolidity/CompilerContext.h +++ b/libsolidity/CompilerContext.h @@ -81,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); } + /// 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); } diff --git a/test/solidityEndToEndTest.cpp b/test/solidityEndToEndTest.cpp index 15722abe1..b6f63aa72 100644 --- a/test/solidityEndToEndTest.cpp +++ b/test/solidityEndToEndTest.cpp @@ -1012,6 +1012,32 @@ BOOST_AUTO_TEST_CASE(strings_in_calls) BOOST_CHECK(callContractFunction(0, bytes({0, 'a', 1})) == bytes({0, 'a', 0, 0, 0})); } +BOOST_AUTO_TEST_CASE(constructor_arguments) +{ + char const* sourceCode = R"( + contract Helper { + string3 name; + bool flag; + function Helper(string3 x, bool f) { + name = x; + flag = f; + } + function getName() returns (string3 ret) { return name; } + function getFlag() returns (bool ret) { return flag; } + } + contract Main { + Helper h; + function Main() { + h = new Helper("abc", true); + } + function getFlag() returns (bool ret) { return h.getFlag(); } + function getName() returns (string3 ret) { return h.getName(); } + })"; + compileAndRun(sourceCode, 0, "Main"); + BOOST_REQUIRE(callContractFunction(0) == bytes({0x01})); + BOOST_REQUIRE(callContractFunction(1) == bytes({'a', 'b', 'c'})); +} + BOOST_AUTO_TEST_SUITE_END() } From a3473ad88d39a1491d3b13da7efd11537eede1f1 Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 15 Dec 2014 16:09:50 +0100 Subject: [PATCH 03/33] Check that constructor does not have "returns" directive. --- libsolidity/AST.cpp | 11 +++++++++++ libsolidity/AST.h | 4 ++++ libsolidity/NameAndTypeResolver.cpp | 6 +----- test/solidityNameAndTypeResolution.cpp | 9 +++++++++ 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 3fcf8ba0b..5e344eadb 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -39,6 +39,17 @@ TypeError ASTNode::createTypeError(string const& _description) const return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description); } +void ContractDefinition::checkTypeRequirements() +{ + FunctionDefinition const* constructor = getConstructor(); + if (constructor && !constructor->getReturnParameters().empty()) + BOOST_THROW_EXCEPTION(constructor->getReturnParameterList()->createTypeError( + "Non-empty \"returns\" directive for constructor.")); + + for (ASTPointer const& function: getDefinedFunctions()) + function->checkTypeRequirements(); +} + vector ContractDefinition::getInterfaceFunctions() const { vector exportedFunctions; diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 3a15bbebe..72f96394f 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -174,6 +174,10 @@ public: std::vector> const& getStateVariables() const { return m_stateVariables; } std::vector> const& getDefinedFunctions() const { return m_definedFunctions; } + /// Checks that the constructor does not have a "returns" statement and calls + /// checkTypeRequirements on all its functions. + void checkTypeRequirements(); + /// @return A shared pointer of an ASTString. /// Can contain a nullptr in which case indicates absence of documentation ASTPointer const& getDocumentation() const { return m_documentation; } diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index 540b066eb..2ad276801 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -62,11 +62,7 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) // First, the parameter types of all functions need to be resolved before we can check // the types, since it is possible to call functions that are only defined later // in the source. - for (ASTPointer const& function: _contract.getDefinedFunctions()) - { - m_currentScope = &m_scopes[function.get()]; - function->checkTypeRequirements(); - } + _contract.checkTypeRequirements(); m_currentScope = &m_scopes[nullptr]; } diff --git a/test/solidityNameAndTypeResolution.cpp b/test/solidityNameAndTypeResolution.cpp index 03eaebb3a..0ea6e51b9 100644 --- a/test/solidityNameAndTypeResolution.cpp +++ b/test/solidityNameAndTypeResolution.cpp @@ -284,6 +284,15 @@ BOOST_AUTO_TEST_CASE(assignment_to_struct) BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); } +BOOST_AUTO_TEST_CASE(returns_in_constructor) +{ + char const* text = "contract test {\n" + " function test() returns (uint a) {\n" + " }\n" + "}\n"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); +} + BOOST_AUTO_TEST_SUITE_END() } From c00153e5c145cc7ac780195357a7ed6be9e9763a Mon Sep 17 00:00:00 2001 From: Christian Date: Tue, 16 Dec 2014 16:15:34 +0100 Subject: [PATCH 04/33] Stylistic changes. --- libevmcore/Assembly.cpp | 8 +++++++- libsolidity/AST.h | 2 +- libsolidity/Types.cpp | 6 +++--- libsolidity/grammar.txt | 2 +- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/libevmcore/Assembly.cpp b/libevmcore/Assembly.cpp index b78f03035..74bd015a6 100644 --- a/libevmcore/Assembly.cpp +++ b/libevmcore/Assembly.cpp @@ -60,7 +60,13 @@ int AssemblyItem::deposit() const { case Operation: return instructionInfo((Instruction)(byte)m_data).ret - instructionInfo((Instruction)(byte)m_data).args; - case Push: case PushString: case PushTag: case PushData: case PushSub: case PushSubSize: case PushProgramSize: + case Push: + case PushString: + case PushTag: + case PushData: + case PushSub: + case PushSubSize: + case PushProgramSize: return 1; case Tag: return 0; diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 72f96394f..4bb623b49 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -763,7 +763,7 @@ public: 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 { return m_contract; } + ContractDefinition const* getContract() const { if (asserts(m_contract)) BOOST_THROW_EXCEPTION(InternalCompilerError()); else return m_contract; } private: ASTPointer m_contractName; diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index f1cd7c228..eba11cb0d 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -314,9 +314,9 @@ shared_ptr const& ContractType::getConstructorType() const { if (!m_constructorType) { - FunctionDefinition const* constr = m_contract.getConstructor(); - if (constr) - m_constructorType = make_shared(*constr); + FunctionDefinition const* constructor = m_contract.getConstructor(); + if (constructor) + m_constructorType = make_shared(*constructor); else m_constructorType = make_shared(TypePointers(), TypePointers()); } diff --git a/libsolidity/grammar.txt b/libsolidity/grammar.txt index 793e91882..a26f717a1 100644 --- a/libsolidity/grammar.txt +++ b/libsolidity/grammar.txt @@ -29,7 +29,7 @@ Expression = Assignment | UnaryOperation | BinaryOperation | FunctionCall | NewE MemberAccess | PrimaryExpression // The expression syntax is actually much more complicated Assignment = Expression (AssignmentOp Expression) -FunctionCall = Expression '(' ( Expression ( ',' Expression )* ) ')' +FunctionCall = Expression '(' Expression ( ',' Expression )* ')' NewExpression = 'new' Identifier '(' ( Expression ( ',' Expression )* ) ')' MemberAccess = Expression '.' Identifier IndexAccess = Expression '[' Expresison ']' From 76db957b529f5d436ba2d53787def77769f7729f Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Tue, 16 Dec 2014 18:00:39 +0100 Subject: [PATCH 05/33] fixed macdeployqt options order --- cmake/EthExecutableHelper.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmake/EthExecutableHelper.cmake b/cmake/EthExecutableHelper.cmake index 56aa3f6c3..9d273522e 100644 --- a/cmake/EthExecutableHelper.cmake +++ b/cmake/EthExecutableHelper.cmake @@ -61,12 +61,13 @@ macro(eth_install_executable EXECUTABLE) if (ETH_INSTALL_EXECUTABLE_QMLDIR) set(eth_qml_dir "-qmldir=${ETH_INSTALL_EXECUTABLE_QMLDIR}") + message(STATUS "${EXECUTABLE} qmldir: ${eth_qml_dir}") endif() if (APPLE) # First have qt5 install plugins and frameworks add_custom_command(TARGET ${EXECUTABLE} POST_BUILD - COMMAND ${MACDEPLOYQT_APP} ${eth_qml_dir} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.app + COMMAND ${MACDEPLOYQT_APP} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.app ${eth_qml_dir} WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) # This tool and next will inspect linked libraries in order to determine which dependencies are required From 381c30ecd0cba487e523551dd1e7737847cb9f5f Mon Sep 17 00:00:00 2001 From: wanderer Date: Fri, 12 Dec 2014 09:35:18 -0500 Subject: [PATCH 06/33] changed output stacktrace format to json --- test/vm.cpp | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/test/vm.cpp b/test/vm.cpp index 49d6ed104..27e1da238 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -262,12 +262,45 @@ eth::OnOpFunc FakeExtVM::simpleTrace() dev::LogOutputStream(true) << o.str(); dev::LogOutputStream(false) << " | " << std::dec << ext.depth << " | " << ext.myAddress << " | #" << steps << " | " << std::hex << std::setw(4) << std::setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " | " << std::dec << vm.gas() << " | -" << std::dec << gasCost << " | " << newMemSize << "x32" << " ]"; + /*creates json stack trace*/ if (eth::VMTraceChannel::verbosity <= g_logVerbosity) { + std::ostringstream ofile; + + u256s stack = vm.stack(); + ofile << endl << "{" << endl << " \"stack\":[" << endl; + for (vector::iterator i = stack.begin(); i != stack.end(); ++i){ + ofile << " \"" << (h256)*i << "\""; + if(next(i) != stack.end()){ + ofile << ","; + } + + ofile << endl; + } + + ofile << " ]," << endl; + ofile << " \"memory\": \""; + for(auto i: vm.memory()) + ofile << setfill('0') << setw(2) << hex << (unsigned)i; + ofile << "\"," << endl; + + ofile << " \"storage\": [" << endl; + for (auto const& i: std::get<2>(ext.addresses.find(ext.myAddress)->second)){ + ofile << " [" << endl; + ofile << " \"" << std::showbase << std::hex << i.first << "\": \"" << i.second << "\"" << std::endl; + } + + ofile << " ]," << endl; + ofile << " \"depth\": " << dec << ext.depth << "," << endl; + ofile << " \"gas\":" << dec < Date: Sun, 14 Dec 2014 13:11:54 -0500 Subject: [PATCH 07/33] using json_spirit --- test/vm.cpp | 77 ++++++++++++++++++++++++++++++++--------------------- test/vm.h | 2 +- 2 files changed, 47 insertions(+), 32 deletions(-) diff --git a/test/vm.cpp b/test/vm.cpp index 27e1da238..39ffdb7b2 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -265,42 +265,57 @@ eth::OnOpFunc FakeExtVM::simpleTrace() /*creates json stack trace*/ if (eth::VMTraceChannel::verbosity <= g_logVerbosity) { - std::ostringstream ofile; - - u256s stack = vm.stack(); - ofile << endl << "{" << endl << " \"stack\":[" << endl; - for (vector::iterator i = stack.begin(); i != stack.end(); ++i){ - ofile << " \"" << (h256)*i << "\""; - if(next(i) != stack.end()){ - ofile << ","; - } + Object o_step; - ofile << endl; - } + /*add the stack*/ + Array a_stack; + for (auto i: vm.stack()) + a_stack.push_back((string)i); - ofile << " ]," << endl; - ofile << " \"memory\": \""; - for(auto i: vm.memory()) - ofile << setfill('0') << setw(2) << hex << (unsigned)i; - ofile << "\"," << endl; + o_step.push_back(Pair( "stack", a_stack )); - ofile << " \"storage\": [" << endl; - for (auto const& i: std::get<2>(ext.addresses.find(ext.myAddress)->second)){ - ofile << " [" << endl; - ofile << " \"" << std::showbase << std::hex << i.first << "\": \"" << i.second << "\"" << std::endl; + /*add the memory*/ + Array a_mem; + for(auto i: vm.memory()) + a_mem.push_back(i); + + o_step.push_back(Pair("memory", a_mem)); + + /*add the storage*/ + Object storage; + for (auto const& i: std::get<2>(ext.addresses.find(ext.myAddress)->second)) + storage.push_back(Pair( (string)i.first , (string)i.second)); + + /*add all the other details*/ + o_step.push_back(Pair("storage", storage)); + o_step.push_back(Pair("depth", to_string(ext.depth))); + o_step.push_back(Pair("gas", (string)vm.gas())); + o_step.push_back(Pair("address", "0x" + toString(ext.myAddress ))); + o_step.push_back(Pair("step", steps )); + o_step.push_back(Pair("pc", (int)vm.curPC())); + o_step.push_back(Pair("opcode", instructionInfo(inst).name )); + + ifstream is( "./stackTrace.json"); + string istr((std::istreambuf_iterator(is) ), (std::istreambuf_iterator())); + is.close(); + Value iv; + Array a_trace; + + /*try to parse the current trace file*/ + try{ + read_string(istr, iv); + a_trace = iv.get_array(); } + catch(...){} + + /*add this step to the array of steps*/ + a_trace.push_back(o_step); + + ofstream os( "./stackTrace.json"); - ofile << " ]," << endl; - ofile << " \"depth\": " << dec << ext.depth << "," << endl; - ofile << " \"gas\":" << dec <. #include #include #include -#include "JsonSpiritHeaders.h" +#include #include #include #include From 4dd5240111dde6368df728fa752f9ae2d7efe53b Mon Sep 17 00:00:00 2001 From: wanderer Date: Tue, 16 Dec 2014 14:28:03 -0500 Subject: [PATCH 08/33] append JSON objects to log --- test/vm.cpp | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/test/vm.cpp b/test/vm.cpp index 39ffdb7b2..010eb4d75 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -295,26 +295,10 @@ eth::OnOpFunc FakeExtVM::simpleTrace() o_step.push_back(Pair("pc", (int)vm.curPC())); o_step.push_back(Pair("opcode", instructionInfo(inst).name )); - ifstream is( "./stackTrace.json"); - string istr((std::istreambuf_iterator(is) ), (std::istreambuf_iterator())); - is.close(); - Value iv; - Array a_trace; - - /*try to parse the current trace file*/ - try{ - read_string(istr, iv); - a_trace = iv.get_array(); - } - catch(...){} - - /*add this step to the array of steps*/ - a_trace.push_back(o_step); - - ofstream os( "./stackTrace.json"); - - Value v(a_trace); - os << write_string(v, true); + /*append the JSON object to the log file*/ + Value v(o_step); + ofstream os( "./stackTrace.json", ofstream::app); + os << write_string(v, true) << ","; os.close(); } }; From 8ecf06df863074e3c2009c0ed30540cb4fccc881 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Tue, 16 Dec 2014 20:17:23 +0100 Subject: [PATCH 09/33] FindCURL in out cmake folder, required to find debug curl library --- cmake/EthDependencies.cmake | 2 +- cmake/FindCURL.cmake | 49 +++++++++++++++++++++++++++++++++++++ cmake/FindGmp.cmake | 4 +-- 3 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 cmake/FindCURL.cmake diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake index ee93a5311..136c86799 100644 --- a/cmake/EthDependencies.cmake +++ b/cmake/EthDependencies.cmake @@ -82,7 +82,7 @@ if (GMP_FOUND) endif() # curl is only requried for tests -# TODO specify min curl version, on windows we are currenly using 7.29 +# TODO specify min curl version, on windows we are currently using 7.29 find_package (CURL) message(" - curl header: ${CURL_INCLUDE_DIRS}") message(" - curl lib : ${CURL_LIBRARIES}") diff --git a/cmake/FindCURL.cmake b/cmake/FindCURL.cmake new file mode 100644 index 000000000..ba6603784 --- /dev/null +++ b/cmake/FindCURL.cmake @@ -0,0 +1,49 @@ +# Find CURL +# +# Find the curl includes and library +# +# if you nee to add a custom library search path, do it via via CMAKE_PREFIX_PATH +# +# This module defines +# CURL_INCLUDE_DIRS, where to find header, etc. +# CURL_LIBRARIES, the libraries needed to use curl. +# CURL_FOUND, If false, do not try to use curl. + +# only look in default directories +find_path( + CURL_INCLUDE_DIR + NAMES curl/curl.h + DOC "curl include dir" +) + +find_library( + CURL_LIBRARY + # names from cmake's FindCURL + NAMES curl curllib libcurl_imp curllib_static libcurl + DOC "curl library" +) + +set(CURL_INCLUDE_DIRS ${CURL_INCLUDE_DIR}) +set(CURL_LIBRARIES ${CURL_LIBRARY}) + +# debug library on windows +# same naming convention as in qt (appending debug library with d) +# boost is using the same "hack" as us with "optimized" and "debug" +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + find_library( + CURL_LIBRARY_DEBUG + NAMES curld libcurld + DOC "curl debug library" + ) + + set(CURL_LIBRARIES optimized ${CURL_LIBRARIES} debug ${CURL_LIBRARY_DEBUG}) + +endif() + +# handle the QUIETLY and REQUIRED arguments and set CURL_FOUND to TRUE +# if all listed variables are TRUE, hide their existence from configuration view +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(CURL DEFAULT_MSG + CURL_INCLUDE_DIR CURL_LIBRARY) +mark_as_advanced (CURL_INCLUDE_DIR CURL_LIBRARY) + diff --git a/cmake/FindGmp.cmake b/cmake/FindGmp.cmake index 1cffa7efa..4570b35fa 100644 --- a/cmake/FindGmp.cmake +++ b/cmake/FindGmp.cmake @@ -14,13 +14,13 @@ find_path( GMP_INCLUDE_DIR NAMES gmp.h DOC "gmp include dir" - ) +) find_library( GMP_LIBRARY NAMES gmp DOC "gmp library" - ) +) set(GMP_INCLUDE_DIRS ${GMP_INCLUDE_DIR}) set(GMP_LIBRARIES ${GMP_LIBRARY}) From 99dce392b03edc5ec5e27039fac6e039953581ed Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Tue, 16 Dec 2014 20:53:48 +0100 Subject: [PATCH 10/33] find jsonrpccpp debug libraries --- cmake/FindJsonRpcCpp.cmake | 47 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/cmake/FindJsonRpcCpp.cmake b/cmake/FindJsonRpcCpp.cmake index 3072ff244..2ca176f68 100644 --- a/cmake/FindJsonRpcCpp.cmake +++ b/cmake/FindJsonRpcCpp.cmake @@ -43,6 +43,53 @@ set (JSON_RPC_CPP_LIBRARIES ${JSON_RPC_CPP_COMMON_LIBRARY} ${JSON_RPC_CPP_SERVER set (JSON_RPC_CPP_SERVER_LIBRARIES ${JSON_RPC_CPP_COMMON_LIBRARY} ${JSON_RPC_CPP_SERVER_LIBRARY}) set (JSON_RPC_CPP_CLIENT_LIBRARIES ${JSON_RPC_CPP_COMMON_LIBRARY} ${JSON_RPC_CPP_CLIENT_LIBRARY}) +# debug library on windows +# same naming convention as in qt (appending debug library with d) +# boost is using the same "hack" as us with "optimized" and "debug" +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + find_library( + JSON_RPC_CPP_COMMON_LIBRARY_DEBUG + NAMES jsonrpccpp-commond + DOC "json-rpc-cpp common debug library" + ) + + find_library( + JSON_RPC_CPP_SERVER_LIBRARY_DEBUG + NAMES jsonrpccpp-serverd + DOC "json-rpc-cpp server debug library" + ) + + find_library( + JSON_RPC_CPP_CLIENT_LIBRARY_DEBUG + NAMES jsonrpccpp-clientd + DOC "json-rpc-cpp client debug library" + ) + + set (JSON_RPC_CPP_LIBRARIES + optimized ${JSON_RPC_CPP_COMMON_LIBRARY} + optimized ${JSON_RPC_CPP_SERVER_LIBRARY} + optimized ${JSON_RPC_CPP_CLIENT_LIBRARY} + debug ${JSON_RPC_CPP_COMMON_LIBRARY_DEBUG} + debug ${JSON_RPC_CPP_SERVER_LIBRARY_DEBUG} + debug ${JSON_RPC_CPP_CLIENT_LIBRARY_DEBUG} + ) + + set (JSON_RPC_CPP_SERVER_LIBRARIES + optimized ${JSON_RPC_CPP_COMMON_LIBRARY} + optimized ${JSON_RPC_CPP_SERVER_LIBRARY} + debug ${JSON_RPC_CPP_COMMON_LIBRARY_DEBUG} + debug ${JSON_RPC_CPP_SERVER_LIBRARY_DEBUG} + ) + + set (JSON_RPC_CPP_CLIENT_LIBRARIES + optimized ${JSON_RPC_CPP_COMMON_LIBRARY} + optimized ${JSON_RPC_CPP_CLIENT_LIBRARY} + debug ${JSON_RPC_CPP_COMMON_LIBRARY_DEBUG} + debug ${JSON_RPC_CPP_CLIENT_LIBRARY_DEBUG} + ) + +endif() + # handle the QUIETLY and REQUIRED arguments and set JSON_RPC_CPP_FOUND to TRUE # if all listed variables are TRUE, hide their existence from configuration view include(FindPackageHandleStandardArgs) From 28409ed583eda067619a36832db8866af8593ac2 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Tue, 16 Dec 2014 23:08:25 +0100 Subject: [PATCH 11/33] Fix for unhandled solc exception with opcodes argument - This should fix #631 --- solc/CommandLineInterface.cpp | 51 +++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 6ace332f7..f13423edb 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -43,6 +43,16 @@ using namespace std; namespace po = boost::program_options; +// LTODO: Maybe some argument class pairing names with +// extensions and other attributes would be a better choice here? +#define ARG_ABI_STR "abi" +#define ARG_ASM_STR "asm" +#define ARG_AST_STR "ast" +#define ARG_BINARY_STR "binary" +#define ARG_OPCODES_STR "opcodes" +#define ARG_NATSPECDEV_STR "natspec-dev" +#define ARG_NATSPECUSER_STR "natspec-user" + namespace dev { namespace solidity @@ -63,8 +73,9 @@ static inline bool argToStdout(po::variables_map const& _args, const char* _name static bool needStdout(po::variables_map const& _args) { - return argToStdout(_args, "abi") || argToStdout(_args, "natspec-user") || argToStdout(_args, "natspec-dev") || - argToStdout(_args, "asm") || argToStdout(_args, "opcodes") || argToStdout(_args, "binary"); + return argToStdout(_args, ARG_ABI_STR) || argToStdout(_args, ARG_NATSPECUSER_STR) || + argToStdout(_args, ARG_NATSPECDEV_STR) || argToStdout(_args, ARG_ASM_STR) || + argToStdout(_args, ARG_OPCODES_STR) || argToStdout(_args, ARG_BINARY_STR); } static inline bool outputToFile(OutputType type) @@ -94,7 +105,7 @@ static std::istream& operator>>(std::istream& _in, OutputType& io_output) void CommandLineInterface::handleBinary(string const& _contract) { - auto choice = m_args["binary"].as(); + auto choice = m_args[ARG_BINARY_STR].as(); if (outputToStdout(choice)) { cout << "Binary: " << endl; @@ -112,7 +123,7 @@ void CommandLineInterface::handleBinary(string const& _contract) void CommandLineInterface::handleOpcode(string const& _contract) { // TODO: Figure out why the wrong operator << (from boost) is used here - auto choice = m_args["opcode"].as(); + auto choice = m_args[ARG_OPCODES_STR].as(); if (outputToStdout(choice)) { cout << "Opcodes: " << endl; @@ -130,9 +141,9 @@ void CommandLineInterface::handleOpcode(string const& _contract) void CommandLineInterface::handleBytecode(string const& _contract) { - if (m_args.count("opcodes")) + if (m_args.count(ARG_OPCODES_STR)) handleOpcode(_contract); - if (m_args.count("binary")) + if (m_args.count(ARG_BINARY_STR)) handleBinary(_contract); } @@ -145,17 +156,17 @@ void CommandLineInterface::handleJson(DocumentationType _type, switch(_type) { case DocumentationType::ABI_INTERFACE: - argName = "abi"; + argName = ARG_ABI_STR; suffix = ".abi"; title = "Contract ABI"; break; case DocumentationType::NATSPEC_USER: - argName = "natspec-user"; + argName = "ARG_NATSPECUSER_STR"; suffix = ".docuser"; title = "User Documentation"; break; case DocumentationType::NATSPEC_DEV: - argName = "natspec-dev"; + argName = ARG_NATSPECDEV_STR; suffix = ".docdev"; title = "Developer Documentation"; break; @@ -195,19 +206,19 @@ bool CommandLineInterface::parseArguments(int argc, char** argv) ("version", "Show version and exit") ("optimize", po::value()->default_value(false), "Optimize bytecode for size") ("input-file", po::value>(), "input file") - ("ast", po::value(), + (ARG_AST_STR, po::value(), "Request to output the AST of the contract. " OUTPUT_TYPE_STR) - ("asm", po::value(), + (ARG_ASM_STR, po::value(), "Request to output the EVM assembly of the contract. " OUTPUT_TYPE_STR) - ("opcodes", po::value(), + (ARG_OPCODES_STR, po::value(), "Request to output the Opcodes of the contract. " OUTPUT_TYPE_STR) - ("binary", po::value(), + (ARG_BINARY_STR, po::value(), "Request to output the contract in binary (hexadecimal). " OUTPUT_TYPE_STR) - ("abi", po::value(), + (ARG_ABI_STR, po::value(), "Request to output the contract's ABI interface. " OUTPUT_TYPE_STR) - ("natspec-user", po::value(), + (ARG_NATSPECUSER_STR, po::value(), "Request to output the contract's Natspec user documentation. " OUTPUT_TYPE_STR) - ("natspec-dev", po::value(), + (ARG_NATSPECDEV_STR, po::value(), "Request to output the contract's Natspec developer documentation. " OUTPUT_TYPE_STR); #undef OUTPUT_TYPE_STR @@ -321,9 +332,9 @@ bool CommandLineInterface::processInput() void CommandLineInterface::actOnInput() { // do we need AST output? - if (m_args.count("ast")) + if (m_args.count(ARG_AST_STR)) { - auto choice = m_args["ast"].as(); + auto choice = m_args[ARG_AST_STR].as(); if (outputToStdout(choice)) { cout << "Syntax trees:" << endl << endl; @@ -355,9 +366,9 @@ void CommandLineInterface::actOnInput() cout << endl << "======= " << contract << " =======" << endl; // do we need EVM assembly? - if (m_args.count("asm")) + if (m_args.count(ARG_ASM_STR)) { - auto choice = m_args["asm"].as(); + auto choice = m_args[ARG_ASM_STR].as(); if (outputToStdout(choice)) { cout << "EVM assembly:" << endl; From b95b6f3b6630bce6ecb73cdf20a58059276df488 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Tue, 16 Dec 2014 23:31:22 +0100 Subject: [PATCH 12/33] Properly outputing opcodes in solc, using normal stream operator --- solc/CommandLineInterface.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index f13423edb..8aa182b5c 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -122,19 +122,18 @@ void CommandLineInterface::handleBinary(string const& _contract) void CommandLineInterface::handleOpcode(string const& _contract) { - // TODO: Figure out why the wrong operator << (from boost) is used here auto choice = m_args[ARG_OPCODES_STR].as(); if (outputToStdout(choice)) { cout << "Opcodes: " << endl; - dev::operator<<(cout, m_compiler.getBytecode(_contract)); + cout << eth::disassemble(m_compiler.getBytecode(_contract)); cout << endl; } if (outputToFile(choice)) { ofstream outFile(_contract + ".opcode"); - dev::operator<<(outFile, m_compiler.getBytecode(_contract)); + outFile << eth::disassemble(m_compiler.getBytecode(_contract)); outFile.close(); } } From 41948b13097a8fed084dbdc8ff970cf1d49c40b1 Mon Sep 17 00:00:00 2001 From: Christian Date: Tue, 16 Dec 2014 23:45:24 +0100 Subject: [PATCH 13/33] Fix: Resolve function types of all contracts before checking types inside functions. --- libsolidity/CompilerStack.cpp | 9 +++++++++ libsolidity/NameAndTypeResolver.cpp | 13 +++++++------ libsolidity/NameAndTypeResolver.h | 2 ++ test/solidityCompiler.cpp | 8 ++++++-- test/solidityExpressionCompiler.cpp | 7 ++++++- test/solidityNameAndTypeResolution.cpp | 18 ++++++++++++++++++ 6 files changed, 48 insertions(+), 9 deletions(-) diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index 23f5fd68f..1242c0aba 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -73,6 +73,15 @@ void CompilerStack::parse() 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; } diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index 2ad276801..80732615c 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -49,8 +49,6 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) m_currentScope = &m_scopes[&_contract]; for (ASTPointer const& structDef: _contract.getDefinedStructs()) ReferencesResolver resolver(*structDef, *this, nullptr); - for (ASTPointer const& structDef: _contract.getDefinedStructs()) - structDef->checkValidityOfMembers(); for (ASTPointer const& variable: _contract.getStateVariables()) ReferencesResolver resolver(*variable, *this, nullptr); for (ASTPointer const& function: _contract.getDefinedFunctions()) @@ -59,13 +57,16 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) ReferencesResolver referencesResolver(*function, *this, function->getReturnParameterList().get()); } - // First, the parameter types of all functions need to be resolved before we can check - // the types, since it is possible to call functions that are only defined later - // in the source. - _contract.checkTypeRequirements(); m_currentScope = &m_scopes[nullptr]; } +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, true); diff --git a/libsolidity/NameAndTypeResolver.h b/libsolidity/NameAndTypeResolver.h index 1ff9febf0..23ac5fe77 100644 --- a/libsolidity/NameAndTypeResolver.h +++ b/libsolidity/NameAndTypeResolver.h @@ -46,6 +46,8 @@ public: 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); diff --git a/test/solidityCompiler.cpp b/test/solidityCompiler.cpp index 29f61454a..9ae8ff50b 100644 --- a/test/solidityCompiler.cpp +++ b/test/solidityCompiler.cpp @@ -52,9 +52,13 @@ bytes compileContract(const string& _sourceCode) resolver.registerDeclarations(*sourceUnit); for (ASTPointer const& node: sourceUnit->getNodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) - { BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); - + for (ASTPointer const& node: sourceUnit->getNodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + BOOST_REQUIRE_NO_THROW(resolver.checkTypeRequirements(*contract)); + for (ASTPointer const& node: sourceUnit->getNodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + { Compiler compiler; compiler.compileContract(*contract, {}, {}); // debug diff --git a/test/solidityExpressionCompiler.cpp b/test/solidityExpressionCompiler.cpp index c05db25d4..2bdc38421 100644 --- a/test/solidityExpressionCompiler.cpp +++ b/test/solidityExpressionCompiler.cpp @@ -95,8 +95,13 @@ bytes compileFirstExpression(const string& _sourceCode, vector> _ resolver.registerDeclarations(*sourceUnit); for (ASTPointer const& node: sourceUnit->getNodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) - { BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); + for (ASTPointer const& node: sourceUnit->getNodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + BOOST_REQUIRE_NO_THROW(resolver.checkTypeRequirements(*contract)); + for (ASTPointer const& node: sourceUnit->getNodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + { FirstExpressionExtractor extractor(*contract); BOOST_REQUIRE(extractor.getExpression() != nullptr); diff --git a/test/solidityNameAndTypeResolution.cpp b/test/solidityNameAndTypeResolution.cpp index 0ea6e51b9..0bda0a1fd 100644 --- a/test/solidityNameAndTypeResolution.cpp +++ b/test/solidityNameAndTypeResolution.cpp @@ -47,6 +47,9 @@ void parseTextAndResolveNames(std::string const& _source) for (ASTPointer const& node: sourceUnit->getNodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) resolver.resolveNamesAndTypes(*contract); + for (ASTPointer const& node: sourceUnit->getNodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + resolver.checkTypeRequirements(*contract); } } @@ -293,6 +296,21 @@ BOOST_AUTO_TEST_CASE(returns_in_constructor) BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); } +BOOST_AUTO_TEST_CASE(forward_function_reference) +{ + char const* text = "contract First {\n" + " function fun() returns (bool ret) {\n" + " return Second(1).fun(1, true, 3) > 0;\n" + " }\n" + "}\n" + "contract Second {\n" + " function fun(uint a, bool b, uint c) returns (uint ret) {\n" + " if (First(2).fun() == true) return 1;\n" + " }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); +} + BOOST_AUTO_TEST_SUITE_END() } From 4e83ba5ae148fdcf887e813e7d1aa45ece312666 Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 15 Dec 2014 22:57:39 +0100 Subject: [PATCH 14/33] Add functions needed by constructor. --- libsolidity/CallGraph.cpp | 65 ++++++++++++++++++++++ libsolidity/CallGraph.h | 53 ++++++++++++++++++ libsolidity/Compiler.cpp | 101 +++++++++++++++++++--------------- libsolidity/Compiler.h | 12 +++- test/solidityEndToEndTest.cpp | 17 ++++++ 5 files changed, 202 insertions(+), 46 deletions(-) create mode 100644 libsolidity/CallGraph.cpp create mode 100644 libsolidity/CallGraph.h diff --git a/libsolidity/CallGraph.cpp b/libsolidity/CallGraph.cpp new file mode 100644 index 000000000..48666ca7f --- /dev/null +++ b/libsolidity/CallGraph.cpp @@ -0,0 +1,65 @@ + +/* + 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 + * Callgraph of functions inside a contract. + */ + +#include +#include + +using namespace std; + +namespace dev { +namespace solidity { + +void CallGraph::addFunction(FunctionDefinition const& _function) +{ + if (!m_functionsSeen.count(&_function)) + { + m_functionsSeen.insert(&_function); + m_workQueue.push(&_function); + } +} + +set const& CallGraph::getCalls() +{ + return m_functionsSeen; +} + +void CallGraph::computeCallGraph() +{ + while (!m_workQueue.empty()) + { + FunctionDefinition const* fun = m_workQueue.front(); + fun->accept(*this); + m_workQueue.pop(); + } +} + +bool CallGraph::visit(Identifier const& _identifier) +{ + FunctionDefinition const* fun = dynamic_cast(_identifier.getReferencedDeclaration()); + if (fun) + addFunction(*fun); + return true; +} + +} +} diff --git a/libsolidity/CallGraph.h b/libsolidity/CallGraph.h new file mode 100644 index 000000000..986e4dc4a --- /dev/null +++ b/libsolidity/CallGraph.h @@ -0,0 +1,53 @@ +/* + 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 + * Callgraph of functions inside a contract. + */ + +#include +#include +#include +#include + +namespace dev { +namespace solidity { + +/** + * Can be used to compute the graph of calls (or rather references) between functions of the same + * contract. Current functionality is limited to computing all functions that are directly + * or indirectly called by some functions. + */ +class CallGraph: private ASTConstVisitor +{ +public: + void addFunction(FunctionDefinition const& _function); + void computeCallGraph(); + + std::set const& getCalls(); + +private: + void addFunctionToQueue(FunctionDefinition const& _function); + virtual bool visit(Identifier const& _identifier) override; + + std::set m_functionsSeen; + std::queue m_workQueue; +}; + +} +} diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index f2877c1ce..0f85cda38 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -27,6 +27,7 @@ #include #include #include +#include using namespace std; @@ -37,68 +38,82 @@ void Compiler::compileContract(ContractDefinition const& _contract, vector const& _contracts) { m_context = CompilerContext(); // clear it just in case - m_context.setCompiledContracts(_contracts); - - for (MagicVariableDeclaration const* variable: _magicGlobals) - m_context.addMagicGlobal(*variable); + initializeContext(_contract, _magicGlobals, _contracts); for (ASTPointer const& function: _contract.getDefinedFunctions()) if (function->getName() != _contract.getName()) // don't add the constructor here m_context.addFunction(*function); - registerStateVariables(_contract); appendFunctionSelector(_contract); for (ASTPointer const& function: _contract.getDefinedFunctions()) if (function->getName() != _contract.getName()) // don't add the constructor here function->accept(*this); - packIntoContractCreator(_contract, _contracts); -} - -void Compiler::packIntoContractCreator(ContractDefinition const& _contract, - map const& _contracts) -{ + // Swap the runtime context with the creation-time context CompilerContext runtimeContext; - runtimeContext.setCompiledContracts(_contracts); swap(m_context, runtimeContext); + initializeContext(_contract, _magicGlobals, _contracts); + packIntoContractCreator(_contract, runtimeContext); +} +void Compiler::initializeContext(ContractDefinition const& _contract, vector const& _magicGlobals, + map const& _contracts) +{ + m_context.setCompiledContracts(_contracts); + for (MagicVariableDeclaration const* variable: _magicGlobals) + m_context.addMagicGlobal(*variable); registerStateVariables(_contract); +} - FunctionDefinition* constructor = nullptr; - for (ASTPointer const& function: _contract.getDefinedFunctions()) - if (function->getName() == _contract.getName()) - { - constructor = function.get(); - break; - } - eth::AssemblyItem sub = m_context.addSubroutine(runtimeContext.getAssembly()); - // stack contains sub size +void Compiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext) +{ + set neededFunctions; + FunctionDefinition const* constructor = _contract.getConstructor(); if (constructor) - { - eth::AssemblyItem returnTag = m_context.pushNewTag(); - m_context.addFunction(*constructor); // note that it cannot be called due to syntactic reasons - // 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 += var->getType()->getCalldataEncodedSize(); - if (argumentSize > 0) - { - m_context << u256(argumentSize); - m_context.appendProgramSize(); - m_context << u256(1); // copy it to byte one as expected for ABI calls - m_context << eth::Instruction::CODECOPY; - appendCalldataUnpacker(*constructor, true); - } - //@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. - m_context.appendJumpTo(m_context.getFunctionEntryLabel(*constructor)); - constructor->accept(*this); - m_context << returnTag; - } + neededFunctions = getFunctionsNeededByConstructor(*constructor); + + for (FunctionDefinition const* fun: neededFunctions) + m_context.addFunction(*fun); + if (constructor) + appendConstructorCall(*constructor); + + eth::AssemblyItem sub = m_context.addSubroutine(_runtimeContext.getAssembly()); + // stack contains sub size m_context << eth::Instruction::DUP1 << sub << u256(0) << eth::Instruction::CODECOPY; m_context << u256(0) << eth::Instruction::RETURN; + + // note that we have to explicitly include all used functions because of absolute jump + // labels + for (FunctionDefinition const* fun: neededFunctions) + fun->accept(*this); +} + +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 += var->getType()->getCalldataEncodedSize(); + if (argumentSize > 0) + { + m_context << u256(argumentSize); + m_context.appendProgramSize(); + m_context << u256(1); // copy it to byte one as expected for ABI calls + m_context << eth::Instruction::CODECOPY; + appendCalldataUnpacker(_constructor, true); + } + m_context.appendJumpTo(m_context.getFunctionEntryLabel(_constructor)); + m_context << returnTag; +} + +set Compiler::getFunctionsNeededByConstructor(FunctionDefinition const& _constructor) +{ + CallGraph callgraph; + callgraph.addFunction(_constructor); + callgraph.computeCallGraph(); + return callgraph.getCalls(); } void Compiler::appendFunctionSelector(ContractDefinition const& _contract) diff --git a/libsolidity/Compiler.h b/libsolidity/Compiler.h index 57e40cdae..b30e85ae4 100644 --- a/libsolidity/Compiler.h +++ b/libsolidity/Compiler.h @@ -38,10 +38,16 @@ public: 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 + /// Registers the global objects and the non-function objects inside the contract with the context. + void initializeContext(ContractDefinition const& _contract, std::vector const& _magicGlobals, + 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, - std::map const& _contracts); + void packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext); + void appendConstructorCall(FunctionDefinition const& _constructor); + /// Recursively searches the call graph and returns all functions needed by the constructor (including itself). + std::set getFunctionsNeededByConstructor(FunctionDefinition const& _constructor); void appendFunctionSelector(ContractDefinition const& _contract); /// Creates code that unpacks the arguments for the given function, from memory if /// @a _fromMemory is true, otherwise from call data. @returns the size of the data in bytes. diff --git a/test/solidityEndToEndTest.cpp b/test/solidityEndToEndTest.cpp index b6f63aa72..78926e94a 100644 --- a/test/solidityEndToEndTest.cpp +++ b/test/solidityEndToEndTest.cpp @@ -1038,6 +1038,23 @@ BOOST_AUTO_TEST_CASE(constructor_arguments) BOOST_REQUIRE(callContractFunction(1) == bytes({'a', 'b', 'c'})); } +BOOST_AUTO_TEST_CASE(functions_called_by_constructor) +{ + char const* sourceCode = R"( + contract Test { + string3 name; + bool flag; + function Test() { + setName("abc"); + } + function getName() returns (string3 ret) { return name; } + private: + function setName(string3 _name) { name = _name; } + })"; + compileAndRun(sourceCode); + BOOST_REQUIRE(callContractFunction(0) == bytes({'a', 'b', 'c'})); +} + BOOST_AUTO_TEST_SUITE_END() } From c99b38a4ed70e578065a59e0dd332474fac6e42d Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Tue, 16 Dec 2014 23:55:38 +0100 Subject: [PATCH 15/33] Using strings instead of #defined literals in solc --- solc/CommandLineInterface.cpp | 76 +++++++++++++++++------------------ 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 8aa182b5c..fd660a0ca 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -43,21 +43,21 @@ using namespace std; namespace po = boost::program_options; -// LTODO: Maybe some argument class pairing names with -// extensions and other attributes would be a better choice here? -#define ARG_ABI_STR "abi" -#define ARG_ASM_STR "asm" -#define ARG_AST_STR "ast" -#define ARG_BINARY_STR "binary" -#define ARG_OPCODES_STR "opcodes" -#define ARG_NATSPECDEV_STR "natspec-dev" -#define ARG_NATSPECUSER_STR "natspec-user" - namespace dev { namespace solidity { +// LTODO: Maybe some argument class pairing names with +// extensions and other attributes would be a better choice here? +static string const g_argAbiStr = "abi"; +static string const g_argAsmStr = "asm"; +static string const g_argAstStr = "ast"; +static string const g_argBinaryStr = "binary"; +static string const g_argOpcodesStr = "opcodes"; +static string const g_argNatspecDevStr = "natspec-dev"; +static string const g_argNatspecUserStr = "natspec-user"; + static void version() { cout << "solc, the solidity complier commandline interface " << dev::Version << endl @@ -66,16 +66,16 @@ static void version() exit(0); } -static inline bool argToStdout(po::variables_map const& _args, const char* _name) +static inline bool argToStdout(po::variables_map const& _args, string const& _name) { return _args.count(_name) && _args[_name].as() != OutputType::FILE; } static bool needStdout(po::variables_map const& _args) { - return argToStdout(_args, ARG_ABI_STR) || argToStdout(_args, ARG_NATSPECUSER_STR) || - argToStdout(_args, ARG_NATSPECDEV_STR) || argToStdout(_args, ARG_ASM_STR) || - argToStdout(_args, ARG_OPCODES_STR) || argToStdout(_args, ARG_BINARY_STR); + return argToStdout(_args, g_argAbiStr) || argToStdout(_args, g_argNatspecUserStr) || + argToStdout(_args, g_argNatspecDevStr) || argToStdout(_args, g_argAsmStr) || + argToStdout(_args, g_argOpcodesStr) || argToStdout(_args, g_argBinaryStr); } static inline bool outputToFile(OutputType type) @@ -105,7 +105,7 @@ static std::istream& operator>>(std::istream& _in, OutputType& io_output) void CommandLineInterface::handleBinary(string const& _contract) { - auto choice = m_args[ARG_BINARY_STR].as(); + auto choice = m_args[g_argBinaryStr].as(); if (outputToStdout(choice)) { cout << "Binary: " << endl; @@ -122,7 +122,7 @@ void CommandLineInterface::handleBinary(string const& _contract) void CommandLineInterface::handleOpcode(string const& _contract) { - auto choice = m_args[ARG_OPCODES_STR].as(); + auto choice = m_args[g_argOpcodesStr].as(); if (outputToStdout(choice)) { cout << "Opcodes: " << endl; @@ -140,9 +140,9 @@ void CommandLineInterface::handleOpcode(string const& _contract) void CommandLineInterface::handleBytecode(string const& _contract) { - if (m_args.count(ARG_OPCODES_STR)) + if (m_args.count(g_argOpcodesStr)) handleOpcode(_contract); - if (m_args.count(ARG_BINARY_STR)) + if (m_args.count(g_argBinaryStr)) handleBinary(_contract); } @@ -155,17 +155,17 @@ void CommandLineInterface::handleJson(DocumentationType _type, switch(_type) { case DocumentationType::ABI_INTERFACE: - argName = ARG_ABI_STR; + argName = g_argAbiStr; suffix = ".abi"; title = "Contract ABI"; break; case DocumentationType::NATSPEC_USER: - argName = "ARG_NATSPECUSER_STR"; + argName = "g_argNatspecUserStr"; suffix = ".docuser"; title = "User Documentation"; break; case DocumentationType::NATSPEC_DEV: - argName = ARG_NATSPECDEV_STR; + argName = g_argNatspecDevStr; suffix = ".docdev"; title = "Developer Documentation"; break; @@ -205,20 +205,20 @@ bool CommandLineInterface::parseArguments(int argc, char** argv) ("version", "Show version and exit") ("optimize", po::value()->default_value(false), "Optimize bytecode for size") ("input-file", po::value>(), "input file") - (ARG_AST_STR, po::value(), + (g_argAstStr.c_str(), po::value(), "Request to output the AST of the contract. " OUTPUT_TYPE_STR) - (ARG_ASM_STR, po::value(), - "Request to output the EVM assembly of the contract. " OUTPUT_TYPE_STR) - (ARG_OPCODES_STR, po::value(), - "Request to output the Opcodes of the contract. " OUTPUT_TYPE_STR) - (ARG_BINARY_STR, po::value(), - "Request to output the contract in binary (hexadecimal). " OUTPUT_TYPE_STR) - (ARG_ABI_STR, po::value(), - "Request to output the contract's ABI interface. " OUTPUT_TYPE_STR) - (ARG_NATSPECUSER_STR, po::value(), - "Request to output the contract's Natspec user documentation. " OUTPUT_TYPE_STR) - (ARG_NATSPECDEV_STR, po::value(), - "Request to output the contract's Natspec developer documentation. " OUTPUT_TYPE_STR); + (g_argAsmStr.c_str(), po::value(), + "Request to output the EVM assembly of the contract. " OUTPUT_TYPE_STR) + (g_argOpcodesStr.c_str(), po::value(), + "Request to output the Opcodes of the contract. " OUTPUT_TYPE_STR) + (g_argBinaryStr.c_str(), po::value(), + "Request to output the contract in binary (hexadecimal). " OUTPUT_TYPE_STR) + (g_argAbiStr.c_str(), po::value(), + "Request to output the contract's ABI interface. " OUTPUT_TYPE_STR) + (g_argNatspecUserStr.c_str(), po::value(), + "Request to output the contract's Natspec user documentation. " OUTPUT_TYPE_STR) + (g_argNatspecDevStr.c_str(), po::value(), + "Request to output the contract's Natspec developer documentation. " OUTPUT_TYPE_STR); #undef OUTPUT_TYPE_STR // All positional options should be interpreted as input files @@ -331,9 +331,9 @@ bool CommandLineInterface::processInput() void CommandLineInterface::actOnInput() { // do we need AST output? - if (m_args.count(ARG_AST_STR)) + if (m_args.count(g_argAstStr)) { - auto choice = m_args[ARG_AST_STR].as(); + auto choice = m_args[g_argAstStr].as(); if (outputToStdout(choice)) { cout << "Syntax trees:" << endl << endl; @@ -365,9 +365,9 @@ void CommandLineInterface::actOnInput() cout << endl << "======= " << contract << " =======" << endl; // do we need EVM assembly? - if (m_args.count(ARG_ASM_STR)) + if (m_args.count(g_argAsmStr)) { - auto choice = m_args[ARG_ASM_STR].as(); + auto choice = m_args[g_argAsmStr].as(); if (outputToStdout(choice)) { cout << "EVM assembly:" << endl; From 30c000d121dc0132a6c5abb9d6e3da96b4dfda4a Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 15 Dec 2014 17:45:18 +0100 Subject: [PATCH 16/33] Adding a ForStatement solidity AST Node. - Adding ForStatement node - Implemented Parsing for ForStatement - A simple parsing test for the ForStatement - Work in progress --- libsolidity/AST.cpp | 7 +++++ libsolidity/AST.h | 24 +++++++++++++++++ libsolidity/ASTForward.h | 1 + libsolidity/ASTPrinter.cpp | 12 +++++++++ libsolidity/ASTPrinter.h | 2 ++ libsolidity/ASTVisitor.h | 4 +++ libsolidity/AST_accept.h | 24 +++++++++++++++++ libsolidity/Compiler.cpp | 7 +++++ libsolidity/Compiler.h | 1 + libsolidity/Parser.cpp | 54 +++++++++++++++++++++++++++++++------- libsolidity/Parser.h | 6 +++++ libsolidity/grammar.txt | 1 + test/solidityParser.cpp | 28 ++++++++++++++++++++ 13 files changed, 162 insertions(+), 9 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 5e344eadb..c286c412b 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -130,6 +130,13 @@ void WhileStatement::checkTypeRequirements() m_body->checkTypeRequirements(); } +void ForStatement::checkTypeRequirements() +{ + // LTODO + m_condExpression->expectType(BoolType()); + m_body->checkTypeRequirements(); +} + void Return::checkTypeRequirements() { if (!m_expression) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 4bb623b49..297b7f4e9 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -509,6 +509,30 @@ private: ASTPointer m_body; }; +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; + +private: + ASTPointer m_initExpression; + ASTPointer m_condExpression; + ASTPointer m_loopExpression; + ASTPointer m_body; +}; + class Continue: public Statement { public: diff --git a/libsolidity/ASTForward.h b/libsolidity/ASTForward.h index d6c4c9f44..c960fc8f0 100644 --- a/libsolidity/ASTForward.h +++ b/libsolidity/ASTForward.h @@ -52,6 +52,7 @@ class Block; class IfStatement; class BreakableStatement; class WhileStatement; +class ForStatement; class Continue; class Break; class Return; diff --git a/libsolidity/ASTPrinter.cpp b/libsolidity/ASTPrinter.cpp index 970822a9a..916fca1ef 100644 --- a/libsolidity/ASTPrinter.cpp +++ b/libsolidity/ASTPrinter.cpp @@ -150,6 +150,13 @@ bool ASTPrinter::visit(WhileStatement const& _node) return goDeeper(); } +bool ASTPrinter::visit(ForStatement const& _node) +{ + writeLine("ForStatement"); + printSourcePart(_node); + return goDeeper(); +} + bool ASTPrinter::visit(Continue const& _node) { writeLine("Continue"); @@ -360,6 +367,11 @@ void ASTPrinter::endVisit(WhileStatement const&) m_indentation--; } +void ASTPrinter::endVisit(ForStatement const&) +{ + m_indentation--; +} + void ASTPrinter::endVisit(Continue const&) { m_indentation--; diff --git a/libsolidity/ASTPrinter.h b/libsolidity/ASTPrinter.h index 15b65e3f0..fc5fb4acb 100644 --- a/libsolidity/ASTPrinter.h +++ b/libsolidity/ASTPrinter.h @@ -57,6 +57,7 @@ public: 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; @@ -90,6 +91,7 @@ public: 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; diff --git a/libsolidity/ASTVisitor.h b/libsolidity/ASTVisitor.h index 5728cd3d0..33a8a3383 100644 --- a/libsolidity/ASTVisitor.h +++ b/libsolidity/ASTVisitor.h @@ -58,6 +58,7 @@ public: 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; } @@ -93,6 +94,7 @@ public: 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&) { } @@ -132,6 +134,7 @@ public: 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; } @@ -167,6 +170,7 @@ public: 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&) { } diff --git a/libsolidity/AST_accept.h b/libsolidity/AST_accept.h index e0454d33e..ffef6f8b2 100644 --- a/libsolidity/AST_accept.h +++ b/libsolidity/AST_accept.h @@ -267,6 +267,30 @@ void WhileStatement::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } +void ForStatement::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_initExpression->accept(_visitor); + m_condExpression->accept(_visitor); + m_loopExpression->accept(_visitor); + m_body->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void ForStatement::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_initExpression->accept(_visitor); + m_condExpression->accept(_visitor); + m_loopExpression->accept(_visitor); + m_body->accept(_visitor); + } + _visitor.endVisit(*this); +} + void Continue::accept(ASTVisitor& _visitor) { _visitor.visit(*this); diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index f2877c1ce..a0cad5374 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -287,6 +287,13 @@ bool Compiler::visit(WhileStatement const& _whileStatement) return false; } +bool Compiler::visit(ForStatement const& _forStatement) +{ + // LTODO + (void) _forStatement; + return false; +} + bool Compiler::visit(Continue const&) { if (!m_continueTags.empty()) diff --git a/libsolidity/Compiler.h b/libsolidity/Compiler.h index 57e40cdae..c4f46d10f 100644 --- a/libsolidity/Compiler.h +++ b/libsolidity/Compiler.h @@ -53,6 +53,7 @@ private: 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; diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 21651eb14..9941fbf43 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -304,6 +304,8 @@ ASTPointer Parser::parseStatement() 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 @@ -328,15 +330,7 @@ ASTPointer Parser::parseStatement() } break; default: - // 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. - if (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)) + if (peekVariableDefinition()) statement = parseVariableDefinition(); else // "ordinary" expression statement statement = parseExpressionStatement(); @@ -377,6 +371,34 @@ ASTPointer Parser::parseWhileStatement() return nodeFactory.createNode(condition, body); } +ASTPointer Parser::parseForStatement() +{ + ASTNodeFactory nodeFactory(*this); + expectToken(Token::FOR); + expectToken(Token::LPAREN); + ASTPointer initExpression = parseVardefOrExprstatement(); + expectToken(Token::SEMICOLON); + ASTPointer conditionExpression = parseExpression(); + expectToken(Token::SEMICOLON); + ASTPointer loopExpression = parseExpressionStatement(); + expectToken(Token::SEMICOLON); + expectToken(Token::RPAREN); + ASTPointer body = parseStatement(); + nodeFactory.setEndPositionFromNode(body); + return nodeFactory.createNode(initExpression, + conditionExpression, + loopExpression, + body); +} + +ASTPointer Parser::parseVardefOrExprstatement() +{ + if (peekVariableDefinition()) + return parseVariableDefinition(); + else + return parseExpressionStatement(); +} + ASTPointer Parser::parseVariableDefinition() { ASTNodeFactory nodeFactory(*this); @@ -566,6 +588,20 @@ vector> Parser::parseFunctionCallArguments() return arguments; } + +// 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. +bool Parser::peekVariableDefinition() +{ + 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) diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index 52a374e03..343a85c0e 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -59,6 +59,8 @@ private: ASTPointer parseStatement(); ASTPointer parseIfStatement(); ASTPointer parseWhileStatement(); + ASTPointer parseForStatement(); + ASTPointer parseVardefOrExprstatement(); ASTPointer parseVariableDefinition(); ASTPointer parseExpressionStatement(); ASTPointer parseExpression(); @@ -72,6 +74,10 @@ private: ///@{ ///@name Helper functions + /// Peeks ahead in the scanner to determine if an expression statement + /// could be a variable definition + bool peekVariableDefinition(); + /// If current token value is not _value, throw exception otherwise advance token. void expectToken(Token::Value _value); Token::Value expectAssignmentOperator(); diff --git a/libsolidity/grammar.txt b/libsolidity/grammar.txt index a26f717a1..99a590014 100644 --- a/libsolidity/grammar.txt +++ b/libsolidity/grammar.txt @@ -20,6 +20,7 @@ Statement = IfStatement | WhileStatement | Block | IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )? WhileStatement = 'while' '(' Expression ')' Statement +ForStatement = 'for' '(' Expressionstatement Expression Expressionstatement ')' Statement Continue = 'continue' ';' Break = 'break' ';' Return = 'return' Expression? ';' diff --git a/test/solidityParser.cpp b/test/solidityParser.cpp index a9e2d531f..8268901be 100644 --- a/test/solidityParser.cpp +++ b/test/solidityParser.cpp @@ -49,6 +49,23 @@ ASTPointer parseText(std::string const& _source) BOOST_FAIL("No contract found in source."); return ASTPointer(); } + +ASTPointer parseTextExplainError(std::string const& _source) +{ + try + { + return parseText(_source); + } + catch (Exception const& exception) + { + // LTODO: Print the error in a kind of a better way? + // In absence of CompilerStack we can't use SourceReferenceFormatter + cout << "Exception while parsing: " << diagnostic_information(exception); + // rethrow to signal test failure + throw exception; + } +} + } @@ -357,6 +374,17 @@ BOOST_AUTO_TEST_CASE(while_loop) BOOST_CHECK_NO_THROW(parseText(text)); } +BOOST_AUTO_TEST_CASE(for_loop) +{ + char const* text = "contract test {\n" + " function fun(uint256 a) {\n" + " for (uint256 i = 0; i < 10; i++;)\n" + " { uint256 x = i; break; continue; }\n" + " }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseTextExplainError(text)); +} + BOOST_AUTO_TEST_CASE(if_statement) { char const* text = "contract test {\n" From e934aa0befb759ae3fafb545dd9398833a49bbe7 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 15 Dec 2014 17:54:29 +0100 Subject: [PATCH 17/33] new ForStatement parsing test and small grammar fix --- libsolidity/Parser.cpp | 1 - libsolidity/grammar.txt | 2 +- test/solidityParser.cpp | 16 ++++++++++++++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 9941fbf43..d936cfc72 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -381,7 +381,6 @@ ASTPointer Parser::parseForStatement() ASTPointer conditionExpression = parseExpression(); expectToken(Token::SEMICOLON); ASTPointer loopExpression = parseExpressionStatement(); - expectToken(Token::SEMICOLON); expectToken(Token::RPAREN); ASTPointer body = parseStatement(); nodeFactory.setEndPositionFromNode(body); diff --git a/libsolidity/grammar.txt b/libsolidity/grammar.txt index 99a590014..76e7bb6b6 100644 --- a/libsolidity/grammar.txt +++ b/libsolidity/grammar.txt @@ -20,7 +20,7 @@ Statement = IfStatement | WhileStatement | Block | IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )? WhileStatement = 'while' '(' Expression ')' Statement -ForStatement = 'for' '(' Expressionstatement Expression Expressionstatement ')' Statement +ForStatement = 'for' '(' VardefOrExpressionstatement ';' Expression ';' Expressionstatement ')' Statement Continue = 'continue' ';' Break = 'break' ';' Return = 'return' Expression? ';' diff --git a/test/solidityParser.cpp b/test/solidityParser.cpp index 8268901be..53bd3f163 100644 --- a/test/solidityParser.cpp +++ b/test/solidityParser.cpp @@ -374,11 +374,23 @@ BOOST_AUTO_TEST_CASE(while_loop) BOOST_CHECK_NO_THROW(parseText(text)); } -BOOST_AUTO_TEST_CASE(for_loop) +BOOST_AUTO_TEST_CASE(for_loop_vardef_initexpr) { char const* text = "contract test {\n" " function fun(uint256 a) {\n" - " for (uint256 i = 0; i < 10; i++;)\n" + " for (uint256 i = 0; i < 10; i++)\n" + " { uint256 x = i; break; continue; }\n" + " }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseTextExplainError(text)); +} + +BOOST_AUTO_TEST_CASE(for_loop_simple_initexpr) +{ + char const* text = "contract test {\n" + " function fun(uint256 a) {\n" + " uint256 i =0;\n" + " for (i = 0; i < 10; i++)\n" " { uint256 x = i; break; continue; }\n" " }\n" "}\n"; From b2992bd659768b0ee8f33ea24bf4e308efea3891 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Tue, 16 Dec 2014 09:51:51 +0100 Subject: [PATCH 18/33] Solidity ForStatements expressions are now optional --- libsolidity/AST_accept.h | 18 ++++++++++++------ libsolidity/Parser.cpp | 17 ++++++++++++++--- libsolidity/grammar.txt | 2 +- test/solidityParser.cpp | 12 ++++++++++++ 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/libsolidity/AST_accept.h b/libsolidity/AST_accept.h index ffef6f8b2..0e5a71b62 100644 --- a/libsolidity/AST_accept.h +++ b/libsolidity/AST_accept.h @@ -271,9 +271,12 @@ void ForStatement::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) { - m_initExpression->accept(_visitor); - m_condExpression->accept(_visitor); - m_loopExpression->accept(_visitor); + 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); @@ -283,9 +286,12 @@ void ForStatement::accept(ASTConstVisitor& _visitor) const { if (_visitor.visit(*this)) { - m_initExpression->accept(_visitor); - m_condExpression->accept(_visitor); - m_loopExpression->accept(_visitor); + 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); diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index d936cfc72..598f5ad47 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -374,14 +374,25 @@ ASTPointer Parser::parseWhileStatement() ASTPointer Parser::parseForStatement() { ASTNodeFactory nodeFactory(*this); + ASTPointer initExpression; + ASTPointer conditionExpression; + ASTPointer loopExpression; expectToken(Token::FOR); expectToken(Token::LPAREN); - ASTPointer initExpression = parseVardefOrExprstatement(); + + // LTODO: Maybe here have some predicate like peekExpression() instead of checking for semicolon and RPAREN? + if (m_scanner->getCurrentToken() != Token::SEMICOLON) + initExpression = parseVardefOrExprstatement(); expectToken(Token::SEMICOLON); - ASTPointer conditionExpression = parseExpression(); + + if (m_scanner->getCurrentToken() != Token::SEMICOLON) + conditionExpression = parseExpression(); expectToken(Token::SEMICOLON); - ASTPointer loopExpression = parseExpressionStatement(); + + if (m_scanner->getCurrentToken() != Token::RPAREN) + loopExpression = parseExpressionStatement(); expectToken(Token::RPAREN); + ASTPointer body = parseStatement(); nodeFactory.setEndPositionFromNode(body); return nodeFactory.createNode(initExpression, diff --git a/libsolidity/grammar.txt b/libsolidity/grammar.txt index 76e7bb6b6..7c0ac3a50 100644 --- a/libsolidity/grammar.txt +++ b/libsolidity/grammar.txt @@ -20,7 +20,7 @@ Statement = IfStatement | WhileStatement | Block | IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )? WhileStatement = 'while' '(' Expression ')' Statement -ForStatement = 'for' '(' VardefOrExpressionstatement ';' Expression ';' Expressionstatement ')' Statement +ForStatement = 'for' '(' (VardefOrExpressionstatement)? ';' (Expression)? ';' (Expressionstatement)? ')' Statement Continue = 'continue' ';' Break = 'break' ';' Return = 'return' Expression? ';' diff --git a/test/solidityParser.cpp b/test/solidityParser.cpp index 53bd3f163..d57754b61 100644 --- a/test/solidityParser.cpp +++ b/test/solidityParser.cpp @@ -397,6 +397,18 @@ BOOST_AUTO_TEST_CASE(for_loop_simple_initexpr) BOOST_CHECK_NO_THROW(parseTextExplainError(text)); } +BOOST_AUTO_TEST_CASE(for_loop_simple_noexpr) +{ + char const* text = "contract test {\n" + " function fun(uint256 a) {\n" + " uint256 i =0;\n" + " for (;;)\n" + " { uint256 x = i; break; continue; }\n" + " }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseTextExplainError(text)); +} + BOOST_AUTO_TEST_CASE(if_statement) { char const* text = "contract test {\n" From 5eec2c5ac6d5a7f543a9eb33bdb3365653146fa1 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Tue, 16 Dec 2014 13:20:50 +0100 Subject: [PATCH 19/33] ForStatement typecheck and initExpression is a Statement --- libsolidity/AST.cpp | 8 ++++++-- libsolidity/AST.h | 4 ++-- libsolidity/Parser.cpp | 4 ++-- libsolidity/Parser.h | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index c286c412b..4c042f2cf 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -132,8 +132,12 @@ void WhileStatement::checkTypeRequirements() void ForStatement::checkTypeRequirements() { - // LTODO - m_condExpression->expectType(BoolType()); + if (m_initExpression) + m_initExpression->checkTypeRequirements(); + if (m_condExpression) + m_condExpression->expectType(BoolType()); + if (m_loopExpression) + m_loopExpression->checkTypeRequirements(); m_body->checkTypeRequirements(); } diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 297b7f4e9..2fb6a2e32 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -513,7 +513,7 @@ class ForStatement: public BreakableStatement { public: ForStatement(Location const& _location, - ASTPointer const& _initExpression, + ASTPointer const& _initExpression, ASTPointer const& _conditionExpression, ASTPointer const& _loopExpression, ASTPointer const& _body): @@ -527,7 +527,7 @@ public: virtual void checkTypeRequirements() override; private: - ASTPointer m_initExpression; + ASTPointer m_initExpression; ASTPointer m_condExpression; ASTPointer m_loopExpression; ASTPointer m_body; diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 598f5ad47..2669ce209 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -374,7 +374,7 @@ ASTPointer Parser::parseWhileStatement() ASTPointer Parser::parseForStatement() { ASTNodeFactory nodeFactory(*this); - ASTPointer initExpression; + ASTPointer initExpression; ASTPointer conditionExpression; ASTPointer loopExpression; expectToken(Token::FOR); @@ -401,7 +401,7 @@ ASTPointer Parser::parseForStatement() body); } -ASTPointer Parser::parseVardefOrExprstatement() +ASTPointer Parser::parseVardefOrExprstatement() { if (peekVariableDefinition()) return parseVariableDefinition(); diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index 343a85c0e..cd3d5bab6 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -60,7 +60,7 @@ private: ASTPointer parseIfStatement(); ASTPointer parseWhileStatement(); ASTPointer parseForStatement(); - ASTPointer parseVardefOrExprstatement(); + ASTPointer parseVardefOrExprstatement(); ASTPointer parseVariableDefinition(); ASTPointer parseExpressionStatement(); ASTPointer parseExpression(); From 1f0346396ae307aa87e184f3ac8f0d7f9a97c832 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Tue, 16 Dec 2014 14:46:17 +0100 Subject: [PATCH 20/33] Minor issues, grammar update, new ForStatement test --- libsolidity/AST.h | 7 +++++++ libsolidity/Parser.cpp | 9 +++------ libsolidity/Parser.h | 5 ++--- libsolidity/grammar.txt | 6 ++++-- test/solidityParser.cpp | 12 ++++++++++++ 5 files changed, 28 insertions(+), 11 deletions(-) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 2fb6a2e32..dcbca1ee8 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -509,6 +509,9 @@ private: ASTPointer m_body; }; +/** + * For loop statement + */ class ForStatement: public BreakableStatement { public: @@ -527,9 +530,13 @@ public: virtual void checkTypeRequirements() override; 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; }; diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 2669ce209..732356751 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -330,10 +330,7 @@ ASTPointer Parser::parseStatement() } break; default: - if (peekVariableDefinition()) - statement = parseVariableDefinition(); - else // "ordinary" expression statement - statement = parseExpressionStatement(); + statement = parseVarDefOrExprStmt(); } expectToken(Token::SEMICOLON); return statement; @@ -382,7 +379,7 @@ ASTPointer Parser::parseForStatement() // LTODO: Maybe here have some predicate like peekExpression() instead of checking for semicolon and RPAREN? if (m_scanner->getCurrentToken() != Token::SEMICOLON) - initExpression = parseVardefOrExprstatement(); + initExpression = parseVarDefOrExprStmt(); expectToken(Token::SEMICOLON); if (m_scanner->getCurrentToken() != Token::SEMICOLON) @@ -401,7 +398,7 @@ ASTPointer Parser::parseForStatement() body); } -ASTPointer Parser::parseVardefOrExprstatement() +ASTPointer Parser::parseVarDefOrExprStmt() { if (peekVariableDefinition()) return parseVariableDefinition(); diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index cd3d5bab6..bf3a6beac 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -60,7 +60,7 @@ private: ASTPointer parseIfStatement(); ASTPointer parseWhileStatement(); ASTPointer parseForStatement(); - ASTPointer parseVardefOrExprstatement(); + ASTPointer parseVarDefOrExprStmt(); ASTPointer parseVariableDefinition(); ASTPointer parseExpressionStatement(); ASTPointer parseExpression(); @@ -74,8 +74,7 @@ private: ///@{ ///@name Helper functions - /// Peeks ahead in the scanner to determine if an expression statement - /// could be a variable definition + /// 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. diff --git a/libsolidity/grammar.txt b/libsolidity/grammar.txt index 7c0ac3a50..8c34997b9 100644 --- a/libsolidity/grammar.txt +++ b/libsolidity/grammar.txt @@ -16,11 +16,13 @@ Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')' Block = '{' Statement* '}' Statement = IfStatement | WhileStatement | Block | - ( Continue | Break | Return | VariableDefinition | Expression ) ';' + ( Continue | Break | Return | VariableDefinition | ExpressionStatement ) ';' +ExpressionStatement = Expression IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )? WhileStatement = 'while' '(' Expression ')' Statement -ForStatement = 'for' '(' (VardefOrExpressionstatement)? ';' (Expression)? ';' (Expressionstatement)? ')' Statement +VardefOrExprStmt = Variabledefinition | ExpressionStatement +ForStatement = 'for' '(' (VardefOrExprStmt)? ';' (Expression)? ';' (ExpressionStatement)? ')' Statement Continue = 'continue' ';' Break = 'break' ';' Return = 'return' Expression? ';' diff --git a/test/solidityParser.cpp b/test/solidityParser.cpp index d57754b61..f978cdd9b 100644 --- a/test/solidityParser.cpp +++ b/test/solidityParser.cpp @@ -409,6 +409,18 @@ BOOST_AUTO_TEST_CASE(for_loop_simple_noexpr) BOOST_CHECK_NO_THROW(parseTextExplainError(text)); } +BOOST_AUTO_TEST_CASE(for_loop_single_stmt_body) +{ + char const* text = "contract test {\n" + " function fun(uint256 a) {\n" + " uint256 i =0;\n" + " for (i = 0; i < 10; i++)\n" + " continue;\n" + " }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseTextExplainError(text)); +} + BOOST_AUTO_TEST_CASE(if_statement) { char const* text = "contract test {\n" From 3c6e966160d828882d243ee37b52b23992891404 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Tue, 16 Dec 2014 16:52:47 +0100 Subject: [PATCH 21/33] Solidity ForStatement Compiler part - Work in progress --- libsolidity/AST.h | 5 +++++ libsolidity/Compiler.cpp | 31 +++++++++++++++++++++++++++++-- test/solidityEndToEndTest.cpp | 22 ++++++++++++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index dcbca1ee8..c83690443 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -529,6 +529,11 @@ public: 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; diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index a0cad5374..8c70b2718 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -289,8 +289,35 @@ bool Compiler::visit(WhileStatement const& _whileStatement) bool Compiler::visit(ForStatement const& _forStatement) { - // LTODO - (void) _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; } diff --git a/test/solidityEndToEndTest.cpp b/test/solidityEndToEndTest.cpp index b6f63aa72..32de9af46 100644 --- a/test/solidityEndToEndTest.cpp +++ b/test/solidityEndToEndTest.cpp @@ -176,6 +176,28 @@ BOOST_AUTO_TEST_CASE(nested_loops) testSolidityAgainstCppOnRange(0, nested_loops_cpp, 0, 12); } +BOOST_AUTO_TEST_CASE(for_loop) +{ + char const* sourceCode = "contract test {\n" + " function f(uint n) returns(uint nfac) {\n" + " nfac = 1;\n" + " for (var i = 2; i <= n; i++)\n" + " nfac *= 1;\n" + " }\n" + "}\n"; + compileAndRun(sourceCode); + + auto for_loop_cpp = [](u256 const& n) -> u256 + { + u256 nfac = 1; + for (auto i = 2; i <= n; i++) + nfac *= i; + return nfac; + }; + + testSolidityAgainstCppOnRange(0, for_loop_cpp, 0, 5); +} + BOOST_AUTO_TEST_CASE(calling_other_functions) { // note that the index of a function is its index in the sorted sequence of functions From 82117dc7dab431b2de6cfb73a6b17343ff71b57b Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Tue, 16 Dec 2014 17:35:00 +0100 Subject: [PATCH 22/33] Solidity: More tests for the ForStatement --- test/solidityEndToEndTest.cpp | 54 ++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/test/solidityEndToEndTest.cpp b/test/solidityEndToEndTest.cpp index 32de9af46..ecd3b63a3 100644 --- a/test/solidityEndToEndTest.cpp +++ b/test/solidityEndToEndTest.cpp @@ -182,7 +182,7 @@ BOOST_AUTO_TEST_CASE(for_loop) " function f(uint n) returns(uint nfac) {\n" " nfac = 1;\n" " for (var i = 2; i <= n; i++)\n" - " nfac *= 1;\n" + " nfac *= i;\n" " }\n" "}\n"; compileAndRun(sourceCode); @@ -198,6 +198,58 @@ BOOST_AUTO_TEST_CASE(for_loop) testSolidityAgainstCppOnRange(0, for_loop_cpp, 0, 5); } +BOOST_AUTO_TEST_CASE(for_loop_empty) +{ + char const* sourceCode = "contract test {\n" + " function f() returns(uint ret) {\n" + " ret = 1;\n" + " for (;;)\n" + " {\n" + " ret += 1;\n" + " if (ret >= 10) break;\n" + " }\n" + " }\n" + "}\n"; + compileAndRun(sourceCode); + + auto for_loop_empty_cpp = []() -> u256 + { + u256 ret = 1; + for (;;) + { + ret += 1; + if (ret >= 10) break; + } + return ret; + }; + + testSolidityAgainstCpp(0, for_loop_empty_cpp); +} + +BOOST_AUTO_TEST_CASE(for_loop_simple_init_expr) +{ + char const* sourceCode = "contract test {\n" + " function f(uint n) returns(uint nfac) {\n" + " nfac = 1;\n" + " uint256 i;\n" + " for (i = 2; i <= n; i++)\n" + " nfac *= i;\n" + " }\n" + "}\n"; + compileAndRun(sourceCode); + + auto for_loop_simple_init_expr_cpp = [](u256 const& n) -> u256 + { + u256 nfac = 1; + u256 i; + for (i = 2; i <= n; i++) + nfac *= i; + return nfac; + }; + + testSolidityAgainstCppOnRange(0, for_loop_simple_init_expr_cpp, 0, 5); +} + BOOST_AUTO_TEST_CASE(calling_other_functions) { // note that the index of a function is its index in the sorted sequence of functions From c9a032aad4f762d15f92135cd0135303de2ad43c Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 17 Dec 2014 00:17:38 +0100 Subject: [PATCH 23/33] better alignment of global string constants --- solc/CommandLineInterface.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index fd660a0ca..81dd25ec7 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -50,13 +50,13 @@ namespace solidity // LTODO: Maybe some argument class pairing names with // extensions and other attributes would be a better choice here? -static string const g_argAbiStr = "abi"; -static string const g_argAsmStr = "asm"; -static string const g_argAstStr = "ast"; -static string const g_argBinaryStr = "binary"; -static string const g_argOpcodesStr = "opcodes"; -static string const g_argNatspecDevStr = "natspec-dev"; -static string const g_argNatspecUserStr = "natspec-user"; +static string const g_argAbiStr = "abi"; +static string const g_argAsmStr = "asm"; +static string const g_argAstStr = "ast"; +static string const g_argBinaryStr = "binary"; +static string const g_argOpcodesStr = "opcodes"; +static string const g_argNatspecDevStr = "natspec-dev"; +static string const g_argNatspecUserStr = "natspec-user"; static void version() { From bcc263070b5851dfa432272d18b06ec32a6fb59c Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 17 Dec 2014 00:28:26 +0100 Subject: [PATCH 24/33] Moving comment to function body --- libsolidity/Parser.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 732356751..e287319d2 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -596,12 +596,12 @@ vector> Parser::parseFunctionCallArguments() } -// 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. 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()) || From f9627b07dcb170a98f51c52da261ace8dfbebc67 Mon Sep 17 00:00:00 2001 From: ethdev Date: Wed, 17 Dec 2014 11:18:59 +0100 Subject: [PATCH 25/33] vs files added to gitignore --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ca18feb5b..c18d5a01d 100644 --- a/.gitignore +++ b/.gitignore @@ -18,11 +18,14 @@ ipch *.sdf *.opensdf *.suo +*.vcxproj +*.vcxproj.filters +*.sln # VIM stuff *.swp -#Xcode stuff +# Xcode stuff build_xc *.user From 373dbcaa5ffec342a870070c35ee01cb2993455e Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 17 Dec 2014 11:48:43 +0100 Subject: [PATCH 26/33] clang requires explicit initialization of map --- test/solidityCompiler.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/solidityCompiler.cpp b/test/solidityCompiler.cpp index 29f61454a..1bdafc599 100644 --- a/test/solidityCompiler.cpp +++ b/test/solidityCompiler.cpp @@ -56,7 +56,11 @@ bytes compileContract(const string& _sourceCode) BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); Compiler compiler; - compiler.compileContract(*contract, {}, {}); + + // clang requires explicit initialization of map + // http://stackoverflow.com/questions/17264067/chosen-constructor-is-explicit-in-copy-initialization-error-with-clang-4-2 + compiler.compileContract(*contract, {}, map{}); + // debug //compiler.streamAssembly(cout); return compiler.getAssembledBytecode(); From edb6072cc9c8bbf9d54ee0596e24c03b1f778681 Mon Sep 17 00:00:00 2001 From: ethdev Date: Wed, 17 Dec 2014 11:57:32 +0100 Subject: [PATCH 27/33] windows fix for initializer-list cannot convert to dev::bytes --- test/solidityEndToEndTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/solidityEndToEndTest.cpp b/test/solidityEndToEndTest.cpp index ecd3b63a3..1ac480da9 100644 --- a/test/solidityEndToEndTest.cpp +++ b/test/solidityEndToEndTest.cpp @@ -1108,7 +1108,7 @@ BOOST_AUTO_TEST_CASE(constructor_arguments) function getName() returns (string3 ret) { return h.getName(); } })"; compileAndRun(sourceCode, 0, "Main"); - BOOST_REQUIRE(callContractFunction(0) == bytes({0x01})); + BOOST_REQUIRE(callContractFunction(0) == bytes{0x01}); BOOST_REQUIRE(callContractFunction(1) == bytes({'a', 'b', 'c'})); } From 2daf84f1bd122861bbc9a0b6e8f1401a6abc4ceb Mon Sep 17 00:00:00 2001 From: ethdev Date: Wed, 17 Dec 2014 12:19:10 +0100 Subject: [PATCH 28/33] initializer-list fix --- test/solidityEndToEndTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/solidityEndToEndTest.cpp b/test/solidityEndToEndTest.cpp index 1ac480da9..ad277a389 100644 --- a/test/solidityEndToEndTest.cpp +++ b/test/solidityEndToEndTest.cpp @@ -1108,7 +1108,7 @@ BOOST_AUTO_TEST_CASE(constructor_arguments) function getName() returns (string3 ret) { return h.getName(); } })"; compileAndRun(sourceCode, 0, "Main"); - BOOST_REQUIRE(callContractFunction(0) == bytes{0x01}); + BOOST_REQUIRE(callContractFunction(0) == bytes({byte(0x01)})); BOOST_REQUIRE(callContractFunction(1) == bytes({'a', 'b', 'c'})); } From d09f6e8c9756d2bf21499b80056fcb9bfcc76b1f Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 17 Dec 2014 15:15:04 +0100 Subject: [PATCH 29/33] Whitespace change. --- libsolidity/CallGraph.cpp | 6 ++++-- libsolidity/CallGraph.h | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/libsolidity/CallGraph.cpp b/libsolidity/CallGraph.cpp index 48666ca7f..b30afb612 100644 --- a/libsolidity/CallGraph.cpp +++ b/libsolidity/CallGraph.cpp @@ -26,8 +26,10 @@ using namespace std; -namespace dev { -namespace solidity { +namespace dev +{ +namespace solidity +{ void CallGraph::addFunction(FunctionDefinition const& _function) { diff --git a/libsolidity/CallGraph.h b/libsolidity/CallGraph.h index 986e4dc4a..f7af64bff 100644 --- a/libsolidity/CallGraph.h +++ b/libsolidity/CallGraph.h @@ -25,8 +25,10 @@ #include #include -namespace dev { -namespace solidity { +namespace dev +{ +namespace solidity +{ /** * Can be used to compute the graph of calls (or rather references) between functions of the same From 46664c44b368f8471d16f607d8ad6467ec5dbf89 Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 17 Dec 2014 16:23:18 +0100 Subject: [PATCH 30/33] Assertions that throw InternalCompilerErrors. --- libsolidity/AST.cpp | 8 ++--- libsolidity/AST.h | 16 +++++----- libsolidity/CompilerContext.cpp | 20 +++++------- libsolidity/CompilerUtils.cpp | 6 ++-- libsolidity/ExpressionCompiler.cpp | 33 ++++++++----------- libsolidity/InterfaceHandler.cpp | 3 +- libsolidity/NameAndTypeResolver.cpp | 15 +++------ libsolidity/Scanner.cpp | 13 +++----- libsolidity/Token.h | 13 +++----- libsolidity/Types.cpp | 13 ++++---- libsolidity/Utils.h | 49 +++++++++++++++++++++++++++++ solc/CommandLineInterface.cpp | 3 +- 12 files changed, 107 insertions(+), 85 deletions(-) create mode 100644 libsolidity/Utils.h diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 4c042f2cf..1fa6d8f6f 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -21,7 +21,7 @@ */ #include - +#include #include #include #include @@ -145,8 +145,7 @@ void Return::checkTypeRequirements() { if (!m_expression) return; - if (asserts(m_returnParameters)) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Return parameters not assigned.")); + solAssert(m_returnParameters, "Return parameters not assigned."); if (m_returnParameters->getParameters().size() != 1) BOOST_THROW_EXCEPTION(createTypeError("Different number of arguments in return statement " "than in returns declaration.")); @@ -336,8 +335,7 @@ void IndexAccess::checkTypeRequirements() void Identifier::checkTypeRequirements() { - if (asserts(m_referencedDeclaration)) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier not resolved.")); + solAssert(m_referencedDeclaration, "Identifier not resolved."); VariableDeclaration const* variable = dynamic_cast(m_referencedDeclaration); if (variable) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index c83690443..b7c3dc6c0 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -364,7 +365,7 @@ public: explicit ElementaryTypeName(Location const& _location, Token::Value _type): TypeName(_location), m_type(_type) { - if (asserts(Token::isElementaryTypeName(_type))) BOOST_THROW_EXCEPTION(InternalCompilerError()); + solAssert(Token::isElementaryTypeName(_type), ""); } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; @@ -575,8 +576,7 @@ public: void setFunctionReturnParameters(ParameterList& _parameters) { m_returnParameters = &_parameters; } ParameterList const& getFunctionReturnParameters() const { - if (asserts(m_returnParameters)) - BOOST_THROW_EXCEPTION(InternalCompilerError()); + solAssert(m_returnParameters, ""); return *m_returnParameters; } Expression const* getExpression() const { return m_expression.get(); } @@ -682,7 +682,7 @@ public: Expression(_location), m_leftHandSide(_leftHandSide), m_assigmentOperator(_assignmentOperator), m_rightHandSide(_rightHandSide) { - if (asserts(Token::isAssignmentOp(_assignmentOperator))) BOOST_THROW_EXCEPTION(InternalCompilerError()); + solAssert(Token::isAssignmentOp(_assignmentOperator), ""); } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; @@ -710,7 +710,7 @@ public: Expression(_location), m_operator(_operator), m_subExpression(_subExpression), m_isPrefix(_isPrefix) { - if (asserts(Token::isUnaryOp(_operator))) BOOST_THROW_EXCEPTION(InternalCompilerError()); + solAssert(Token::isUnaryOp(_operator), ""); } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; @@ -737,7 +737,7 @@ public: Token::Value _operator, ASTPointer const& _right): Expression(_location), m_left(_left), m_operator(_operator), m_right(_right) { - if (asserts(Token::isBinaryOp(_operator) || Token::isCompareOp(_operator))) BOOST_THROW_EXCEPTION(InternalCompilerError()); + solAssert(Token::isBinaryOp(_operator) || Token::isCompareOp(_operator), ""); } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; @@ -799,7 +799,7 @@ public: 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 { if (asserts(m_contract)) BOOST_THROW_EXCEPTION(InternalCompilerError()); else return m_contract; } + ContractDefinition const* getContract() const { solAssert(m_contract, ""); return m_contract; } private: ASTPointer m_contractName; @@ -894,7 +894,7 @@ public: ElementaryTypeNameExpression(Location const& _location, Token::Value _typeToken): PrimaryExpression(_location), m_typeToken(_typeToken) { - if (asserts(Token::isElementaryTypeName(_typeToken))) BOOST_THROW_EXCEPTION(InternalCompilerError()); + solAssert(Token::isElementaryTypeName(_typeToken), ""); } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; diff --git a/libsolidity/CompilerContext.cpp b/libsolidity/CompilerContext.cpp index 47401436a..18357bf0c 100644 --- a/libsolidity/CompilerContext.cpp +++ b/libsolidity/CompilerContext.cpp @@ -27,8 +27,10 @@ using namespace std; -namespace dev { -namespace solidity { +namespace dev +{ +namespace solidity +{ void CompilerContext::addMagicGlobal(MagicVariableDeclaration const& _declaration) { @@ -65,8 +67,7 @@ void CompilerContext::addFunction(FunctionDefinition const& _function) 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.")); + solAssert(ret != m_compiledContracts.end(), "Compiled contract not found."); return *ret->second; } @@ -78,16 +79,14 @@ bool CompilerContext::isLocalVariable(Declaration const* _declaration) const eth::AssemblyItem CompilerContext::getFunctionEntryLabel(FunctionDefinition const& _function) const { auto res = m_functionEntryLabels.find(&_function); - if (asserts(res != m_functionEntryLabels.end())) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Function entry label not found.")); + solAssert(res != m_functionEntryLabels.end(), "Function entry label not found."); return res->second.tag(); } unsigned CompilerContext::getBaseStackOffsetOfVariable(Declaration const& _declaration) const { auto res = m_localVariables.find(&_declaration); - if (asserts(res != m_localVariables.end())) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Variable not found on stack.")); + solAssert(res != m_localVariables.end(), "Variable not found on stack."); return m_localVariablesSize - res->second - 1; } @@ -99,12 +98,9 @@ unsigned CompilerContext::baseToCurrentStackOffset(unsigned _baseOffset) const u256 CompilerContext::getStorageLocationOfVariable(const Declaration& _declaration) const { auto it = m_stateVariables.find(&_declaration); - if (it == m_stateVariables.end()) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Variable not found in storage.")); + solAssert(it != m_stateVariables.end(), "Variable not found in storage."); return it->second; } - - } } diff --git a/libsolidity/CompilerUtils.cpp b/libsolidity/CompilerUtils.cpp index 9f474896e..680e9190b 100644 --- a/libsolidity/CompilerUtils.cpp +++ b/libsolidity/CompilerUtils.cpp @@ -39,8 +39,7 @@ void CompilerUtils::loadFromMemory(unsigned _offset, unsigned _bytes, bool _left return; } eth::Instruction load = _fromCalldata ? eth::Instruction::CALLDATALOAD : eth::Instruction::MLOAD; - if (asserts(_bytes <= 32)) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Memory load of more than 32 bytes requested.")); + solAssert(_bytes <= 32, "Memory load of more than 32 bytes requested."); if (_bytes == 32) m_context << u256(_offset) << load; else @@ -63,8 +62,7 @@ void CompilerUtils::storeInMemory(unsigned _offset, unsigned _bytes, bool _leftA m_context << eth::Instruction::POP; return; } - if (asserts(_bytes <= 32)) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Memory store of more than 32 bytes requested.")); + solAssert(_bytes <= 32, "Memory store of more than 32 bytes requested."); if (_bytes != 32 && !_leftAligned) // shift the value accordingly before storing m_context << (u256(1) << ((32 - _bytes) * 8)) << eth::Instruction::MUL; diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 743503bbf..f58c157d9 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -30,8 +30,10 @@ using namespace std; -namespace dev { -namespace solidity { +namespace dev +{ +namespace solidity +{ void ExpressionCompiler::compileExpression(CompilerContext& _context, Expression const& _expression, bool _optimize) { @@ -51,8 +53,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) _assignment.getRightHandSide().accept(*this); appendTypeConversion(*_assignment.getRightHandSide().getType(), *_assignment.getType()); _assignment.getLeftHandSide().accept(*this); - if (asserts(m_currentLValue.isValid())) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("LValue not retrieved.")); + solAssert(m_currentLValue.isValid(), "LValue not retrieved."); Token::Value op = _assignment.getAssignmentOperator(); if (op != Token::ASSIGN) // compound assignment @@ -84,8 +85,7 @@ void ExpressionCompiler::endVisit(UnaryOperation const& _unaryOperation) break; case Token::DELETE: // delete // @todo semantics change for complex types - if (asserts(m_currentLValue.isValid())) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("LValue not retrieved.")); + solAssert(m_currentLValue.isValid(), "LValue not retrieved."); m_context << u256(0); if (m_currentLValue.storesReferenceOnStack()) @@ -95,8 +95,7 @@ void ExpressionCompiler::endVisit(UnaryOperation const& _unaryOperation) break; case Token::INC: // ++ (pre- or postfix) case Token::DEC: // -- (pre- or postfix) - if (asserts(m_currentLValue.isValid())) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("LValue not retrieved.")); + solAssert(m_currentLValue.isValid(), "LValue not retrieved."); m_currentLValue.retrieveValue(_unaryOperation); if (!_unaryOperation.isPrefixOperation()) { @@ -179,8 +178,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) if (_functionCall.isTypeConversion()) { //@todo struct construction - if (asserts(_functionCall.getArguments().size() == 1)) - BOOST_THROW_EXCEPTION(InternalCompilerError()); + solAssert(_functionCall.getArguments().size() == 1, ""); Expression const& firstArgument = *_functionCall.getArguments().front(); firstArgument.accept(*this); if (firstArgument.getType()->getCategory() == Type::Category::CONTRACT && @@ -195,8 +193,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) { FunctionType const& function = dynamic_cast(*_functionCall.getExpression().getType()); vector> arguments = _functionCall.getArguments(); - if (asserts(arguments.size() == function.getParameterTypes().size())) - BOOST_THROW_EXCEPTION(InternalCompilerError()); + solAssert(arguments.size() == function.getParameterTypes().size(), ""); switch (function.getLocation()) { @@ -282,12 +279,10 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) bool ExpressionCompiler::visit(NewExpression const& _newExpression) { ContractType const* type = dynamic_cast(_newExpression.getType().get()); - if (asserts(type)) - BOOST_THROW_EXCEPTION(InternalCompilerError()); + solAssert(type, ""); TypePointers const& types = type->getConstructorType()->getParameterTypes(); vector> arguments = _newExpression.getArguments(); - if (asserts(arguments.size() == types.size())) - BOOST_THROW_EXCEPTION(InternalCompilerError()); + solAssert(arguments.size() == types.size(), ""); // copy the contracts code into memory bytes const& bytecode = m_context.getCompiledContract(*_newExpression.getContract()); @@ -439,8 +434,7 @@ void ExpressionCompiler::endVisit(Literal const& _literal) void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryOperation) { Token::Value const op = _binaryOperation.getOperator(); - if (asserts(op == Token::OR || op == Token::AND)) - BOOST_THROW_EXCEPTION(InternalCompilerError()); + solAssert(op == Token::OR || op == Token::AND, ""); _binaryOperation.getLeftExpression().accept(*this); m_context << eth::Instruction::DUP1; @@ -592,8 +586,7 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio vector> const& _arguments, FunctionCallOptions const& _options) { - if (asserts(_arguments.size() == _functionType.getParameterTypes().size())) - BOOST_THROW_EXCEPTION(InternalCompilerError()); + solAssert(_arguments.size() == _functionType.getParameterTypes().size(), ""); unsigned dataOffset = _options.bare ? 0 : 1; // reserve one byte for the function index for (unsigned i = 0; i < _arguments.size(); ++i) diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index 1310f504a..c734fa38c 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -198,8 +198,7 @@ std::string::const_iterator InterfaceHandler::appendDocTagParam(std::string::con std::string::const_iterator _end) { // Should never be called with an empty vector - if (asserts(!m_params.empty())) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Internal: Tried to append to empty parameter")); + solAssert(!m_params.empty(), "Internal: Tried to append to empty parameter"); auto pair = m_params.back(); pair.second += " "; diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index 80732615c..3774537d1 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -70,8 +70,7 @@ void NameAndTypeResolver::checkTypeRequirements(ContractDefinition& _contract) void NameAndTypeResolver::updateDeclaration(Declaration const& _declaration) { m_scopes[nullptr].registerDeclaration(_declaration, true); - if (asserts(_declaration.getScope() == nullptr)) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Updated declaration outside global scope.")); + solAssert(_declaration.getScope() == nullptr, "Updated declaration outside global scope."); } Declaration const* NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const @@ -133,8 +132,7 @@ void DeclarationRegistrationHelper::endVisit(VariableDefinition& _variableDefini { // Register the local variables with the function // This does not fit here perfectly, but it saves us another AST visit. - if (asserts(m_currentFunction)) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Variable definition without function.")); + solAssert(m_currentFunction, "Variable definition without function."); m_currentFunction->addLocalVariable(_variableDefinition.getDeclaration()); } @@ -149,15 +147,13 @@ void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declara map::iterator iter; bool newlyAdded; tie(iter, newlyAdded) = m_scopes.emplace(&_declaration, DeclarationContainer(m_currentScope, &m_scopes[m_currentScope])); - if (asserts(newlyAdded)) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to add new scope.")); + solAssert(newlyAdded, "Unable to add new scope."); m_currentScope = &_declaration; } void DeclarationRegistrationHelper::closeCurrentScope() { - if (asserts(m_currentScope)) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Closed non-existing scope.")); + solAssert(m_currentScope, "Closed non-existing scope."); m_currentScope = m_scopes[m_currentScope].getEnclosingDeclaration(); } @@ -196,8 +192,7 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable) bool ReferencesResolver::visit(Return& _return) { - if (asserts(m_returnParameters)) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Return parameters not set.")); + solAssert(m_returnParameters, "Return parameters not set."); _return.setFunctionReturnParameters(*m_returnParameters); return true; } diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index 08bf744d4..1a21149a1 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -52,6 +52,7 @@ #include #include +#include #include using namespace std; @@ -249,8 +250,7 @@ Token::Value Scanner::scanDocumentationComment() Token::Value Scanner::skipMultiLineComment() { - if (asserts(m_char == '*')) - BOOST_THROW_EXCEPTION(InternalCompilerError()); + solAssert(m_char == '*', ""); advance(); while (!isSourcePastEndOfInput()) { @@ -597,8 +597,7 @@ Token::Value Scanner::scanNumber(char _charSeen) // scan exponent, if any if (m_char == 'e' || m_char == 'E') { - if (asserts(kind != HEX)) // 'e'/'E' must be scanned as part of the hex number - BOOST_THROW_EXCEPTION(InternalCompilerError()); + solAssert(kind != HEX, "'e'/'E' must be scanned as part of the hex number"); if (kind != DECIMAL) return Token::ILLEGAL; // scan exponent @@ -639,8 +638,7 @@ static Token::Value keywordOrIdentifierToken(string const& _input) Token::Value Scanner::scanIdentifierOrKeyword() { - if (asserts(isIdentifierStart(m_char))) - BOOST_THROW_EXCEPTION(InternalCompilerError()); + solAssert(isIdentifierStart(m_char), ""); LiteralScope literal(this, LITERAL_TYPE_STRING); addLiteralCharAndAdvance(); // Scan the rest of the identifier characters. @@ -662,8 +660,7 @@ char CharStream::advanceAndGet(size_t _chars) char CharStream::rollback(size_t _amount) { - if (asserts(m_pos >= _amount)) - BOOST_THROW_EXCEPTION(InternalCompilerError()); + solAssert(m_pos >= _amount, ""); m_pos -= _amount; return get(); } diff --git a/libsolidity/Token.h b/libsolidity/Token.h index 22378ae07..32656096a 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -44,6 +44,7 @@ #include #include +#include #include namespace dev @@ -344,8 +345,7 @@ public: // (e.g. "LT" for the token LT). static char const* getName(Value tok) { - if (asserts(tok < NUM_TOKENS)) - BOOST_THROW_EXCEPTION(InternalCompilerError()); + solAssert(tok < NUM_TOKENS, ""); return m_name[tok]; } @@ -360,8 +360,7 @@ public: static Value AssignmentToBinaryOp(Value op) { - if (asserts(isAssignmentOp(op) && op != ASSIGN)) - BOOST_THROW_EXCEPTION(InternalCompilerError()); + solAssert(isAssignmentOp(op) && op != ASSIGN, ""); return Token::Value(op + (BIT_OR - ASSIGN_BIT_OR)); } @@ -375,8 +374,7 @@ public: // have a (unique) string (e.g. an IDENTIFIER). static char const* toString(Value tok) { - if (asserts(tok < NUM_TOKENS)) - BOOST_THROW_EXCEPTION(InternalCompilerError()); + solAssert(tok < NUM_TOKENS, ""); return m_string[tok]; } @@ -384,8 +382,7 @@ public: // operators; returns 0 otherwise. static int precedence(Value tok) { - if (asserts(tok < NUM_TOKENS)) - BOOST_THROW_EXCEPTION(InternalCompilerError()); + solAssert(tok < NUM_TOKENS, ""); return m_precedence[tok]; } diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index eba11cb0d..71319c3ac 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -34,8 +35,7 @@ namespace solidity shared_ptr Type::fromElementaryTypeName(Token::Value _typeToken) { - if (asserts(Token::isElementaryTypeName(_typeToken))) - BOOST_THROW_EXCEPTION(InternalCompilerError()); + solAssert(Token::isElementaryTypeName(_typeToken), ""); if (Token::INT <= _typeToken && _typeToken <= Token::HASH256) { @@ -120,8 +120,8 @@ IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier): { if (isAddress()) m_bits = 160; - if (asserts(m_bits > 0 && m_bits <= 256 && m_bits % 8 == 0)) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid bit number for integer type: " + dev::toString(_bits))); + 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 @@ -215,9 +215,8 @@ shared_ptr StaticStringType::smallestTypeForLiteral(string con StaticStringType::StaticStringType(int _bytes): m_bytes(_bytes) { - if (asserts(m_bytes >= 0 && m_bytes <= 32)) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid byte number for static string type: " + - dev::toString(m_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 diff --git a/libsolidity/Utils.h b/libsolidity/Utils.h new file mode 100644 index 000000000..8d6a3ab08 --- /dev/null +++ b/libsolidity/Utils.h @@ -0,0 +1,49 @@ +/* + 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)); +} + +} +} diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 81dd25ec7..399f52436 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -311,7 +311,8 @@ bool CommandLineInterface::processInput() } catch (InternalCompilerError const& exception) { - SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Internal compiler error", m_compiler); + cerr << "Internal compiler error during compilation:" << endl + << boost::diagnostic_information(exception); return false; } catch (Exception const& exception) From 0f75aaf6e63fd1c22ddc0eb828af8874bc2cd018 Mon Sep 17 00:00:00 2001 From: ethdev Date: Wed, 17 Dec 2014 16:25:24 +0100 Subject: [PATCH 31/33] cpack nsis windows installer --- CMakeLists.txt | 26 +++++++++++++++++++++++++- cmake/EthExecutableHelper.cmake | 15 ++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 02e3a1a05..1293b4427 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -156,4 +156,28 @@ add_test(NAME alltests WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/test COMMAND testet #unset(TARGET_PLATFORM CACHE) - +if (WIN32) + # packaging stuff + include(InstallRequiredSystemLibraries) + set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "ethereum") + set(CPACK_PACKAGE_VENDOR "ethereum.org") + set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md") + set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") + set(CPACK_PACKAGE_VERSION "0.7") + set(CPACK_GENERATOR "NSIS") + + # our stuff + set(CPACK_COMPONENT_ALETHZERO_GROUP "Applications") + set(CPACK_COMPONENT_THIRD_GROUP "Applications") + set(CPACK_COMPONENT_MIX_GROUP "Applications") + set(CPACK_COMPONENTS_ALL alethzero third mix) + + # nsis specific stuff + set(CPACK_NSIS_DISPLAY_NAME "${CPACK_PACKAGE_INSTALL_DIRECTORY} ethereum") + set(CPACK_NSIS_HELP_LINK "https://github.com/ethereum/cpp-ethereum") + set(CPACK_NSIS_URL_INFO_ABOUT "https://github.com/ethereum/cpp-ethereum") + set(CPACK_NSIS_CONTACT "ethereum.org") + set(CPACK_NSIS_MODIFY_PATH ON) + + include(CPack) +endif (WIN32) diff --git a/cmake/EthExecutableHelper.cmake b/cmake/EthExecutableHelper.cmake index 9d273522e..0ec1e10d4 100644 --- a/cmake/EthExecutableHelper.cmake +++ b/cmake/EthExecutableHelper.cmake @@ -108,7 +108,20 @@ macro(eth_install_executable EXECUTABLE) $/platforms ) - install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin) + install( FILES ${DLLS} + DESTINATION bin + COMPONENT ${EXECUTABLE} + ) + + install( DIRECTORY ${ETH_DEPENDENCY_INSTALL_DIR}/plugins/platforms + DESTINATION bin + COMPONENT ${EXECUTABLE} + ) + + install( TARGETS ${EXECUTABLE} RUNTIME + DESTINATION bin + COMPONENT ${EXECUTABLE} + ) else() install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin) From 8468d896a73b5ea44c94ca92afe78189044d8024 Mon Sep 17 00:00:00 2001 From: ethdev Date: Wed, 17 Dec 2014 16:47:22 +0100 Subject: [PATCH 32/33] fixed msvc not expanding macros correctly --- test/CMakeLists.txt | 1 + test/solidityCompiler.cpp | 7 ++++--- test/solidityExpressionCompiler.cpp | 4 ++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f862de24c..7cedc117b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -3,6 +3,7 @@ cmake_policy(SET CMP0015 NEW) aux_source_directory(. SRC_LIST) list(REMOVE_ITEM SRC_LIST "./createRandomTest.cpp") +include_directories(${Boost_INCLUDE_DIRS}) include_directories(${CRYPTOPP_INCLUDE_DIRS}) include_directories(${JSONCPP_INCLUDE_DIRS}) include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) diff --git a/test/solidityCompiler.cpp b/test/solidityCompiler.cpp index be2c83838..385a3e577 100644 --- a/test/solidityCompiler.cpp +++ b/test/solidityCompiler.cpp @@ -52,17 +52,18 @@ bytes compileContract(const string& _sourceCode) resolver.registerDeclarations(*sourceUnit); for (ASTPointer const& node: sourceUnit->getNodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) + { BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); + } for (ASTPointer const& node: sourceUnit->getNodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) + { BOOST_REQUIRE_NO_THROW(resolver.checkTypeRequirements(*contract)); + } for (ASTPointer const& node: sourceUnit->getNodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { Compiler compiler; - - // clang requires explicit initialization of map - // http://stackoverflow.com/questions/17264067/chosen-constructor-is-explicit-in-copy-initialization-error-with-clang-4-2 compiler.compileContract(*contract, {}, map{}); // debug diff --git a/test/solidityExpressionCompiler.cpp b/test/solidityExpressionCompiler.cpp index 2bdc38421..9c375418e 100644 --- a/test/solidityExpressionCompiler.cpp +++ b/test/solidityExpressionCompiler.cpp @@ -95,10 +95,14 @@ bytes compileFirstExpression(const string& _sourceCode, vector> _ resolver.registerDeclarations(*sourceUnit); for (ASTPointer const& node: sourceUnit->getNodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) + { BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); + } for (ASTPointer const& node: sourceUnit->getNodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) + { BOOST_REQUIRE_NO_THROW(resolver.checkTypeRequirements(*contract)); + } for (ASTPointer const& node: sourceUnit->getNodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { From 83b218908b8220aa47a3bb1ea8b2e3cdc60474b7 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 17 Dec 2014 17:08:57 +0100 Subject: [PATCH 33/33] Format catch arguments as function parameters. --- solc/CommandLineInterface.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 399f52436..2a79521ef 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -230,9 +230,9 @@ bool CommandLineInterface::parseArguments(int argc, char** argv) { po::store(po::command_line_parser(argc, argv).options(desc).positional(p).allow_unregistered().run(), m_args); } - catch (po::error const& exception) + catch (po::error const& _exception) { - cout << exception.what() << endl; + cout << _exception.what() << endl; return false; } po::notify(m_args); @@ -289,35 +289,35 @@ bool CommandLineInterface::processInput() // TODO: Perhaps we should not compile unless requested m_compiler.compile(m_args["optimize"].as()); } - catch (ParserError const& exception) + catch (ParserError const& _exception) { - SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Parser error", m_compiler); + SourceReferenceFormatter::printExceptionInformation(cerr, _exception, "Parser error", m_compiler); return false; } - catch (DeclarationError const& exception) + catch (DeclarationError const& _exception) { - SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Declaration error", m_compiler); + SourceReferenceFormatter::printExceptionInformation(cerr, _exception, "Declaration error", m_compiler); return false; } - catch (TypeError const& exception) + catch (TypeError const& _exception) { - SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Type error", m_compiler); + SourceReferenceFormatter::printExceptionInformation(cerr, _exception, "Type error", m_compiler); return false; } - catch (CompilerError const& exception) + catch (CompilerError const& _exception) { - SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Compiler error", m_compiler); + SourceReferenceFormatter::printExceptionInformation(cerr, _exception, "Compiler error", m_compiler); return false; } - catch (InternalCompilerError const& exception) + catch (InternalCompilerError const& _exception) { cerr << "Internal compiler error during compilation:" << endl - << boost::diagnostic_information(exception); + << boost::diagnostic_information(_exception); return false; } - catch (Exception const& exception) + catch (Exception const& _exception) { - cerr << "Exception during compilation: " << boost::diagnostic_information(exception) << endl; + cerr << "Exception during compilation: " << boost::diagnostic_information(_exception) << endl; return false; } catch (...)