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.setConsequent(firstAssignment.getRightValue());
conditionalExpr.setAlternative(secondAssignment.getRightValue()); conditionalExpr.setAlternative(secondAssignment.getRightValue());
conditionalExpr.setLocation(statement.getCondition().getLocation()); conditionalExpr.setLocation(statement.getCondition().getLocation());
conditionalExpr.setVariableIndex(firstAssignment.getRightValue().getVariableIndex());
AssignmentStatement assignment = new AssignmentStatement(); AssignmentStatement assignment = new AssignmentStatement();
assignment.setLocation(conditionalExpr.getLocation()); assignment.setLocation(conditionalExpr.getLocation());
VariableExpr lhs = new VariableExpr(); VariableExpr lhs = new VariableExpr();

View File

@ -538,12 +538,16 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
var elseType = typeInference.getResult(); var elseType = typeInference.getResult();
conditional.getElseBlock().setType(elseType); conditional.getElseBlock().setType(elseType);
assert thenType == elseType; conditional.setType(condBlockType(thenType, elseType, expr));
conditional.setType(thenType);
result = conditional; result = conditional;
} }
protected WasmType condBlockType(WasmType thenType, WasmType elseType, ConditionalExpr conditional) {
assert thenType == elseType;
return thenType;
}
@Override @Override
public void visit(SequentialStatement statement) { public void visit(SequentialStatement statement) {
for (var part : statement.getSequence()) { 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.WasmStructure;
import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmArrayCopy; 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.WasmArrayLength;
import org.teavm.backend.wasm.model.expression.WasmArrayNewDefault; import org.teavm.backend.wasm.model.expression.WasmArrayNewDefault;
import org.teavm.backend.wasm.model.expression.WasmCall; 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.FieldReference;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.PrimitiveType;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
import org.teavm.model.analysis.ClassInitializerInfo; import org.teavm.model.analysis.ClassInitializerInfo;
import org.teavm.model.analysis.ClassMetadataRequirements; import org.teavm.model.analysis.ClassMetadataRequirements;
@ -85,6 +87,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
ValueType.parse(Class.class)); ValueType.parse(Class.class));
private static final FieldReference FAKE_CLASS_FIELD = new FieldReference(Object.class.getName(), "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 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 final WasmModule module;
private ClassReaderSource classSource; private ClassReaderSource classSource;
@ -119,6 +122,13 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
private int classNewArrayOffset; private int classNewArrayOffset;
private int classSupertypeFunctionOffset; private int classSupertypeFunctionOffset;
private int virtualTableFieldOffset; 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, public WasmGCClassGenerator(WasmModule module, ClassReaderSource classSource,
WasmFunctionTypes functionTypes, TagRegistry tagRegistry, WasmFunctionTypes functionTypes, TagRegistry tagRegistry,
@ -249,10 +259,18 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
classInfo.structure = standardClasses.objectClass().structure; classInfo.structure = standardClasses.objectClass().structure;
} else { } else {
var finalClassInfo = classInfo; 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, classInfo.structure = new WasmStructure(name != null ? names.forClass(name) : null,
fields -> fillFields(finalClassInfo, fields, type)); fields -> fillFields(finalClassInfo, fields, type));
module.types.add(classInfo.structure); module.types.add(classInfo.structure);
} }
}
if (name != null) { if (name != null) {
if (!isInterface) { if (!isInterface) {
virtualTable = virtualTables.lookup(name); virtualTable = virtualTables.lookup(name);
@ -272,7 +290,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
if (type instanceof ValueType.Object) { if (type instanceof ValueType.Object) {
classStructure = initRegularClassStructure(((ValueType.Object) type).getClassName()); classStructure = initRegularClassStructure(((ValueType.Object) type).getClassName());
} else { } else {
classStructure = standardClasses.objectClass().getVirtualTableStructure(); classStructure = getArrayVirtualTableStructure();
} }
} else { } else {
classStructure = standardClasses.classClass().getStructure(); 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) { WasmStructure objectStructure) {
var virtualTable = virtualTables.lookup("java.lang.Object"); var virtualTable = virtualTables.lookup("java.lang.Object");
var structure = standardClasses.objectClass().getVirtualTableStructure(); var structure = getArrayVirtualTableStructure();
for (var entry : virtualTable.getEntries()) { for (var entry : virtualTable.getEntries()) {
if (entry.getMethod().getName().equals("clone")) { if (entry.getMethod().getName().equals("clone")) {
@ -447,6 +465,157 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
fillVirtualTableEntry(target, global, structure, virtualTable, entry); 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, 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) { private void initArrayClass(WasmGCClassInfo classInfo, ValueType.Array type) {
classInfo.initializer = target -> { classInfo.initializer = target -> {
var itemTypeInfo = getClassInfo(type.getItemType()); var itemTypeInfo = getClassInfo(type.getItemType());
@ -564,7 +767,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
new WasmGetGlobal(classInfo.pointer), new WasmGetGlobal(classInfo.pointer),
new WasmGetGlobal(itemTypeInfo.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(); throw new IllegalArgumentException();
} }
} else { } else {
wasmElementType = WasmType.Reference.STRUCT.asStorage(); wasmElementType = standardClasses.objectClass().getType().asStorage();
} }
var wasmArray = new WasmArray(null, wasmElementType); var wasmArray = new WasmArray(null, wasmElementType);
module.types.add(wasmArray); module.types.add(wasmArray);
classInfo.structure.getFields().add(new WasmField(wasmArray.getReference().asStorage(), "data")); classInfo.structure.getFields().add(new WasmField(wasmArray.getReference().asStorage(), "data"));
classInfo.array = wasmArray;
} }
private WasmFunction getCreatePrimitiveClassFunction() { private WasmFunction getCreatePrimitiveClassFunction() {

View File

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

View File

@ -16,6 +16,7 @@
package org.teavm.backend.wasm.generate.gc.classes; package org.teavm.backend.wasm.generate.gc.classes;
import org.teavm.backend.wasm.model.WasmGlobal; import org.teavm.backend.wasm.model.WasmGlobal;
import org.teavm.backend.wasm.model.WasmStructure;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
@ -27,6 +28,8 @@ public interface WasmGCClassInfoProvider {
WasmGCClassInfo getClassInfo(ValueType type); WasmGCClassInfo getClassInfo(ValueType type);
WasmStructure getArrayVirtualTableStructure();
int getFieldIndex(FieldReference fieldRef); int getFieldIndex(FieldReference fieldRef);
WasmGlobal getStaticFieldLocation(FieldReference fieldRef); WasmGlobal getStaticFieldLocation(FieldReference fieldRef);
@ -41,6 +44,10 @@ public interface WasmGCClassInfoProvider {
int getClassNameOffset(); int getClassNameOffset();
int getArrayGetOffset();
int getArrayLengthOffset();
default WasmGCClassInfo getClassInfo(String name) { default WasmGCClassInfo getClassInfo(String name) {
return getClassInfo(ValueType.object(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 java.util.List;
import org.teavm.ast.ArrayType; import org.teavm.ast.ArrayType;
import org.teavm.ast.BinaryExpr; import org.teavm.ast.BinaryExpr;
import org.teavm.ast.ConditionalExpr;
import org.teavm.ast.Expr; import org.teavm.ast.Expr;
import org.teavm.ast.InvocationExpr; import org.teavm.ast.InvocationExpr;
import org.teavm.ast.InvocationType; 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.BaseWasmFunctionRepository;
import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.gc.PreciseTypeInference; 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.TemporaryVariablePool;
import org.teavm.backend.wasm.generate.common.methods.BaseWasmGenerationVisitor; import org.teavm.backend.wasm.generate.common.methods.BaseWasmGenerationVisitor;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider; 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 { private class SimpleCallSite extends CallSiteIdentifier {
@Override @Override
public void generateRegister(List<WasmExpression> consumer, TextLocation location) { public void generateRegister(List<WasmExpression> consumer, TextLocation location) {
@ -593,5 +613,10 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
public TemporaryVariablePool tempVars() { public TemporaryVariablePool tempVars() {
return 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.BaseWasmFunctionRepository;
import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.gc.PreciseTypeInference; 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.TemporaryVariablePool;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider; import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper; import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper;
@ -44,4 +45,6 @@ public interface WasmGCIntrinsicContext {
WasmGCClassInfoProvider classInfoProvider(); WasmGCClassInfoProvider classInfoProvider();
TemporaryVariablePool tempVars(); TemporaryVariablePool tempVars();
ExpressionCache exprCache();
} }

View File

@ -15,6 +15,7 @@
*/ */
package org.teavm.backend.wasm.intrinsics.gc; package org.teavm.backend.wasm.intrinsics.gc;
import java.lang.reflect.Array;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -33,6 +34,7 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider {
fillClass(); fillClass();
fillSystem(); fillSystem();
fillLongAndInteger(); fillLongAndInteger();
fillArray();
} }
private void fillWasmRuntime() { private void fillWasmRuntime() {
@ -91,6 +93,12 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider {
intrinsic); 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 @Override
public WasmGCIntrinsic get(MethodReference method) { public WasmGCIntrinsic get(MethodReference method) {
return intrinsics.get(method); return intrinsics.get(method);

View File

@ -37,8 +37,14 @@ public class ClassMetadataRequirements {
"getEnclosingClass", Class.class); "getEnclosingClass", Class.class);
private static final MethodReference NEW_ARRAY = new MethodReference(Array.class, private static final MethodReference NEW_ARRAY = new MethodReference(Array.class,
"newInstance", Class.class, int.class, Object.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 static final ClassInfo EMPTY_INFO = new ClassInfo();
private Map<ValueType, ClassInfo> requirements = new HashMap<>(); private Map<ValueType, ClassInfo> requirements = new HashMap<>();
private boolean hasArrayGet;
private boolean hasArrayLength;
public ClassMetadataRequirements(DependencyInfo dependencyInfo) { public ClassMetadataRequirements(DependencyInfo dependencyInfo) {
MethodDependencyInfo getNameMethod = dependencyInfo.getMethod(GET_NAME_METHOD); MethodDependencyInfo getNameMethod = dependencyInfo.getMethod(GET_NAME_METHOD);
@ -96,6 +102,24 @@ public class ClassMetadataRequirements {
requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).newArray = true; 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) { public Info getInfo(String className) {
@ -110,6 +134,14 @@ public class ClassMetadataRequirements {
return result; return result;
} }
public boolean hasArrayGet() {
return hasArrayGet;
}
public boolean hasArrayLength() {
return hasArrayLength;
}
private void addClassesRequiringName(Map<ValueType, ClassInfo> target, String[] source) { private void addClassesRequiringName(Map<ValueType, ClassInfo> target, String[] source) {
for (String typeName : source) { for (String typeName : source) {
target.computeIfAbsent(decodeType(typeName), k -> new ClassInfo()).name = true; target.computeIfAbsent(decodeType(typeName), k -> new ClassInfo()).name = true;
@ -134,6 +166,8 @@ public class ClassMetadataRequirements {
boolean superclass; boolean superclass;
boolean isAssignable; boolean isAssignable;
boolean newArray; boolean newArray;
boolean arrayLength;
boolean arrayGet;
@Override @Override
public boolean name() { public boolean name() {
@ -169,6 +203,16 @@ public class ClassMetadataRequirements {
public boolean newArray() { public boolean newArray() {
return newArray; return newArray;
} }
@Override
public boolean arrayLength() {
return arrayLength;
}
@Override
public boolean arrayGet() {
return arrayGet;
}
} }
public interface Info { public interface Info {
@ -185,5 +229,9 @@ public class ClassMetadataRequirements {
boolean isAssignable(); boolean isAssignable();
boolean newArray(); 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.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.teavm.junit.EachTestCompiledSeparately; import org.teavm.junit.EachTestCompiledSeparately;
@ -43,7 +45,24 @@ public class ArrayTest {
} }
@Test @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() { public void setWorks() {
Object array = Array.newInstance(String.class, 2); Object array = Array.newInstance(String.class, 2);
Array.set(array, 0, "foo"); Array.set(array, 0, "foo");
@ -52,7 +71,7 @@ public class ArrayTest {
} }
@Test @Test
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY, TestPlatform.WASI}) @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY, TestPlatform.WASI, TestPlatform.WEBASSEMBLY_GC})
public void setPrimitiveWorks() { public void setPrimitiveWorks() {
Object array = Array.newInstance(int.class, 2); Object array = Array.newInstance(int.class, 2);
Array.set(array, 0, 23); Array.set(array, 0, 23);