Problem bei der Verwendung des erstellten Arrays aus der Assembly

Ich habe den Code aus dem Solidity-Assembly-Tutorial hier verwendet , um ein eindimensionales Array in Assembly zu erstellen. Der Code lautet wie folgt:

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

}

Wenn ich die Funktion alleine in Remix ausführe, ist alles in Ordnung und das Array wird im richtigen Format ausgegeben. Das Problem tritt auf, wenn ich versuche, einen beliebigen Wert im Array zu indizieren, wenn er von einer anderen Funktion zurückgegeben wird. Es scheint ein Array der Größe 32 zurückzugeben, wobei alle Elemente auf 32 gesetzt sind.

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

Das Obige zeigt das Problem deutlicher, aber ich verwende diese Methode der Array-Erstellung tatsächlich, um billig zwischen uint- und byte-Arrays direkt im Speicher zu konvertieren (zur Verwendung mit dem neuen vorkompilierten modexp-Vertrag). Danke.

Antworten (1)

Der Autor der Github-Seite, auf die Sie verwiesen haben, hat den freien Speicherzeiger nach der Zuweisung des Speichers nicht aktualisiert. Auch wie Tjaden Hess in den Kommentaren anmerkte

returnals Opcode und returnals Solidity-Anweisung sind sehr unterschiedlich. Der Opcode bewirkt, dass die gesamte Vertragsausführung an diesem Punkt mit dem Rückgabewert zurückkehrt, den Sie im Speicher festlegen. Die Solidity-Rückgabe springt einfach aus dem Aufrufrahmen heraus und kehrt zur aufrufenden Funktion zurück

Unten ist der richtige 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];
    }
}

Ich habe eine Pull-Anforderung erstellt, um dies auf Github https://github.com/androlo/solidity-workshop/pull/5 zu beheben

Ein weiterer Teil des Problems besteht darin, dass returnein Opcode und returneine Solidity-Anweisung sehr unterschiedlich sind. Der Opcode bewirkt, dass die gesamte Vertragsausführung an diesem Punkt mit dem Rückgabewert zurückkehrt, den Sie im Speicher festlegen. Die Solidity-Rückgabe springt einfach aus dem Aufrufrahmen heraus und kehrt zur aufrufenden Funktion zurück
@TjadenHess Danke für den Hinweis. Ich habe es in die Antwort aufgenommen.
@TjadenHess weißt du übrigens, warum im ursprünglichen Code 4 Speicherwörter zugewiesen und zurückgegeben werden?
Im Original wird die ABI-Codierung verwendet, was korrekt ist, wenn Sie returnden Wert tatsächlich -ingen. Das Array selbst besteht aus 3 Wörtern, aber das erste Wort zeigt auf den Anfang der Daten, wie in solidity.readthedocs.io/en/develop/… angegeben.