From eccfaff889c296a5c12051802dd89f3eaf8387bb Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 11 Sep 2024 20:07:05 +0200 Subject: [PATCH] wasm gc: fix bugs with Class --- .../org/teavm/classlib/java/lang/TClass.java | 11 +++--- .../methods/BaseWasmGenerationVisitor.java | 4 ++- .../gc/classes/WasmGCClassGenerator.java | 24 ++++++++++++- .../gc/classes/WasmGCClassInfoProvider.java | 4 +++ .../gc/methods/WasmGCMethodGenerator.java | 17 ++++++++++ .../wasm/generators/gc/ClassGenerators.java | 34 +++++++++---------- .../gc/WasmGCCustomGeneratorContext.java | 9 +++++ .../generators/gc/WasmGCCustomGenerators.java | 3 +- .../wasm/intrinsics/gc/ClassIntrinsic.java | 30 ++++++++++++++-- .../wasm/intrinsics/gc/WasmGCIntrinsics.java | 3 ++ .../gc/BaseClassesTransformation.java | 3 +- .../teavm/classlib/java/lang/ClassTest.java | 3 -- 12 files changed, 114 insertions(+), 31 deletions(-) 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 3d4e1667a..fd31d0c38 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 @@ -46,9 +46,7 @@ import org.teavm.dependency.PluggableDependency; import org.teavm.interop.Address; import org.teavm.interop.DelegateTo; import org.teavm.interop.NoSideEffects; -import org.teavm.interop.Platforms; import org.teavm.interop.Unmanaged; -import org.teavm.interop.UnsupportedOn; import org.teavm.jso.core.JSArray; import org.teavm.platform.Platform; import org.teavm.platform.PlatformClass; @@ -101,8 +99,10 @@ public final class TClass extends TObject implements TAnnotatedElement, TType } @DelegateTo("isInstanceLowLevel") - @UnsupportedOn(Platforms.WEBASSEMBLY_GC) public boolean isInstance(TObject obj) { + if (PlatformDetector.isWebAssemblyGC()) { + return obj != null && isAssignableFrom((TClass) (Object) obj.getClass()); + } return Platform.isInstance(Platform.getPlatformObject(obj), platformClass); } @@ -112,7 +112,6 @@ public final class TClass extends TObject implements TAnnotatedElement, TType } @DelegateTo("isAssignableFromLowLevel") - @UnsupportedOn(Platforms.WEBASSEMBLY_GC) public boolean isAssignableFrom(TClass obj) { return Platform.isAssignable(obj.getPlatformClass(), platformClass); } @@ -268,7 +267,9 @@ public final class TClass extends TObject implements TAnnotatedElement, TType } private boolean isSynthetic() { - if (PlatformDetector.isJavaScript()) { + if (PlatformDetector.isWebAssemblyGC()) { + return (getWasmGCFlags() & WasmGCClassFlags.SYNTHETIC) != 0; + } else if (PlatformDetector.isJavaScript()) { return (platformClass.getMetadata().getAccessLevel() & Flags.SYNTHETIC) != 0; } else { return (RuntimeClass.getClass(Address.ofObject(this).toStructure()).flags & RuntimeClass.SYNTHETIC) != 0; 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 9a3489905..7a4987ff4 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 @@ -92,6 +92,8 @@ import org.teavm.backend.wasm.model.expression.WasmInt64Constant; import org.teavm.backend.wasm.model.expression.WasmIntBinary; import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation; import org.teavm.backend.wasm.model.expression.WasmIntType; +import org.teavm.backend.wasm.model.expression.WasmIntUnary; +import org.teavm.backend.wasm.model.expression.WasmIntUnaryOperation; import org.teavm.backend.wasm.model.expression.WasmReturn; import org.teavm.backend.wasm.model.expression.WasmSetLocal; import org.teavm.backend.wasm.model.expression.WasmSwitch; @@ -1446,7 +1448,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp } } - var result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, expr, new WasmInt32Constant(0)); + var result = new WasmIntUnary(WasmIntType.INT32, WasmIntUnaryOperation.EQZ, expr); result.setLocation(expr.getLocation()); return result; } 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 1b62b43db..2e9864994 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 @@ -129,12 +129,14 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit private int classFlagsOffset; private int classNameOffset; private int classSimpleNameOffset; + private int classCanonicalNameOffset; private int classParentOffset; private int classArrayOffset; private int classArrayItemOffset; private int classNewArrayOffset; private int classSupertypeFunctionOffset; private int classEnclosingClassOffset; + private int classDeclaringClassOffset; private int virtualTableFieldOffset; private int enumConstantsFunctionOffset; private int arrayLengthOffset = -1; @@ -380,6 +382,12 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit return classEnclosingClassOffset; } + @Override + public int getClassDeclaringClassOffset() { + standardClasses.classClass().getStructure().init(); + return classDeclaringClassOffset; + } + @Override public int getClassParentOffset() { standardClasses.classClass().getStructure().init(); @@ -398,6 +406,12 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit return classSimpleNameOffset; } + @Override + public int getClassCanonicalNameOffset() { + standardClasses.classClass().getStructure().init(); + return classCanonicalNameOffset; + } + @Override public int getNewArrayFunctionOffset() { standardClasses.classClass().getStructure().init(); @@ -498,10 +512,14 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit var parent = getClassInfo(cls.getParent()); target.add(setClassField(classInfo, classParentOffset, new WasmGetGlobal(parent.pointer))); } - if (cls.getOwnerName() != null && metadataReq.superclass()) { + if (cls.getOwnerName() != null && metadataReq.enclosingClass()) { var owner = getClassInfo(cls.getOwnerName()); target.add(setClassField(classInfo, classEnclosingClassOffset, new WasmGetGlobal(owner.pointer))); } + if (cls.getDeclaringClassName() != null && metadataReq.declaringClass()) { + var owner = getClassInfo(cls.getDeclaringClassName()); + target.add(setClassField(classInfo, classDeclaringClassOffset, new WasmGetGlobal(owner.pointer))); + } if (metadataReq.cloneMethod()) { WasmFunction cloneFunction; if (hierarchy.isSuperType("java.lang.Cloneable", name, false)) { @@ -1067,10 +1085,14 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit "createArrayInstance")); classEnclosingClassOffset = fields.size(); fields.add(createClassField(standardClasses.classClass().getType().asStorage(), "enclosingClass")); + classDeclaringClassOffset = fields.size(); + fields.add(createClassField(standardClasses.classClass().getType().asStorage(), "declaringClass")); classNameOffset = fields.size(); fields.add(createClassField(standardClasses.stringClass().getType().asStorage(), "name")); classSimpleNameOffset = fields.size(); fields.add(createClassField(standardClasses.stringClass().getType().asStorage(), "simpleName")); + classCanonicalNameOffset = fields.size(); + fields.add(createClassField(standardClasses.stringClass().getType().asStorage(), "canonicalName")); cloneOffset = fields.size(); fields.add(createClassField(functionTypes.of(standardClasses.objectClass().getType(), standardClasses.objectClass().getType()).getReference().asStorage(), "clone")); 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 9213e8d16..ce5820cbe 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 @@ -44,6 +44,8 @@ public interface WasmGCClassInfoProvider { int getClassEnclosingClassOffset(); + int getClassDeclaringClassOffset(); + int getClassParentOffset(); int getNewArrayFunctionOffset(); @@ -52,6 +54,8 @@ public interface WasmGCClassInfoProvider { int getClassSimpleNameOffset(); + int getClassCanonicalNameOffset(); + int getArrayGetOffset(); int getArrayLengthOffset(); 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 9df9788f6..8ba2e164c 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 @@ -37,8 +37,10 @@ import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringProvider; import org.teavm.backend.wasm.generators.gc.WasmGCCustomGenerator; import org.teavm.backend.wasm.generators.gc.WasmGCCustomGeneratorContext; import org.teavm.backend.wasm.model.WasmFunction; +import org.teavm.backend.wasm.model.WasmGlobal; import org.teavm.backend.wasm.model.WasmLocal; import org.teavm.backend.wasm.model.WasmModule; +import org.teavm.backend.wasm.model.WasmTag; import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.expression.WasmFunctionReference; import org.teavm.backend.wasm.model.expression.WasmReturn; @@ -364,5 +366,20 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository { public WasmGCNameProvider names() { return names; } + + @Override + public WasmGlobal exceptionGlobal() { + return context.exceptionGlobal(); + } + + @Override + public WasmTag exceptionTag() { + return context.getExceptionTag(); + } + + @Override + public BaseWasmFunctionRepository functions() { + return WasmGCMethodGenerator.this; + } }; } 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 b22025d03..9a95583cd 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 @@ -15,55 +15,55 @@ */ 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.WasmCall; 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.WasmSetGlobal; import org.teavm.backend.wasm.model.expression.WasmStructGet; +import org.teavm.backend.wasm.model.expression.WasmThrow; +import org.teavm.backend.wasm.runtime.WasmGCSupport; 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); + case "isAssignableFrom": + generateIsAssignable(function, context); break; default: throw new IllegalArgumentException("Unsupported method: " + method); } } - private void generateIsInstance(WasmFunction function, WasmGCCustomGeneratorContext context) { + private void generateIsAssignable(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()); + var otherClassVar = new WasmLocal(classCls.getType()); function.add(thisVar); - function.add(objectVar); + function.add(otherClassVar); - var conditional = new WasmConditional(new WasmReferencesEqual(new WasmGetLocal(objectVar), + var conditional = new WasmConditional(new WasmReferencesEqual(new WasmGetLocal(otherClassVar), new WasmNullConstant(WasmType.Reference.STRUCT))); - conditional.setType(WasmType.INT32); - conditional.getThenBlock().getBody().add(new WasmInt32Constant(0)); + function.getBody().add(conditional); + var npe = new WasmCall(context.functions().forStaticMethod(new MethodReference(WasmGCSupport.class, "npe", + NullPointerException.class))); + conditional.getThenBlock().getBody().add(new WasmSetGlobal(context.exceptionGlobal(), npe)); + conditional.getThenBlock().getBody().add(new WasmThrow(context.exceptionTag())); - 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); + call.getArguments().add(new WasmGetLocal(otherClassVar)); - function.getBody().add(new WasmReturn(conditional)); + function.getBody().add(new WasmReturn(call)); } - } 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 e76616b9d..7b6ba33c2 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 @@ -15,11 +15,14 @@ */ package org.teavm.backend.wasm.generators.gc; +import org.teavm.backend.wasm.BaseWasmFunctionRepository; import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.generate.gc.WasmGCNameProvider; import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider; import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper; +import org.teavm.backend.wasm.model.WasmGlobal; import org.teavm.backend.wasm.model.WasmModule; +import org.teavm.backend.wasm.model.WasmTag; public interface WasmGCCustomGeneratorContext { WasmModule module(); @@ -31,4 +34,10 @@ public interface WasmGCCustomGeneratorContext { WasmGCClassInfoProvider classInfoProvider(); WasmGCNameProvider names(); + + WasmGlobal exceptionGlobal(); + + BaseWasmFunctionRepository functions(); + + WasmTag exceptionTag(); } 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 2236d6a95..d7f4fbbaa 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 @@ -34,7 +34,8 @@ public class WasmGCCustomGenerators implements WasmGCCustomGeneratorProvider { 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, "isAssignableFrom", Class.class, boolean.class), + classGenerators); } private void fillStringPool() { diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ClassIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ClassIntrinsic.java index 9c26536ee..085716f96 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ClassIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ClassIntrinsic.java @@ -47,11 +47,18 @@ public class ClassIntrinsic implements WasmGCIntrinsic { result.setLocation(invocation.getLocation()); return result; } - case "getSuperclass": { + case "getDeclaringClass": { 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().getClassParentOffset()); + context.classInfoProvider().getClassDeclaringClassOffset()); + result.setLocation(invocation.getLocation()); + return result; + } + case "getSuperclass": { + 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().getClassParentOffset()); result.setLocation(invocation.getLocation()); return result; } @@ -63,6 +70,10 @@ public class ClassIntrinsic implements WasmGCIntrinsic { return generateGetSimpleName(invocation, context); case "setSimpleNameCache": return generateSetSimpleName(invocation, context); + case "getCanonicalNameCache": + return generateGetCanonicalName(invocation, context); + case "setCanonicalNameCache": + return generateSetCanonicalName(invocation, context); default: throw new IllegalArgumentException("Unsupported invocation method: " + invocation.getMethod()); } @@ -95,4 +106,19 @@ public class ClassIntrinsic implements WasmGCIntrinsic { return new WasmStructSet(classCls.getStructure(), arg, context.classInfoProvider().getClassSimpleNameOffset(), value); } + + private WasmExpression generateGetCanonicalName(InvocationExpr invocation, WasmGCIntrinsicContext context) { + var classCls = context.classInfoProvider().getClassInfo("java.lang.Class"); + var arg = context.generate(invocation.getArguments().get(0)); + return new WasmStructGet(classCls.getStructure(), arg, + context.classInfoProvider().getClassCanonicalNameOffset()); + } + + private WasmExpression generateSetCanonicalName(InvocationExpr invocation, WasmGCIntrinsicContext context) { + var classCls = context.classInfoProvider().getClassInfo("java.lang.Class"); + var arg = context.generate(invocation.getArguments().get(0)); + var value = context.generate(invocation.getArguments().get(1)); + return new WasmStructSet(classCls.getStructure(), arg, + context.classInfoProvider().getClassCanonicalNameOffset(), value); + } } 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 df918f576..89f88411b 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 @@ -86,9 +86,12 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider { add(new MethodReference(Class.class, "getNameImpl", String.class), intrinsic); add(new MethodReference(Class.class, "setNameImpl", String.class, void.class), intrinsic); add(new MethodReference(Class.class, "getEnclosingClass", Class.class), intrinsic); + add(new MethodReference(Class.class, "getDeclaringClass", Class.class), intrinsic); add(new MethodReference(Class.class, "getSuperclass", Class.class), intrinsic); add(new MethodReference(Class.class, "getSimpleNameCache", Class.class, String.class), intrinsic); add(new MethodReference(Class.class, "setSimpleNameCache", Class.class, String.class, void.class), intrinsic); + add(new MethodReference(Class.class, "getCanonicalNameCache", String.class), intrinsic); + add(new MethodReference(Class.class, "setCanonicalNameCache", String.class, void.class), intrinsic); } private void fillClassSupport() { 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 index 962ea7dd6..0a7b48bd8 100644 --- 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 @@ -69,8 +69,9 @@ public class BaseClassesTransformation implements ClassHolderTransformer { for (var method : cls.getMethods()) { switch (method.getName()) { case "getComponentType": - case "isInstance": + case "isAssignableFrom": case "getEnclosingClass": + case "getDeclaringClass": case "getSimpleNameCache": case "setSimpleNameCache": case "getSuperclass": diff --git a/tests/src/test/java/org/teavm/classlib/java/lang/ClassTest.java b/tests/src/test/java/org/teavm/classlib/java/lang/ClassTest.java index ba1f27689..f20541a25 100644 --- a/tests/src/test/java/org/teavm/classlib/java/lang/ClassTest.java +++ b/tests/src/test/java/org/teavm/classlib/java/lang/ClassTest.java @@ -103,14 +103,12 @@ public class ClassTest { } @Test - @SkipPlatform(TestPlatform.WEBASSEMBLY_GC) public void castingAppropriateObject() { Object obj = 23; assertEquals(Integer.valueOf(23), Integer.class.cast(obj)); } @Test(expected = ClassCastException.class) - @SkipPlatform(TestPlatform.WEBASSEMBLY_GC) public void inappropriateObjectCastingFails() { Object obj = 23; Float.class.cast(obj); @@ -150,7 +148,6 @@ public class ClassTest { } @Test - @SkipPlatform(TestPlatform.WEBASSEMBLY_GC) public void classProperties() { class B { }