Skip to content

Commit ed616f8

Browse files
committed
wasm gc: support Array.set
1 parent 8527ea0 commit ed616f8

File tree

6 files changed

+182
-6
lines changed

6 files changed

+182
-6
lines changed

core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java

Lines changed: 146 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
import org.teavm.backend.wasm.model.expression.WasmArrayLength;
5959
import org.teavm.backend.wasm.model.expression.WasmArrayNewDefault;
6060
import org.teavm.backend.wasm.model.expression.WasmArrayNewFixed;
61+
import org.teavm.backend.wasm.model.expression.WasmArraySet;
6162
import org.teavm.backend.wasm.model.expression.WasmBlock;
6263
import org.teavm.backend.wasm.model.expression.WasmCall;
6364
import org.teavm.backend.wasm.model.expression.WasmCallReference;
@@ -169,15 +170,18 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
169170
private int enumConstantsFunctionOffset = -1;
170171
private int arrayLengthOffset = -1;
171172
private int arrayGetOffset = -1;
173+
private int arraySetOffset = -1;
172174
private int arrayCopyOffset = -1;
173175
private int cloneOffset = -1;
174176
private int servicesOffset = -1;
175177
private int throwableNativeOffset = -1;
176178
private WasmStructure arrayVirtualTableStruct;
177179
private WasmFunction arrayGetObjectFunction;
180+
private WasmFunction arraySetObjectFunction;
178181
private WasmFunction arrayLengthObjectFunction;
179182
private WasmFunction arrayCopyObjectFunction;
180183
private WasmFunctionType arrayGetType;
184+
private WasmFunctionType arraySetType;
181185
private WasmFunctionType arrayLengthType;
182186
private WasmFunctionType arrayCopyType;
183187
private List<WasmFunction> multiArrayFunctions = new ArrayList<>();
@@ -943,6 +947,10 @@ private void applyArrayVirtualTable(WasmExpression[] entries, ValueType.Array ty
943947
var getFunction = getArrayGetFunction(itemType);
944948
entries[arrayGetOffset] = new WasmFunctionReference(getFunction);
945949
}
950+
if (info.arraySet()) {
951+
var setFunction = getArraySetFunction(itemType);
952+
entries[arraySetOffset] = new WasmFunctionReference(setFunction);
953+
}
946954
if (info.arrayCopy()) {
947955
var copyFunction = getArrayCopyFunction(itemType);
948956
entries[arrayCopyOffset] = new WasmFunctionReference(copyFunction);
@@ -979,6 +987,11 @@ private void fillArrayVirtualTableMethods(ValueType type, List<WasmExpression> t
979987
target.add(new WasmStructSet(structure, new WasmGetGlobal(global), arrayGetOffset,
980988
new WasmFunctionReference(getFunction)));
981989
}
990+
if (info.arraySet()) {
991+
var setFunction = getArraySetFunction(itemType);
992+
target.add(new WasmStructSet(structure, new WasmGetGlobal(global), arraySetOffset,
993+
new WasmFunctionReference(setFunction)));
994+
}
982995
if (info.arrayCopy()) {
983996
var copyFunction = getArrayCopyFunction(itemType);
984997
target.add(new WasmStructSet(structure, new WasmGetGlobal(global), arrayCopyOffset,
@@ -1054,8 +1067,7 @@ private WasmFunction getArrayGetObjectFunction() {
10541067

10551068
private WasmFunction generateArrayGetPrimitiveFunction(PrimitiveType type) {
10561069
var function = new WasmFunction(getArrayGetType());
1057-
function.setName(names.topLevel("Array<" + names.suggestForType(ValueType.primitive(type))
1058-
+ ">::get"));
1070+
function.setName(names.topLevel("Array<" + names.suggestForType(ValueType.primitive(type)) + ">::get"));
10591071
module.functions.add(function);
10601072
function.setReferenced(true);
10611073

@@ -1123,13 +1135,120 @@ private WasmFunction generateArrayGetPrimitiveFunction(PrimitiveType type) {
11231135
return function;
11241136
}
11251137

1138+
private WasmFunction getArraySetFunction(ValueType itemType) {
1139+
if (itemType instanceof ValueType.Primitive) {
1140+
return generateArraySetPrimitiveFunction(((ValueType.Primitive) itemType).getKind());
1141+
}
1142+
return getArraySetObjectFunction();
1143+
}
1144+
11261145
private WasmFunction getArrayCopyFunction(ValueType itemType) {
11271146
if (itemType instanceof ValueType.Primitive) {
11281147
return createArrayCopyFunction(itemType);
11291148
}
11301149
return getArrayCopyObjectFunction();
11311150
}
11321151

1152+
private WasmFunction getArraySetObjectFunction() {
1153+
if (arraySetObjectFunction == null) {
1154+
arraySetObjectFunction = new WasmFunction(getArraySetType());
1155+
arraySetObjectFunction.setName(names.topLevel("Array<" + names.suggestForClass("java.lang.Object")
1156+
+ "::set"));
1157+
module.functions.add(arraySetObjectFunction);
1158+
arraySetObjectFunction.setReferenced(true);
1159+
1160+
var arrayStruct = getClassInfo(ValueType.arrayOf(OBJECT_TYPE)).structure;
1161+
var arrayDataTypeRef = (WasmType.CompositeReference) arrayStruct.getFields()
1162+
.get(ARRAY_DATA_FIELD_OFFSET).getUnpackedType();
1163+
var arrayDataType = (WasmArray) arrayDataTypeRef.composite;
1164+
var selfLocal = new WasmLocal(standardClasses.classClass().getType(), "this");
1165+
var objectLocal = new WasmLocal(standardClasses.objectClass().getType(), "object");
1166+
var indexLocal = new WasmLocal(WasmType.INT32, "index");
1167+
var valueLocal = new WasmLocal(standardClasses.objectClass().getType(), "value");
1168+
arraySetObjectFunction.add(selfLocal);
1169+
arraySetObjectFunction.add(objectLocal);
1170+
arraySetObjectFunction.add(indexLocal);
1171+
arraySetObjectFunction.add(valueLocal);
1172+
1173+
var array = new WasmCast(new WasmGetLocal(objectLocal), arrayStruct.getNonNullReference());
1174+
var arrayData = new WasmStructGet(arrayStruct, array, ARRAY_DATA_FIELD_OFFSET);
1175+
var set = new WasmArraySet(arrayDataType, arrayData, new WasmGetLocal(indexLocal),
1176+
new WasmGetLocal(valueLocal));
1177+
arraySetObjectFunction.getBody().add(set);
1178+
}
1179+
return arraySetObjectFunction;
1180+
}
1181+
1182+
private WasmFunction generateArraySetPrimitiveFunction(PrimitiveType type) {
1183+
var function = new WasmFunction(getArraySetType());
1184+
function.setName(names.topLevel("Array<" + names.suggestForType(ValueType.primitive(type)) + ">::set"));
1185+
module.functions.add(function);
1186+
function.setReferenced(true);
1187+
1188+
var arrayStruct = getClassInfo(ValueType.arrayOf(ValueType.primitive(type))).structure;
1189+
var arrayDataTypeRef = (WasmType.CompositeReference) arrayStruct.getFields()
1190+
.get(ARRAY_DATA_FIELD_OFFSET).getUnpackedType();
1191+
var arrayDataType = (WasmArray) arrayDataTypeRef.composite;
1192+
var classLocal = new WasmLocal(standardClasses.classClass().getType(), "this");
1193+
var objectLocal = new WasmLocal(standardClasses.objectClass().getType(), "object");
1194+
var indexLocal = new WasmLocal(WasmType.INT32, "index");
1195+
var valueLocal = new WasmLocal(standardClasses.objectClass().getType(), "value");
1196+
function.add(classLocal);
1197+
function.add(objectLocal);
1198+
function.add(indexLocal);
1199+
function.add(valueLocal);
1200+
1201+
var array = new WasmCast(new WasmGetLocal(objectLocal), arrayStruct.getNonNullReference());
1202+
var arrayData = new WasmStructGet(arrayStruct, array, ARRAY_DATA_FIELD_OFFSET);
1203+
var set = new WasmArraySet(arrayDataType, arrayData, new WasmGetLocal(indexLocal),
1204+
new WasmGetLocal(valueLocal));
1205+
Class<?> primitiveType;
1206+
Class<?> wrapperType;
1207+
switch (type) {
1208+
case BOOLEAN:
1209+
primitiveType = boolean.class;
1210+
wrapperType = Boolean.class;
1211+
break;
1212+
case BYTE:
1213+
primitiveType = byte.class;
1214+
wrapperType = Byte.class;
1215+
break;
1216+
case SHORT:
1217+
primitiveType = short.class;
1218+
wrapperType = Short.class;
1219+
break;
1220+
case CHARACTER:
1221+
primitiveType = char.class;
1222+
wrapperType = Character.class;
1223+
break;
1224+
case INTEGER:
1225+
primitiveType = int.class;
1226+
wrapperType = Integer.class;
1227+
break;
1228+
case LONG:
1229+
primitiveType = long.class;
1230+
wrapperType = Long.class;
1231+
break;
1232+
case FLOAT:
1233+
primitiveType = float.class;
1234+
wrapperType = Float.class;
1235+
break;
1236+
case DOUBLE:
1237+
primitiveType = double.class;
1238+
wrapperType = Double.class;
1239+
break;
1240+
default:
1241+
throw new IllegalArgumentException();
1242+
}
1243+
var method = new MethodReference(wrapperType, primitiveType.getName() + "Value", primitiveType);
1244+
var unwrapFunction = functionProvider.forInstanceMethod(method);
1245+
set.setValue(new WasmCast(set.getValue(), getClassInfo(ValueType.parse(wrapperType)).getType()));
1246+
set.setValue(new WasmCall(unwrapFunction, set.getValue()));
1247+
function.getBody().add(set);
1248+
1249+
return function;
1250+
}
1251+
11331252
private WasmFunction getArrayCopyObjectFunction() {
11341253
if (arrayCopyObjectFunction == null) {
11351254
arrayCopyObjectFunction = createArrayCopyFunction(OBJECT_TYPE);
@@ -1180,6 +1299,14 @@ private WasmFunctionType getArrayGetType() {
11801299
return arrayGetType;
11811300
}
11821301

1302+
private WasmFunctionType getArraySetType() {
1303+
if (arraySetType == null) {
1304+
arraySetType = functionTypes.of(null, standardClasses.classClass().getType(),
1305+
standardClasses.objectClass().getType(), WasmType.INT32, standardClasses.objectClass().getType());
1306+
}
1307+
return arraySetType;
1308+
}
1309+
11831310
private WasmFunctionType getArrayLengthType() {
11841311
if (arrayLengthType == null) {
11851312
arrayLengthType = functionTypes.of(WasmType.INT32, standardClasses.classClass().getType(),
@@ -1345,6 +1472,12 @@ public WasmStructure getArrayVirtualTableStructure() {
13451472
fields.add(new WasmField(arrayGetType.getReference().asStorage(),
13461473
names.structureField("@arrayGet")));
13471474
}
1475+
if (metadataRequirements.hasArraySet()) {
1476+
arraySetOffset = fields.size();
1477+
var arraySetType = getArraySetType();
1478+
fields.add(new WasmField(arraySetType.getReference().asStorage(),
1479+
names.structureField("@arraySet")));
1480+
}
13481481
if (metadataRequirements.hasArrayCopy()) {
13491482
arrayCopyOffset = fields.size();
13501483
var arrayCopyType = getArrayCopyType();
@@ -1371,6 +1504,12 @@ public int getArrayGetOffset() {
13711504
return arrayGetOffset;
13721505
}
13731506

1507+
@Override
1508+
public int getArraySetOffset() {
1509+
initStructures();
1510+
return arraySetOffset;
1511+
}
1512+
13741513
@Override
13751514
public int getArrayCopyOffset() {
13761515
initStructures();
@@ -1993,6 +2132,11 @@ private void fillArrayVirtualTableMethods(List<WasmExpression> target, WasmLocal
19932132
target.add(new WasmStructSet(structure, new WasmGetLocal(vt), arrayGetOffset,
19942133
new WasmFunctionReference(getFunction)));
19952134
}
2135+
if (arraySetOffset >= 0) {
2136+
var setFunction = getArraySetObjectFunction();
2137+
target.add(new WasmStructSet(structure, new WasmGetLocal(vt), arraySetOffset,
2138+
new WasmFunctionReference(setFunction)));
2139+
}
19962140
if (arrayCopyOffset >= 0) {
19972141
var copyFunction = getArrayCopyObjectFunction();
19982142
target.add(new WasmStructSet(structure, new WasmGetLocal(vt), arrayCopyOffset,

core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfoProvider.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ public interface WasmGCClassInfoProvider {
9393

9494
int getArrayGetOffset();
9595

96+
int getArraySetOffset();
97+
9698
int getArrayLengthOffset();
9799

98100
int getArrayCopyOffset();

core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ArrayIntrinsic.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext co
3333
return arrayLength(invocation, context);
3434
case "getImpl":
3535
return arrayGet(invocation, context);
36+
case "setImpl":
37+
return arraySet(invocation, context);
3638
default:
3739
throw new IllegalArgumentException("Unknown method: " + invocation.getMethod());
3840
}
@@ -46,6 +48,10 @@ private WasmExpression arrayGet(InvocationExpr invocation, WasmGCIntrinsicContex
4648
return arrayVirtualCall(invocation, context, context.classInfoProvider().getArrayGetOffset());
4749
}
4850

51+
private WasmExpression arraySet(InvocationExpr invocation, WasmGCIntrinsicContext context) {
52+
return arrayVirtualCall(invocation, context, context.classInfoProvider().getArraySetOffset());
53+
}
54+
4955
private WasmExpression arrayVirtualCall(InvocationExpr invocation, WasmGCIntrinsicContext context,
5056
int offset) {
5157
var objectStruct = context.classInfoProvider().getClassInfo("java.lang.Object").getStructure();

core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsics.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ private void fillArray() {
171171
var intrinsic = new ArrayIntrinsic();
172172
add(new MethodReference(Array.class, "getLength", Object.class, int.class), intrinsic);
173173
add(new MethodReference(Array.class, "getImpl", Object.class, int.class, Object.class), intrinsic);
174+
add(new MethodReference(Array.class, "setImpl", Object.class, int.class, Object.class, void.class), intrinsic);
174175
}
175176

176177
private void fillString() {

core/src/main/java/org/teavm/model/analysis/ClassMetadataRequirements.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,16 @@ public class ClassMetadataRequirements {
4444
"newInstance", Class.class, int.class, Object.class);
4545
private static final MethodReference ARRAY_GET = new MethodReference(Array.class,
4646
"get", Object.class, int.class, Object.class);
47+
private static final MethodReference ARRAY_SET = new MethodReference(Array.class,
48+
"set", Object.class, int.class, Object.class, void.class);
4749
private static final MethodReference ARRAY_LENGTH = new MethodReference(Array.class,
4850
"getLength", Object.class, int.class);
4951
private static final MethodReference ARRAY_COPY = new MethodReference(System.class,
5052
"arraycopy", Object.class, int.class, Object.class, int.class, int.class, void.class);
5153
private static final ClassInfo EMPTY_INFO = new ClassInfo();
5254
private Map<ValueType, ClassInfo> requirements = new HashMap<>();
5355
private boolean hasArrayGet;
56+
private boolean hasArraySet;
5457
private boolean hasArrayLength;
5558
private boolean hasArrayCopy;
5659
private boolean hasEnumConstants;
@@ -141,6 +144,15 @@ public ClassMetadataRequirements(DependencyInfo dependencyInfo) {
141144
}
142145
}
143146

147+
var arraySet = dependencyInfo.getMethod(ARRAY_SET);
148+
if (arraySet != null) {
149+
hasArraySet = arraySet.isUsed();
150+
var classNames = arraySet.getVariable(1).getTypes();
151+
for (var className : classNames) {
152+
requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).arraySet = true;
153+
}
154+
}
155+
144156
var arrayLength = dependencyInfo.getMethod(ARRAY_LENGTH);
145157
if (arrayLength != null) {
146158
hasArrayLength = arrayLength.isUsed();
@@ -252,6 +264,10 @@ public boolean hasArrayGet() {
252264
return hasArrayGet;
253265
}
254266

267+
public boolean hasArraySet() {
268+
return hasArraySet;
269+
}
270+
255271
public boolean hasArrayLength() {
256272
return hasArrayLength;
257273
}
@@ -342,6 +358,7 @@ static class ClassInfo implements Info {
342358
boolean newArray;
343359
boolean arrayLength;
344360
boolean arrayGet;
361+
boolean arraySet;
345362
boolean arrayCopy;
346363
boolean cloneMethod;
347364
boolean enumConstants;
@@ -399,6 +416,11 @@ public boolean arrayGet() {
399416
return arrayGet;
400417
}
401418

419+
@Override
420+
public boolean arraySet() {
421+
return arraySet;
422+
}
423+
402424
@Override
403425
public boolean cloneMethod() {
404426
return cloneMethod;
@@ -444,6 +466,8 @@ public interface Info {
444466

445467
boolean arrayGet();
446468

469+
boolean arraySet();
470+
447471
boolean arrayCopy();
448472

449473
boolean cloneMethod();

tests/src/test/java/org/teavm/classlib/java/lang/reflect/ArrayTest.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import java.lang.reflect.Array;
2121
import java.util.ArrayList;
2222
import java.util.List;
23-
import org.junit.Ignore;
2423
import org.junit.Test;
2524
import org.junit.runner.RunWith;
2625
import org.teavm.junit.EachTestCompiledSeparately;
@@ -46,7 +45,7 @@ public void createsNewPrimitiveInstance() {
4645
}
4746

4847
@Test
49-
@Ignore
48+
@SkipPlatform({TestPlatform.WEBASSEMBLY, TestPlatform.WASI, TestPlatform.C})
5049
public void getWorks() {
5150
var intArray = new int[] { 23, 42 };
5251
var stringArray = new String[] { "asd", "qwe" };
@@ -64,7 +63,7 @@ private void copyToList(List<Object> target, Object array) {
6463
}
6564

6665
@Test
67-
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY, TestPlatform.WASI, TestPlatform.WEBASSEMBLY_GC})
66+
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY, TestPlatform.WASI})
6867
public void setWorks() {
6968
Object array = Array.newInstance(String.class, 2);
7069
Array.set(array, 0, "foo");
@@ -73,7 +72,7 @@ public void setWorks() {
7372
}
7473

7574
@Test
76-
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY, TestPlatform.WASI, TestPlatform.WEBASSEMBLY_GC})
75+
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY, TestPlatform.WASI})
7776
public void setPrimitiveWorks() {
7877
Object array = Array.newInstance(int.class, 2);
7978
Array.set(array, 0, 23);

0 commit comments

Comments
 (0)