From 199032d48a88344dbac38ffd3843599dd1a83632 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Sat, 17 Aug 2024 20:48:03 +0200 Subject: [PATCH] wasm gc: support Array.newInstance --- .../classlib/java/lang/reflect/TArray.java | 9 ++- .../wasm/generate/WasmGenerationVisitor.java | 5 ++ .../methods/BaseWasmGenerationVisitor.java | 8 ++- .../gc/classes/WasmGCClassGenerator.java | 33 ++++++++-- .../gc/classes/WasmGCClassInfoProvider.java | 4 ++ .../WasmGCNewArrayFunctionGenerator.java | 64 +++++++++++++++++++ .../gc/methods/WasmGCGenerationVisitor.java | 5 ++ .../wasm/generators/gc/ArrayGenerator.java | 45 +++++++++++++ .../wasm/generators/gc/ClassGenerators.java | 13 ++++ .../generators/gc/WasmGCCustomGenerators.java | 9 +++ .../analysis/ClassMetadataRequirements.java | 19 ++++++ 11 files changed, 205 insertions(+), 9 deletions(-) create mode 100644 core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCNewArrayFunctionGenerator.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/generators/gc/ArrayGenerator.java diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TArray.java b/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TArray.java index 99197e583..a3154f146 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TArray.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TArray.java @@ -16,6 +16,7 @@ package org.teavm.classlib.java.lang.reflect; import org.teavm.backend.javascript.spi.GeneratedBy; +import org.teavm.classlib.PlatformDetector; import org.teavm.classlib.java.lang.TArrayIndexOutOfBoundsException; import org.teavm.classlib.java.lang.TClass; import org.teavm.classlib.java.lang.TIllegalArgumentException; @@ -59,9 +60,15 @@ public final class TArray extends TObject { if (length < 0) { throw new TNegativeArraySizeException(); } - return newInstanceImpl(((TClass) (Object) componentType).getPlatformClass(), length); + if (PlatformDetector.isWebAssemblyGC()) { + return newInstanceImpl(componentType, length); + } else { + return newInstanceImpl(((TClass) (Object) componentType).getPlatformClass(), length); + } } + private static native TObject newInstanceImpl(Class componentType, int length); + @GeneratedBy(ArrayNativeGenerator.class) @DelegateTo("newInstanceLowLevel") @NoSideEffects diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java index 04ee57d9a..90a2eaa0d 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java @@ -304,6 +304,11 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor { return new WasmInt32Constant(0); } + @Override + protected WasmExpression nullLiteral(WasmType type) { + return new WasmInt32Constant(0); + } + @Override protected WasmExpression genIsNull(WasmExpression value) { return new WasmIntUnary(WasmIntType.INT32, WasmIntUnaryOperation.EQZ, value); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java index 809c22ef8..4d1cbe762 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java @@ -575,6 +575,8 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp protected abstract WasmExpression nullLiteral(Expr expr); + protected abstract WasmExpression nullLiteral(WasmType type); + protected abstract WasmExpression stringLiteral(String s); protected abstract WasmExpression classLiteral(ValueType type); @@ -1179,13 +1181,13 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp var valueToCast = exprCache.create(result, wasmSourceType, expr.getLocation(), block.getBody()); var nullCheck = new WasmBranch(genIsNull(valueToCast.expr()), block); - nullCheck.setResult(valueToCast.expr()); + nullCheck.setResult(nullLiteral(wasmTargetType)); block.getBody().add(new WasmDrop(nullCheck)); var supertypeCall = generateInstanceOf(valueToCast.expr(), expr.getTarget()); var breakIfPassed = new WasmBranch(supertypeCall, block); - breakIfPassed.setResult(valueToCast.expr()); + breakIfPassed.setResult(generateCast(valueToCast.expr(), wasmTargetType)); block.getBody().add(new WasmDrop(breakIfPassed)); var callSiteId = generateCallSiteId(expr.getLocation()); @@ -1194,7 +1196,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp callSiteId.generateThrow(block.getBody(), expr.getLocation()); valueToCast.release(); - result = generateCast(block, wasmTargetType); + result = block; } protected abstract WasmExpression generateCast(WasmExpression value, WasmType targetType); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java index d58951c9d..5cf5d6cb7 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java @@ -96,6 +96,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit private WasmFunction createPrimitiveClassFunction; private WasmFunction createArrayClassFunction; private final WasmGCSupertypeFunctionGenerator supertypeGenerator; + private final WasmGCNewArrayFunctionGenerator newArrayGenerator; private int classTagOffset; private int classFlagsOffset; @@ -103,6 +104,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit private int classParentOffset; private int classArrayOffset; private int classArrayItemOffset; + private int classNewArrayOffset; private int classSupertypeFunctionOffset; private int virtualTableFieldOffset; @@ -123,6 +125,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit standardClasses = new WasmGCStandardClasses(this); strings = new WasmGCStringPool(standardClasses, module, functionProvider); supertypeGenerator = new WasmGCSupertypeFunctionGenerator(module, this, names, tagRegistry, functionTypes); + newArrayGenerator = new WasmGCNewArrayFunctionGenerator(module, functionTypes, this); typeMapper = new WasmGCTypeMapper(this, functionTypes, module); } @@ -190,11 +193,19 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit initializerFunctionStatements.clear(); for (var classInfo : classInfoMap.values()) { var req = metadataRequirements.getInfo(classInfo.getValueType()); - if (req != null && req.isAssignable()) { - var supertypeFunction = supertypeGenerator.getIsSupertypeFunction(classInfo.getValueType()); - supertypeFunction.setReferenced(true); - function.getBody().add(setClassField(classInfo, classSupertypeFunctionOffset, - new WasmFunctionReference(supertypeFunction))); + if (req != null) { + if (req.isAssignable()) { + var supertypeFunction = supertypeGenerator.getIsSupertypeFunction(classInfo.getValueType()); + supertypeFunction.setReferenced(true); + function.getBody().add(setClassField(classInfo, classSupertypeFunctionOffset, + new WasmFunctionReference(supertypeFunction))); + } + if (req.newArray()) { + var newArrayFunction = newArrayGenerator.generateNewArrayFunction(classInfo.getValueType()); + newArrayFunction.setReferenced(true); + function.getBody().add(setClassField(classInfo, classNewArrayOffset, + new WasmFunctionReference(newArrayFunction))); + } } function.getBody().add(setClassField(classInfo, CLASS_FIELD_OFFSET, new WasmGetGlobal(classClass.pointer))); @@ -275,6 +286,16 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit return classSupertypeFunctionOffset; } + @Override + public int getClassNameOffset() { + return classNameOffset; + } + + @Override + public int getNewArrayFunctionOffset() { + return classNewArrayOffset; + } + @Override public int getVirtualMethodsOffset() { return virtualTableFieldOffset; @@ -546,6 +567,8 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit fields.add(standardClasses.classClass().getType().asStorage()); classSupertypeFunctionOffset = fields.size(); fields.add(supertypeGenerator.getFunctionType().getReference().asStorage()); + classNewArrayOffset = fields.size(); + fields.add(newArrayGenerator.getNewArrayFunctionType().getReference().asStorage()); virtualTableFieldOffset = fields.size(); classNameOffset = fieldIndexes.getOrDefault(new FieldReference(className, "name"), -1); } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfoProvider.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfoProvider.java index cb1230516..1b76788a4 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfoProvider.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfoProvider.java @@ -37,6 +37,10 @@ public interface WasmGCClassInfoProvider { int getClassSupertypeFunctionOffset(); + int getNewArrayFunctionOffset(); + + int getClassNameOffset(); + default WasmGCClassInfo getClassInfo(String name) { return getClassInfo(ValueType.object(name)); } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCNewArrayFunctionGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCNewArrayFunctionGenerator.java new file mode 100644 index 000000000..30d7fe9ed --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCNewArrayFunctionGenerator.java @@ -0,0 +1,64 @@ +/* + * 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.generate.gc.classes; + +import org.teavm.backend.wasm.WasmFunctionTypes; +import org.teavm.backend.wasm.generate.TemporaryVariablePool; +import org.teavm.backend.wasm.generate.gc.methods.WasmGCGenerationUtil; +import org.teavm.backend.wasm.model.WasmFunction; +import org.teavm.backend.wasm.model.WasmFunctionType; +import org.teavm.backend.wasm.model.WasmLocal; +import org.teavm.backend.wasm.model.WasmModule; +import org.teavm.backend.wasm.model.WasmType; +import org.teavm.backend.wasm.model.expression.WasmGetLocal; +import org.teavm.backend.wasm.model.expression.WasmReturn; +import org.teavm.model.ValueType; + +class WasmGCNewArrayFunctionGenerator { + private WasmModule module; + private WasmFunctionTypes functionTypes; + private WasmGCClassInfoProvider classInfoProvider; + private WasmFunctionType newArrayFunctionType; + + WasmGCNewArrayFunctionGenerator(WasmModule module, WasmFunctionTypes functionTypes, + WasmGCClassInfoProvider classInfoProvider) { + this.module = module; + this.functionTypes = functionTypes; + this.classInfoProvider = classInfoProvider; + } + + WasmFunction generateNewArrayFunction(ValueType itemType) { + var function = new WasmFunction(getNewArrayFunctionType()); + module.functions.add(function); + var sizeLocal = new WasmLocal(WasmType.INT32); + function.add(sizeLocal); + var tempVars = new TemporaryVariablePool(function); + var genUtil = new WasmGCGenerationUtil(classInfoProvider, tempVars); + var targetVar = new WasmLocal(classInfoProvider.getClassInfo(ValueType.arrayOf(itemType)).getType()); + function.add(targetVar); + genUtil.allocateArray(itemType, new WasmGetLocal(sizeLocal), null, targetVar, function.getBody()); + function.getBody().add(new WasmReturn(new WasmGetLocal(targetVar))); + return function; + } + + WasmFunctionType getNewArrayFunctionType() { + if (newArrayFunctionType == null) { + newArrayFunctionType = functionTypes.of(classInfoProvider.getClassInfo("java.lang.Object").getType(), + WasmType.INT32); + } + return newArrayFunctionType; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java index 41dbb354b..5fad8f228 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java @@ -211,6 +211,11 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { : WasmType.Reference.STRUCT); } + @Override + protected WasmExpression nullLiteral(WasmType type) { + return new WasmNullConstant((WasmType.Reference) type); + } + @Override protected WasmExpression genIsNull(WasmExpression value) { return new WasmReferencesEqual(value, new WasmNullConstant(WasmType.Reference.STRUCT)); diff --git a/core/src/main/java/org/teavm/backend/wasm/generators/gc/ArrayGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generators/gc/ArrayGenerator.java new file mode 100644 index 000000000..b7f14d73d --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generators/gc/ArrayGenerator.java @@ -0,0 +1,45 @@ +/* + * 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.generators.gc; + +import org.teavm.backend.wasm.model.WasmFunction; +import org.teavm.backend.wasm.model.WasmLocal; +import org.teavm.backend.wasm.model.WasmType; +import org.teavm.backend.wasm.model.expression.WasmCallReference; +import org.teavm.backend.wasm.model.expression.WasmGetLocal; +import org.teavm.backend.wasm.model.expression.WasmReturn; +import org.teavm.backend.wasm.model.expression.WasmStructGet; +import org.teavm.model.MethodReference; + +public class ArrayGenerator implements WasmGCCustomGenerator { + @Override + public void apply(MethodReference method, WasmFunction function, WasmGCCustomGeneratorContext context) { + var clsStruct = context.classInfoProvider().getClassInfo("java.lang.Class").getStructure(); + var classLocal = new WasmLocal(clsStruct.getReference()); + var sizeLocal = new WasmLocal(WasmType.INT32); + function.add(classLocal); + function.add(sizeLocal); + var constructorRef = new WasmStructGet(clsStruct, new WasmGetLocal(classLocal), + context.classInfoProvider().getNewArrayFunctionOffset()); + var functionType = context.functionTypes().of( + context.classInfoProvider().getClassInfo("java.lang.Object").getType(), + WasmType.INT32 + ); + var result = new WasmCallReference(constructorRef, functionType); + result.getArguments().add(new WasmGetLocal(sizeLocal)); + function.getBody().add(new WasmReturn(result)); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generators/gc/ClassGenerators.java b/core/src/main/java/org/teavm/backend/wasm/generators/gc/ClassGenerators.java index d06401e7a..90714d453 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generators/gc/ClassGenerators.java +++ b/core/src/main/java/org/teavm/backend/wasm/generators/gc/ClassGenerators.java @@ -36,6 +36,9 @@ public class ClassGenerators implements WasmGCCustomGenerator { case "isInstance": generateIsInstance(function, context); break; + case "getName": + generateGetName(function, context); + break; default: throw new IllegalArgumentException("Unsupported method: " + method); } @@ -65,4 +68,14 @@ public class ClassGenerators implements WasmGCCustomGenerator { function.getBody().add(new WasmReturn(conditional)); } + + private void generateGetName(WasmFunction function, WasmGCCustomGeneratorContext context) { + var classCls = context.classInfoProvider().getClassInfo("java.lang.Class"); + var thisVar = new WasmLocal(classCls.getType()); + function.add(thisVar); + + var nameRef = new WasmStructGet(classCls.getStructure(), new WasmGetLocal(thisVar), + context.classInfoProvider().getClassNameOffset()); + function.getBody().add(new WasmReturn(nameRef)); + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGenerators.java b/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGenerators.java index 3a4dd55db..5e2a1ff7d 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGenerators.java +++ b/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGenerators.java @@ -15,6 +15,7 @@ */ package org.teavm.backend.wasm.generators.gc; +import java.lang.reflect.Array; import java.util.HashMap; import java.util.Map; import org.teavm.backend.wasm.generate.gc.methods.WasmGCCustomGeneratorProvider; @@ -28,11 +29,13 @@ public class WasmGCCustomGenerators implements WasmGCCustomGeneratorProvider { fillClass(); fillStringPool(); fillSystem(); + fillArray(); } private void fillClass() { var classGenerators = new ClassGenerators(); generators.put(new MethodReference(Class.class, "isInstance", Object.class, boolean.class), classGenerators); + generators.put(new MethodReference(Class.class, "getName", String.class), classGenerators); } private void fillStringPool() { @@ -50,6 +53,12 @@ public class WasmGCCustomGenerators implements WasmGCCustomGeneratorProvider { ); } + private void fillArray() { + var arrayGenerator = new ArrayGenerator(); + generators.put(new MethodReference(Array.class, "newInstanceImpl", Class.class, int.class, Object.class), + arrayGenerator); + } + @Override public WasmGCCustomGenerator get(MethodReference method) { return generators.get(method); diff --git a/core/src/main/java/org/teavm/model/analysis/ClassMetadataRequirements.java b/core/src/main/java/org/teavm/model/analysis/ClassMetadataRequirements.java index f8c5dea4c..c3917609c 100644 --- a/core/src/main/java/org/teavm/model/analysis/ClassMetadataRequirements.java +++ b/core/src/main/java/org/teavm/model/analysis/ClassMetadataRequirements.java @@ -15,6 +15,7 @@ */ package org.teavm.model.analysis; +import java.lang.reflect.Array; import java.util.HashMap; import java.util.Map; import org.teavm.dependency.DependencyInfo; @@ -34,6 +35,8 @@ public class ClassMetadataRequirements { "getDeclaringClass", Class.class); private static final MethodReference GET_ENCLOSING_CLASS_METHOD = new MethodReference(Class.class, "getEnclosingClass", Class.class); + private static final MethodReference NEW_ARRAY = new MethodReference(Array.class, + "newInstance", Class.class, int.class, Object.class); private static final ClassInfo EMPTY_INFO = new ClassInfo(); private Map requirements = new HashMap<>(); @@ -85,6 +88,14 @@ public class ClassMetadataRequirements { requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).enclosingClass = true; } } + + var newArrayMethod = dependencyInfo.getMethod(NEW_ARRAY); + if (newArrayMethod != null) { + var classNames = newArrayMethod.getVariable(1).getClassValueNode().getTypes(); + for (var className : classNames) { + requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).newArray = true; + } + } } public Info getInfo(String className) { @@ -122,6 +133,7 @@ public class ClassMetadataRequirements { boolean enclosingClass; boolean superclass; boolean isAssignable; + boolean newArray; @Override public boolean name() { @@ -152,6 +164,11 @@ public class ClassMetadataRequirements { public boolean isAssignable() { return isAssignable; } + + @Override + public boolean newArray() { + return newArray; + } } public interface Info { @@ -166,5 +183,7 @@ public class ClassMetadataRequirements { boolean superclass(); boolean isAssignable(); + + boolean newArray(); } }