mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
wasm gc: implement Array.getLength and Array.get
This commit is contained in:
parent
ba08fb395c
commit
ebac13a363
|
@ -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();
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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,9 +259,17 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||||
classInfo.structure = standardClasses.objectClass().structure;
|
classInfo.structure = standardClasses.objectClass().structure;
|
||||||
} else {
|
} else {
|
||||||
var finalClassInfo = classInfo;
|
var finalClassInfo = classInfo;
|
||||||
classInfo.structure = new WasmStructure(name != null ? names.forClass(name) : null,
|
if (type instanceof ValueType.Array) {
|
||||||
fields -> fillFields(finalClassInfo, fields, type));
|
var itemType = ((ValueType.Array) type).getItemType();
|
||||||
module.types.add(classInfo.structure);
|
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 (name != null) {
|
||||||
if (!isInterface) {
|
if (!isInterface) {
|
||||||
|
@ -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() {
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user