diff options
24 files changed, 354 insertions, 49 deletions
diff --git a/Changelog.md b/Changelog.md index 34c3b0e9..9618dfa7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,8 +5,10 @@ Features: * Commandline interface: Error when missing or inaccessible file detected. Suppress it with the ``--ignore-missing`` flag. * General: Support accessing dynamic return data in post-byzantium EVMs. * Interfaces: Allow overriding external functions in interfaces with public in an implementing contract. + * Optimizer: Remove useless ``SWAP1`` instruction preceding a commutative instruction (such as ``ADD``, ``MUL``, etc). * Optimizer: Optimize across ``mload`` if ``msize()`` is not used. * Syntax Checker: Issue warning for empty structs (or error as experimental 0.5.0 feature). + * General: Introduce new constructor syntax using the ``constructor`` keyword as experimental 0.5.0 feature. Bugfixes: * Code Generator: Allow ``block.blockhash`` without being called. diff --git a/docs/assembly.rst b/docs/assembly.rst index cf9bf840..705cd1b8 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -647,6 +647,11 @@ Solidity manages memory in a very simple way: There is a "free memory pointer" at position ``0x40`` in memory. If you want to allocate memory, just use the memory from that point on and update the pointer accordingly. +The first 64 bytes of memory can be used as "scratch space" for short-term +allocation. The 32 bytes after the free memory pointer (i.e. starting at ``0x60``) +is meant to be zero permanently and is used as the initial value for +empty dynamic memory arrays. + Elements in memory arrays in Solidity always occupy multiples of 32 bytes (yes, this is even true for ``byte[]``, but not for ``bytes`` and ``string``). Multi-dimensional memory arrays are pointers to memory arrays. The length of a dynamic array is stored at the diff --git a/docs/contracts.rst b/docs/contracts.rst index 8cc4f6b2..f8a44fb3 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -40,7 +40,7 @@ This means that cyclic creation dependencies are impossible. :: - pragma solidity ^0.4.16; + pragma solidity >0.4.21; contract OwnedToken { // TokenCreator is a contract type that is defined below. @@ -52,7 +52,7 @@ This means that cyclic creation dependencies are impossible. // This is the constructor which registers the // creator and the assigned name. - function OwnedToken(bytes32 _name) public { + constructor(bytes32 _name) public { // State variables are accessed via their name // and not via e.g. this.owner. This also applies // to functions and especially in the constructors, @@ -803,7 +803,7 @@ as topics. The event call above can be performed in the same way as } where the long hexadecimal number is equal to -``keccak256("Deposit(address,hash256,uint256)")``, the signature of the event. +``keccak256("Deposit(address,bytes32,uint256)")``, the signature of the event. Additional Resources for Understanding Events ============================================== @@ -976,8 +976,31 @@ virtual method lookup. Constructors ============ -A constructor is an optional function with the same name as the contract which is executed upon contract creation. -Constructor functions can be either ``public`` or ``internal``. +A constructor is an optional function declared with the ``constructor`` keyword which is executed upon contract creation. +Constructor functions can be either ``public`` or ``internal``. If there is no constructor, the contract will assume the +default constructor: ``contructor() public {}``. + + +:: + + pragma solidity >0.4.21; + + contract A { + uint public a; + + constructor(uint _a) internal { + a = _a; + } + } + + contract B is A(1) { + constructor() public {} + } + +A constructor set as ``internal`` causes the contract to be marked as :ref:`abstract <abstract-contract>`. + +.. note :: + Prior to version 0.4.22, constructors were defined as functions with the same name as the contract. This syntax is now deprecated. :: @@ -995,7 +1018,6 @@ Constructor functions can be either ``public`` or ``internal``. function B() public {} } -A constructor set as ``internal`` causes the contract to be marked as :ref:`abstract <abstract-contract>`. .. index:: ! base;constructor @@ -1009,11 +1031,11 @@ the base constructors. This can be done in two ways:: contract Base { uint x; - function Base(uint _x) public { x = _x; } + constructor(uint _x) public { x = _x; } } contract Derived is Base(7) { - function Derived(uint _y) Base(_y * _y) public { + constructor(uint _y) Base(_y * _y) public { } } diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 01154854..20400aa2 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -64,12 +64,15 @@ The position of ``data[4][9].b`` is at ``keccak256(uint256(9) . keccak256(uint25 Layout in Memory **************** -Solidity reserves three 256-bit slots: +Solidity reserves four 32 byte slots: -- 0 - 64: scratch space for hashing methods -- 64 - 96: currently allocated memory size (aka. free memory pointer) +- ``0x00`` - ``0x3f``: scratch space for hashing methods +- ``0x40`` - ``0x5f``: currently allocated memory size (aka. free memory pointer) +- ``0x60`` - ``0x7f``: zero slot -Scratch space can be used between statements (ie. within inline assembly). +Scratch space can be used between statements (ie. within inline assembly). The zero slot +is used as initial value for dynamic memory arrays and should never be written to +(the free memory pointer points to ``0x80`` initially). Solidity always places new objects at the free memory pointer and memory is never freed (this might change in the future). diff --git a/libevmasm/PeepholeOptimiser.cpp b/libevmasm/PeepholeOptimiser.cpp index 168d1109..30646545 100644 --- a/libevmasm/PeepholeOptimiser.cpp +++ b/libevmasm/PeepholeOptimiser.cpp @@ -154,6 +154,25 @@ struct DoublePush: SimplePeepholeOptimizerMethod<DoublePush, 2> } }; +struct CommutativeSwap: SimplePeepholeOptimizerMethod<CommutativeSwap, 2> +{ + static bool applySimple(AssemblyItem const& _swap, AssemblyItem const& _op, std::back_insert_iterator<AssemblyItems> _out) + { + // Remove SWAP1 if following instruction is commutative + if ( + _swap.type() == Operation && + _swap.instruction() == Instruction::SWAP1 && + SemanticInformation::isCommutativeOperation(_op) + ) + { + *_out = _op; + return true; + } + else + return false; + } +}; + struct JumpToNext: SimplePeepholeOptimizerMethod<JumpToNext, 3> { static size_t applySimple( @@ -260,7 +279,7 @@ bool PeepholeOptimiser::optimise() { OptimiserState state {m_items, 0, std::back_inserter(m_optimisedItems)}; while (state.i < m_items.size()) - applyMethods(state, PushPop(), OpPop(), DoublePush(), DoubleSwap(), JumpToNext(), UnreachableCode(), TagConjunctions(), Identity()); + applyMethods(state, PushPop(), OpPop(), DoublePush(), DoubleSwap(), CommutativeSwap(), JumpToNext(), UnreachableCode(), TagConjunctions(), Identity()); if (m_optimisedItems.size() < m_items.size() || ( m_optimisedItems.size() == m_items.size() && ( eth::bytesRequired(m_optimisedItems, 3) < eth::bytesRequired(m_items, 3) || diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index b595c4d1..343b4ba8 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -216,7 +216,22 @@ bool SyntaxChecker::visit(FunctionDefinition const& _function) if (v050 && _function.noVisibilitySpecified()) m_errorReporter.syntaxError(_function.location(), "No visibility specified."); - + + if (_function.isOldStyleConstructor()) + { + if (v050) + m_errorReporter.syntaxError( + _function.location(), + "Functions are not allowed to have the same name as the contract. " + "If you intend this to be a constructor, use \"constructor(...) { ... }\" to define it." + ); + else + m_errorReporter.warning( + _function.location(), + "Defining constructors as functions with the same name as the contract is deprecated. " + "Use \"constructor(...) { ... }\" instead." + ); + } return true; } diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 9c67d354..56bb412c 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -607,7 +607,8 @@ public: StateMutability stateMutability() const { return m_stateMutability; } bool isConstructor() const { return m_isConstructor; } - bool isFallback() const { return name().empty(); } + bool isOldStyleConstructor() const { return m_isConstructor && !name().empty(); } + bool isFallback() const { return !m_isConstructor && name().empty(); } bool isPayable() const { return m_stateMutability == StateMutability::Payable; } std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; } std::vector<ASTPointer<VariableDeclaration>> const& returnParameters() const { return m_returnParameters->parameters(); } diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index deaef017..79aef7b0 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -21,6 +21,7 @@ */ #include <libsolidity/codegen/CompilerUtils.h> + #include <libsolidity/ast/AST.h> #include <libsolidity/codegen/ArrayUtils.h> #include <libsolidity/codegen/LValue.h> @@ -39,11 +40,17 @@ namespace solidity const unsigned CompilerUtils::dataStartOffset = 4; const size_t CompilerUtils::freeMemoryPointer = 64; +const size_t CompilerUtils::zeroPointer = CompilerUtils::freeMemoryPointer + 32; +const size_t CompilerUtils::generalPurposeMemoryStart = CompilerUtils::zeroPointer + 32; const unsigned CompilerUtils::identityContractAddress = 4; +static_assert(CompilerUtils::freeMemoryPointer >= 64, "Free memory pointer must not overlap with scratch area."); +static_assert(CompilerUtils::zeroPointer >= CompilerUtils::freeMemoryPointer + 32, "Zero pointer must not overlap with free memory pointer."); +static_assert(CompilerUtils::generalPurposeMemoryStart >= CompilerUtils::zeroPointer + 32, "General purpose memory must not overlap with zero area."); + void CompilerUtils::initialiseFreeMemoryPointer() { - m_context << u256(freeMemoryPointer + 32); + m_context << u256(generalPurposeMemoryStart); storeFreeMemoryPointer(); } @@ -1051,6 +1058,13 @@ void CompilerUtils::pushZeroValue(Type const& _type) return; } solAssert(referenceType->location() == DataLocation::Memory, ""); + if (auto arrayType = dynamic_cast<ArrayType const*>(&_type)) + if (arrayType->isDynamicallySized()) + { + // Push a memory location that is (hopefully) always zero. + pushZeroPointer(); + return; + } TypePointer type = _type.shared_from_this(); m_context.callLowLevelFunction( @@ -1071,13 +1085,8 @@ void CompilerUtils::pushZeroValue(Type const& _type) } else if (auto arrayType = dynamic_cast<ArrayType const*>(type.get())) { - if (arrayType->isDynamicallySized()) - { - // zero length - _context << u256(0); - utils.storeInMemoryDynamic(IntegerType(256)); - } - else if (arrayType->length() > 0) + solAssert(!arrayType->isDynamicallySized(), ""); + if (arrayType->length() > 0) { _context << arrayType->length() << Instruction::SWAP1; // stack: items_to_do memory_pos @@ -1094,6 +1103,11 @@ void CompilerUtils::pushZeroValue(Type const& _type) ); } +void CompilerUtils::pushZeroPointer() +{ + m_context << u256(zeroPointer); +} + void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable) { unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.baseStackOffsetOfVariable(_variable)); diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 389673ef..a32c5c6e 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -210,6 +210,9 @@ public: /// Creates a zero-value for the given type and puts it onto the stack. This might allocate /// memory for memory references. void pushZeroValue(Type const& _type); + /// Pushes a pointer to the stack that points to a (potentially shared) location in memory + /// that always contains a zero. It is not allowed to write there. + void pushZeroPointer(); /// Moves the value that is at the top of the stack to a stack variable. void moveToStackVariable(VariableDeclaration const& _variable); @@ -255,6 +258,10 @@ public: /// Position of the free-memory-pointer in memory; static const size_t freeMemoryPointer; + /// Position of the memory slot that is always zero. + static const size_t zeroPointer; + /// Starting offset for memory available to the user (aka the contract). + static const size_t generalPurposeMemoryStart; private: /// Address of the precompiled identity contract. diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 8c97f55f..3dbd4c8f 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -238,7 +238,10 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition(Token::Value _exp Token::Value currentTokenValue = m_scanner->currentToken(); if (currentTokenValue == Token::RBrace) break; - else if (currentTokenValue == Token::Function) + else if ( + currentTokenValue == Token::Function || + (currentTokenValue == Token::Identifier && m_scanner->currentLiteral() == "constructor") + ) // This can be a function or a state variable of function type (especially // complicated to distinguish fallback function from function type state variable) subNodes.push_back(parseFunctionDefinitionOrFunctionTypeStateVariable(name.get())); @@ -329,15 +332,31 @@ StateMutability Parser::parseStateMutability(Token::Value _token) return stateMutability; } -Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers) +Parser::FunctionHeaderParserResult Parser::parseFunctionHeader( + bool _forceEmptyName, + bool _allowModifiers, + ASTString const* _contractName +) { RecursionGuard recursionGuard(*this); FunctionHeaderParserResult result; - expectToken(Token::Function); - if (_forceEmptyName || m_scanner->currentToken() == Token::LParen) - result.name = make_shared<ASTString>(); // anonymous function + + result.isConstructor = false; + + if (m_scanner->currentToken() == Token::Identifier && m_scanner->currentLiteral() == "constructor") + result.isConstructor = true; + else if (m_scanner->currentToken() != Token::Function) + solAssert(false, "Function or constructor expected."); + m_scanner->next(); + + if (result.isConstructor || _forceEmptyName || m_scanner->currentToken() == Token::LParen) + result.name = make_shared<ASTString>(); else result.name = expectIdentifierToken(); + + if (!result.name->empty() && _contractName && *result.name == *_contractName) + result.isConstructor = true; + VarDeclParserOptions options; options.allowLocationSpecifier = true; result.parameters = parseParameterList(options); @@ -407,7 +426,7 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(A if (m_scanner->currentCommentLiteral() != "") docstring = make_shared<ASTString>(m_scanner->currentCommentLiteral()); - FunctionHeaderParserResult header = parseFunctionHeader(false, true); + FunctionHeaderParserResult header = parseFunctionHeader(false, true, _contractName); if ( !header.modifiers.empty() || @@ -426,12 +445,11 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(A } else m_scanner->next(); // just consume the ';' - bool const c_isConstructor = (_contractName && *header.name == *_contractName); return nodeFactory.createNode<FunctionDefinition>( header.name, header.visibility, header.stateMutability, - c_isConstructor, + header.isConstructor, docstring, header.parameters, header.modifiers, diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 3f780af9..eb120a61 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -56,6 +56,7 @@ private: /// This struct is shared for parsing a function header and a function type. struct FunctionHeaderParserResult { + bool isConstructor; ASTPointer<ASTString> name; ASTPointer<ParameterList> parameters; ASTPointer<ParameterList> returnParameters; @@ -73,7 +74,11 @@ private: ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier(); Declaration::Visibility parseVisibilitySpecifier(Token::Value _token); StateMutability parseStateMutability(Token::Value _token); - FunctionHeaderParserResult parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers); + FunctionHeaderParserResult parseFunctionHeader( + bool _forceEmptyName, + bool _allowModifiers, + ASTString const* _contractName = nullptr + ); ASTPointer<ASTNode> parseFunctionDefinitionOrFunctionTypeStateVariable(ASTString const* _contractName); ASTPointer<FunctionDefinition> parseFunctionDefinition(ASTString const* _contractName); ASTPointer<StructDefinition> parseStructDefinition(); diff --git a/scripts/cpp-ethereum/build.sh b/scripts/cpp-ethereum/build.sh new file mode 100755 index 00000000..23ed1290 --- /dev/null +++ b/scripts/cpp-ethereum/build.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env sh + +# Script to build the eth binary from latest develop +# for ubuntu trusty and ubuntu artful. +# Requires docker. + +set -e + +REPO_ROOT="$(dirname "$0")"/../.. + +for rel in artful trusty +do + docker build -t eth_$rel -f "$REPO_ROOT"/scripts/cpp-ethereum/eth_$rel.docker . + tmp_container=$(docker create eth_$rel sh) + echo "Built eth ($rel) at $REPO_ROOT/build/eth_$rel" + docker cp ${tmp_container}:/build/eth/eth "$REPO_ROOT"/build/eth_$rel +done
\ No newline at end of file diff --git a/scripts/cpp-ethereum/eth_artful.docker b/scripts/cpp-ethereum/eth_artful.docker new file mode 100644 index 00000000..7ce9faae --- /dev/null +++ b/scripts/cpp-ethereum/eth_artful.docker @@ -0,0 +1,7 @@ +FROM ubuntu:artful + +RUN apt update +RUN apt -y install libleveldb-dev cmake g++ git +RUN git clone --recursive https://github.com/ethereum/cpp-ethereum --branch develop --single-branch --depth 1 +RUN mkdir /build && cd /build && cmake /cpp-ethereum -DCMAKE_BUILD_TYPE=RelWithDebInfo -DTOOLS=Off -DTESTS=Off +RUN cd /build && make eth diff --git a/scripts/cpp-ethereum/eth_trusty.docker b/scripts/cpp-ethereum/eth_trusty.docker new file mode 100644 index 00000000..0c4d7a02 --- /dev/null +++ b/scripts/cpp-ethereum/eth_trusty.docker @@ -0,0 +1,7 @@ +FROM ubuntu:trusty + +RUN apt update +RUN apt -y install libleveldb-dev cmake g++ git +RUN git clone --recursive https://github.com/ethereum/cpp-ethereum --branch develop --single-branch --depth 1 +RUN mkdir /build && cd /build && cmake /cpp-ethereum -DCMAKE_BUILD_TYPE=RelWithDebInfo -DTOOLS=Off -DTESTS=Off +RUN cd /build && make eth diff --git a/std/StandardToken.sol b/std/StandardToken.sol index 1b218d67..5afc9747 100644 --- a/std/StandardToken.sol +++ b/std/StandardToken.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity >0.4.21; import "./Token.sol"; @@ -8,7 +8,7 @@ contract StandardToken is Token { mapping (address => mapping (address => uint256)) m_allowance; - function StandardToken(address _initialOwner, uint256 _supply) public { + constructor(address _initialOwner, uint256 _supply) public { supply = _supply; balance[_initialOwner] = _supply; } diff --git a/std/owned.sol b/std/owned.sol index ee9860d3..8e1d5917 100644 --- a/std/owned.sol +++ b/std/owned.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity >0.4.21; contract owned { address owner; @@ -9,7 +9,7 @@ contract owned { } } - function owned() public { + constructor() public { owner = msg.sender; } } diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index 28f6b8c0..b622b4fb 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -858,6 +858,82 @@ BOOST_AUTO_TEST_CASE(peephole_pop_calldatasize) BOOST_CHECK(items.empty()); } +BOOST_AUTO_TEST_CASE(peephole_commutative_swap1) +{ + vector<Instruction> ops{ + Instruction::ADD, + Instruction::MUL, + Instruction::EQ, + Instruction::AND, + Instruction::OR, + Instruction::XOR + }; + for (Instruction const op: ops) + { + AssemblyItems items{ + u256(1), + u256(2), + Instruction::SWAP1, + op, + u256(4), + u256(5) + }; + AssemblyItems expectation{ + u256(1), + u256(2), + op, + u256(4), + u256(5) + }; + PeepholeOptimiser peepOpt(items); + BOOST_REQUIRE(peepOpt.optimise()); + BOOST_CHECK_EQUAL_COLLECTIONS( + items.begin(), items.end(), + expectation.begin(), expectation.end() + ); + } +} + +BOOST_AUTO_TEST_CASE(peephole_noncommutative_swap1) +{ + // NOTE: not comprehensive + vector<Instruction> ops{ + Instruction::SUB, + Instruction::DIV, + Instruction::SDIV, + Instruction::MOD, + Instruction::SMOD, + Instruction::EXP, + Instruction::LT, + Instruction::GT + }; + for (Instruction const op: ops) + { + AssemblyItems items{ + u256(1), + u256(2), + Instruction::SWAP1, + op, + u256(4), + u256(5) + }; + AssemblyItems expectation{ + u256(1), + u256(2), + Instruction::SWAP1, + op, + u256(4), + u256(5) + }; + PeepholeOptimiser peepOpt(items); + BOOST_REQUIRE(!peepOpt.optimise()); + BOOST_CHECK_EQUAL_COLLECTIONS( + items.begin(), items.end(), + expectation.begin(), expectation.end() + ); + } +} + BOOST_AUTO_TEST_CASE(jumpdest_removal) { AssemblyItems items{ diff --git a/test/libsolidity/JSONCompiler.cpp b/test/libsolidity/JSONCompiler.cpp index aed0a370..cdcc22a6 100644 --- a/test/libsolidity/JSONCompiler.cpp +++ b/test/libsolidity/JSONCompiler.cpp @@ -111,12 +111,12 @@ BOOST_AUTO_TEST_CASE(basic_compilation) BOOST_CHECK(contract["bytecode"].isString()); BOOST_CHECK_EQUAL( dev::test::bytecodeSansMetadata(contract["bytecode"].asString()), - "60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00" + "60806040523415600e57600080fd5b603580601b6000396000f3006080604052600080fd00" ); BOOST_CHECK(contract["runtimeBytecode"].isString()); BOOST_CHECK_EQUAL( dev::test::bytecodeSansMetadata(contract["runtimeBytecode"].asString()), - "6060604052600080fd00" + "6080604052600080fd00" ); BOOST_CHECK(contract["functionHashes"].isObject()); BOOST_CHECK(contract["gasEstimates"].isObject()); @@ -153,12 +153,12 @@ BOOST_AUTO_TEST_CASE(single_compilation) BOOST_CHECK(contract["bytecode"].isString()); BOOST_CHECK_EQUAL( dev::test::bytecodeSansMetadata(contract["bytecode"].asString()), - "60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00" + "60806040523415600e57600080fd5b603580601b6000396000f3006080604052600080fd00" ); BOOST_CHECK(contract["runtimeBytecode"].isString()); BOOST_CHECK_EQUAL( dev::test::bytecodeSansMetadata(contract["runtimeBytecode"].asString()), - "6060604052600080fd00" + "6080604052600080fd00" ); BOOST_CHECK(contract["functionHashes"].isObject()); BOOST_CHECK(contract["gasEstimates"].isObject()); diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 38d3ce4d..b58ebee4 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -7687,7 +7687,6 @@ BOOST_AUTO_TEST_CASE(create_memory_array_allocation_size) ABI_CHECK(callContractFunction("f()"), encodeArgs(0x40, 0x40, 0x20 + 256)); } - BOOST_AUTO_TEST_CASE(memory_arrays_of_various_sizes) { // Computes binomial coefficients the chinese way @@ -7710,6 +7709,41 @@ BOOST_AUTO_TEST_CASE(memory_arrays_of_various_sizes) ABI_CHECK(callContractFunction("f(uint256,uint256)", encodeArgs(u256(9), u256(5))), encodeArgs(u256(70))); } +BOOST_AUTO_TEST_CASE(create_multiple_dynamic_arrays) +{ + char const* sourceCode = R"( + contract C { + function f() returns (uint) { + uint[][] memory x = new uint[][](42); + assert(x[0].length == 0); + x[0] = new uint[](1); + x[0][0] = 1; + assert(x[4].length == 0); + x[4] = new uint[](1); + x[4][0] = 2; + assert(x[10].length == 0); + x[10] = new uint[](1); + x[10][0] = 44; + uint[][] memory y = new uint[][](24); + assert(y[0].length == 0); + y[0] = new uint[](1); + y[0][0] = 1; + assert(y[4].length == 0); + y[4] = new uint[](1); + y[4][0] = 2; + assert(y[10].length == 0); + y[10] = new uint[](1); + y[10][0] = 88; + if ((x[0][0] == y[0][0]) && (x[4][0] == y[4][0]) && (x[10][0] == 44) && (y[10][0] == 88)) + return 7; + return 0; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7))); +} + BOOST_AUTO_TEST_CASE(memory_overwrite) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index dcdc1519..b6596327 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -962,6 +962,35 @@ BOOST_AUTO_TEST_CASE(base_constructor_arguments_override) CHECK_SUCCESS(text); } +BOOST_AUTO_TEST_CASE(new_constructor_syntax) +{ + char const* text = R"( + contract A { constructor() public {} } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(old_constructor_syntax) +{ + char const* text = R"( + contract A { function A() public {} } + )"; + CHECK_WARNING( + text, + "Defining constructors as functions with the same name as the contract is deprecated." + ); + + text = R"( + pragma experimental "v0.5.0"; + contract A { function A() public {} } + )"; + CHECK_ERROR( + text, + SyntaxError, + "Functions are not allowed to have the same name as the contract." + ); +} + BOOST_AUTO_TEST_CASE(implicit_derived_to_base_conversion) { char const* text = R"( @@ -6916,7 +6945,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_ignores_constructor) { char const* text = R"( contract C { - function C() public {} + constructor() public {} } )"; CHECK_SUCCESS_NO_WARNINGS(text); @@ -7328,7 +7357,7 @@ BOOST_AUTO_TEST_CASE(using_this_in_constructor) { char const* text = R"( contract C { - function C() public { + constructor() public { this.f(); } function f() pure public { diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index cf4550c7..a0df76f3 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -93,8 +93,10 @@ public: { m_contractAddress = m_nonOptimizedContract; bytes nonOptimizedOutput = callContractFunction(_sig, _arguments...); + m_gasUsedNonOptimized = m_gasUsed; m_contractAddress = m_optimizedContract; bytes optimizedOutput = callContractFunction(_sig, _arguments...); + m_gasUsedOptimized = m_gasUsed; BOOST_CHECK_MESSAGE(!optimizedOutput.empty(), "No optimized output for " + _sig); BOOST_CHECK_MESSAGE(!nonOptimizedOutput.empty(), "No un-optimized output for " + _sig); BOOST_CHECK_MESSAGE(nonOptimizedOutput == optimizedOutput, "Computed values do not match." @@ -120,6 +122,8 @@ public: } protected: + u256 m_gasUsedOptimized; + u256 m_gasUsedNonOptimized; bytes m_nonOptimizedBytecode; bytes m_optimizedBytecode; Address m_optimizedContract; @@ -584,6 +588,26 @@ BOOST_AUTO_TEST_CASE(invalid_state_at_control_flow_join) compareVersions("test()"); } +BOOST_AUTO_TEST_CASE(init_empty_dynamic_arrays) +{ + // This is not so much an optimizer test, but rather a test + // that allocating empty arrays is implemented efficiently. + // In particular, initializing a dynamic memory array does + // not use any memory. + char const* sourceCode = R"( + contract Test { + function f() pure returns (uint r) { + uint[][] memory x = new uint[][](20000); + return x.length; + } + } + )"; + compileBothVersions(sourceCode); + compareVersions("f()"); + BOOST_CHECK_LE(m_gasUsedNonOptimized, 1900000); + BOOST_CHECK_LE(1600000, m_gasUsedNonOptimized); +} + BOOST_AUTO_TEST_CASE(optimise_multi_stores) { char const* sourceCode = R"( diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index dd6eb7c4..b285a2a0 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -261,14 +261,14 @@ BOOST_AUTO_TEST_CASE(basic_compilation) BOOST_CHECK(contract["evm"]["bytecode"]["object"].isString()); BOOST_CHECK_EQUAL( dev::test::bytecodeSansMetadata(contract["evm"]["bytecode"]["object"].asString()), - "60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00" + "60806040523415600e57600080fd5b603580601b6000396000f3006080604052600080fd00" ); BOOST_CHECK(contract["evm"]["assembly"].isString()); BOOST_CHECK(contract["evm"]["assembly"].asString().find( - " /* \"fileA\":0:14 contract A { } */\n mstore(0x40, 0x60)\n jumpi(tag_1, iszero(callvalue))\n" + " /* \"fileA\":0:14 contract A { } */\n mstore(0x40, 0x80)\n jumpi(tag_1, iszero(callvalue))\n" " 0x0\n dup1\n revert\ntag_1:\n dataSize(sub_0)\n dup1\n dataOffset(sub_0)\n 0x0\n codecopy\n 0x0\n" " return\nstop\n\nsub_0: assembly {\n /* \"fileA\":0:14 contract A { } */\n" - " mstore(0x40, 0x60)\n 0x0\n dup1\n revert\n\n" + " mstore(0x40, 0x80)\n 0x0\n dup1\n revert\n\n" " auxdata: 0xa165627a7a7230582") == 0); BOOST_CHECK(contract["evm"]["gasEstimates"].isObject()); BOOST_CHECK_EQUAL( diff --git a/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol b/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol index 76df0657..9607ed60 100644 --- a/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol +++ b/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol @@ -1,5 +1,5 @@ contract Base { - function Base(uint) public {} + constructor(uint) public {} } contract Derived is Base(2) { } contract Derived2 is Base(), Derived() { } diff --git a/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol b/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol index 82aba308..45a0770f 100644 --- a/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol +++ b/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol @@ -1,9 +1,9 @@ contract Base { - function Base(uint, uint) public {} + constructor(uint, uint) public {} } contract Derived is Base(2) { } contract Derived2 is Base { - function Derived2() Base(2) public { } + constructor() Base(2) public { } } // ---- // TypeError: Wrong argument count for constructor call: 1 arguments given but expected 2. |