Problema al usar la matriz creada a partir del ensamblaje

He usado el código del tutorial de ensamblaje de solidez aquí para crear una matriz unidimensional en ensamblaje. El código es el siguiente:

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)
    }
}

}

Cuando ejecuto la función por sí sola en Remix, todo está bien y genera la matriz en el formato correcto. El problema ocurre cuando trato de indexar cualquier valor en la matriz cuando se devuelve desde otra función. Parece devolver una matriz de tamaño 32, con todos los elementos establecidos en 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.
    }

Lo anterior demuestra el problema más claramente, pero de hecho estoy usando este método de creación de matrices para convertir de manera económica entre matrices uint y byte directamente en la memoria (para usar con el nuevo contrato modexp precompilado). Gracias.

Respuestas (1)

El autor de la página de github a la que hizo referencia no actualizó el puntero de memoria libre después de asignar la memoria. También como señaló Tjaden Hess en los comentarios.

returncomo código de operación y returncomo declaración de solidez son muy diferentes. El código de operación hace que la ejecución del contrato completo regrese en ese punto, con el valor de retorno que usted designe en la memoria. El retorno de Solidity simplemente sale del marco de llamada y vuelve a la función de llamada

A continuación se muestra el código correcto:

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];
    }
}

Creé una solicitud de extracción para arreglar esto en Github https://github.com/androlo/solidity-workshop/pull/5

Otra parte del problema es que returncomo código de operación y returncomo declaración de solidez son muy diferentes. El código de operación hace que la ejecución del contrato completo regrese en ese punto, con el valor de retorno que usted designe en la memoria. El retorno de Solidity simplemente sale del marco de llamada y vuelve a la función de llamada
@TjadenHess Gracias por señalar esto. Lo incluí en la respuesta.
@TjadenHess por cierto, ¿sabe por qué en el código original se asignan y devuelven 4 palabras de memoria?
En el original, utiliza la codificación ABI, que es correcta cuando en realidad se está returnmodificando el valor. La matriz en sí está contenida en 3 palabras, pero la primera palabra apunta al comienzo de los datos, como se especifica en solidity.readthedocs.io/en/develop/…