From b0c3579e25f809ed77ba4f6110091e1a87c3a74d Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Fri, 30 Jan 2015 01:26:00 +0800 Subject: [PATCH 01/15] implement named arguments --- libsolidity/AST.cpp | 43 ++++++++++++++++++++++++++-- libsolidity/AST.h | 6 ++-- libsolidity/ExpressionCompiler.cpp | 32 +++++++++++++++++++-- libsolidity/Parser.cpp | 45 ++++++++++++++++++++++++++---- libsolidity/Parser.h | 3 +- 5 files changed, 116 insertions(+), 13 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index d95a254e9..bda366aed 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -430,6 +430,8 @@ void FunctionCall::checkTypeRequirements() // number of non-mapping members if (m_arguments.size() != 1) BOOST_THROW_EXCEPTION(createTypeError("More than one argument for explicit type conversion.")); + if (!m_names.empty()) + BOOST_THROW_EXCEPTION(createTypeError("Type conversion can't allow named arguments.")); if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type.getActualType())) BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed.")); m_type = type.getActualType(); @@ -442,9 +444,44 @@ void FunctionCall::checkTypeRequirements() 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) - if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i])) - BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in function call.")); + + if (m_names.empty()) + { + 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 function call.")); + } + else + { + auto const& parameterNames = functionType->getParameterNames(); + if (parameterNames.size() != m_names.size()) + BOOST_THROW_EXCEPTION(createTypeError("Some argument names are missing.")); + + // check duplicate names + for (size_t i = 0; i < m_names.size(); i++) { + for (size_t j = i + 1; j < m_names.size(); j++) { + if (m_names[i] == m_names[j]) + BOOST_THROW_EXCEPTION(createTypeError("Duplicate named argument.")); + } + } + + for (size_t i = 0; i < m_names.size(); i++) { + bool found = false; + for (size_t j = 0; j < parameterNames.size(); j++) { + if (parameterNames[j] == m_names[i]) { + // check type convertible + if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[j])) + BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in function call.")); + + found = true; + break; + } + } + if (!found) + BOOST_THROW_EXCEPTION(createTypeError("Named argument doesn't match function declaration.")); + } + } + // @todo actually the return type should be an anonymous struct, // but we change it to the type of the first return value until we have structs if (functionType->getReturnParameterTypes().empty()) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index f3b18d392..cf86780d6 100755 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -983,14 +983,15 @@ class FunctionCall: public Expression { public: FunctionCall(Location const& _location, ASTPointer const& _expression, - std::vector> const& _arguments): - Expression(_location), m_expression(_expression), m_arguments(_arguments) {} + std::vector> const& _arguments, std::vector const& _names): + Expression(_location), m_expression(_expression), m_arguments(_arguments), m_names(_names) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; virtual void checkTypeRequirements() override; Expression const& getExpression() const { return *m_expression; } std::vector> getArguments() const { return {m_arguments.begin(), m_arguments.end()}; } + std::vector const& getNames() const { return m_names; } /// Returns true if this is not an actual function call, but an explicit type conversion /// or constructor call. @@ -999,6 +1000,7 @@ public: private: ASTPointer m_expression; std::vector> m_arguments; + std::vector m_names; }; /** diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 5d44c86f3..62066ac69 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -193,6 +193,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) { //@todo struct construction solAssert(_functionCall.getArguments().size() == 1, ""); + solAssert(_functionCall.getNames().empty(), ""); Expression const& firstArgument = *_functionCall.getArguments().front(); firstArgument.accept(*this); appendTypeConversion(*firstArgument.getType(), *_functionCall.getType()); @@ -200,8 +201,35 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) else { FunctionType const& function = dynamic_cast(*_functionCall.getExpression().getType()); - vector> arguments = _functionCall.getArguments(); - solAssert(arguments.size() == function.getParameterTypes().size(), ""); + TypePointers const& parameterTypes = function.getParameterTypes(); + vector const& parameterNames = function.getParameterNames(); + vector> const& callArguments = _functionCall.getArguments(); + vector const& callArgumentNames = _functionCall.getNames(); + solAssert(callArguments.size() == parameterTypes.size(), ""); + + vector> arguments; + if (callArgumentNames.empty()) + { + // normal arguments + arguments = {callArguments.begin(), callArguments.end()}; + } + else + { + // named arguments + for (size_t i = 0; i < parameterNames.size(); i++) { + bool found = false; + for (size_t j = 0; j < callArgumentNames.size(); j++) { + if (parameterNames[i] == callArgumentNames[j]) { + // we found the actual parameter position + arguments.push_back(callArguments[j]); + + found = true; + break; + } + } + solAssert(found, ""); + } + } switch (function.getLocation()) { diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 5cfc8f462..3d76eeecc 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -172,7 +172,7 @@ ASTPointer Parser::parseInheritanceSpecifier() if (m_scanner->getCurrentToken() == Token::LPAREN) { m_scanner->next(); - arguments = parseFunctionCallArguments(); + arguments = parseFunctionCallListArguments(); nodeFactory.markEndPosition(); expectToken(Token::RPAREN); } @@ -288,7 +288,7 @@ ASTPointer Parser::parseModifierInvocation() if (m_scanner->getCurrentToken() == Token::LPAREN) { m_scanner->next(); - arguments = parseFunctionCallArguments(); + arguments = parseFunctionCallListArguments(); nodeFactory.markEndPosition(); expectToken(Token::RPAREN); } @@ -621,10 +621,12 @@ ASTPointer Parser::parseLeftHandSideExpression() case Token::LPAREN: { m_scanner->next(); - vector> arguments = parseFunctionCallArguments(); + vector> arguments; + vector names; + parseFunctionCallArguments(arguments, names); nodeFactory.markEndPosition(); expectToken(Token::RPAREN); - expression = nodeFactory.createNode(expression, arguments); + expression = nodeFactory.createNode(expression, arguments, names); } break; default: @@ -677,7 +679,7 @@ ASTPointer Parser::parsePrimaryExpression() return expression; } -vector> Parser::parseFunctionCallArguments() +vector> Parser::parseFunctionCallListArguments() { vector> arguments; if (m_scanner->getCurrentToken() != Token::RPAREN) @@ -692,6 +694,39 @@ vector> Parser::parseFunctionCallArguments() return arguments; } +void Parser::parseFunctionCallArguments(vector>& _arguments, vector& _names) +{ + Token::Value token = m_scanner->getCurrentToken(); + if (token == Token::LBRACE) + { + // call({arg1 : 1, arg2 : 2 }) + expectToken(Token::LBRACE); + while (m_scanner->getCurrentToken() != Token::RBRACE) + { + string identifier = *expectIdentifierToken(); + expectToken(Token::COLON); + ASTPointer expression = parseExpression(); + + _arguments.push_back(expression); + _names.push_back(identifier); + + if (m_scanner->getCurrentToken() == Token::COMMA) + { + expectToken(Token::COMMA); + } + else + { + break; + } + } + expectToken(Token::RBRACE); + } + else + { + _arguments = parseFunctionCallListArguments(); + } +} + bool Parser::peekVariableDefinition() { diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index d3bff67e5..2a2f2312a 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -72,7 +72,8 @@ private: ASTPointer parseUnaryExpression(); ASTPointer parseLeftHandSideExpression(); ASTPointer parsePrimaryExpression(); - std::vector> parseFunctionCallArguments(); + std::vector> parseFunctionCallListArguments(); + void parseFunctionCallArguments(std::vector> & _arguments, std::vector & _names); ///@} ///@{ From c6c8a1ceebb4fd8b57df640c882c84b358a87675 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 30 Jan 2015 16:06:56 +0100 Subject: [PATCH 02/15] Adding mapping treatment to FunctionType Plus a TypeResolution test for it --- libsolidity/Types.cpp | 23 ++++++++++++++++++----- test/SolidityNameAndTypeResolution.cpp | 10 +++++++++- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index ab401332a..7fa4561e3 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -621,12 +621,25 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal FunctionType::FunctionType(VariableDeclaration const& _varDecl): m_location(Location::EXTERNAL), m_isConstant(true), m_declaration(&_varDecl) { - TypePointers params({}); - vector paramNames({}); - TypePointers retParams({_varDecl.getType()}); - vector retParamNames({ _varDecl.getName()}); - // for now, no input parameters LTODO: change for some things like mapping + TypePointers params; + vector paramNames; + TypePointers retParams; + vector retParamNames; + TypePointer varDeclType = _varDecl.getType(); + auto mappingType = dynamic_cast(varDeclType.get()); + if (mappingType!= nullptr) + { + params.push_back(mappingType->getKeyType()); + paramNames.push_back(mappingType->getKeyType()->toString()); + retParams.push_back(mappingType->getValueType()); + retParamNames.push_back(mappingType->getValueType()->toString()); + } + else // elelemntary type + { + retParams.push_back(varDeclType); + retParamNames.push_back(_varDecl.getName()); + } swap(params, m_parameterTypes); swap(paramNames, m_parameterNames); swap(retParams, m_returnParameterTypes); diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index b9a7140f7..df0e07e1d 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -637,6 +637,7 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors) " uint64(2);\n" " }\n" "uint256 foo;\n" + "mapping(uint=>string4) map;\n" "}\n"; ASTPointer source; @@ -644,10 +645,17 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors) BOOST_CHECK_NO_THROW(source = parseTextAndResolveNamesWithChecks(text)); BOOST_REQUIRE((contract = retrieveContract(source, 0)) != nullptr); FunctionTypePointer function = retrieveFunctionBySignature(contract, "foo()"); - BOOST_REQUIRE(function->hasDeclaration()); + BOOST_REQUIRE(function && function->hasDeclaration()); auto returnParams = function->getReturnParameterTypeNames(); BOOST_CHECK_EQUAL(returnParams.at(0), "uint256"); BOOST_CHECK(function->isConstant()); + function = retrieveFunctionBySignature(contract, "map(uint256)"); + BOOST_REQUIRE(function && function->hasDeclaration()); + auto Params = function->getParameterTypeNames(); + BOOST_CHECK_EQUAL(returnParams.at(0), "uint256"); + returnParams = function->getReturnParameterTypeNames(); + BOOST_CHECK_EQUAL(returnParams.at(0), "string4"); + BOOST_CHECK(function->isConstant()); } BOOST_AUTO_TEST_CASE(function_clash_with_state_variable_accessor) From 4795991f2abadff12d80855ee82dd25c9ffaa0c3 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Sun, 1 Feb 2015 02:41:14 +0100 Subject: [PATCH 03/15] Code generation for mapping state variable accessor - Work in progress --- libsolidity/Compiler.cpp | 4 -- libsolidity/ExpressionCompiler.cpp | 75 ++++++++++++++++++++++++++---- libsolidity/ExpressionCompiler.h | 4 ++ test/SolidityEndToEndTest.cpp | 18 +++++++ 4 files changed, 89 insertions(+), 12 deletions(-) diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 3c46d4552..389f826b7 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -240,10 +240,6 @@ bool Compiler::visit(VariableDeclaration const& _variableDeclaration) m_context << m_context.getFunctionEntryLabel(_variableDeclaration); ExpressionCompiler::appendStateVariableAccessor(m_context, _variableDeclaration); - unsigned sizeOnStack = _variableDeclaration.getType()->getSizeOnStack(); - solAssert(sizeOnStack <= 15, "Stack too deep."); - m_context << eth::dupInstruction(sizeOnStack + 1) << eth::Instruction::JUMP; - return false; } diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 7d58ea2e2..edd04256f 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -823,26 +823,85 @@ unsigned ExpressionCompiler::appendArgumentCopyToMemory(TypePointers const& _typ return length; } -unsigned ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, - Expression const& _expression, unsigned _memoryOffset) +unsigned ExpressionCompiler::appendTypeCopyToMemory(Type const& _expectedType, TypePointer const& _type, + Location const& _location, unsigned _memoryOffset) { - _expression.accept(*this); - appendTypeConversion(*_expression.getType(), _expectedType, true); + appendTypeConversion(*_type, _expectedType, true); unsigned const c_numBytes = CompilerUtils::getPaddedSize(_expectedType.getCalldataEncodedSize()); if (c_numBytes == 0 || c_numBytes > 32) BOOST_THROW_EXCEPTION(CompilerError() - << errinfo_sourceLocation(_expression.getLocation()) + << errinfo_sourceLocation(_location) << errinfo_comment("Type " + _expectedType.toString() + " not yet supported.")); bool const c_leftAligned = _expectedType.getCategory() == Type::Category::STRING; bool const c_padToWords = true; return CompilerUtils(m_context).storeInMemory(_memoryOffset, c_numBytes, c_leftAligned, c_padToWords); } +unsigned ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, + Expression const& _expression, + unsigned _memoryOffset) +{ + _expression.accept(*this); + return appendTypeCopyToMemory(_expectedType, _expression.getType(), _expression.getLocation(), _memoryOffset); +} + void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl) { - m_currentLValue.fromStateVariable(_varDecl, _varDecl.getType()); - solAssert(m_currentLValue.isInStorage(), ""); - m_currentLValue.retrieveValue(_varDecl.getType(), Location(), true); + auto mappingType = dynamic_cast(_varDecl.getType().get()); + unsigned sizeOnStack; + if (mappingType != nullptr) + { + // this copies from Compiler::visit(FunctionDefinition..) for argument reading + unsigned parameterSize = mappingType->getKeyType()->getSizeOnStack(); + m_context.adjustStackOffset(parameterSize); + m_context.addVariable(_varDecl, parameterSize); + // this copies from ExpressionCompiler::visit(IndexAccess .. ) for mapping access + TypePointer const& keyType = mappingType->getKeyType(); + unsigned length = appendTypeCopyToMemory(*keyType, mappingType->getValueType(), Location()); + solAssert(length == 32, "Mapping key has to take 32 bytes in memory (for now)."); + // @todo move this once we actually use memory + length += CompilerUtils(m_context).storeInMemory(length); + m_context << u256(length) << u256(0) << eth::Instruction::SHA3; + + m_currentLValue = LValue(m_context, LValue::STORAGE, *mappingType->getValueType()); + m_currentLValue.retrieveValue(mappingType->getValueType(), Location(), true); + + unsigned const c_argumentsSize = keyType->getSizeOnStack(); + unsigned const c_returnValuesSize = mappingType->getValueType()->getSizeOnStack(); + unsigned const c_localVariablesSize = 0; + + vector stackLayout; + stackLayout.push_back(c_returnValuesSize); // target of return address + stackLayout += vector(c_argumentsSize, -1); // discard all arguments + for (unsigned i = 0; i < c_returnValuesSize; ++i) + stackLayout.push_back(i); + stackLayout += vector(c_localVariablesSize, -1); + + while (stackLayout.back() != int(stackLayout.size() - 1)) + if (stackLayout.back() < 0) + { + m_context << eth::Instruction::POP; + stackLayout.pop_back(); + } + else + { + m_context << eth::swapInstruction(stackLayout.size() - stackLayout.back() - 1); + swap(stackLayout[stackLayout.back()], stackLayout.back()); + } + //@todo assert that everything is in place now + + m_context << eth::Instruction::JUMP; + } + else + { + m_currentLValue.fromStateVariable(_varDecl, _varDecl.getType()); + solAssert(m_currentLValue.isInStorage(), ""); + m_currentLValue.retrieveValue(_varDecl.getType(), Location(), true); + sizeOnStack = _varDecl.getType()->getSizeOnStack(); + solAssert(sizeOnStack <= 15, "Stack too deep."); + m_context << eth::dupInstruction(sizeOnStack + 1) << eth::Instruction::JUMP; + } + } ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType, diff --git a/libsolidity/ExpressionCompiler.h b/libsolidity/ExpressionCompiler.h index caecbfe8d..d93ab28ed 100644 --- a/libsolidity/ExpressionCompiler.h +++ b/libsolidity/ExpressionCompiler.h @@ -97,6 +97,10 @@ private: unsigned appendArgumentCopyToMemory(TypePointers const& _types, std::vector> const& _arguments, unsigned _memoryOffset = 0); + /// Appends code that copies a type to memory. + /// @returns the number of bytes copied to memory + unsigned appendTypeCopyToMemory(Type const& _expectedType, TypePointer const& _type, + Location const& _location, unsigned _memoryOffset = 0); /// Appends code that evaluates a single expression and copies it to memory (with optional offset). /// @returns the number of bytes copied to memory unsigned appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression, diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index 7edc250c8..63a8ebcd3 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -919,6 +919,24 @@ BOOST_AUTO_TEST_CASE(multiple_elementary_accessors) BOOST_CHECK(callContractFunction("super_secret_data()") == bytes()); } +BOOST_AUTO_TEST_CASE(complex_accessors) +{ + char const* sourceCode = "contract test {\n" + " mapping(uint256 => string4) to_string_map;\n" + " mapping(uint256 => bool) to_bool_map;\n" + " mapping(uint256 => uint256) to_uint_map;\n" + " function test() {\n" + " to_string_map[42] = \"24\";\n" + " to_bool_map[42] = false;\n" + " to_uint_map[42] = 12;\n" + " }\n" + "}\n"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("to_string_map(uint256)", 42) == encodeArgs("24")); + BOOST_CHECK(callContractFunction("to_bool_map(uint256)", 42) == encodeArgs(false)); + BOOST_CHECK(callContractFunction("to_uint_map(uint256)", 42) == encodeArgs(12)); +} + BOOST_AUTO_TEST_CASE(balance) { char const* sourceCode = "contract test {\n" From 6441a1d93d51b2f0ae896d16daf447389cc9f164 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 2 Feb 2015 15:03:44 +0100 Subject: [PATCH 04/15] Simple mapping accessors working --- libsolidity/ExpressionCompiler.cpp | 53 ++++++++---------------------- libsolidity/ExpressionCompiler.h | 4 +-- 2 files changed, 16 insertions(+), 41 deletions(-) diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index edd04256f..722ac9895 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -823,10 +823,10 @@ unsigned ExpressionCompiler::appendArgumentCopyToMemory(TypePointers const& _typ return length; } -unsigned ExpressionCompiler::appendTypeCopyToMemory(Type const& _expectedType, TypePointer const& _type, - Location const& _location, unsigned _memoryOffset) +unsigned ExpressionCompiler::appendTypeConversionAndMoveToMemory(Type const& _expectedType, Type const& _type, + Location const& _location, unsigned _memoryOffset) { - appendTypeConversion(*_type, _expectedType, true); + appendTypeConversion(_type, _expectedType, true); unsigned const c_numBytes = CompilerUtils::getPaddedSize(_expectedType.getCalldataEncodedSize()); if (c_numBytes == 0 || c_numBytes > 32) BOOST_THROW_EXCEPTION(CompilerError() @@ -842,65 +842,40 @@ unsigned ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedT unsigned _memoryOffset) { _expression.accept(*this); - return appendTypeCopyToMemory(_expectedType, _expression.getType(), _expression.getLocation(), _memoryOffset); + return appendTypeConversionAndMoveToMemory(_expectedType, *_expression.getType(), _expression.getLocation(), _memoryOffset); } void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl) { - auto mappingType = dynamic_cast(_varDecl.getType().get()); + TypePointer resultType = _varDecl.getType(); + auto mappingType = dynamic_cast(resultType.get()); unsigned sizeOnStack; + if (mappingType != nullptr) { - // this copies from Compiler::visit(FunctionDefinition..) for argument reading - unsigned parameterSize = mappingType->getKeyType()->getSizeOnStack(); - m_context.adjustStackOffset(parameterSize); - m_context.addVariable(_varDecl, parameterSize); // this copies from ExpressionCompiler::visit(IndexAccess .. ) for mapping access TypePointer const& keyType = mappingType->getKeyType(); - unsigned length = appendTypeCopyToMemory(*keyType, mappingType->getValueType(), Location()); + unsigned length = appendTypeConversionAndMoveToMemory(*keyType, *keyType, Location()); solAssert(length == 32, "Mapping key has to take 32 bytes in memory (for now)."); // @todo move this once we actually use memory + m_context << m_context.getStorageLocationOfVariable(_varDecl); length += CompilerUtils(m_context).storeInMemory(length); m_context << u256(length) << u256(0) << eth::Instruction::SHA3; - m_currentLValue = LValue(m_context, LValue::STORAGE, *mappingType->getValueType()); m_currentLValue.retrieveValue(mappingType->getValueType(), Location(), true); + m_currentLValue.reset(); - unsigned const c_argumentsSize = keyType->getSizeOnStack(); - unsigned const c_returnValuesSize = mappingType->getValueType()->getSizeOnStack(); - unsigned const c_localVariablesSize = 0; - - vector stackLayout; - stackLayout.push_back(c_returnValuesSize); // target of return address - stackLayout += vector(c_argumentsSize, -1); // discard all arguments - for (unsigned i = 0; i < c_returnValuesSize; ++i) - stackLayout.push_back(i); - stackLayout += vector(c_localVariablesSize, -1); - - while (stackLayout.back() != int(stackLayout.size() - 1)) - if (stackLayout.back() < 0) - { - m_context << eth::Instruction::POP; - stackLayout.pop_back(); - } - else - { - m_context << eth::swapInstruction(stackLayout.size() - stackLayout.back() - 1); - swap(stackLayout[stackLayout.back()], stackLayout.back()); - } - //@todo assert that everything is in place now - - m_context << eth::Instruction::JUMP; + resultType = mappingType->getValueType(); } else { m_currentLValue.fromStateVariable(_varDecl, _varDecl.getType()); solAssert(m_currentLValue.isInStorage(), ""); m_currentLValue.retrieveValue(_varDecl.getType(), Location(), true); - sizeOnStack = _varDecl.getType()->getSizeOnStack(); - solAssert(sizeOnStack <= 15, "Stack too deep."); - m_context << eth::dupInstruction(sizeOnStack + 1) << eth::Instruction::JUMP; } + sizeOnStack = _varDecl.getType()->getSizeOnStack(); + solAssert(sizeOnStack <= 15, "Stack too deep."); + m_context << eth::dupInstruction(sizeOnStack + 1) << eth::Instruction::JUMP; } diff --git a/libsolidity/ExpressionCompiler.h b/libsolidity/ExpressionCompiler.h index d93ab28ed..7de577e6c 100644 --- a/libsolidity/ExpressionCompiler.h +++ b/libsolidity/ExpressionCompiler.h @@ -99,8 +99,8 @@ private: unsigned _memoryOffset = 0); /// Appends code that copies a type to memory. /// @returns the number of bytes copied to memory - unsigned appendTypeCopyToMemory(Type const& _expectedType, TypePointer const& _type, - Location const& _location, unsigned _memoryOffset = 0); + unsigned appendTypeConversionAndMoveToMemory(Type const& _expectedType, Type const& _type, + Location const& _location, unsigned _memoryOffset = 0); /// Appends code that evaluates a single expression and copies it to memory (with optional offset). /// @returns the number of bytes copied to memory unsigned appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression, From b7070ec12f4d112edfea9f5df79cd6b33656fc16 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 2 Feb 2015 15:30:33 +0100 Subject: [PATCH 05/15] bug fixes https://www.pivotaltracker.com/n/projects/1265056/stories/87395458 --- mix/qml/StateDialog.qml | 1 + mix/qml/StateList.qml | 1 + mix/qml/StateListModel.qml | 5 +++++ 3 files changed, 7 insertions(+) diff --git a/mix/qml/StateDialog.qml b/mix/qml/StateDialog.qml index c00e3226f..37e36621e 100644 --- a/mix/qml/StateDialog.qml +++ b/mix/qml/StateDialog.qml @@ -167,6 +167,7 @@ Window { onClicked: transactionsModel.editTransaction(index) } ToolButton { + visible: !transactionsModel.get(index).executeConstructor text: qsTr("Delete"); Layout.fillHeight: true onClicked: transactionsModel.deleteTransaction(index) diff --git a/mix/qml/StateList.qml b/mix/qml/StateList.qml index ad14cf30e..c20d1379d 100644 --- a/mix/qml/StateList.qml +++ b/mix/qml/StateList.qml @@ -48,6 +48,7 @@ Rectangle { onClicked: list.model.editState(index); } ToolButton { + visible: !list.model.isDefaultState(index) text: qsTr("Delete"); Layout.fillHeight: true onClicked: list.model.deleteState(index); diff --git a/mix/qml/StateListModel.qml b/mix/qml/StateListModel.qml index c158112e1..5a4722e84 100644 --- a/mix/qml/StateListModel.qml +++ b/mix/qml/StateListModel.qml @@ -176,6 +176,11 @@ Item { runState(defaultStateIndex); } + function isDefaultState(index) + { + return index === defaultStateIndex; + } + function runState(index) { var item = stateList[index]; clientModel.setupState(item); From 76da204e17b54959aebba5feb0d02d516e319168 Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 2 Feb 2015 17:24:09 +0100 Subject: [PATCH 06/15] Visibility specifiers. --- libsolidity/AST.cpp | 2 +- libsolidity/AST.h | 35 +++++++----- libsolidity/Parser.cpp | 49 +++++++++++----- libsolidity/Parser.h | 4 +- libsolidity/Token.h | 2 + libsolidity/grammar.txt | 7 ++- test/SolidityEndToEndTest.cpp | 14 ++--- test/SolidityNameAndTypeResolution.cpp | 77 ++++++++++++++++++++++++-- test/SolidityParser.cpp | 31 +++++++++-- 9 files changed, 169 insertions(+), 52 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 10464726e..b07959b39 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -133,7 +133,7 @@ void ContractDefinition::checkIllegalOverrides() const FunctionDefinition const*& override = functions[name]; if (!override) override = function.get(); - else if (override->isPublic() != function->isPublic() || + else if (override->getVisibility() != function->getVisibility() || override->isDeclaredConst() != function->isDeclaredConst() || FunctionType(*override) != FunctionType(*function)) BOOST_THROW_EXCEPTION(override->createTypeError("Override changes extended function signature.")); diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 950553cd4..8e8641c78 100755 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -133,12 +133,17 @@ class Declaration: public ASTNode { public: enum class LValueType { NONE, LOCAL, STORAGE }; + enum class Visibility { DEFAULT, PUBLIC, PROTECTED, PRIVATE }; - Declaration(Location const& _location, ASTPointer const& _name): - ASTNode(_location), m_name(_name), m_scope(nullptr) {} + Declaration(Location const& _location, ASTPointer const& _name, + Visibility _visibility = Visibility::DEFAULT): + ASTNode(_location), m_name(_name), m_visibility(_visibility), m_scope(nullptr) {} /// @returns the declared name. ASTString const& getName() const { return *m_name; } + Visibility getVisibility() const { return m_visibility == Visibility::DEFAULT ? getDefaultVisibility() : m_visibility; } + bool isPublic() const { return getVisibility() == Visibility::PUBLIC; } + /// @returns the scope this declaration resides in. Can be nullptr if it is the global scope. /// Available only after name and type resolution step. Declaration const* getScope() const { return m_scope; } @@ -151,8 +156,12 @@ public: /// @returns the lvalue type of expressions referencing this declaration virtual LValueType getLValueType() const { return LValueType::NONE; } +protected: + virtual Visibility getDefaultVisibility() const { return Visibility::PUBLIC; } + private: ASTPointer m_name; + Visibility m_visibility; Declaration const* m_scope; }; @@ -330,16 +339,15 @@ class FunctionDefinition: public Declaration, public VariableScope, public Docum { public: FunctionDefinition(Location const& _location, ASTPointer const& _name, - bool _isPublic, - bool _isConstructor, + Declaration::Visibility _visibility, bool _isConstructor, ASTPointer const& _documentation, ASTPointer const& _parameters, bool _isDeclaredConst, std::vector> const& _modifiers, ASTPointer const& _returnParameters, ASTPointer const& _body): - Declaration(_location, _name), Documented(_documentation), - m_isPublic(_isPublic), m_isConstructor(_isConstructor), + Declaration(_location, _name, _visibility), Documented(_documentation), + m_isConstructor(_isConstructor), m_parameters(_parameters), m_isDeclaredConst(_isDeclaredConst), m_functionModifiers(_modifiers), @@ -350,7 +358,6 @@ public: virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - bool isPublic() const { return m_isPublic; } bool isConstructor() const { return m_isConstructor; } bool isDeclaredConst() const { return m_isDeclaredConst; } std::vector> const& getModifiers() const { return m_functionModifiers; } @@ -371,7 +378,6 @@ public: std::string getCanonicalSignature() const; private: - bool m_isPublic; bool m_isConstructor; ASTPointer m_parameters; bool m_isDeclaredConst; @@ -388,10 +394,10 @@ class VariableDeclaration: public Declaration { public: VariableDeclaration(Location const& _location, ASTPointer const& _type, - ASTPointer const& _name, bool _isPublic, bool _isStateVar = false, - bool _isIndexed = false): - Declaration(_location, _name), m_typeName(_type), - m_isPublic(_isPublic), m_isStateVariable(_isStateVar), m_isIndexed(_isIndexed) {} + ASTPointer const& _name, Visibility _visibility, + bool _isStateVar = false, bool _isIndexed = false): + Declaration(_location, _name, _visibility), m_typeName(_type), + m_isStateVariable(_isStateVar), m_isIndexed(_isIndexed) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; @@ -404,13 +410,14 @@ public: virtual LValueType getLValueType() const override; bool isLocalVariable() const { return !!dynamic_cast(getScope()); } - bool isPublic() const { return m_isPublic; } bool isStateVariable() const { return m_isStateVariable; } bool isIndexed() const { return m_isIndexed; } +protected: + Visibility getDefaultVisibility() const override { return Visibility::PROTECTED; } + private: ASTPointer m_typeName; ///< can be empty ("var") - bool m_isPublic; ///< Whether there is an accessor for it or not bool m_isStateVariable; ///< Whether or not this is a contract state variable bool m_isIndexed; ///< Whether this is an indexed variable (used by events). diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index d2e888a8d..cc75fc0a2 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -131,27 +131,19 @@ ASTPointer Parser::parseContractDefinition() } while (m_scanner->getCurrentToken() == Token::COMMA); expectToken(Token::LBRACE); - bool visibilityIsPublic = true; while (true) { Token::Value currentToken = m_scanner->getCurrentToken(); if (currentToken == Token::RBRACE) break; - else if (currentToken == Token::PUBLIC || currentToken == Token::PRIVATE) - { - visibilityIsPublic = (m_scanner->getCurrentToken() == Token::PUBLIC); - m_scanner->next(); - expectToken(Token::COLON); - } else if (currentToken == Token::FUNCTION) - functions.push_back(parseFunctionDefinition(visibilityIsPublic, name.get())); + functions.push_back(parseFunctionDefinition(name.get())); else if (currentToken == Token::STRUCT) structs.push_back(parseStructDefinition()); else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING || Token::isElementaryTypeName(currentToken)) { VarDeclParserOptions options; - options.isPublic = visibilityIsPublic; options.isStateVariable = true; stateVariables.push_back(parseVariableDeclaration(options)); expectToken(Token::SEMICOLON); @@ -186,7 +178,22 @@ ASTPointer Parser::parseInheritanceSpecifier() return nodeFactory.createNode(name, arguments); } -ASTPointer Parser::parseFunctionDefinition(bool _isPublic, ASTString const* _contractName) +Declaration::Visibility Parser::parseVisibilitySpecifier(Token::Value _token) +{ + Declaration::Visibility visibility; + if (_token == Token::PUBLIC) + visibility = Declaration::Visibility::PUBLIC; + else if (_token == Token::PROTECTED) + visibility = Declaration::Visibility::PROTECTED; + else if (_token == Token::PRIVATE) + visibility = Declaration::Visibility::PRIVATE; + else + solAssert(false, "Invalid visibility specifier."); + m_scanner->next(); + return visibility; +} + +ASTPointer Parser::parseFunctionDefinition(ASTString const* _contractName) { ASTNodeFactory nodeFactory(*this); ASTPointer docstring; @@ -201,16 +208,24 @@ ASTPointer Parser::parseFunctionDefinition(bool _isPublic, A name = expectIdentifierToken(); ASTPointer parameters(parseParameterList()); bool isDeclaredConst = false; + Declaration::Visibility visibility(Declaration::Visibility::DEFAULT); vector> modifiers; while (true) { - if (m_scanner->getCurrentToken() == Token::CONST) + Token::Value token = m_scanner->getCurrentToken(); + if (token == Token::CONST) { isDeclaredConst = true; m_scanner->next(); } - else if (m_scanner->getCurrentToken() == Token::IDENTIFIER) + else if (token == Token::IDENTIFIER) modifiers.push_back(parseModifierInvocation()); + else if (Token::isVisibilitySpecifier(token)) + { + if (visibility != Declaration::Visibility::DEFAULT) + BOOST_THROW_EXCEPTION(createParserError("Multiple visibility specifiers.")); + visibility = parseVisibilitySpecifier(token); + } else break; } @@ -226,7 +241,7 @@ ASTPointer Parser::parseFunctionDefinition(bool _isPublic, A ASTPointer block = parseBlock(); nodeFactory.setEndPositionFromNode(block); bool const c_isConstructor = (_contractName && *name == *_contractName); - return nodeFactory.createNode(name, _isPublic, c_isConstructor, docstring, + return nodeFactory.createNode(name, visibility, c_isConstructor, docstring, parameters, isDeclaredConst, modifiers, returnParameters, block); } @@ -253,14 +268,18 @@ ASTPointer Parser::parseVariableDeclaration(VarDeclParserOp ASTNodeFactory nodeFactory(*this); ASTPointer type = parseTypeName(_options.allowVar); bool isIndexed = false; - if (_options.allowIndexed && m_scanner->getCurrentToken() == Token::INDEXED) + Token::Value token = m_scanner->getCurrentToken(); + if (_options.allowIndexed && token == Token::INDEXED) { isIndexed = true; m_scanner->next(); } + Declaration::Visibility visibility(Declaration::Visibility::DEFAULT); + if (_options.isStateVariable && Token::isVisibilitySpecifier(token)) + visibility = parseVisibilitySpecifier(token); nodeFactory.markEndPosition(); return nodeFactory.createNode(type, expectIdentifierToken(), - _options.isPublic, _options.isStateVariable, + visibility, _options.isStateVariable, isIndexed); } diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index 413a2711e..388fd7a96 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -48,7 +48,6 @@ private: struct VarDeclParserOptions { VarDeclParserOptions() {} bool allowVar = false; - bool isPublic = false; bool isStateVariable = false; bool allowIndexed = false; }; @@ -58,7 +57,8 @@ private: ASTPointer parseImportDirective(); ASTPointer parseContractDefinition(); ASTPointer parseInheritanceSpecifier(); - ASTPointer parseFunctionDefinition(bool _isPublic, ASTString const* _contractName); + Declaration::Visibility parseVisibilitySpecifier(Token::Value _token); + ASTPointer parseFunctionDefinition(ASTString const* _contractName); ASTPointer parseStructDefinition(); ASTPointer parseVariableDeclaration(VarDeclParserOptions const& _options = VarDeclParserOptions()); ASTPointer parseModifierDefinition(); diff --git a/libsolidity/Token.h b/libsolidity/Token.h index ed42f90cc..76e504499 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -165,6 +165,7 @@ namespace solidity K(NEW, "new", 0) \ K(PUBLIC, "public", 0) \ K(PRIVATE, "private", 0) \ + K(PROTECTED, "protected", 0) \ K(RETURN, "return", 0) \ K(RETURNS, "returns", 0) \ K(STRUCT, "struct", 0) \ @@ -376,6 +377,7 @@ public: static bool isUnaryOp(Value op) { return (NOT <= op && op <= DELETE) || op == ADD || op == SUB; } static bool isCountOp(Value op) { return op == INC || op == DEC; } static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); } + static bool isVisibilitySpecifier(Value op) { return op == PUBLIC || op == PRIVATE || op == PROTECTED; } // Returns a string corresponding to the JS token string // (.e., "<" for the token LT) or NULL if the token doesn't diff --git a/libsolidity/grammar.txt b/libsolidity/grammar.txt index b97dac5db..1785b516c 100644 --- a/libsolidity/grammar.txt +++ b/libsolidity/grammar.txt @@ -1,14 +1,15 @@ ContractDefinition = 'contract' Identifier ( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )? '{' ContractPart* '}' -ContractPart = VariableDeclaration ';' | StructDefinition | ModifierDefinition | - FunctionDefinition | 'public:' | 'private:' +ContractPart = StateVariableDeclaration | StructDefinition | ModifierDefinition | FunctionDefinition InheritanceSpecifier = Identifier ( '(' Expression ( ',' Expression )* ')' )? StructDefinition = 'struct' Identifier '{' ( VariableDeclaration (';' VariableDeclaration)* )? '} +StateVariableDeclaration = TypeName ( 'public' | 'protected' | 'private' )? Identifier ';' ModifierDefinition = 'modifier' Identifier ParameterList? Block -FunctionDefinition = 'function' Identifier ParameterList ( Identifier | 'constant' )* +FunctionDefinition = 'function' Identifier ParameterList + ( Identifier | 'constant' | 'public' | 'protected' | 'private' )* ( 'returns' ParameterList )? Block ParameterList = '(' ( VariableDeclaration (',' VariableDeclaration)* )? ')' // semantic restriction: mappings and structs (recursively) containing mappings diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index 7edc250c8..7166b95fb 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -885,7 +885,7 @@ BOOST_AUTO_TEST_CASE(constructor) BOOST_AUTO_TEST_CASE(simple_accessor) { char const* sourceCode = "contract test {\n" - " uint256 data;\n" + " uint256 public data;\n" " function test() {\n" " data = 8;\n" " }\n" @@ -897,10 +897,10 @@ BOOST_AUTO_TEST_CASE(simple_accessor) BOOST_AUTO_TEST_CASE(multiple_elementary_accessors) { char const* sourceCode = "contract test {\n" - " uint256 data;\n" - " string6 name;\n" - " hash a_hash;\n" - " address an_address;\n" + " uint256 public data;\n" + " string6 public name;\n" + " hash public a_hash;\n" + " address public an_address;\n" " function test() {\n" " data = 8;\n" " name = \"Celina\";\n" @@ -908,7 +908,6 @@ BOOST_AUTO_TEST_CASE(multiple_elementary_accessors) " an_address = address(0x1337);\n" " super_secret_data = 42;\n" " }\n" - " private:" " uint256 super_secret_data;" "}\n"; compileAndRun(sourceCode); @@ -1490,8 +1489,7 @@ BOOST_AUTO_TEST_CASE(functions_called_by_constructor) setName("abc"); } function getName() returns (string3 ret) { return name; } - private: - function setName(string3 _name) { name = _name; } + function setName(string3 _name) private { name = _name; } })"; compileAndRun(sourceCode); BOOST_REQUIRE(callContractFunction("getName()") == encodeArgs("abc")); diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index b9a7140f7..4e01d02d0 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -467,6 +467,24 @@ BOOST_AUTO_TEST_CASE(illegal_override_indirect) BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); } +BOOST_AUTO_TEST_CASE(illegal_override_visibility) +{ + char const* text = R"( + contract B { function f() protected {} } + contract C is B { function f() public {} } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); +} + +BOOST_AUTO_TEST_CASE(illegal_override_constness) +{ + char const* text = R"( + contract B { function f() constant {} } + contract C is B { function f() {} } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); +} + BOOST_AUTO_TEST_CASE(complex_inheritance) { char const* text = R"( @@ -636,7 +654,7 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors) " function fun() {\n" " uint64(2);\n" " }\n" - "uint256 foo;\n" + "uint256 public foo;\n" "}\n"; ASTPointer source; @@ -668,16 +686,19 @@ BOOST_AUTO_TEST_CASE(private_state_variable) " function fun() {\n" " uint64(2);\n" " }\n" - "private:\n" - "uint256 foo;\n" + "uint256 private foo;\n" + "uint256 protected bar;\n" "}\n"; ASTPointer source; ContractDefinition const* contract; BOOST_CHECK_NO_THROW(source = parseTextAndResolveNamesWithChecks(text)); BOOST_CHECK((contract = retrieveContract(source, 0)) != nullptr); - FunctionTypePointer function = retrieveFunctionBySignature(contract, "foo()"); + FunctionTypePointer function; + function = retrieveFunctionBySignature(contract, "foo()"); BOOST_CHECK_MESSAGE(function == nullptr, "Accessor function of a private variable should not exist"); + function = retrieveFunctionBySignature(contract, "bar()"); + BOOST_CHECK_MESSAGE(function == nullptr, "Accessor function of a protected variable should not exist"); } BOOST_AUTO_TEST_CASE(fallback_function) @@ -780,6 +801,54 @@ BOOST_AUTO_TEST_CASE(multiple_events_argument_clash) BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); } +BOOST_AUTO_TEST_CASE(access_to_default_function_visibility) +{ + char const* text = R"( + contract c { + function f() {} + } + contract d { + function g() { c(0).f(); } + })"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); +} + +BOOST_AUTO_TEST_CASE(access_to_protected_function) +{ + char const* text = R"( + contract c { + function f() protected {} + } + contract d { + function g() { c(0).f(); } + })"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); +} + +BOOST_AUTO_TEST_CASE(access_to_default_state_variable_visibility) +{ + char const* text = R"( + contract c { + uint a; + } + contract d { + function g() { c(0).a(); } + })"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); +} + +BOOST_AUTO_TEST_CASE(access_to_protected_state_variable) +{ + char const* text = R"( + contract c { + uint public a; + } + contract d { + function g() { c(0).a(); } + })"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/SolidityParser.cpp b/test/SolidityParser.cpp index 4adee9c66..4ccdcd57a 100644 --- a/test/SolidityParser.cpp +++ b/test/SolidityParser.cpp @@ -129,9 +129,7 @@ BOOST_AUTO_TEST_CASE(function_natspec_documentation) ASTPointer contract; ASTPointer function; char const* text = "contract test {\n" - " private:\n" - " uint256 stateVar;\n" - " public:\n" + " uint256 stateVar;\n" " /// This is a test function\n" " function functionName(hash hashin) returns (hash hashout) {}\n" "}\n"; @@ -162,9 +160,7 @@ BOOST_AUTO_TEST_CASE(multiple_functions_natspec_documentation) ASTPointer contract; ASTPointer function; char const* text = "contract test {\n" - " private:\n" " uint256 stateVar;\n" - " public:\n" " /// This is test function 1\n" " function functionName1(hash hashin) returns (hash hashout) {}\n" " /// This is test function 2\n" @@ -621,6 +617,31 @@ BOOST_AUTO_TEST_CASE(event_arguments_indexed) BOOST_CHECK_NO_THROW(parseText(text)); } +BOOST_AUTO_TEST_CASE(visibility_specifiers) +{ + char const* text = R"( + contract c { + uint private a; + uint protected b; + uint public c; + uint d; + function f() {} + function f_priv() private {} + function f_public() public {} + function f_protected() protected {} + })"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + +BOOST_AUTO_TEST_CASE(multiple_visibility_specifiers) +{ + char const* text = R"( + contract c { + uint private protected a; + })"; + BOOST_CHECK_THROW(parseText(text), ParserError); +} + BOOST_AUTO_TEST_SUITE_END() } From 6c5120978ececc5dc015721a0554c3a360e68827 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 2 Feb 2015 17:52:50 +0100 Subject: [PATCH 07/15] Accessors for multiple mappings implemented --- libsolidity/ExpressionCompiler.cpp | 45 +++++++++++++------------- libsolidity/Types.cpp | 22 ++++++------- test/SolidityEndToEndTest.cpp | 3 ++ test/SolidityNameAndTypeResolution.cpp | 15 +++++++-- 4 files changed, 49 insertions(+), 36 deletions(-) diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 722ac9895..45e0e80b0 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -847,36 +848,34 @@ unsigned ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedT void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl) { - TypePointer resultType = _varDecl.getType(); - auto mappingType = dynamic_cast(resultType.get()); + FunctionType thisType(_varDecl); + solAssert(thisType.getReturnParameterTypes().size() == 1, ""); + TypePointer const& resultType = thisType.getReturnParameterTypes().front(); unsigned sizeOnStack; - if (mappingType != nullptr) - { - // this copies from ExpressionCompiler::visit(IndexAccess .. ) for mapping access - TypePointer const& keyType = mappingType->getKeyType(); - unsigned length = appendTypeConversionAndMoveToMemory(*keyType, *keyType, Location()); - solAssert(length == 32, "Mapping key has to take 32 bytes in memory (for now)."); - // @todo move this once we actually use memory - m_context << m_context.getStorageLocationOfVariable(_varDecl); - length += CompilerUtils(m_context).storeInMemory(length); - m_context << u256(length) << u256(0) << eth::Instruction::SHA3; - m_currentLValue = LValue(m_context, LValue::STORAGE, *mappingType->getValueType()); - m_currentLValue.retrieveValue(mappingType->getValueType(), Location(), true); - m_currentLValue.reset(); + unsigned length = 0; + TypePointers const& params = thisType.getParameterTypes(); + // move arguments to memory + for (TypePointer const& param: boost::adaptors::reverse(params)) + length += appendTypeConversionAndMoveToMemory(*param, *param, Location(), length); - resultType = mappingType->getValueType(); - } - else + // retrieve the position of the mapping + m_context << m_context.getStorageLocationOfVariable(_varDecl); + + for (TypePointer const& param: params) { - m_currentLValue.fromStateVariable(_varDecl, _varDecl.getType()); - solAssert(m_currentLValue.isInStorage(), ""); - m_currentLValue.retrieveValue(_varDecl.getType(), Location(), true); + // move offset to memory + CompilerUtils(m_context).storeInMemory(length); + unsigned argLen = CompilerUtils::getPaddedSize(param->getCalldataEncodedSize()); + length -= argLen; + m_context << u256(argLen + 32) << u256(length) << eth::Instruction::SHA3; } - sizeOnStack = _varDecl.getType()->getSizeOnStack(); + + m_currentLValue = LValue(m_context, LValue::STORAGE, *resultType); + m_currentLValue.retrieveValue(resultType, Location(), true); + sizeOnStack = resultType->getSizeOnStack(); solAssert(sizeOnStack <= 15, "Stack too deep."); m_context << eth::dupInstruction(sizeOnStack + 1) << eth::Instruction::JUMP; - } ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType, diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 7fa4561e3..cfb852c2f 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -626,20 +626,20 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): TypePointers retParams; vector retParamNames; TypePointer varDeclType = _varDecl.getType(); - auto mappingType = dynamic_cast(varDeclType.get()); - if (mappingType!= nullptr) - { - params.push_back(mappingType->getKeyType()); - paramNames.push_back(mappingType->getKeyType()->toString()); + auto mappingType = dynamic_cast(varDeclType.get()); + auto returnType = varDeclType; - retParams.push_back(mappingType->getValueType()); - retParamNames.push_back(mappingType->getValueType()->toString()); - } - else // elelemntary type + while (mappingType!= nullptr) { - retParams.push_back(varDeclType); - retParamNames.push_back(_varDecl.getName()); + params.push_back(mappingType->getKeyType()); + paramNames.push_back(""); + returnType = mappingType->getValueType(); + mappingType = dynamic_cast(mappingType->getValueType().get()); } + + retParams.push_back(returnType); + retParamNames.push_back(""); + swap(params, m_parameterTypes); swap(paramNames, m_parameterNames); swap(retParams, m_returnParameterTypes); diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index 63a8ebcd3..9a04e02d3 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -925,16 +925,19 @@ BOOST_AUTO_TEST_CASE(complex_accessors) " mapping(uint256 => string4) to_string_map;\n" " mapping(uint256 => bool) to_bool_map;\n" " mapping(uint256 => uint256) to_uint_map;\n" + " mapping(uint256 => mapping(uint256 => uint256)) to_multiple_map;\n" " function test() {\n" " to_string_map[42] = \"24\";\n" " to_bool_map[42] = false;\n" " to_uint_map[42] = 12;\n" + " to_multiple_map[42][23] = 31;\n" " }\n" "}\n"; compileAndRun(sourceCode); BOOST_CHECK(callContractFunction("to_string_map(uint256)", 42) == encodeArgs("24")); BOOST_CHECK(callContractFunction("to_bool_map(uint256)", 42) == encodeArgs(false)); BOOST_CHECK(callContractFunction("to_uint_map(uint256)", 42) == encodeArgs(12)); + BOOST_CHECK(callContractFunction("to_multiple_map(uint256,uint256)", 42, 23) == encodeArgs(31)); } BOOST_AUTO_TEST_CASE(balance) diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index df0e07e1d..2fe3288ad 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -638,6 +638,7 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors) " }\n" "uint256 foo;\n" "mapping(uint=>string4) map;\n" + "mapping(uint=>mapping(uint=>string4)) multiple_map;\n" "}\n"; ASTPointer source; @@ -649,10 +650,20 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors) auto returnParams = function->getReturnParameterTypeNames(); BOOST_CHECK_EQUAL(returnParams.at(0), "uint256"); BOOST_CHECK(function->isConstant()); + function = retrieveFunctionBySignature(contract, "map(uint256)"); BOOST_REQUIRE(function && function->hasDeclaration()); - auto Params = function->getParameterTypeNames(); - BOOST_CHECK_EQUAL(returnParams.at(0), "uint256"); + auto params = function->getParameterTypeNames(); + BOOST_CHECK_EQUAL(params.at(0), "uint256"); + returnParams = function->getReturnParameterTypeNames(); + BOOST_CHECK_EQUAL(returnParams.at(0), "string4"); + BOOST_CHECK(function->isConstant()); + + function = retrieveFunctionBySignature(contract, "multiple_map(uint256,uint256)"); + BOOST_REQUIRE(function && function->hasDeclaration()); + params = function->getParameterTypeNames(); + BOOST_CHECK_EQUAL(params.at(0), "uint256"); + BOOST_CHECK_EQUAL(params.at(1), "uint256"); returnParams = function->getReturnParameterTypeNames(); BOOST_CHECK_EQUAL(returnParams.at(0), "string4"); BOOST_CHECK(function->isConstant()); From 51e2c1424d75317c791755e8ecf696f0b59c3729 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 3 Feb 2015 11:38:25 +0100 Subject: [PATCH 08/15] Change behavior when running/deleting the default state. --- mix/qml/StateDialog.qml | 14 ++------ mix/qml/StateList.qml | 2 +- mix/qml/StateListModel.qml | 65 ++++++++++++++++++++------------------ mix/qml/main.qml | 17 ++++++++-- 4 files changed, 53 insertions(+), 45 deletions(-) diff --git a/mix/qml/StateDialog.qml b/mix/qml/StateDialog.qml index 37e36621e..9e5c60d3d 100644 --- a/mix/qml/StateDialog.qml +++ b/mix/qml/StateDialog.qml @@ -17,12 +17,11 @@ Window { property alias stateTitle: titleField.text property alias stateBalance: balanceField.value - property alias isDefault: defaultCheckBox.checked property int stateIndex property var stateTransactions: [] signal accepted - function open(index, item, setDefault) { + function open(index, item) { stateIndex = index; stateTitle = item.title; balanceField.value = item.balance; @@ -33,7 +32,6 @@ Window { transactionsModel.append(item.transactions[t]); stateTransactions.push(item.transactions[t]); } - isDefault = setDefault; visible = true; titleField.focus = true; } @@ -79,14 +77,6 @@ Window { Layout.fillWidth: true } - Label { - text: qsTr("Default") - } - CheckBox { - id: defaultCheckBox - Layout.fillWidth: true - } - Label { text: qsTr("Transactions") } @@ -167,7 +157,7 @@ Window { onClicked: transactionsModel.editTransaction(index) } ToolButton { - visible: !transactionsModel.get(index).executeConstructor + visible: index >= 0 ? !transactionsModel.get(index).executeConstructor : false text: qsTr("Delete"); Layout.fillHeight: true onClicked: transactionsModel.deleteTransaction(index) diff --git a/mix/qml/StateList.qml b/mix/qml/StateList.qml index c20d1379d..3674e0dbc 100644 --- a/mix/qml/StateList.qml +++ b/mix/qml/StateList.qml @@ -48,7 +48,7 @@ Rectangle { onClicked: list.model.editState(index); } ToolButton { - visible: !list.model.isDefaultState(index) + visible: list.model.count - 1 != index text: qsTr("Delete"); Layout.fillHeight: true onClicked: list.model.deleteState(index); diff --git a/mix/qml/StateListModel.qml b/mix/qml/StateListModel.qml index 5a4722e84..0b5645d37 100644 --- a/mix/qml/StateListModel.qml +++ b/mix/qml/StateListModel.qml @@ -8,7 +8,7 @@ import "js/QEtherHelper.js" as QEtherHelper Item { - property int defaultStateIndex: -1 + property int defaultStateIndex: 0 property alias model: stateListModel property var stateList: [] @@ -70,20 +70,7 @@ Item { stateListModel.clear(); stateList = []; } - onProjectLoaded: { - if (!projectData.states) - projectData.states = []; - if (projectData.defaultStateIndex !== undefined) - defaultStateIndex = projectData.defaultStateIndex; - else - defaultStateIndex = -1; - var items = projectData.states; - for(var i = 0; i < items.length; i++) { - var item = fromPlainStateItem(items[i]); - stateListModel.append(item); - stateList.push(item); - } - } + onProjectLoaded: stateListModel.loadStatesFromProject(projectData); onProjectSaving: { projectData.states = [] for(var i = 0; i < stateListModel.count; i++) { @@ -103,18 +90,16 @@ Item { id: stateDialog onAccepted: { var item = stateDialog.getItem(); - if (stateDialog.stateIndex < stateListModel.count) { - if (stateDialog.isDefault) - defaultStateIndex = stateIndex; + if (stateDialog.stateIndex < stateListModel.count) + { stateList[stateDialog.stateIndex] = item; stateListModel.set(stateDialog.stateIndex, item); - } else { - if (stateDialog.isDefault) - defaultStateIndex = 0; + } + else + { stateList.push(item); stateListModel.append(item); } - stateListModel.save(); } } @@ -126,6 +111,9 @@ Item { ListModel { id: stateListModel + signal defaultStateChanged; + signal stateListModelReady; + function defaultTransactionItem() { return { value: QEtherHelper.createEther("100", QEther.Wei), @@ -164,11 +152,11 @@ Item { function addState() { var item = createDefaultState(); - stateDialog.open(stateListModel.count, item, defaultStateIndex === -1); + stateDialog.open(stateListModel.count, item); } function editState(index) { - stateDialog.open(index, stateList[index], defaultStateIndex === index); + stateDialog.open(index, stateList[index]); } function debugDefaultState() { @@ -176,11 +164,6 @@ Item { runState(defaultStateIndex); } - function isDefaultState(index) - { - return index === defaultStateIndex; - } - function runState(index) { var item = stateList[index]; clientModel.setupState(item); @@ -190,12 +173,34 @@ Item { stateListModel.remove(index); stateList.splice(index, 1); if (index === defaultStateIndex) - defaultStateIndex = -1; + defaultStateChanged(); save(); } function save() { projectModel.saveProject(); } + + function defaultStateName() + { + return stateList[defaultStateIndex].title; + } + + function loadStatesFromProject(projectData) + { + if (!projectData.states) + projectData.states = []; + if (projectData.defaultStateIndex !== undefined) + defaultStateIndex = projectData.defaultStateIndex; + else + defaultStateIndex = 0; + var items = projectData.states; + for(var i = 0; i < items.length; i++) { + var item = fromPlainStateItem(items[i]); + stateListModel.append(item); + stateList.push(item); + } + stateListModelReady(); + } } } diff --git a/mix/qml/main.qml b/mix/qml/main.qml index a0a4ba423..2e939fa12 100644 --- a/mix/qml/main.qml +++ b/mix/qml/main.qml @@ -34,7 +34,7 @@ ApplicationWindow { MenuItem { action: exitAppAction } } Menu { - title: qsTr("Debug") + title: qsTr("Deploy") MenuItem { action: debugRunAction } MenuItem { action: debugResetStateAction } MenuItem { action: mineAction } @@ -87,9 +87,22 @@ ApplicationWindow { onTriggered: clientModel.mine(); enabled: codeModel.hasContract && !clientModel.running } + + Connections { + target: projectModel.stateListModel + onDefaultStateChanged: + { + debugRunAction.text = "&Deploy" + " \"" + projectModel.stateListModel.defaultStateName() + "\""; + } + onStateListModelReady: + { + debugRunAction.text = "&Deploy" + " \"" + projectModel.stateListModel.defaultStateName() + "\""; + } + } + Action { id: debugRunAction - text: "&Run" + text: "&Deploy" shortcut: "F5" onTriggered: mainContent.startQuickDebugging() enabled: codeModel.hasContract && !clientModel.running From edea2145f9e62adbb94ea717cd49a807ba0c4f53 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 3 Feb 2015 15:08:09 +0100 Subject: [PATCH 09/15] misc --- mix/qml/main.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mix/qml/main.qml b/mix/qml/main.qml index 2ca2bc133..a90c90feb 100644 --- a/mix/qml/main.qml +++ b/mix/qml/main.qml @@ -94,11 +94,11 @@ ApplicationWindow { target: projectModel.stateListModel onDefaultStateChanged: { - debugRunAction.text = "&Deploy" + " \"" + projectModel.stateListModel.defaultStateName() + "\""; + debugRunAction.text = qsTr("Deploy") + " \"" + projectModel.stateListModel.defaultStateName() + "\""; } onStateListModelReady: { - debugRunAction.text = "&Deploy" + " \"" + projectModel.stateListModel.defaultStateName() + "\""; + debugRunAction.text = qsTr("Deploy") + " \"" + projectModel.stateListModel.defaultStateName() + "\""; } } From 1e60fb5d095d59e2bf9ab00182089227b0dbcce2 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 3 Feb 2015 16:05:52 +0100 Subject: [PATCH 10/15] add checkbox in statedialog. --- mix/qml/StateDialog.qml | 15 ++++++++++++++- mix/qml/StateListModel.qml | 20 +++++++++++++------- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/mix/qml/StateDialog.qml b/mix/qml/StateDialog.qml index 9e5c60d3d..a2f9b7fa8 100644 --- a/mix/qml/StateDialog.qml +++ b/mix/qml/StateDialog.qml @@ -17,11 +17,12 @@ Window { property alias stateTitle: titleField.text property alias stateBalance: balanceField.value + property alias isDefault: defaultCheckBox.checked property int stateIndex property var stateTransactions: [] signal accepted - function open(index, item) { + function open(index, item, setDefault) { stateIndex = index; stateTitle = item.title; balanceField.value = item.balance; @@ -33,7 +34,10 @@ Window { stateTransactions.push(item.transactions[t]); } visible = true; + isDefault = setDefault; titleField.focus = true; + defaultCheckBox.enabled = !isDefault; + } function close() { @@ -77,6 +81,15 @@ Window { Layout.fillWidth: true } + Label { + text: qsTr("Default") + } + CheckBox { + id: defaultCheckBox + Layout.fillWidth: true + } + + Label { text: qsTr("Transactions") } diff --git a/mix/qml/StateListModel.qml b/mix/qml/StateListModel.qml index f87ecbea0..a1b24fe0f 100644 --- a/mix/qml/StateListModel.qml +++ b/mix/qml/StateListModel.qml @@ -125,16 +125,19 @@ Item { id: stateDialog onAccepted: { var item = stateDialog.getItem(); - if (stateDialog.stateIndex < stateListModel.count) - { + if (stateDialog.stateIndex < stateListModel.count) { + if (stateDialog.isDefault) + defaultStateIndex = stateIndex; stateList[stateDialog.stateIndex] = item; stateListModel.set(stateDialog.stateIndex, item); - } - else - { + } else { + if (stateDialog.isDefault) + defaultStateIndex = 0; stateList.push(item); stateListModel.append(item); } + if (stateDialog.isDefault) + stateListModel.defaultStateChanged(); stateListModel.save(); } } @@ -187,11 +190,11 @@ Item { function addState() { var item = createDefaultState(); - stateDialog.open(stateListModel.count, item); + stateDialog.open(stateListModel.count, item, false); } function editState(index) { - stateDialog.open(index, stateList[index]); + stateDialog.open(index, stateList[index], defaultStateIndex === index); } function debugDefaultState() { @@ -208,7 +211,10 @@ Item { stateListModel.remove(index); stateList.splice(index, 1); if (index === defaultStateIndex) + { + defaultStateIndex = 0; defaultStateChanged(); + } save(); } From b0dd99ab13622560d75cf748b2f0e9c3721b822c Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 3 Feb 2015 16:56:56 +0100 Subject: [PATCH 11/15] misc --- mix/qml/StateDialog.qml | 2 -- mix/qml/main.qml | 10 +++++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/mix/qml/StateDialog.qml b/mix/qml/StateDialog.qml index a2f9b7fa8..fa48c640e 100644 --- a/mix/qml/StateDialog.qml +++ b/mix/qml/StateDialog.qml @@ -37,7 +37,6 @@ Window { isDefault = setDefault; titleField.focus = true; defaultCheckBox.enabled = !isDefault; - } function close() { @@ -89,7 +88,6 @@ Window { Layout.fillWidth: true } - Label { text: qsTr("Transactions") } diff --git a/mix/qml/main.qml b/mix/qml/main.qml index a90c90feb..720d5070d 100644 --- a/mix/qml/main.qml +++ b/mix/qml/main.qml @@ -92,14 +92,14 @@ ApplicationWindow { Connections { target: projectModel.stateListModel - onDefaultStateChanged: - { - debugRunAction.text = qsTr("Deploy") + " \"" + projectModel.stateListModel.defaultStateName() + "\""; - } - onStateListModelReady: + + function updateRunLabel() { debugRunAction.text = qsTr("Deploy") + " \"" + projectModel.stateListModel.defaultStateName() + "\""; } + + onDefaultStateChanged: updateRunLabel() + onStateListModelReady: updateRunLabel() } Action { From 101915767f61f3b5935e1ac115f92f4c7df240a6 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 3 Feb 2015 08:48:11 -0800 Subject: [PATCH 12/15] Minor typo. --- libsolidity/Types.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index cfb852c2f..54e701c10 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -629,7 +629,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): auto mappingType = dynamic_cast(varDeclType.get()); auto returnType = varDeclType; - while (mappingType!= nullptr) + while (mappingType != nullptr) { params.push_back(mappingType->getKeyType()); paramNames.push_back(""); From f6955b370e6c8c55436b31a94fd79f87890cce89 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 3 Feb 2015 12:25:08 -0800 Subject: [PATCH 13/15] Fixes for named-args. --- libsolidity/AST.cpp | 2 +- libsolidity/AST.h | 6 +++--- libsolidity/ExpressionCompiler.cpp | 21 ++++++--------------- libsolidity/Parser.cpp | 24 ++++++++---------------- libsolidity/Parser.h | 2 +- 5 files changed, 19 insertions(+), 36 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 33cb4ac32..6028c07cf 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -513,7 +513,7 @@ void FunctionCall::checkTypeRequirements() for (size_t i = 0; i < m_names.size(); i++) { bool found = false; for (size_t j = 0; j < parameterNames.size(); j++) { - if (parameterNames[j] == m_names[i]) { + if (parameterNames[j] == *m_names[i]) { // check type convertible if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[j])) BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in function call.")); diff --git a/libsolidity/AST.h b/libsolidity/AST.h index bd1535138..525907bf4 100755 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -963,7 +963,7 @@ class FunctionCall: public Expression { public: FunctionCall(Location const& _location, ASTPointer const& _expression, - std::vector> const& _arguments, std::vector const& _names): + std::vector> const& _arguments, std::vector> const& _names): Expression(_location), m_expression(_expression), m_arguments(_arguments), m_names(_names) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; @@ -971,7 +971,7 @@ public: Expression const& getExpression() const { return *m_expression; } std::vector> getArguments() const { return {m_arguments.begin(), m_arguments.end()}; } - std::vector const& getNames() const { return m_names; } + std::vector> const& getNames() const { return m_names; } /// Returns true if this is not an actual function call, but an explicit type conversion /// or constructor call. @@ -980,7 +980,7 @@ public: private: ASTPointer m_expression; std::vector> m_arguments; - std::vector m_names; + std::vector> m_names; }; /** diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 13d8ccf12..875e00bc2 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -204,34 +204,25 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) { FunctionType const& function = dynamic_cast(*_functionCall.getExpression().getType()); TypePointers const& parameterTypes = function.getParameterTypes(); - vector const& parameterNames = function.getParameterNames(); vector> const& callArguments = _functionCall.getArguments(); - vector const& callArgumentNames = _functionCall.getNames(); + vector> const& callArgumentNames = _functionCall.getNames(); solAssert(callArguments.size() == parameterTypes.size(), ""); vector> arguments; if (callArgumentNames.empty()) - { // normal arguments - arguments = {callArguments.begin(), callArguments.end()}; - } + arguments = callArguments; else - { // named arguments - for (size_t i = 0; i < parameterNames.size(); i++) { + for (auto const& parameterName: function.getParameterNames()) + { bool found = false; - for (size_t j = 0; j < callArgumentNames.size(); j++) { - if (parameterNames[i] == callArgumentNames[j]) { + for (size_t j = 0; j < callArgumentNames.size() && !found; j++) + if ((found = (parameterName == *callArgumentNames[j]))) // we found the actual parameter position arguments.push_back(callArguments[j]); - - found = true; - break; - } - } solAssert(found, ""); } - } switch (function.getLocation()) { diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 1cf0bce5f..74d6c982a 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -592,7 +592,6 @@ ASTPointer Parser::parseBinaryExpression(int _minPrecedence) ASTPointer expression = parseUnaryExpression(); int precedence = Token::precedence(m_scanner->getCurrentToken()); for (; precedence >= _minPrecedence; --precedence) - { while (Token::precedence(m_scanner->getCurrentToken()) == precedence) { Token::Value op = m_scanner->getCurrentToken(); @@ -601,7 +600,6 @@ ASTPointer Parser::parseBinaryExpression(int _minPrecedence) nodeFactory.setEndPositionFromNode(right); expression = nodeFactory.createNode(expression, op, right); } - } return expression; } @@ -668,8 +666,8 @@ ASTPointer Parser::parseLeftHandSideExpression() { m_scanner->next(); vector> arguments; - vector names; - parseFunctionCallArguments(arguments, names); + vector> names; + std::tie(arguments, names) = parseFunctionCallArguments(); nodeFactory.markEndPosition(); expectToken(Token::RPAREN); expression = nodeFactory.createNode(expression, arguments, names); @@ -740,8 +738,9 @@ vector> Parser::parseFunctionCallListArguments() return arguments; } -void Parser::parseFunctionCallArguments(vector>& _arguments, vector& _names) +pair>, vector>> Parser::parseFunctionCallArguments() { + pair>, vector>> ret; Token::Value token = m_scanner->getCurrentToken(); if (token == Token::LBRACE) { @@ -749,28 +748,21 @@ void Parser::parseFunctionCallArguments(vector>& _argumen expectToken(Token::LBRACE); while (m_scanner->getCurrentToken() != Token::RBRACE) { - string identifier = *expectIdentifierToken(); expectToken(Token::COLON); - ASTPointer expression = parseExpression(); - _arguments.push_back(expression); - _names.push_back(identifier); + ret.first.push_back(parseExpression()); + ret.second.push_back(expectIdentifierToken()); if (m_scanner->getCurrentToken() == Token::COMMA) - { expectToken(Token::COMMA); - } else - { break; - } } expectToken(Token::RBRACE); } else - { - _arguments = parseFunctionCallListArguments(); - } + ret.first = parseFunctionCallListArguments(); + return ret; } diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index e8d521c94..19e0af1aa 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -82,7 +82,7 @@ private: ASTPointer parseLeftHandSideExpression(); ASTPointer parsePrimaryExpression(); std::vector> parseFunctionCallListArguments(); - void parseFunctionCallArguments(std::vector> & _arguments, std::vector & _names); + std::pair>, std::vector>> parseFunctionCallArguments(); ///@} ///@{ From eb8b02d22f06799825a030b806b70ae00eb00426 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 3 Feb 2015 12:45:16 -0800 Subject: [PATCH 14/15] Fixes. --- libsolidity/Parser.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 74d6c982a..0ad7bd7ca 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -748,10 +748,9 @@ pair>, vector>> Parser::pars expectToken(Token::LBRACE); while (m_scanner->getCurrentToken() != Token::RBRACE) { + ret.second.push_back(expectIdentifierToken()); expectToken(Token::COLON); - ret.first.push_back(parseExpression()); - ret.second.push_back(expectIdentifierToken()); if (m_scanner->getCurrentToken() == Token::COMMA) expectToken(Token::COMMA); From 11203141a45e4ff5083ffa3c823121e0b4791187 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 3 Feb 2015 14:28:49 -0800 Subject: [PATCH 15/15] Fix #940 --- libethereum/State.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libethereum/State.cpp b/libethereum/State.cpp index ecb5b2606..3f81cd4c9 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -800,7 +800,7 @@ void State::completeMine() ret.appendRaw(m_currentTxs); ret.appendRaw(m_currentUncles); ret.swapOut(m_currentBytes); - m_currentBlock.hash = sha3(m_currentBytes); + m_currentBlock.hash = sha3(RLP(m_currentBytes)[0].data()); cnote << "Mined " << m_currentBlock.hash.abridged() << "(parent: " << m_currentBlock.parentHash.abridged() << ")"; // Quickly reset the transactions.