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

Updated TypeReference object to support nested types to support truly… #2023

Closed
Closed
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
293 changes: 215 additions & 78 deletions abi/src/main/java/org/web3j/abi/TypeDecoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,8 @@
import org.web3j.abi.datatypes.primitive.Double;
import org.web3j.abi.datatypes.primitive.Float;
import org.web3j.utils.Numeric;

import static org.web3j.abi.DefaultFunctionReturnDecoder.getDataOffset;
import static org.web3j.abi.TypeReference.makeTypeReference;
import static org.web3j.abi.Utils.findStructConstructor;
import static org.web3j.abi.Utils.getSimpleTypeName;
import static org.web3j.abi.Utils.staticStructNestedPublicFieldsFlatList;

Expand Down Expand Up @@ -127,6 +125,10 @@
return decode(input, 0, type);
}

public static <T extends Type> T decode(String input, TypeReference<?> type) throws ClassNotFoundException {
return decode(input, 0, ((TypeReference<T>)type).getClassType());
}

public static Address decodeAddress(String input) {
return new Address(decodeNumeric(input, Uint160.class));
}
Expand Down Expand Up @@ -373,7 +375,7 @@
final BiFunction<List<T>, String, T> consumer) {
try {
Class<T> classType = typeReference.getClassType();
Constructor<?> constructor = findStructConstructor(classType);
Constructor<?> constructor = Utils.findStructConstructor(classType);
final int length = constructor.getParameterCount();
List<T> elements = new ArrayList<>(length);

Expand Down Expand Up @@ -419,9 +421,15 @@
final TypeReference<T> typeReference, final List<T> parameters) {
try {
Class<T> classType = typeReference.getClassType();
Constructor ctor = findStructConstructor(classType);
ctor.setAccessible(true);
return (T) ctor.newInstance(parameters.toArray());
Constructor ctor;
if(!classType.isAssignableFrom(DynamicStruct.class)) {
ctor = Utils.findStructConstructor(classType);
ctor.setAccessible(true);

return (T) ctor.newInstance(parameters.toArray());
}
else
return (T)new DynamicStruct((List<Type>)parameters);
} catch (ReflectiveOperationException e) {
throw new UnsupportedOperationException(
"Constructor cannot accept" + Arrays.toString(parameters.toArray()), e);
Expand All @@ -443,7 +451,7 @@
}

public static <T extends Type> T decodeDynamicStruct(
String input, int offset, TypeReference<T> typeReference) {
String input, int offset, TypeReference<T> typeReference) throws ClassNotFoundException {

BiFunction<List<T>, String, T> function =
(elements, typeName) -> {
Expand All @@ -463,100 +471,229 @@
final String input,
final int offset,
final TypeReference<T> typeReference,
final BiFunction<List<T>, String, T> consumer) {
try {
final BiFunction<List<T>, String, T> consumer) throws ClassNotFoundException {

final Class<T> classType = typeReference.getClassType();
Constructor<?> constructor = findStructConstructor(classType);
final int length = constructor.getParameterCount();
final Map<Integer, T> parameters = new HashMap<>();
int staticOffset = 0;
final List<Integer> parameterOffsets = new ArrayList<>();
for (int i = 0; i < length; ++i) {
final Class<T> declaredField = (Class<T>) constructor.getParameterTypes()[i];
final T value;
final int beginIndex = offset + staticOffset;
if (isDynamic(declaredField)) {
final int parameterOffset =
decodeDynamicStructDynamicParameterOffset(
input.substring(beginIndex, beginIndex + 64))
+ offset;
parameterOffsets.add(parameterOffset);
staticOffset += 64;

if(classType.isAssignableFrom(DynamicStruct.class)) {
// We handle it dynamically. User has not constructed a class representing their struct but instead we have a DynamicStruct type reference with inner references
return decodeDynamicStructElementsWithInnerTypeRefs(classType, input, offset, typeReference, consumer);
}
else {
// we handle it the constructor way
return decodeDynamicStructElementsWithCtor(classType, input, offset, typeReference, consumer);
}

}

private static <T extends Type> T decodeDynamicStructElementsWithInnerTypeRefs(
final Class<T> classType,
final String input,
final int offset,
final TypeReference<T> typeReference,
final BiFunction<List<T>, String, T> consumer) throws ClassNotFoundException {

final List<TypeReference<?>> InnerTypes = typeReference.getInnerTypeReferences();
final int length = InnerTypes.size();
final Map<Integer, T> parameters = new HashMap<>();
int staticOffset = 0;
final List<Integer> parameterOffsets = new ArrayList<>();

for (int i = 0; i < length; ++i) {
final Class<T> declaredField = (Class<T>) InnerTypes.get(i).getClassType();
final T value;
final int beginIndex = offset + staticOffset;
if (isDynamic(declaredField)) {
final int parameterOffset =
decodeDynamicStructDynamicParameterOffset(
input.substring(beginIndex, beginIndex + 64))
+ offset;
parameterOffsets.add(parameterOffset);
staticOffset += 64;
} else {
if (StaticStruct.class.isAssignableFrom(declaredField)) {
value =
decodeStaticStruct(
input.substring(beginIndex),

Check warning on line 517 in abi/src/main/java/org/web3j/abi/TypeDecoder.java

View check run for this annotation

Codecov / codecov/patch

abi/src/main/java/org/web3j/abi/TypeDecoder.java#L515-L517

Added lines #L515 - L517 were not covered by tests
0,
TypeReference.create(declaredField));
staticOffset +=
staticStructNestedPublicFieldsFlatList((Class<Type>) declaredField)
.size()

Check warning on line 522 in abi/src/main/java/org/web3j/abi/TypeDecoder.java

View check run for this annotation

Codecov / codecov/patch

abi/src/main/java/org/web3j/abi/TypeDecoder.java#L519-L522

Added lines #L519 - L522 were not covered by tests
* MAX_BYTE_LENGTH_FOR_HEX_STRING;
} else {
if (StaticStruct.class.isAssignableFrom(declaredField)) {
value =
decodeStaticStruct(
input.substring(beginIndex),
0,
TypeReference.create(declaredField));
staticOffset +=
staticStructNestedPublicFieldsFlatList((Class<Type>) declaredField)
.size()
* MAX_BYTE_LENGTH_FOR_HEX_STRING;
} else {
value = decode(input.substring(beginIndex), 0, declaredField);
staticOffset += value.bytes32PaddedLength() * 2;
}
parameters.put(i, value);
value = decode(input.substring(beginIndex), 0, declaredField);
staticOffset += value.bytes32PaddedLength() * 2;
}
parameters.put(i, value);
}
int dynamicParametersProcessed = 0;
int dynamicParametersToProcess =
getDynamicStructDynamicParametersCount(constructor.getParameterTypes());
for (int i = 0; i < length; ++i) {
final Class<T> declaredField = (Class<T>) constructor.getParameterTypes()[i];
if (isDynamic(declaredField)) {
final boolean isLastParameterInStruct =
dynamicParametersProcessed == (dynamicParametersToProcess - 1);
final int parameterLength =
isLastParameterInStruct
? input.length()
- parameterOffsets.get(dynamicParametersProcessed)
: parameterOffsets.get(dynamicParametersProcessed + 1)
- parameterOffsets.get(dynamicParametersProcessed);
final Class<T> parameterFromAnnotation =
Utils.extractParameterFromAnnotation(
constructor.getParameterAnnotations()[i]);
parameters.put(
i,
decodeDynamicParameterFromStruct(
input,
parameterOffsets.get(dynamicParametersProcessed),
parameterLength,
declaredField,
parameterFromAnnotation));
dynamicParametersProcessed++;
}
}
int dynamicParametersProcessed = 0;

List<Class<?>> classes = new ArrayList<>();
for(var type : InnerTypes) {
classes.add(type.getClassType());
}

int dynamicParametersToProcess = getDynamicStructDynamicParametersCount(classes.toArray(new Class<?>[0]));
for (int i = 0; i < length; ++i) {
var declaredField = InnerTypes.get(i);
if (isDynamic(declaredField.getClassType())) {
final boolean isLastParameterInStruct =
dynamicParametersProcessed == (dynamicParametersToProcess - 1);
final int parameterLength =
isLastParameterInStruct
? input.length()
- parameterOffsets.get(dynamicParametersProcessed)
: parameterOffsets.get(dynamicParametersProcessed + 1)
- parameterOffsets.get(dynamicParametersProcessed);

parameters.put(
i,
decodeDynamicParameterFromStruct(
input,
parameterOffsets.get(dynamicParametersProcessed),
parameterLength,
declaredField,
null));
dynamicParametersProcessed++;
}
}

String typeName = getSimpleTypeName(classType);
String typeName = getSimpleTypeName(classType);

final List<T> elements = new ArrayList<>();
for (int i = 0; i < length; ++i) {
elements.add(parameters.get(i));
}

return consumer.apply(elements, typeName);

final List<T> elements = new ArrayList<>();
for (int i = 0; i < length; ++i) {
elements.add(parameters.get(i));
}


private static <T extends Type> T decodeDynamicStructElementsWithCtor(
final Class<T> classType,
final String input,
final int offset,
final TypeReference<T> typeReference,
final BiFunction<List<T>, String, T> consumer) throws ClassNotFoundException {
Constructor<?> constructor = Utils.findStructConstructor(classType);
final int length = constructor.getParameterCount();
final Map<Integer, T> parameters = new HashMap<>();
int staticOffset = 0;
final List<Integer> parameterOffsets = new ArrayList<>();
for (int i = 0; i < length; ++i) {
final Class<T> declaredField = (Class<T>) constructor.getParameterTypes()[i];
final T value;
final int beginIndex = offset + staticOffset;
if (isDynamic(declaredField)) {
final int parameterOffset =
decodeDynamicStructDynamicParameterOffset(
input.substring(beginIndex, beginIndex + 64))
+ offset;
parameterOffsets.add(parameterOffset);
staticOffset += 64;
} else {
if (StaticStruct.class.isAssignableFrom(declaredField)) {
value =
decodeStaticStruct(
input.substring(beginIndex),
0,
TypeReference.create(declaredField));
staticOffset +=
staticStructNestedPublicFieldsFlatList((Class<Type>) declaredField)
.size()
* MAX_BYTE_LENGTH_FOR_HEX_STRING;
} else {
value = decode(input.substring(beginIndex), 0, declaredField);
staticOffset += value.bytes32PaddedLength() * 2;
}
parameters.put(i, value);
}
}
int dynamicParametersProcessed = 0;
int dynamicParametersToProcess =
getDynamicStructDynamicParametersCount(constructor.getParameterTypes());
for (int i = 0; i < length; ++i) {
final Class<T> declaredField = (Class<T>) constructor.getParameterTypes()[i];
if (isDynamic(declaredField)) {
final boolean isLastParameterInStruct =
dynamicParametersProcessed == (dynamicParametersToProcess - 1);
final int parameterLength =
isLastParameterInStruct
? input.length()
- parameterOffsets.get(dynamicParametersProcessed)
: parameterOffsets.get(dynamicParametersProcessed + 1)
- parameterOffsets.get(dynamicParametersProcessed);
final Class<T> parameterFromAnnotation =
Utils.extractParameterFromAnnotation(
constructor.getParameterAnnotations()[i]);
parameters.put(
i,
decodeDynamicParameterFromStruct(
input,
parameterOffsets.get(dynamicParametersProcessed),
parameterLength,
declaredField,
parameterFromAnnotation));
dynamicParametersProcessed++;
}
}

return consumer.apply(elements, typeName);
} catch (ClassNotFoundException e) {
throw new UnsupportedOperationException(
"Unable to access parameterized type "
+ Utils.getTypeName(typeReference.getType()),
e);
String typeName = getSimpleTypeName(classType);

final List<T> elements = new ArrayList<>();
for (int i = 0; i < length; ++i) {
elements.add(parameters.get(i));
}

return consumer.apply(elements, typeName);

}


@SuppressWarnings("unchecked")
private static <T extends Type> int getDynamicStructDynamicParametersCount(
final Class<?>[] cls) {
return (int) Arrays.stream(cls).filter(c -> isDynamic((Class<T>) c)).count();
}

private static <T extends Type> T decodeDynamicParameterFromStruct(
final String input,
final int parameterOffset,
final int parameterLength,
final TypeReference<?> declaredField,
final Class<T> parameter) throws ClassNotFoundException {
final String dynamicElementData =
input.substring(parameterOffset, parameterOffset + parameterLength);

final T value;
if (DynamicStruct.class.isAssignableFrom(declaredField.getClassType())) {
value = decodeDynamicStruct(dynamicElementData, 0, (TypeReference<? extends T>) declaredField);
} else if (DynamicArray.class.isAssignableFrom(declaredField.getClassType())) {
if (parameter == null) {
throw new RuntimeException(

Check warning on line 676 in abi/src/main/java/org/web3j/abi/TypeDecoder.java

View check run for this annotation

Codecov / codecov/patch

abi/src/main/java/org/web3j/abi/TypeDecoder.java#L676

Added line #L676 was not covered by tests
"parameter can not be null, try to use annotation @Parameterized to specify the parameter type");
}
value =

Check warning on line 679 in abi/src/main/java/org/web3j/abi/TypeDecoder.java

View check run for this annotation

Codecov / codecov/patch

abi/src/main/java/org/web3j/abi/TypeDecoder.java#L679

Added line #L679 was not covered by tests
(T)
decodeDynamicArray(

Check warning on line 681 in abi/src/main/java/org/web3j/abi/TypeDecoder.java

View check run for this annotation

Codecov / codecov/patch

abi/src/main/java/org/web3j/abi/TypeDecoder.java#L681

Added line #L681 was not covered by tests
dynamicElementData,
0,
Utils.getDynamicArrayTypeReference(parameter));

Check warning on line 684 in abi/src/main/java/org/web3j/abi/TypeDecoder.java

View check run for this annotation

Codecov / codecov/patch

abi/src/main/java/org/web3j/abi/TypeDecoder.java#L684

Added line #L684 was not covered by tests
} else {
value = decode(dynamicElementData, declaredField);
}
return value;
}

private static <T extends Type> T decodeDynamicParameterFromStruct(
final String input,
final int parameterOffset,
final int parameterLength,
final Class<T> declaredField,
final Class<T> parameter) {
final Class<T> parameter) throws ClassNotFoundException {
final String dynamicElementData =
input.substring(parameterOffset, parameterOffset + parameterLength);

Expand Down
Loading
Loading