Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Big fix for codegen when Array of struct and struct used together #2068

Merged
merged 4 commits into from
Jul 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public class SolidityFunctionWrapper extends Generator {
private final boolean abiFuncs;
private final int addressLength;

private final HashMap<Integer, ClassName> structClassNameMap = new HashMap<>();
private final HashMap<String, ClassName> structClassNameMap = new HashMap<>();

private final List<NamedType> structsNamedTypeList = new ArrayList<>();

Expand Down Expand Up @@ -587,10 +587,6 @@ private List<TypeSpec> buildStructTypes(final List<AbiDefinition> functionDefini
private static String getStructName(String internalType) {
final String fullStructName = internalType.substring(internalType.lastIndexOf(" ") + 1);
String tempStructName = fullStructName.substring(fullStructName.lastIndexOf(".") + 1);
int arrayPos = tempStructName.indexOf("[");
Copy link
Contributor Author

@tonykwok1992 tonykwok1992 Jun 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was added in my previous PR merged https://github.com/hyperledger/web3j/pull/2061/files#diff-11944d4d901c6c86e0bb4f4bb1af18f9b3644a325a39e7cc7437878613330294R590

However, after fixing normalizeNamedType to handle static array, this is no longer needed, although there is no harm to leave it there

if (arrayPos > -1) {
tempStructName = tempStructName.substring(0, arrayPos);
}
final String structName =
SourceVersion.isName(tempStructName) ? tempStructName : "_" + tempStructName;
return structName;
Expand All @@ -611,6 +607,7 @@ private String adjustToNativeTypeIfNecessary(NamedType component) {
}

private NamedType normalizeNamedType(NamedType namedType) {
// dynamic array
if (namedType.getType().endsWith("[]") && namedType.getInternalType().endsWith("[]")) {
return new NamedType(
namedType.getName(),
Expand All @@ -620,6 +617,18 @@ private NamedType normalizeNamedType(NamedType namedType) {
.getInternalType()
.substring(0, namedType.getInternalType().length() - 2),
namedType.isIndexed());
} else if (namedType.getType().startsWith("tuple[")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Main change is to add handling for static array in normalizedType

&& namedType.getInternalType().contains("[")
&& namedType.getInternalType().endsWith("]")) { // static array

return new NamedType(
namedType.getName(),
namedType.getType().substring(0, namedType.getType().indexOf("[")),
namedType.getComponents(),
namedType
.getInternalType()
.substring(0, namedType.getInternalType().indexOf("[")),
namedType.isIndexed());
} else {
return namedType;
}
Expand All @@ -628,7 +637,7 @@ private NamedType normalizeNamedType(NamedType namedType) {
@NotNull
private List<AbiDefinition.NamedType> extractStructs(
final List<AbiDefinition> functionDefinitions) {
final HashMap<Integer, AbiDefinition.NamedType> structMap = new LinkedHashMap<>();
final HashMap<String, AbiDefinition.NamedType> structMap = new LinkedHashMap<>();
functionDefinitions.stream()
.flatMap(
definition -> {
Expand All @@ -637,7 +646,7 @@ private List<AbiDefinition.NamedType> extractStructs(
parameters.addAll(definition.getOutputs());
return parameters.stream()
.map(this::normalizeNamedType)
.filter(namedType -> namedType.getType().startsWith("tuple"));
Copy link
Contributor Author

@tonykwok1992 tonykwok1992 Jun 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was added in my previous PR merged https://github.com/hyperledger/web3j/pull/2061/files#diff-11944d4d901c6c86e0bb4f4bb1af18f9b3644a325a39e7cc7437878613330294R590

However, after fixing normalizeNamedType to handle static array, this is no longer needed, although there is no harm to leave it there

.filter(namedType -> namedType.getType().equals("tuple"));
})
.forEach(
namedType -> {
Expand All @@ -646,7 +655,7 @@ private List<AbiDefinition.NamedType> extractStructs(
.map(this::normalizeNamedType)
.filter(
nestedNamedStruct ->
nestedNamedStruct.getType().startsWith("tuple"))
nestedNamedStruct.getType().equals("tuple"))
.forEach(
nestedNamedType ->
structMap.put(
Expand Down Expand Up @@ -2288,7 +2297,7 @@ public boolean isIndexed() {
return namedType.isIndexed();
}

public int structIdentifier() {
public String structIdentifier() {
return namedType.structIdentifier();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,28 @@ public void testArrayOfStructClassGenerationCompareJavaFile() throws Exception {
compareJavaFile("ArrayOfStructClassGeneration", true);
}

@Test
public void testArrayOfStructAndStructGeneration() throws Exception {
testCodeGeneration(
"arrayofstructandstruct", "ArrayOfStructAndStruct", JAVA_TYPES_ARG, false);
}

@Test
public void testArrayOfStructAndStructCompareJavaFile() throws Exception {
compareJavaFile("ArrayOfStructAndStruct", true);
}

@Test
public void testStaticArrayOfStructsInStructGeneration() throws Exception {
testCodeGeneration(
"staticarrayofstructsinstruct", "StaticArrayOfStructsInStruct", JAVA_TYPES_ARG, false);
}

@Test
public void testStaticArrayOfStructsInStructGenerationCompareJavaFile() throws Exception {
compareJavaFile("StaticArrayOfStructsInStruct", true);
}

private void compareJavaFile(String inputFileName, boolean useBin) throws Exception {
String contract = inputFileName.toLowerCase();
String packagePath =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;


contract ArrayOfStructAndStruct {

struct Foo {
bool dummy;
}

constructor() {

}
function test(Foo[2] calldata foos) external {
revert();
}

function testSingle(Foo calldata foo) external {
revert();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"components":[{"internalType":"bool","name":"dummy","type":"bool"}],"internalType":"struct ArrayOfStructAndStruct.Foo[2]","name":"foos","type":"tuple[2]"}],"name":"test","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bool","name":"dummy","type":"bool"}],"internalType":"struct ArrayOfStructAndStruct.Foo","name":"foo","type":"tuple"}],"name":"testSingle","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
608060405234801561000f575f80fd5b5061014f8061001d5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80631ae18a6414610038578063f0cd1e3b14610054575b5f80fd5b610052600480360381019061004d91906100a1565b610070565b005b61006e600480360381019061006991906100ee565b610074565b005b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f8190508260206002028201111561009b5761009a61007c565b5b92915050565b5f604082840312156100b6576100b5610078565b5b5f6100c384828501610080565b91505092915050565b5f80fd5b5f602082840312156100e5576100e46100cc565b5b81905092915050565b5f6020828403121561010357610102610078565b5b5f610110848285016100d0565b9150509291505056fea2646970667358221220deb8c438006301a2bb5f5ed22ad6516a4f075c63a360dad46a6571057187273164736f6c63430008140033
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package org.web3j.unittests.java;

import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Bool;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.StaticStruct;
import org.web3j.abi.datatypes.Type;
import org.web3j.crypto.Credentials;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.RemoteCall;
import org.web3j.protocol.core.RemoteFunctionCall;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.tx.Contract;
import org.web3j.tx.TransactionManager;
import org.web3j.tx.gas.ContractGasProvider;

/**
* <p>Auto generated code.
* <p><strong>Do not modify!</strong>
* <p>Please use the <a href="https://docs.web3j.io/command_line.html">web3j command line tools</a>,
* or the org.web3j.codegen.SolidityFunctionWrapperGenerator in the
* <a href="https://github.com/web3j/web3j/tree/main/codegen">codegen module</a> to update.
*
* <p>Generated with web3j version none.
*/
@SuppressWarnings("rawtypes")
public class ArrayOfStructAndStruct extends Contract {
public static final String BINARY = "608060405234801561000f575f80fd5b5061014f8061001d5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80631ae18a6414610038578063f0cd1e3b14610054575b5f80fd5b610052600480360381019061004d91906100a1565b610070565b005b61006e600480360381019061006991906100ee565b610074565b005b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f8190508260206002028201111561009b5761009a61007c565b5b92915050565b5f604082840312156100b6576100b5610078565b5b5f6100c384828501610080565b91505092915050565b5f80fd5b5f602082840312156100e5576100e46100cc565b5b81905092915050565b5f6020828403121561010357610102610078565b5b5f610110848285016100d0565b9150509291505056fea2646970667358221220deb8c438006301a2bb5f5ed22ad6516a4f075c63a360dad46a6571057187273164736f6c63430008140033";

private static String librariesLinkedBinary;

public static final String FUNC_TEST = "test";

public static final String FUNC_TESTSINGLE = "testSingle";

@Deprecated
protected ArrayOfStructAndStruct(String contractAddress, Web3j web3j, Credentials credentials,
BigInteger gasPrice, BigInteger gasLimit) {
super(BINARY, contractAddress, web3j, credentials, gasPrice, gasLimit);
}

protected ArrayOfStructAndStruct(String contractAddress, Web3j web3j, Credentials credentials,
ContractGasProvider contractGasProvider) {
super(BINARY, contractAddress, web3j, credentials, contractGasProvider);
}

@Deprecated
protected ArrayOfStructAndStruct(String contractAddress, Web3j web3j,
TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
super(BINARY, contractAddress, web3j, transactionManager, gasPrice, gasLimit);
}

protected ArrayOfStructAndStruct(String contractAddress, Web3j web3j,
TransactionManager transactionManager, ContractGasProvider contractGasProvider) {
super(BINARY, contractAddress, web3j, transactionManager, contractGasProvider);
}

public RemoteFunctionCall<TransactionReceipt> test(List<Foo> foos) {
final Function function = new Function(
FUNC_TEST,
Arrays.<Type>asList(new org.web3j.abi.datatypes.generated.StaticArray2<Foo>(Foo.class, foos)),
Collections.<TypeReference<?>>emptyList());
return executeRemoteCallTransaction(function);
}

public RemoteFunctionCall<TransactionReceipt> testSingle(Foo foo) {
final Function function = new Function(
FUNC_TESTSINGLE,
Arrays.<Type>asList(foo),
Collections.<TypeReference<?>>emptyList());
return executeRemoteCallTransaction(function);
}

@Deprecated
public static ArrayOfStructAndStruct load(String contractAddress, Web3j web3j,
Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
return new ArrayOfStructAndStruct(contractAddress, web3j, credentials, gasPrice, gasLimit);
}

@Deprecated
public static ArrayOfStructAndStruct load(String contractAddress, Web3j web3j,
TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
return new ArrayOfStructAndStruct(contractAddress, web3j, transactionManager, gasPrice, gasLimit);
}

public static ArrayOfStructAndStruct load(String contractAddress, Web3j web3j,
Credentials credentials, ContractGasProvider contractGasProvider) {
return new ArrayOfStructAndStruct(contractAddress, web3j, credentials, contractGasProvider);
}

public static ArrayOfStructAndStruct load(String contractAddress, Web3j web3j,
TransactionManager transactionManager, ContractGasProvider contractGasProvider) {
return new ArrayOfStructAndStruct(contractAddress, web3j, transactionManager, contractGasProvider);
}

public static RemoteCall<ArrayOfStructAndStruct> deploy(Web3j web3j, Credentials credentials,
ContractGasProvider contractGasProvider) {
return deployRemoteCall(ArrayOfStructAndStruct.class, web3j, credentials, contractGasProvider, getDeploymentBinary(), "");
}

public static RemoteCall<ArrayOfStructAndStruct> deploy(Web3j web3j,
TransactionManager transactionManager, ContractGasProvider contractGasProvider) {
return deployRemoteCall(ArrayOfStructAndStruct.class, web3j, transactionManager, contractGasProvider, getDeploymentBinary(), "");
}

@Deprecated
public static RemoteCall<ArrayOfStructAndStruct> deploy(Web3j web3j, Credentials credentials,
BigInteger gasPrice, BigInteger gasLimit) {
return deployRemoteCall(ArrayOfStructAndStruct.class, web3j, credentials, gasPrice, gasLimit, getDeploymentBinary(), "");
}

@Deprecated
public static RemoteCall<ArrayOfStructAndStruct> deploy(Web3j web3j,
TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
return deployRemoteCall(ArrayOfStructAndStruct.class, web3j, transactionManager, gasPrice, gasLimit, getDeploymentBinary(), "");
}

public static void linkLibraries(List<Contract.LinkReference> references) {
librariesLinkedBinary = linkBinaryWithReferences(BINARY, references);
}

private static String getDeploymentBinary() {
if (librariesLinkedBinary != null) {
return librariesLinkedBinary;
} else {
return BINARY;
}
}

public static class Foo extends StaticStruct {
public Boolean dummy;

public Foo(Boolean dummy) {
super(new org.web3j.abi.datatypes.Bool(dummy));
this.dummy = dummy;
}

public Foo(Bool dummy) {
super(dummy);
this.dummy = dummy.getValue();
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;


contract StaticArrayOfStructsInStruct {

struct Player {
address addr;
uint256 timeLeft;
}
struct Config{
uint64 index;
Player[2] players;
}

function test(Config calldata config) external {
revert();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"inputs":[{"components":[{"internalType":"uint64","name":"index","type":"uint64"},{"components":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"timeLeft","type":"uint256"}],"internalType":"struct StaticArrayOfStructsInStruct.Player[2]","name":"players","type":"tuple[2]"}],"internalType":"struct StaticArrayOfStructsInStruct.Config","name":"config","type":"tuple"}],"name":"test","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
608060405234801561000f575f80fd5b5060c58061001c5f395ff3fe6080604052348015600e575f80fd5b50600436106026575f3560e01c80639acc3bf114602a575b5f80fd5b60406004803603810190603c91906069565b6042565b005b5f80fd5b5f80fd5b5f80fd5b5f60a08284031215606057605f604a565b5b81905092915050565b5f60a08284031215607b57607a6046565b5b5f608684828501604e565b9150509291505056fea2646970667358221220a67d48af17079065cb181167be61364e31cd9d588c89b4a1ca7ceb1a9bf640a864736f6c63430008140033
Loading
Loading