Skip to content
Permalink
Browse files

#733 Fix array issues

 * Make StaticArray abstract to be consistent with array model
 * Make static array value getter unmodifiable to prevent unwanted changes
 * Make Array.getTypeAsString abstract to make subclasses explicit
 * Remove zero-length check as static arrays will prevent it explicitly
  • Loading branch information...
xaviarias committed Jan 18, 2019
1 parent 4d0f968 commit 843d0890cd1ba88d052909ee79f210eca2fe8c8a
@@ -106,17 +106,20 @@ private FunctionReturnDecoder() { }
result = TypeDecoder.decodeDynamicArray(
input, hexStringDataOffset, typeReference);
offset += MAX_BYTE_LENGTH_FOR_HEX_STRING;

} else if (typeReference instanceof TypeReference.StaticArrayTypeReference) {
int length = ((TypeReference.StaticArrayTypeReference) typeReference).getSize();
result = TypeDecoder.decodeStaticArray(
input, hexStringDataOffset, typeReference, length);
offset += length * MAX_BYTE_LENGTH_FOR_HEX_STRING;

} else if (StaticArray.class.isAssignableFrom(type)) {
int length = Integer.parseInt(type.getSimpleName()
.substring(StaticArray.class.getSimpleName().length()));
result = TypeDecoder.decodeStaticArray(
input, hexStringDataOffset, typeReference, length);
offset += length * MAX_BYTE_LENGTH_FOR_HEX_STRING;

} else {
result = TypeDecoder.decode(input, hexStringDataOffset, type);
offset += MAX_BYTE_LENGTH_FOR_HEX_STRING;
@@ -8,6 +8,7 @@
import java.util.List;
import java.util.function.BiFunction;

import org.web3j.abi.TypeReference.StaticArrayTypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Array;
import org.web3j.abi.datatypes.Bool;
@@ -206,7 +207,7 @@ static Utf8String decodeUtf8String(String input, int offset) {
if (elements.isEmpty()) {
throw new UnsupportedOperationException("Zero length fixed array is invalid type");
} else {
return instantiateStaticArray(typeReference, elements);
return instantiateStaticArray(typeReference, elements, length);
}
};

@@ -215,13 +216,15 @@ static Utf8String decodeUtf8String(String input, int offset) {

@SuppressWarnings("unchecked")
private static <T extends Type> T instantiateStaticArray(
TypeReference<T> typeReference, List<T> elements) {
TypeReference<T> typeReference, List<T> elements, int length) {
try {
Class<List> listClass = List.class;
return typeReference.getClassType().getConstructor(listClass).newInstance(elements);
Class<? extends StaticArray> arrayClass =
(Class<? extends StaticArray>) Class.forName(
"org.web3j.abi.datatypes.generated.StaticArray" + length);

return (T) arrayClass.getConstructor(List.class).newInstance(elements);
} catch (ReflectiveOperationException e) {
//noinspection unchecked
return (T) new StaticArray<>(elements);
throw new UnsupportedOperationException(e);
}
}

@@ -1,7 +1,7 @@
package org.web3j.abi.datatypes;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

@@ -29,7 +29,7 @@

@Deprecated
Array(String type) {
this(type, Collections.emptyList());
this(type, new ArrayList<>());
}

@SafeVarargs
@@ -54,18 +54,11 @@
}

@Override
public String getTypeAsString() {
return AbiTypes.getTypeAString(type) + "[]";
}
public abstract String getTypeAsString();

private void checkValid(Class<T> type, List<T> values) {
Objects.requireNonNull(type);
Objects.requireNonNull(values);

if (values.size() == 0) {
throw new UnsupportedOperationException(
"If empty list is provided, use empty array instance");
}
}

@Override
@@ -41,4 +41,9 @@ public DynamicArray(Class<T> type, List<T> values) {
public DynamicArray(Class<T> type, T... values) {
super(type, values);
}

@Override
public String getTypeAsString() {
return AbiTypes.getTypeAString(getComponentType()) + "[]";
}
}
@@ -1,60 +1,49 @@
package org.web3j.abi.datatypes;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.web3j.abi.datatypes.generated.AbiTypes;

/**
* Static array type.
*/
public class StaticArray<T extends Type> extends Array<T> {
public abstract class StaticArray<T extends Type> extends Array<T> {

/**
* Warning: increasing this constant will cause more generated StaticArrayN types, see:
* org.web3j.codegen.AbiTypesGenerator#generateStaticArrayTypes
*/
public static int MAX_SIZE_OF_STATIC_ARRAY = 32;

private final Integer expectedSize;
public static final int MAX_SIZE_OF_STATIC_ARRAY = 32;

@Deprecated
@SafeVarargs
@SuppressWarnings("unchecked")
public StaticArray(T... values) {
super((Class<T>) AbiTypes.getType(values[0].getTypeAsString()), values);
this.expectedSize = null;
isValid();
this(values.length, values);
}

@Deprecated
@SafeVarargs
@SuppressWarnings("unchecked")
public StaticArray(int expectedSize, T... values) {
super((Class<T>) AbiTypes.getType(values[0].getTypeAsString()), values);
this.expectedSize = expectedSize;
isValid();
this(expectedSize, Arrays.asList(values));
}

@Deprecated
@SuppressWarnings("unchecked")
public StaticArray(List<T> values) {
super((Class<T>) AbiTypes.getType(values.get(0).getTypeAsString()), values);
this.expectedSize = null;
isValid();
this(values.size(), values);
}

@Deprecated
@SuppressWarnings("unchecked")
public StaticArray(int expectedSize, List<T> values) {
super((Class<T>) AbiTypes.getType(values.get(0).getTypeAsString()), values);
this.expectedSize = expectedSize;
isValid();
checkValid(expectedSize);
}

@SafeVarargs
public StaticArray(Class<T> type, T... values) {
this(type, Arrays.asList(values));
this(type, Arrays.asList(values));
}

@SafeVarargs
@@ -63,28 +52,31 @@ public StaticArray(Class<T> type, int expectedSize, T... values) {
}

public StaticArray(Class<T> type, List<T> values) {
super(type, values);
this.expectedSize = null;
isValid();
this(type, values == null ? 0 : values.size(), values);
}

public StaticArray(Class<T> type, int expectedSize, List<T> values) {
super(type, values);
this.expectedSize = expectedSize;
isValid();
checkValid(expectedSize);
}

@Override
public List<T> getValue() {
// Static arrays cannot be modified
return Collections.unmodifiableList(value);
}

@Override
public String getTypeAsString() {
return AbiTypes.getTypeAString(getComponentType()) + "[" + value.size() + "]";
}

private void isValid() {
MAX_SIZE_OF_STATIC_ARRAY = 32;
if (expectedSize == null && value.size() > MAX_SIZE_OF_STATIC_ARRAY) {
private void checkValid(int expectedSize) {
if (value.size() > MAX_SIZE_OF_STATIC_ARRAY) {
throw new UnsupportedOperationException(
"Static arrays with a length greater than 32 are not supported.");
} else if (expectedSize != null && value.size() != expectedSize) {
"Static arrays with a length greater than "
+ MAX_SIZE_OF_STATIC_ARRAY + " are not supported.");
} else if (value.size() != expectedSize) {
throw new UnsupportedOperationException(
"Expected array of type [" + getClass().getSimpleName() + "] to have ["
+ expectedSize + "] elements.");
@@ -17,6 +17,7 @@
import org.web3j.abi.datatypes.Utf8String;
import org.web3j.abi.datatypes.generated.Bytes16;
import org.web3j.abi.datatypes.generated.Bytes32;
import org.web3j.abi.datatypes.generated.StaticArray2;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.crypto.Hash;
import org.web3j.utils.Numeric;
@@ -124,15 +125,17 @@ public void testDecodeStaticArrayValue() {
new TypeReference.StaticArrayTypeReference<StaticArray<Uint256>>(2) {});
outputParameters.add((TypeReference) new TypeReference<Uint256>() {});


List<Type> decoded = FunctionReturnDecoder.decode(
"0x0000000000000000000000000000000000000000000000000000000000000037"
+ "0000000000000000000000000000000000000000000000000000000000000001"
+ "000000000000000000000000000000000000000000000000000000000000000a",
outputParameters);

StaticArray2<Uint256> uint256StaticArray2 = new StaticArray2<>(
new Uint256(BigInteger.valueOf(55)), new Uint256(BigInteger.ONE));

List<Type> expected = Arrays.asList(
new StaticArray<>(new Uint256(BigInteger.valueOf(55)), new Uint256(BigInteger.ONE)),
uint256StaticArray2,
new Uint256(BigInteger.TEN));
assertThat(decoded, equalTo(expected));
}
@@ -16,6 +16,7 @@
import org.web3j.abi.datatypes.generated.Bytes6;
import org.web3j.abi.datatypes.generated.Int256;
import org.web3j.abi.datatypes.generated.Int64;
import org.web3j.abi.datatypes.generated.StaticArray2;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.abi.datatypes.generated.Uint64;

@@ -251,8 +252,7 @@ public void testStaticArray() {
0,
new TypeReference.StaticArrayTypeReference<StaticArray<Uint256>>(2) {},
2),
is(new StaticArray<>(Uint256.class,
new Uint256(BigInteger.TEN),
is(new StaticArray2<>(Uint256.class, new Uint256(BigInteger.TEN),
new Uint256(BigInteger.valueOf(Long.MAX_VALUE)))));

assertThat(TypeDecoder.decodeStaticArray(
@@ -264,7 +264,7 @@ public void testStaticArray() {
new TypeReference.StaticArrayTypeReference<StaticArray<Utf8String>>(2){},
2
),
equalTo(new StaticArray<>(Utf8String.class,
equalTo(new StaticArray2<>(Utf8String.class,
new Utf8String("Hello, world!"),
new Utf8String("world! Hello,"))));
}
@@ -18,6 +18,7 @@
import org.web3j.abi.datatypes.generated.Bytes4;
import org.web3j.abi.datatypes.generated.Bytes6;
import org.web3j.abi.datatypes.generated.Int64;
import org.web3j.abi.datatypes.generated.StaticArray2;
import org.web3j.abi.datatypes.generated.Uint64;
import org.web3j.utils.Numeric;

@@ -207,7 +208,7 @@ public void testUtf8String() {

@Test
public void testFixedArray() {
StaticArray<Ufixed> array = new StaticArray<>(Ufixed.class,
StaticArray<Ufixed> array = new StaticArray2<>(Ufixed.class,
new Ufixed(BigInteger.valueOf(0x2), BigInteger.valueOf(0x2)),
new Ufixed(BigInteger.valueOf(0x8), BigInteger.valueOf(0x8))
);
@@ -256,7 +257,7 @@ public void testArrayOfBytes() {
"0x9215c928b97e0ebeeefd10003a4e3eea23f2eb3acba"
+ "b477eeb589d7a8874d7c5"))
);
DynamicArray emptyArray = DynamicArray.empty("bytes[]");
DynamicArray emptyArray = DynamicArray.empty("bytes");
DynamicArray<DynamicBytes> arrayOfEmptyBytes = new DynamicArray<>(
new DynamicBytes(new byte[0]),
new DynamicBytes(new byte[0])
@@ -320,7 +321,7 @@ public void testArrayOfStrings() {
new Utf8String(""),
new Utf8String("web3j")
);
DynamicArray emptyArray = DynamicArray.empty("string[]");
DynamicArray emptyArray = DynamicArray.empty("string");
DynamicArray<Utf8String> arrayOfEmptyStrings = new DynamicArray<>(
new Utf8String(""),
new Utf8String("")
@@ -5,6 +5,7 @@
import org.junit.Test;

import org.web3j.abi.datatypes.generated.StaticArray3;
import org.web3j.abi.datatypes.generated.StaticArray32;
import org.web3j.abi.datatypes.generated.Uint8;

import static org.hamcrest.CoreMatchers.equalTo;
@@ -15,7 +16,7 @@

@Test
public void canBeInstantiatedWithLessThan32Elements() {
final StaticArray<Uint> array = new StaticArray<>(arrayOfUints(32));
final StaticArray<Uint> array = new StaticArray32<>(arrayOfUints(32));

assertThat(array.getValue().size(), equalTo(32));
}
@@ -41,7 +42,7 @@ public void throwsIfSizeDoesntMatchType() {
@Test
public void throwsIfSizeIsAboveMaxOf32() {
try {
new StaticArray<>(arrayOfUints(33));
new StaticArray32<>(arrayOfUints(33));
fail();
} catch (UnsupportedOperationException e) {
assertThat(e.getMessage(), equalTo(

0 comments on commit 843d089

Please sign in to comment.
You can’t perform that action at this time.