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.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();
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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.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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue
Block a user