Skip to content

Commit

Permalink
Updated TypeReference object to support nested types to support truly…
Browse files Browse the repository at this point in the history
… dynamic stucts.

Signed-off-by: Antlion12 <[email protected]>
  • Loading branch information
calmacfadden authored and Antlion12 committed Jul 10, 2024
1 parent 45540b9 commit 43ef0c2
Show file tree
Hide file tree
Showing 4 changed files with 284 additions and 92 deletions.
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 @@ -133,6 +131,10 @@ public static <T extends Type> T decode(String input, Class<T> type) {
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 @@ -382,7 +384,7 @@ private static <T extends Type> T decodeStaticStructElement(
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 @@ -428,9 +430,15 @@ private static <T extends Type> T instantiateStruct(
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 @@ -452,7 +460,7 @@ public static <T extends Type> T decodeDynamicArray(
}

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 @@ -472,100 +480,229 @@ private static <T extends Type> T decodeDynamicStructElements(
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),
0,
TypeReference.create(declaredField));
staticOffset +=
staticStructNestedPublicFieldsFlatList((Class<Type>) declaredField)
.size()
* 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(
"parameter can not be null, try to use annotation @Parameterized to specify the parameter type");
}
value =
(T)
decodeDynamicArray(
dynamicElementData,
0,
Utils.getDynamicArrayTypeReference(parameter));
} 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

0 comments on commit 43ef0c2

Please sign in to comment.