Skip to content

Commit e32afbf

Browse files
authored
Merge pull request #30 from icon-project/fix-typeconverter-list-conversion
Fix TypeConverter to return List object correctly
2 parents c086de0 + 72403cb commit e32afbf

File tree

4 files changed

+292
-19
lines changed

4 files changed

+292
-19
lines changed

unittest/src/main/java/score/Context.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public static<T> T call(Class<T> cls, BigInteger value,
9292

9393
public static Object call(BigInteger value,
9494
Address targetAddress, String method, Object... params) {
95-
return TypeConverter.normalize(sm.call(value, targetAddress, method, params));
95+
return TypeConverter.cast(sm.call(value, targetAddress, method, params));
9696
}
9797

9898
public static<T> T call(Class<T> cls,
@@ -101,7 +101,7 @@ public static<T> T call(Class<T> cls,
101101
}
102102

103103
public static Object call(Address targetAddress, String method, Object... params) {
104-
return TypeConverter.normalize(sm.call(BigInteger.ZERO, targetAddress, method, params));
104+
return TypeConverter.cast(sm.call(BigInteger.ZERO, targetAddress, method, params));
105105
}
106106

107107
public static void transfer(Address targetAddress, BigInteger value) {

unittest/src/main/java/score/impl/TypeConverter.java

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,11 @@
3232
import java.util.Map;
3333

3434
public class TypeConverter {
35-
@SuppressWarnings("unchecked")
36-
public static Object normalize(Object so) {
35+
private static Object normalize(Object so) {
36+
return normalize(so, false);
37+
}
38+
39+
private static Object normalize(Object so, boolean ret) {
3740
if (so==null) {
3841
return null;
3942
}
@@ -68,56 +71,57 @@ public static Object normalize(Object so) {
6871
for (int i = 0 ; i<o.length ; i++) {
6972
no[i] = o[i];
7073
}
71-
return no;
74+
return ret ? List.of(no) : no;
7275
} else if (so instanceof char[]) {
7376
var o = (char[]) so;
7477
var no = new Object[o.length];
7578
for (int i = 0 ; i<o.length ; i++) {
7679
no[i] = BigInteger.valueOf(o[i]);
7780
}
78-
return no;
81+
return ret ? List.of(no) : no;
7982
} else if (so instanceof short[]) {
8083
var o = (short[]) so;
8184
var no = new Object[o.length];
8285
for (int i = 0 ; i<o.length ; i++) {
8386
no[i] = BigInteger.valueOf(o[i]);
8487
}
85-
return no;
88+
return ret ? List.of(no) : no;
8689
} else if (so instanceof int[]) {
8790
var o = (int[]) so;
8891
var no = new Object[o.length];
8992
for (int i = 0 ; i<o.length ; i++) {
9093
no[i] = BigInteger.valueOf(o[i]);
9194
}
92-
return no;
95+
return ret ? List.of(no) : no;
9396
} else if (so instanceof long[]) {
9497
var o = (long[]) so;
9598
var no = new Object[o.length];
9699
for (int i = 0; i < o.length; i++) {
97100
no[i] = BigInteger.valueOf(o[i]);
98101
}
99-
return no;
102+
return ret ? List.of(no) : no;
100103
} else if (so instanceof List) {
101104
var o = (List<?>)so;
102105
var no = new Object[o.size()];
103106
for (int i=0 ; i<no.length ; i++) {
104-
no[i] = normalize(o.get(i));
107+
no[i] = normalize(o.get(i), ret);
105108
}
106-
return no;
109+
return ret ? List.of(no) : no;
107110
} else if (so instanceof Map) {
111+
@SuppressWarnings("unchecked")
108112
var o = (Map<String,Object>)so;
109113
var no = new LinkedHashMap<>();
110114
for (Map.Entry<String,Object> pair : o.entrySet()) {
111-
no.put(pair.getKey(), normalize(pair.getValue()));
115+
no.put(pair.getKey(), normalize(pair.getValue(), ret));
112116
}
113117
return no;
114118
} else if (clz.isArray()){
115119
var o = (Object[])so;
116120
var no = new Object[o.length];
117121
for (int i=0 ; i<o.length ; i++) {
118-
no[i] = normalize(o[i]);
122+
no[i] = normalize(o[i], ret);
119123
}
120-
return no;
124+
return ret ? List.of(no) : no;
121125
} else {
122126
var rProps = Property.getReadableProperties(so);
123127
if (rProps.isEmpty()) {
@@ -126,7 +130,7 @@ public static Object normalize(Object so) {
126130
var map = new java.util.TreeMap<>();
127131
for (var rp : rProps) {
128132
try {
129-
map.put(rp.getName(), normalize(rp.get(so)));
133+
map.put(rp.getName(), normalize(rp.get(so), ret));
130134
} catch (InvocationTargetException|IllegalAccessException e) {
131135
throw new IllegalArgumentException(e);
132136
}
@@ -197,8 +201,12 @@ public static<T> T cast(Object so, Class<T> cls) {
197201
return (T)specialize(normalize(so), cls);
198202
}
199203

204+
public static Object cast(Object so) {
205+
return normalize(so, true);
206+
}
207+
200208
@SuppressWarnings("unchecked")
201-
public static Object specialize(Object so, Class<?> cls) {
209+
private static Object specialize(Object so, Class<?> cls) {
202210
if (so == null ) {
203211
return null;
204212
}
@@ -272,7 +280,7 @@ public static boolean isValidEventValue(Object obj) {
272280
public static Object[] asEventObjects(Object[] objs) {
273281
Object[] normalized = new Object[objs.length];
274282
for (int i = 0; i < objs.length; i++) {
275-
normalized[i] = normalize(objs[i]);
283+
normalized[i] = normalize(objs[i], false);
276284
}
277285
return normalized;
278286
}

unittest/src/test/java/score/IntercallTest.java

Lines changed: 213 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import score.annotation.Payable;
2727

2828
import java.math.BigInteger;
29+
import java.util.Arrays;
30+
import java.util.List;
2931

3032
import static org.junit.jupiter.api.Assertions.*;
3133

@@ -57,6 +59,36 @@ public void deposit() {
5759
public BigInteger getStored() {
5860
return store.getOrDefault(BigInteger.ZERO);
5961
}
62+
63+
@External(readonly=true)
64+
public boolean[] getBoolArray(boolean[] v) {
65+
return v;
66+
}
67+
68+
@External(readonly=true)
69+
public int[] getIntArray(int[] v) {
70+
return v;
71+
}
72+
73+
@External(readonly=true)
74+
public String[] getStrArray(String[] v) {
75+
return v;
76+
}
77+
78+
@External(readonly=true)
79+
public byte[][] getBytesArray(byte[][] v) {
80+
return v;
81+
}
82+
83+
@External(readonly=true)
84+
public Address[] getAddressArray(Address[] v) {
85+
return v;
86+
}
87+
88+
@External(readonly=true)
89+
public BigInteger[] getBigIntArray(BigInteger[] v) {
90+
return v;
91+
}
6092
}
6193

6294
public static class CalleeWithoutFallback {
@@ -94,12 +126,180 @@ public void invoke(Address target, BigInteger value, String method) {
94126
public void transfer(Address target, BigInteger value) {
95127
Context.transfer(target, value);
96128
}
129+
130+
@External
131+
public void callWithType(Address target, String type) {
132+
switch (type) {
133+
case "bool": {
134+
var ba = new boolean[]{false, true};
135+
var ret = (List<Object>) Context.call(target, "getBoolArray", (Object) ba);
136+
Context.require(ret.size() == ba.length);
137+
for (int i = 0; i < ba.length; i++) {
138+
var e = ret.get(i);
139+
if (e instanceof Boolean) {
140+
Context.require(((Boolean) e) == ba[i]);
141+
} else {
142+
Context.revert("not a boolean");
143+
}
144+
}
145+
break;
146+
}
147+
case "int": {
148+
var ia = new int[]{1, 2, 3};
149+
var ret = (List<Object>) Context.call(target, "getIntArray", (Object) ia);
150+
Context.require(ret.size() == ia.length);
151+
for (int i = 0; i < ia.length; i++) {
152+
var e = ret.get(i);
153+
if (e instanceof BigInteger) {
154+
Context.require(((BigInteger) e).intValue() == ia[i]);
155+
} else {
156+
Context.revert("not a BigInteger");
157+
}
158+
}
159+
break;
160+
}
161+
case "str": {
162+
var sa = new String[]{"a", "b", "c"};
163+
var ret = (List<Object>) Context.call(target, "getStrArray", (Object) sa);
164+
Context.require(ret.size() == sa.length);
165+
for (int i = 0; i < sa.length; i++) {
166+
var e = ret.get(i);
167+
if (e instanceof String) {
168+
Context.require(e.equals(sa[i]));
169+
} else {
170+
Context.revert("not a String");
171+
}
172+
}
173+
break;
174+
}
175+
case "bytes": {
176+
var ba = new byte[][]{new byte[]{0x1, 0x2}, new byte[]{0x3, 0x4}};
177+
var ret = (List<Object>) Context.call(target, "getBytesArray", (Object) ba);
178+
Context.require(ret.size() == ba.length);
179+
for (int i = 0; i < ba.length; i++) {
180+
var e = ret.get(i);
181+
if (e instanceof byte[]) {
182+
Context.require(Arrays.equals((byte[]) e, ba[i]));
183+
} else {
184+
Context.revert("not a byte[]");
185+
}
186+
}
187+
break;
188+
}
189+
case "Address": {
190+
var aa = new Address[]{
191+
Address.fromString("hx0000000000000000000000000000000000000001"),
192+
Address.fromString("hx0000000000000000000000000000000000000002")
193+
};
194+
var ret = (List<Object>) Context.call(target, "getAddressArray", (Object) aa);
195+
Context.require(ret.size() == aa.length);
196+
for (int i = 0; i < aa.length; i++) {
197+
var e = ret.get(i);
198+
if (e instanceof Address) {
199+
Context.require(e.equals(aa[i]));
200+
} else {
201+
Context.revert("not an Address");
202+
}
203+
}
204+
break;
205+
}
206+
case "bigInt": {
207+
var ba = new BigInteger[]{BigInteger.ZERO, BigInteger.ONE, BigInteger.TWO};
208+
var ret = (List<Object>) Context.call(target, "getBigIntArray", (Object) ba);
209+
Context.require(ret.size() == ba.length);
210+
for (int i = 0; i < ba.length; i++) {
211+
var e = ret.get(i);
212+
if (e instanceof BigInteger) {
213+
Context.require(e.equals(ba[i]));
214+
} else {
215+
Context.revert("not a BigInteger");
216+
}
217+
}
218+
break;
219+
}
220+
case "boolList": {
221+
var bl = List.of(false, true);
222+
var ret = (List<Object>) Context.call(target, "getBoolArray", (Object) bl);
223+
Context.require(ret.size() == bl.size());
224+
for (int i = 0; i < bl.size(); i++) {
225+
var e = ret.get(i);
226+
if (e instanceof Boolean) {
227+
Context.require(e == bl.get(i));
228+
} else {
229+
Context.revert("not a boolean");
230+
}
231+
}
232+
break;
233+
}
234+
case "intList": {
235+
var il = List.of(1, 2, 3);
236+
var ret = (List<Object>) Context.call(target, "getIntArray", (Object) il);
237+
Context.require(ret.size() == il.size());
238+
for (int i = 0; i < il.size(); i++) {
239+
var e = ret.get(i);
240+
if (e instanceof BigInteger) {
241+
Context.require(((BigInteger) e).intValue() == il.get(i));
242+
} else {
243+
Context.revert("not a BigInteger");
244+
}
245+
}
246+
break;
247+
}
248+
case "strList": {
249+
var sl = List.of("a", "b", "c");
250+
var ret = (List<Object>) Context.call(target, "getStrArray", (Object) sl);
251+
Context.require(ret.size() == sl.size());
252+
for (int i = 0; i < sl.size(); i++) {
253+
var e = ret.get(i);
254+
if (e instanceof String) {
255+
Context.require(e.equals(sl.get(i)));
256+
} else {
257+
Context.revert("not a String");
258+
}
259+
}
260+
break;
261+
}
262+
case "bytesList": {
263+
var bl = List.of(new byte[]{0x1, 0x2}, new byte[]{0x3, 0x4});
264+
var ret = (List<Object>) Context.call(target, "getBytesArray", (Object) bl);
265+
Context.require(ret.size() == bl.size());
266+
for (int i = 0; i < bl.size(); i++) {
267+
var e = ret.get(i);
268+
if (e instanceof byte[]) {
269+
Context.require(Arrays.equals((byte[]) e, bl.get(i)));
270+
} else {
271+
Context.revert("not a byte[]");
272+
}
273+
}
274+
break;
275+
}
276+
case "AddressList": {
277+
var aa = List.of(
278+
Address.fromString("hx0000000000000000000000000000000000000001"),
279+
Address.fromString("hx0000000000000000000000000000000000000002")
280+
);
281+
var ret = (List<Object>) Context.call(target, "getAddressArray", (Object) aa);
282+
Context.require(ret.size() == aa.size());
283+
for (int i = 0; i < aa.size(); i++) {
284+
var e = ret.get(i);
285+
if (e instanceof Address) {
286+
Context.require(e.equals(aa.get(i)));
287+
} else {
288+
Context.revert("not an Address");
289+
}
290+
}
291+
break;
292+
}
293+
default:
294+
Context.revert("Unknown type: " + type);
295+
}
296+
}
97297
}
98298

99299
private static Score callee;
100300
private static Score callee2;
101301
private static Score caller;
102-
private static Account user1 = sm.createAccount();
302+
private static final Account user1 = sm.createAccount();
103303

104304
@BeforeAll
105305
static void setup() throws Exception {
@@ -109,12 +309,23 @@ static void setup() throws Exception {
109309
}
110310

111311
@Test
112-
void testProxyCall() throws Exception {
312+
void testProxyCall() {
113313
assertDoesNotThrow(() ->
114314
caller.invoke(owner, "proxyCall", callee.getAddress())
115315
);
116316
}
117317

318+
@Test
319+
void testTypeConversion() {
320+
String[] tests = {
321+
"bool", "int", "str", "bytes", "Address", "bigInt",
322+
"boolList", "intList", "strList", "bytesList", "AddressList"};
323+
for (var type : tests) {
324+
assertDoesNotThrow(() ->
325+
caller.invoke(owner, "callWithType", callee.getAddress(), type));
326+
}
327+
}
328+
118329
@Test
119330
void testTransfer() {
120331
BigInteger balance;

0 commit comments

Comments
 (0)