diff options
-rw-r--r-- | libsolidity/codegen/CompilerUtils.cpp | 24 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerUtils.h | 5 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 47 | ||||
-rw-r--r-- | test/libsolidity/SolidityEndToEndTest.cpp | 44 |
4 files changed, 111 insertions, 9 deletions
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 2ebf5b64..357013e6 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -266,6 +266,19 @@ void CompilerUtils::encodeToMemory( popStackSlots(argSize + dynPointers + 1); } +void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type) +{ + auto repeat = m_context.newTag(); + m_context << repeat; + pushZeroValue(*_type.baseType()); + storeInMemoryDynamic(*_type.baseType()); + m_context << eth::Instruction::SWAP1 << u256(1) << eth::Instruction::SWAP1; + m_context << eth::Instruction::SUB << eth::Instruction::SWAP1; + m_context << eth::Instruction::DUP2; + m_context.appendConditionalJumpTo(repeat); + m_context << eth::Instruction::SWAP1 << eth::Instruction::POP; +} + void CompilerUtils::memoryCopy() { // Stack here: size target source @@ -646,15 +659,8 @@ void CompilerUtils::pushZeroValue(Type const& _type) { m_context << arrayType->length() << eth::Instruction::SWAP1; // stack: items_to_do memory_pos - auto repeat = m_context.newTag(); - m_context << repeat; - pushZeroValue(*arrayType->baseType()); - storeInMemoryDynamic(*arrayType->baseType()); - m_context << eth::Instruction::SWAP1 << u256(1) << eth::Instruction::SWAP1; - m_context << eth::Instruction::SUB << eth::Instruction::SWAP1; - m_context << eth::Instruction::DUP2; - m_context.appendConditionalJumpTo(repeat); - m_context << eth::Instruction::SWAP1 << eth::Instruction::POP; + zeroInitialiseMemoryArray(*arrayType); + // stack: updated_memory_pos } } else diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 6292e5c7..134afd78 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -103,6 +103,11 @@ public: bool _encodeAsLibraryTypes = false ); + /// Zero-initialises (the data part of) an already allocated memory array. + /// Stack pre: <length> <memptr> + /// Stack post: <updated_memptr> + void zeroInitialiseMemoryArray(ArrayType const& _type); + /// Uses a CALL to the identity contract to perform a memory-to-memory copy. /// Stack pre: <size> <target> <source> /// Stack post: diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 1a089d63..c94c988b 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -703,6 +703,53 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) StorageByteArrayElement(m_context).storeValue(*type, _functionCall.location(), true); break; } + case Location::ObjectCreation: + { + // Will allocate at the end of memory (MSIZE) and not write at all unless the base + // type is dynamically sized. + ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*_functionCall.annotation().type); + _functionCall.expression().accept(*this); + solAssert(arguments.size() == 1, ""); + + // Fetch requested length. + arguments[0]->accept(*this); + utils().convertType(*arguments[0]->annotation().type, IntegerType(256)); + + // Stack: requested_length + // Allocate at max(MSIZE, freeMemoryPointer) + utils().fetchFreeMemoryPointer(); + m_context << eth::Instruction::DUP1 << eth::Instruction::MSIZE; + m_context << eth::Instruction::LT; + auto initialise = m_context.appendConditionalJump(); + // Free memory pointer does not point to empty memory, use MSIZE. + m_context << eth::Instruction::POP; + m_context << eth::Instruction::MSIZE; + m_context << initialise; + + // Stack: requested_length memptr + m_context << eth::Instruction::SWAP1; + // Stack: memptr requested_length + // store length + m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::MSTORE; + // Stack: memptr requested_length + // update free memory pointer + m_context << eth::Instruction::DUP1 << arrayType.baseType()->memoryHeadSize(); + m_context << eth::Instruction::MUL << u256(32) << eth::Instruction::ADD; + m_context << eth::Instruction::DUP3 << eth::Instruction::ADD; + utils().storeFreeMemoryPointer(); + // Stack: memptr requested_length + + // We only have to initialise if the base type is a not a value type. + if (dynamic_cast<ReferenceType const*>(arrayType.baseType().get())) + { + m_context << eth::Instruction::DUP2 << u256(32) << eth::Instruction::ADD; + utils().zeroInitialiseMemoryArray(arrayType); + m_context << eth::Instruction::POP; + } + else + m_context << eth::Instruction::POP; + break; + } default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid function type.")); } diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 681ab107..0b356145 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -5816,6 +5816,50 @@ BOOST_AUTO_TEST_CASE(lone_struct_array_type) BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(3))); } +BOOST_AUTO_TEST_CASE(create_memory_array) +{ + char const* sourceCode = R"( + contract C { + struct S { uint[2] a; bytes b; } + function f() returns (byte, uint, uint, byte) { + var x = new bytes(200); + x[199] = 'A'; + var y = new uint[2][](300); + y[203][1] = 8; + var z = new S[](180); + z[170].a[1] = 4; + z[170].b = new bytes(102); + z[170].b[99] = 'B'; + return (x[199], y[203][1], z[170].a[1], z[170].b[99]); + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("f()") == encodeArgs(string("A"), u256(8), u256(4), string("B"))); +} + +BOOST_AUTO_TEST_CASE(memory_arrays_of_various_sizes) +{ + // Computes binomial coefficients the chinese way + char const* sourceCode = R"( + contract C { + function f(uint n, uint k) returns (uint) { + uint[][] memory rows = new uint[][](n + 1); + for (uint i = 1; i <= n; i++) { + rows[i] = new uint[](i); + rows[i][0] = rows[i][rows[i].length - 1] = 1; + for (uint j = 1; j < i - 1; j++) + rows[i][j] = rows[i - 1][j - 1] + rows[i - 1][j]; + } + return rows[n][k - 1]; + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("f(uint256,uint256)", encodeArgs(u256(3), u256(1))) == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("f(uint256,uint256)", encodeArgs(u256(9), u256(5))) == encodeArgs(u256(70))); +} + BOOST_AUTO_TEST_CASE(memory_overwrite) { char const* sourceCode = R"( |