wasm gc: implement Array.getLength and Array.get

This commit is contained in:
Alexey Andreev 2024-08-25 21:01:31 +02:00
parent ba08fb395c
commit ebac13a363
11 changed files with 407 additions and 16 deletions

View File

@ -1097,6 +1097,7 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
conditionalExpr.setConsequent(firstAssignment.getRightValue());
conditionalExpr.setAlternative(secondAssignment.getRightValue());
conditionalExpr.setLocation(statement.getCondition().getLocation());
conditionalExpr.setVariableIndex(firstAssignment.getRightValue().getVariableIndex());
AssignmentStatement assignment = new AssignmentStatement();
assignment.setLocation(conditionalExpr.getLocation());
VariableExpr lhs = new VariableExpr();

View File

@ -538,12 +538,16 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
var elseType = typeInference.getResult();
conditional.getElseBlock().setType(elseType);
assert thenType == elseType;
conditional.setType(thenType);
conditional.setType(condBlockType(thenType, elseType, expr));
result = conditional;
}
protected WasmType condBlockType(WasmType thenType, WasmType elseType, ConditionalExpr conditional) {
assert thenType == elseType;
return thenType;
}
@Override
public void visit(SequentialStatement statement) {
for (var part : statement.getSequence()) {

View File

@ -44,6 +44,7 @@ import org.teavm.backend.wasm.model.WasmStorageType;
import org.teavm.backend.wasm.model.WasmStructure;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmArrayCopy;
import org.teavm.backend.wasm.model.expression.WasmArrayGet;
import org.teavm.backend.wasm.model.expression.WasmArrayLength;
import org.teavm.backend.wasm.model.expression.WasmArrayNewDefault;
import org.teavm.backend.wasm.model.expression.WasmCall;
@ -71,6 +72,7 @@ import org.teavm.model.ElementModifier;
import org.teavm.model.FieldReference;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference;
import org.teavm.model.PrimitiveType;
import org.teavm.model.ValueType;
import org.teavm.model.analysis.ClassInitializerInfo;
import org.teavm.model.analysis.ClassMetadataRequirements;
@ -85,6 +87,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
ValueType.parse(Class.class));
private static final FieldReference FAKE_CLASS_FIELD = new FieldReference(Object.class.getName(), "class");
private static final FieldReference FAKE_MONITOR_FIELD = new FieldReference(Object.class.getName(), "monitor");
private static final ValueType OBJECT_TYPE = ValueType.parse(Object.class);
private final WasmModule module;
private ClassReaderSource classSource;
@ -119,6 +122,13 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
private int classNewArrayOffset;
private int classSupertypeFunctionOffset;
private int virtualTableFieldOffset;
private int arrayLengthOffset = -1;
private int arrayGetOffset = -1;
private WasmStructure arrayVirtualTableStruct;
private WasmFunction arrayGetObjectFunction;
private WasmFunction arrayLengthObjectFunction;
private WasmFunctionType arrayGetType;
private WasmFunctionType arrayLengthType;
public WasmGCClassGenerator(WasmModule module, ClassReaderSource classSource,
WasmFunctionTypes functionTypes, TagRegistry tagRegistry,
@ -249,10 +259,18 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
classInfo.structure = standardClasses.objectClass().structure;
} else {
var finalClassInfo = classInfo;
if (type instanceof ValueType.Array) {
var itemType = ((ValueType.Array) type).getItemType();
if (!(itemType instanceof ValueType.Primitive) && !itemType.equals(OBJECT_TYPE)) {
classInfo.structure = getClassInfo(ValueType.arrayOf(OBJECT_TYPE)).structure;
}
}
if (classInfo.structure == null) {
classInfo.structure = new WasmStructure(name != null ? names.forClass(name) : null,
fields -> fillFields(finalClassInfo, fields, type));
module.types.add(classInfo.structure);
}
}
if (name != null) {
if (!isInterface) {
virtualTable = virtualTables.lookup(name);
@ -272,7 +290,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
if (type instanceof ValueType.Object) {
classStructure = initRegularClassStructure(((ValueType.Object) type).getClassName());
} else {
classStructure = standardClasses.objectClass().getVirtualTableStructure();
classStructure = getArrayVirtualTableStructure();
}
} else {
classStructure = standardClasses.classClass().getStructure();
@ -431,10 +449,10 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
}
}
private void fillArrayVirtualTableMethods(List<WasmExpression> target, WasmGlobal global,
private void fillArrayVirtualTableMethods(ValueType type, List<WasmExpression> target, WasmGlobal global,
WasmStructure objectStructure) {
var virtualTable = virtualTables.lookup("java.lang.Object");
var structure = standardClasses.objectClass().getVirtualTableStructure();
var structure = getArrayVirtualTableStructure();
for (var entry : virtualTable.getEntries()) {
if (entry.getMethod().getName().equals("clone")) {
@ -447,6 +465,157 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
fillVirtualTableEntry(target, global, structure, virtualTable, entry);
}
}
var itemType = ((ValueType.Array) type).getItemType();
var info = metadataRequirements.getInfo(type);
if (info.arrayLength()) {
var lengthFunction = getArrayLengthFunction(objectStructure);
target.add(new WasmStructSet(structure, new WasmGetGlobal(global), arrayLengthOffset,
new WasmFunctionReference(lengthFunction)));
}
if (info.arrayGet()) {
var getFunction = getArrayGetFunction(itemType);
target.add(new WasmStructSet(structure, new WasmGetGlobal(global), arrayGetOffset,
new WasmFunctionReference(getFunction)));
}
}
private WasmFunction getArrayLengthFunction(WasmStructure objectStructure) {
var arrayTypeRef = (WasmType.CompositeReference) objectStructure.getFields().get(ARRAY_DATA_FIELD_OFFSET)
.getUnpackedType();
var arrayType = (WasmArray) arrayTypeRef.composite;
var elementType = arrayType.getElementType().asUnpackedType();
if (elementType instanceof WasmType.Reference) {
if (arrayLengthObjectFunction == null) {
arrayLengthObjectFunction = getArrayLengthFunction(objectStructure, arrayType);
}
return arrayLengthObjectFunction;
}
return getArrayLengthFunction(objectStructure, arrayType);
}
private WasmFunction getArrayLengthFunction(WasmStructure objectStructure, WasmArray arrayType) {
var function = new WasmFunction(functionTypes.of(WasmType.INT32, standardClasses.objectClass().getType()));
function.setReferenced(true);
module.functions.add(function);
var objectLocal = new WasmLocal(standardClasses.objectClass().getType());
function.add(objectLocal);
var castObject = new WasmCast(new WasmGetLocal(objectLocal), objectStructure.getReference());
var arrayField = new WasmStructGet(objectStructure, castObject, ARRAY_DATA_FIELD_OFFSET);
var result = new WasmArrayLength(arrayField);
function.getBody().add(new WasmReturn(result));
return function;
}
private WasmFunction getArrayGetFunction(ValueType itemType) {
if (itemType instanceof ValueType.Primitive) {
return generateArrayGetPrimitiveFunction(((ValueType.Primitive) itemType).getKind());
}
return getArrayGetObjectFunction();
}
private WasmFunction getArrayGetObjectFunction() {
if (arrayGetObjectFunction == null) {
arrayGetObjectFunction = new WasmFunction(getArrayGetType());
module.functions.add(arrayGetObjectFunction);
arrayGetObjectFunction.setReferenced(true);
var arrayStruct = getClassInfo(ValueType.arrayOf(OBJECT_TYPE)).structure;
var arrayDataTypeRef = (WasmType.CompositeReference) arrayStruct.getFields()
.get(ARRAY_DATA_FIELD_OFFSET).getUnpackedType();
var arrayDataType = (WasmArray) arrayDataTypeRef.composite;
var objectLocal = new WasmLocal(standardClasses.objectClass().getType());
var indexLocal = new WasmLocal(WasmType.INT32);
arrayGetObjectFunction.add(objectLocal);
arrayGetObjectFunction.add(indexLocal);
var array = new WasmCast(new WasmGetLocal(objectLocal), arrayStruct.getReference());
var arrayData = new WasmStructGet(arrayStruct, array, ARRAY_DATA_FIELD_OFFSET);
var result = new WasmArrayGet(arrayDataType, arrayData, new WasmGetLocal(indexLocal));
arrayGetObjectFunction.getBody().add(new WasmReturn(result));
}
return arrayGetObjectFunction;
}
private WasmFunction generateArrayGetPrimitiveFunction(PrimitiveType type) {
var function = new WasmFunction(getArrayGetType());
module.functions.add(function);
function.setReferenced(true);
var arrayStruct = getClassInfo(ValueType.arrayOf(ValueType.primitive(type))).structure;
var arrayDataTypeRef = (WasmType.CompositeReference) arrayStruct.getFields()
.get(ARRAY_DATA_FIELD_OFFSET).getUnpackedType();
var arrayDataType = (WasmArray) arrayDataTypeRef.composite;
var objectLocal = new WasmLocal(standardClasses.objectClass().getType());
var indexLocal = new WasmLocal(WasmType.INT32);
function.add(objectLocal);
function.add(indexLocal);
var array = new WasmCast(new WasmGetLocal(objectLocal), arrayStruct.getReference());
var arrayData = new WasmStructGet(arrayStruct, array, ARRAY_DATA_FIELD_OFFSET);
var result = new WasmArrayGet(arrayDataType, arrayData, new WasmGetLocal(indexLocal));
Class<?> primitiveType;
Class<?> wrapperType;
switch (type) {
case BOOLEAN:
primitiveType = boolean.class;
wrapperType = Boolean.class;
break;
case BYTE:
primitiveType = byte.class;
wrapperType = Byte.class;
break;
case SHORT:
primitiveType = short.class;
wrapperType = Short.class;
break;
case CHARACTER:
primitiveType = char.class;
wrapperType = Character.class;
break;
case INTEGER:
primitiveType = int.class;
wrapperType = Integer.class;
break;
case LONG:
primitiveType = long.class;
wrapperType = Long.class;
break;
case FLOAT:
primitiveType = float.class;
wrapperType = Float.class;
break;
case DOUBLE:
primitiveType = double.class;
wrapperType = Double.class;
break;
default:
throw new IllegalArgumentException();
}
var method = new MethodReference(wrapperType, "valueOf", primitiveType, wrapperType);
var wrapFunction = functionProvider.forStaticMethod(method);
var castResult = new WasmCall(wrapFunction, result);
function.getBody().add(new WasmReturn(castResult));
return function;
}
private WasmFunctionType getArrayGetType() {
if (arrayGetType == null) {
arrayGetType = functionTypes.of(standardClasses.objectClass().getType(),
standardClasses.objectClass().getType(), WasmType.INT32);
}
return arrayGetType;
}
private WasmFunctionType getArrayLengthType() {
if (arrayLengthType == null) {
arrayLengthType = functionTypes.of(WasmType.INT32, standardClasses.objectClass().getType());
}
return arrayLengthType;
}
private void fillVirtualTableEntry(List<WasmExpression> target, WasmGlobal global,
@ -556,6 +725,40 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
}
}
@Override
public WasmStructure getArrayVirtualTableStructure() {
if (arrayVirtualTableStruct == null) {
arrayVirtualTableStruct = new WasmStructure(null);
arrayVirtualTableStruct.setSupertype(standardClasses.objectClass().getVirtualTableStructure());
module.types.add(arrayVirtualTableStruct);
addSystemFields(arrayVirtualTableStruct.getFields());
fillSimpleClassFields(arrayVirtualTableStruct.getFields(), "java.lang.Class");
addVirtualTableFields(arrayVirtualTableStruct, virtualTables.lookup("java.lang.Object"));
if (metadataRequirements.hasArrayLength()) {
arrayLengthOffset = arrayVirtualTableStruct.getFields().size();
var arrayLengthType = getArrayLengthType();
arrayVirtualTableStruct.getFields().add(new WasmField(arrayLengthType.getReference().asStorage()));
}
if (metadataRequirements.hasArrayGet()) {
arrayGetOffset = arrayVirtualTableStruct.getFields().size();
var arrayGetType = getArrayGetType();
arrayVirtualTableStruct.getFields().add(new WasmField(arrayGetType.getReference().asStorage()));
}
}
return arrayVirtualTableStruct;
}
@Override
public int getArrayLengthOffset() {
return arrayLengthOffset;
}
@Override
public int getArrayGetOffset() {
return arrayGetOffset;
}
private void initArrayClass(WasmGCClassInfo classInfo, ValueType.Array type) {
classInfo.initializer = target -> {
var itemTypeInfo = getClassInfo(type.getItemType());
@ -564,7 +767,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
new WasmGetGlobal(classInfo.pointer),
new WasmGetGlobal(itemTypeInfo.pointer)
));
fillArrayVirtualTableMethods(target, classInfo.pointer, classInfo.structure);
fillArrayVirtualTableMethods(classInfo.getValueType(), target, classInfo.pointer, classInfo.structure);
};
}
@ -744,12 +947,11 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
throw new IllegalArgumentException();
}
} else {
wasmElementType = WasmType.Reference.STRUCT.asStorage();
wasmElementType = standardClasses.objectClass().getType().asStorage();
}
var wasmArray = new WasmArray(null, wasmElementType);
module.types.add(wasmArray);
classInfo.structure.getFields().add(new WasmField(wasmArray.getReference().asStorage(), "data"));
classInfo.array = wasmArray;
}
private WasmFunction getCreatePrimitiveClassFunction() {

View File

@ -27,7 +27,6 @@ import org.teavm.model.ValueType;
public class WasmGCClassInfo {
private ValueType valueType;
WasmStructure structure;
WasmArray array;
boolean hasOwnVirtualTable;
WasmStructure virtualTableStructure;
WasmGlobal pointer;
@ -47,8 +46,9 @@ public class WasmGCClassInfo {
}
public WasmArray getArray() {
structure.init();
return array;
var field = structure.getFields().get(WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET);
var type = (WasmType.CompositeReference) field.getUnpackedType();
return (WasmArray) type.composite;
}
public WasmStructure getVirtualTableStructure() {

View File

@ -16,6 +16,7 @@
package org.teavm.backend.wasm.generate.gc.classes;
import org.teavm.backend.wasm.model.WasmGlobal;
import org.teavm.backend.wasm.model.WasmStructure;
import org.teavm.model.FieldReference;
import org.teavm.model.ValueType;
@ -27,6 +28,8 @@ public interface WasmGCClassInfoProvider {
WasmGCClassInfo getClassInfo(ValueType type);
WasmStructure getArrayVirtualTableStructure();
int getFieldIndex(FieldReference fieldRef);
WasmGlobal getStaticFieldLocation(FieldReference fieldRef);
@ -41,6 +44,10 @@ public interface WasmGCClassInfoProvider {
int getClassNameOffset();
int getArrayGetOffset();
int getArrayLengthOffset();
default WasmGCClassInfo getClassInfo(String name) {
return getClassInfo(ValueType.object(name));
}

View File

@ -18,6 +18,7 @@ package org.teavm.backend.wasm.generate.gc.methods;
import java.util.List;
import org.teavm.ast.ArrayType;
import org.teavm.ast.BinaryExpr;
import org.teavm.ast.ConditionalExpr;
import org.teavm.ast.Expr;
import org.teavm.ast.InvocationExpr;
import org.teavm.ast.InvocationType;
@ -26,6 +27,7 @@ import org.teavm.ast.SubscriptExpr;
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.gc.PreciseTypeInference;
import org.teavm.backend.wasm.generate.ExpressionCache;
import org.teavm.backend.wasm.generate.TemporaryVariablePool;
import org.teavm.backend.wasm.generate.common.methods.BaseWasmGenerationVisitor;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
@ -533,6 +535,24 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
}
}
@Override
protected WasmType condBlockType(WasmType thenType, WasmType elseType, ConditionalExpr conditional) {
if (conditional.getVariableIndex() >= 0) {
var javaType = types.typeOf(conditional.getVariableIndex());
if (javaType != null) {
return mapType(javaType.valueType);
}
}
if (conditional.getConsequent().getVariableIndex() >= 0
&& conditional.getConsequent().getVariableIndex() == conditional.getAlternative().getVariableIndex()) {
var javaType = types.typeOf(conditional.getConsequent().getVariableIndex());
if (javaType != null) {
return mapType(javaType.valueType);
}
}
return super.condBlockType(thenType, elseType, conditional);
}
private class SimpleCallSite extends CallSiteIdentifier {
@Override
public void generateRegister(List<WasmExpression> consumer, TextLocation location) {
@ -593,5 +613,10 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
public TemporaryVariablePool tempVars() {
return tempVars;
}
@Override
public ExpressionCache exprCache() {
return exprCache;
}
};
}

View File

@ -0,0 +1,74 @@
/*
* Copyright 2024 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.intrinsics.gc;
import org.teavm.ast.InvocationExpr;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
import org.teavm.backend.wasm.model.WasmFunctionType;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmBlock;
import org.teavm.backend.wasm.model.expression.WasmCallReference;
import org.teavm.backend.wasm.model.expression.WasmCast;
import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmStructGet;
public class ArrayIntrinsic implements WasmGCIntrinsic {
@Override
public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) {
switch (invocation.getMethod().getName()) {
case "getLength":
return arrayLength(invocation, context);
case "getImpl":
return arrayGet(invocation, context);
default:
throw new IllegalArgumentException("Unknown method: " + invocation.getMethod());
}
}
private WasmExpression arrayLength(InvocationExpr invocation, WasmGCIntrinsicContext context) {
return arrayVirtualCall(invocation, context, context.classInfoProvider().getArrayLengthOffset());
}
private WasmExpression arrayGet(InvocationExpr invocation, WasmGCIntrinsicContext context) {
return arrayVirtualCall(invocation, context, context.classInfoProvider().getArrayGetOffset());
}
private WasmExpression arrayVirtualCall(InvocationExpr invocation, WasmGCIntrinsicContext context,
int offset) {
var objectStruct = context.classInfoProvider().getClassInfo("java.lang.Object").getStructure();
var vtStruct = context.classInfoProvider().getArrayVirtualTableStructure();
var type = (WasmType.CompositeReference) vtStruct.getFields().get(offset).getUnpackedType();
var functionType = (WasmFunctionType) type.composite;
var block = new WasmBlock(false);
block.setType(functionType.getReturnType());
var originalObject = context.generate(invocation.getArguments().get(0));
var object = context.exprCache().create(originalObject, objectStruct.getReference(),
invocation.getLocation(), block.getBody());
var classRef = new WasmStructGet(objectStruct, object.expr(), WasmGCClassInfoProvider.CLASS_FIELD_OFFSET);
var vt = new WasmCast(classRef, vtStruct.getReference());
var function = new WasmStructGet(vtStruct, vt, offset);
var call = new WasmCallReference(function, functionType);
call.getArguments().add(object.expr());
for (var i = 1; i < invocation.getArguments().size(); ++i) {
call.getArguments().add(context.generate(invocation.getArguments().get(i)));
}
block.getBody().add(call);
object.release();
return block;
}
}

View File

@ -19,6 +19,7 @@ import org.teavm.ast.Expr;
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.gc.PreciseTypeInference;
import org.teavm.backend.wasm.generate.ExpressionCache;
import org.teavm.backend.wasm.generate.TemporaryVariablePool;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper;
@ -44,4 +45,6 @@ public interface WasmGCIntrinsicContext {
WasmGCClassInfoProvider classInfoProvider();
TemporaryVariablePool tempVars();
ExpressionCache exprCache();
}

View File

@ -15,6 +15,7 @@
*/
package org.teavm.backend.wasm.intrinsics.gc;
import java.lang.reflect.Array;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -33,6 +34,7 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider {
fillClass();
fillSystem();
fillLongAndInteger();
fillArray();
}
private void fillWasmRuntime() {
@ -91,6 +93,12 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider {
intrinsic);
}
private void fillArray() {
var intrinsic = new ArrayIntrinsic();
intrinsics.put(new MethodReference(Array.class, "getLength", Object.class, int.class), intrinsic);
intrinsics.put(new MethodReference(Array.class, "getImpl", Object.class, int.class, Object.class), intrinsic);
}
@Override
public WasmGCIntrinsic get(MethodReference method) {
return intrinsics.get(method);

View File

@ -37,8 +37,14 @@ public class ClassMetadataRequirements {
"getEnclosingClass", Class.class);
private static final MethodReference NEW_ARRAY = new MethodReference(Array.class,
"newInstance", Class.class, int.class, Object.class);
private static final MethodReference ARRAY_GET = new MethodReference(Array.class,
"get", Object.class, int.class, Object.class);
private static final MethodReference ARRAY_LENGTH = new MethodReference(Array.class,
"getLength", Object.class, int.class);
private static final ClassInfo EMPTY_INFO = new ClassInfo();
private Map<ValueType, ClassInfo> requirements = new HashMap<>();
private boolean hasArrayGet;
private boolean hasArrayLength;
public ClassMetadataRequirements(DependencyInfo dependencyInfo) {
MethodDependencyInfo getNameMethod = dependencyInfo.getMethod(GET_NAME_METHOD);
@ -96,6 +102,24 @@ public class ClassMetadataRequirements {
requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).newArray = true;
}
}
var arrayGet = dependencyInfo.getMethod(ARRAY_GET);
if (arrayGet != null) {
hasArrayGet = arrayGet.isUsed();
var classNames = arrayGet.getVariable(1).getTypes();
for (var className : classNames) {
requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).arrayGet = true;
}
}
var arrayLength = dependencyInfo.getMethod(ARRAY_LENGTH);
if (arrayLength != null) {
hasArrayLength = arrayLength.isUsed();
var classNames = arrayLength.getVariable(1).getTypes();
for (var className : classNames) {
requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).arrayLength = true;
}
}
}
public Info getInfo(String className) {
@ -110,6 +134,14 @@ public class ClassMetadataRequirements {
return result;
}
public boolean hasArrayGet() {
return hasArrayGet;
}
public boolean hasArrayLength() {
return hasArrayLength;
}
private void addClassesRequiringName(Map<ValueType, ClassInfo> target, String[] source) {
for (String typeName : source) {
target.computeIfAbsent(decodeType(typeName), k -> new ClassInfo()).name = true;
@ -134,6 +166,8 @@ public class ClassMetadataRequirements {
boolean superclass;
boolean isAssignable;
boolean newArray;
boolean arrayLength;
boolean arrayGet;
@Override
public boolean name() {
@ -169,6 +203,16 @@ public class ClassMetadataRequirements {
public boolean newArray() {
return newArray;
}
@Override
public boolean arrayLength() {
return arrayLength;
}
@Override
public boolean arrayGet() {
return arrayGet;
}
}
public interface Info {
@ -185,5 +229,9 @@ public class ClassMetadataRequirements {
boolean isAssignable();
boolean newArray();
boolean arrayLength();
boolean arrayGet();
}
}

View File

@ -18,6 +18,8 @@ package org.teavm.classlib.java.lang.reflect;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.teavm.junit.EachTestCompiledSeparately;
@ -43,7 +45,24 @@ public class ArrayTest {
}
@Test
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY, TestPlatform.WASI})
public void getWorks() {
var intArray = new int[] { 23, 42 };
var stringArray = new String[] { "asd", "qwe" };
var list = new ArrayList<>();
copyToList(list, intArray);
copyToList(list, stringArray);
assertEquals(List.of(23, 42, "asd", "qwe"), list);
}
private void copyToList(List<Object> target, Object array) {
var length = Array.getLength(array);
for (var i = 0; i < length; ++i) {
target.add(Array.get(array, i));
}
}
@Test
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY, TestPlatform.WASI, TestPlatform.WEBASSEMBLY_GC})
public void setWorks() {
Object array = Array.newInstance(String.class, 2);
Array.set(array, 0, "foo");
@ -52,7 +71,7 @@ public class ArrayTest {
}
@Test
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY, TestPlatform.WASI})
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY, TestPlatform.WASI, TestPlatform.WEBASSEMBLY_GC})
public void setPrimitiveWorks() {
Object array = Array.newInstance(int.class, 2);
Array.set(array, 0, 23);