Solidity – Problem Using Created Array from Assembly

assemblybytessolidity

I've used the code from the solidity assembly tutorial here to create a single dimensional array in assembly. The code is as follows:

contract C {
function f(uint a, uint b) constant returns (uint[]) {
    assembly {
        // Create an dynamic sized array manually.
        let memOffset := mload(0x40) // 0x40 is the address where next free memory slot is stored in Solidity.
        mstore(memOffset, 0x20) // single dimensional array, data offset is 0x20
        mstore(add(memOffset, 32), 2) // Set size to 2
        mstore(add(memOffset, 64), a) // array[0] = a
        mstore(add(memOffset, 96), b) // array[1] = b
        return(memOffset, 128)
    }
}

}

When I run the function by itself in Remix, everything is ok, and it outputs the array in the correct format. The issue occurs when I try to index any value in the array when it's returned from another function. It seems to return an array of size 32, with all elements set to 32.

function get_f(uint a, uint b) public returns(uint){
        uint[] ret = f(a,b);
        return(ret[0]); //should return a, instead returns 32.
    }

The above demonstrates the problem more clearly, but I am in fact using this method of array creation to cheaply convert between uint and byte arrays directly in memory (for use with the new precompiled modexp contract). thanks.

Best Answer

The author of the github page that you referenced didn't update the free memory pointer after allocating the memory. Also as Tjaden Hess noted in the comments

return as an opcode and return as a Solidity statement are very different. The opcode causes the entire contract execution to return at that point, with the return value that you designate in memory. The Solidity return just pops off the call frame and returns to the calling function

Below is the correct code:

contract C {
    function f(uint a, uint b) pure public returns (uint[] memory memOffset) {
        assembly {
             // Create an dynamic sized array manually.
             // Don't need to define the data type here as the EVM will prefix it
             memOffset := msize() // Get the highest available block of memory
             mstore(add(memOffset, 0x00), 2) // Set size to 2
             mstore(add(memOffset, 0x20), a) // array[0] = a
             mstore(add(memOffset, 0x40), b) // array[1] = b
             mstore(0x40, add(memOffset, 0x60)) // Update the msize offset to be our memory reference plus the amount of bytes we're using
        }
    }

    function get_f(uint a, uint b) public returns(uint){
        uint[] memory ret = f(a,b);
        return ret[0];
    }
}

I created a pull request to fix this on Github https://github.com/androlo/solidity-workshop/pull/5