diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java index cfe4f416e..e594edb56 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java @@ -53,7 +53,7 @@ import org.teavm.platform.PlatformSequence; import org.teavm.runtime.RuntimeClass; import org.teavm.runtime.RuntimeObject; -public class TClass extends TObject implements TAnnotatedElement, TType { +public final class TClass extends TObject implements TAnnotatedElement, TType { String name; String simpleName; String canonicalName; diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java index 3b32c7b36..41be0c3b1 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java @@ -26,6 +26,7 @@ import org.teavm.backend.wasm.render.WasmBinaryRenderer; import org.teavm.backend.wasm.render.WasmBinaryStatsCollector; import org.teavm.backend.wasm.render.WasmBinaryVersion; import org.teavm.backend.wasm.render.WasmBinaryWriter; +import org.teavm.backend.wasm.transformation.gc.BaseClassesTransformation; import org.teavm.dependency.DependencyAnalyzer; import org.teavm.dependency.DependencyListener; import org.teavm.interop.Platforms; @@ -70,7 +71,9 @@ public class WasmGCTarget implements TeaVMTarget { @Override public List getTransformers() { - return List.of(); + return List.of( + new BaseClassesTransformation() + ); } @Override 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 7e7820203..3d98528d1 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 @@ -70,6 +70,8 @@ import org.teavm.model.util.ReflectionUtil; public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInitializerContributor { private static final MethodDescriptor CLINIT_METHOD_DESC = new MethodDescriptor("", ValueType.VOID); + private static final MethodDescriptor GET_CLASS_METHOD = new MethodDescriptor("getClass", + ValueType.parse(Class.class)); private final WasmModule module; private ClassReaderSource classSource; @@ -231,10 +233,16 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit return classTagOffset; } + @Override public int getClassArrayItemOffset() { return classArrayItemOffset; } + @Override + public int getClassSupertypeFunctionOffset() { + return classSupertypeFunctionOffset; + } + @Override public int getVirtualMethodsOffset() { return virtualTableFieldOffset; @@ -330,13 +338,10 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit private int fillVirtualTableMethods(List target, WasmStructure structure, WasmGlobal global, VirtualTable virtualTable, int index, String origin, Set filled) { - if (virtualTable.getParent() != null) { - index = fillVirtualTableMethods(target, structure, global, virtualTable.getParent(), index, origin, - filled); - } for (var method : virtualTable.getMethods()) { var entry = virtualTable.getEntry(method); - if (entry != null && entry.getImplementor() != null && filled.add(method)) { + if (entry != null && entry.getImplementor() != null && filled.add(method) + && !method.equals(GET_CLASS_METHOD)) { var function = functionProvider.forInstanceMethod(entry.getImplementor()); if (!origin.equals(entry.getImplementor().getClassName())) { var functionType = getFunctionType(virtualTable.getClassName(), method); @@ -356,9 +361,12 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit } function.setReferenced(true); var ref = new WasmFunctionReference(function); - target.add(new WasmStructSet(structure, new WasmGetGlobal(global), index, ref)); + target.add(new WasmStructSet(structure, new WasmGetGlobal(global), index + entry.getIndex(), ref)); } - ++index; + } + if (virtualTable.getParent() != null) { + index = fillVirtualTableMethods(target, structure, global, virtualTable.getParent(), index, origin, + filled); } return index; } 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 411cab08c..cb1230516 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 @@ -33,6 +33,10 @@ public interface WasmGCClassInfoProvider { int getVirtualMethodsOffset(); + int getClassArrayItemOffset(); + + int getClassSupertypeFunctionOffset(); + default WasmGCClassInfo getClassInfo(String name) { return getClassInfo(ValueType.object(name)); } 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 6de26a952..1ffbdee35 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 @@ -582,5 +582,10 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { public WasmGCTypeMapper typeMapper() { return context.typeMapper(); } + + @Override + public WasmGCClassInfoProvider classInfoProvider() { + return context.classInfoProvider(); + } }; } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java index 08c0a4943..78e53a167 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java @@ -335,5 +335,10 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository { public WasmGCTypeMapper typeMapper() { return typeMapper; } + + @Override + public WasmGCClassInfoProvider classInfoProvider() { + return classInfoProvider; + } }; } 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 new file mode 100644 index 000000000..d06401e7a --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generators/gc/ClassGenerators.java @@ -0,0 +1,68 @@ +/* + * 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.generate.gc.classes.WasmGCClassInfoProvider; +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.WasmConditional; +import org.teavm.backend.wasm.model.expression.WasmGetLocal; +import org.teavm.backend.wasm.model.expression.WasmInt32Constant; +import org.teavm.backend.wasm.model.expression.WasmNullConstant; +import org.teavm.backend.wasm.model.expression.WasmReferencesEqual; +import org.teavm.backend.wasm.model.expression.WasmReturn; +import org.teavm.backend.wasm.model.expression.WasmStructGet; +import org.teavm.model.MethodReference; + +public class ClassGenerators implements WasmGCCustomGenerator { + @Override + public void apply(MethodReference method, WasmFunction function, WasmGCCustomGeneratorContext context) { + switch (method.getName()) { + case "isInstance": + generateIsInstance(function, context); + break; + default: + throw new IllegalArgumentException("Unsupported method: " + method); + } + } + + private void generateIsInstance(WasmFunction function, WasmGCCustomGeneratorContext context) { + var classCls = context.classInfoProvider().getClassInfo("java.lang.Class"); + var objectCls = context.classInfoProvider().getClassInfo("java.lang.Object"); + var thisVar = new WasmLocal(classCls.getType()); + var objectVar = new WasmLocal(objectCls.getType()); + function.add(thisVar); + function.add(objectVar); + + var conditional = new WasmConditional(new WasmReferencesEqual(new WasmGetLocal(objectVar), + new WasmNullConstant(WasmType.Reference.ANY))); + conditional.setType(WasmType.INT32); + conditional.getThenBlock().getBody().add(new WasmInt32Constant(0)); + + var objectClass = new WasmStructGet(objectCls.getStructure(), new WasmGetLocal(objectVar), + WasmGCClassInfoProvider.CLASS_FIELD_OFFSET); + var functionRef = new WasmStructGet(classCls.getStructure(), new WasmGetLocal(thisVar), + context.classInfoProvider().getClassSupertypeFunctionOffset()); + var call = new WasmCallReference(functionRef, + context.functionTypes().of(WasmType.INT32, classCls.getType())); + call.getArguments().add(objectClass); + conditional.getElseBlock().getBody().add(call); + + function.getBody().add(new WasmReturn(conditional)); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGeneratorContext.java b/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGeneratorContext.java index 0d944c79c..5ba27bc16 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGeneratorContext.java +++ b/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGeneratorContext.java @@ -16,6 +16,7 @@ package org.teavm.backend.wasm.generators.gc; import org.teavm.backend.wasm.WasmFunctionTypes; +import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider; import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper; import org.teavm.backend.wasm.model.WasmModule; @@ -25,4 +26,6 @@ public interface WasmGCCustomGeneratorContext { WasmFunctionTypes functionTypes(); WasmGCTypeMapper typeMapper(); + + WasmGCClassInfoProvider classInfoProvider(); } 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 41c67e178..3a4dd55db 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 @@ -25,10 +25,16 @@ public class WasmGCCustomGenerators implements WasmGCCustomGeneratorProvider { private Map generators = new HashMap<>(); public WasmGCCustomGenerators() { + fillClass(); fillStringPool(); fillSystem(); } + private void fillClass() { + var classGenerators = new ClassGenerators(); + generators.put(new MethodReference(Class.class, "isInstance", Object.class, boolean.class), classGenerators); + } + private void fillStringPool() { generators.put( new MethodReference(WasmGCSupport.class, "nextByte", byte.class), diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ClassIntrinsics.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ClassIntrinsics.java new file mode 100644 index 000000000..b8bbcc166 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ClassIntrinsics.java @@ -0,0 +1,37 @@ +/* + * 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.model.expression.WasmExpression; +import org.teavm.backend.wasm.model.expression.WasmStructGet; + +public class ClassIntrinsics implements WasmGCIntrinsic { + @Override + public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) { + switch (invocation.getMethod().getName()) { + case "getComponentType": + var cls = context.generate(invocation.getArguments().get(0)); + var clsStruct = context.classInfoProvider().getClassInfo("java.lang.Class").getStructure(); + var result = new WasmStructGet(clsStruct, cls, + context.classInfoProvider().getClassArrayItemOffset()); + result.setLocation(invocation.getLocation()); + return result; + default: + throw new IllegalArgumentException("Unsupported invocation method: " + invocation.getMethod()); + } + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ObjectIntrinsics.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ObjectIntrinsics.java new file mode 100644 index 000000000..065460c55 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ObjectIntrinsics.java @@ -0,0 +1,32 @@ +/* + * 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.expression.WasmExpression; +import org.teavm.backend.wasm.model.expression.WasmStructGet; + +public class ObjectIntrinsics implements WasmGCIntrinsic { + @Override + public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) { + var obj = context.generate(invocation.getArguments().get(0)); + var objectStruct = context.classInfoProvider().getClassInfo("java.lang.Object").getStructure(); + var result = new WasmStructGet(objectStruct, obj, WasmGCClassInfoProvider.CLASS_FIELD_OFFSET); + result.setLocation(invocation.getLocation()); + return result; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsicContext.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsicContext.java index 56c066eb5..3cc21bbc6 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsicContext.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsicContext.java @@ -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.gc.classes.WasmGCClassInfoProvider; import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper; import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.model.expression.WasmExpression; @@ -38,4 +39,6 @@ public interface WasmGCIntrinsicContext { ClassHierarchy hierarchy(); WasmGCTypeMapper typeMapper(); + + WasmGCClassInfoProvider classInfoProvider(); } diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsics.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsics.java index f0f30e435..f016c5133 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsics.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsics.java @@ -24,9 +24,21 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider { private Map intrinsics = new HashMap<>(); public WasmGCIntrinsics() { + fillObject(); + fillClass(); fillSystem(); } + private void fillObject() { + var objectIntrinsics = new ObjectIntrinsics(); + intrinsics.put(new MethodReference(Object.class, "getClass", Class.class), objectIntrinsics); + } + + private void fillClass() { + var classIntrinsics = new ClassIntrinsics(); + intrinsics.put(new MethodReference(Class.class, "getComponentType", Class.class), classIntrinsics); + } + private void fillSystem() { intrinsics.put(new MethodReference(System.class, "arraycopy", Object.class, int.class, Object.class, int.class, int.class, void.class), new SystemArrayCopyIntrinsic()); diff --git a/core/src/main/java/org/teavm/backend/wasm/transformation/gc/BaseClassesTransformation.java b/core/src/main/java/org/teavm/backend/wasm/transformation/gc/BaseClassesTransformation.java new file mode 100644 index 000000000..937a72cea --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/transformation/gc/BaseClassesTransformation.java @@ -0,0 +1,47 @@ +/* + * 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.transformation.gc; + +import org.teavm.model.ClassHolder; +import org.teavm.model.ClassHolderTransformer; +import org.teavm.model.ClassHolderTransformerContext; +import org.teavm.model.ElementModifier; + +public class BaseClassesTransformation implements ClassHolderTransformer { + @Override + public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) { + if (cls.getName().equals("java.lang.Object")) { + for (var method : cls.getMethods()) { + switch (method.getName()) { + case "getClass": + method.setProgram(null); + method.getModifiers().add(ElementModifier.NATIVE); + break; + } + } + } else if (cls.getName().equals("java.lang.Class")) { + for (var method : cls.getMethods()) { + switch (method.getName()) { + case "getComponentType": + case "isInstance": + method.setProgram(null); + method.getModifiers().add(ElementModifier.NATIVE); + break; + } + } + } + } +}