From 726702dd7f2844786934bf38c67603318712c5e6 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 8 Nov 2023 19:59:24 +0100 Subject: [PATCH] wasm: fix programmatic initialization of a class that is statically initialized by optimizer --- .../java/org/teavm/backend/c/CTarget.java | 2 + .../c/intrinsic/FunctionClassIntrinsic.java | 36 +++++++++++++++++ .../c/intrinsic/FunctionIntrinsic.java | 4 +- .../org/teavm/backend/wasm/WasmTarget.java | 2 + .../intrinsics/FunctionClassIntrinsic.java | 39 +++++++++++++++++++ .../wasm/intrinsics/FunctionIntrinsic.java | 5 +++ .../main/java/org/teavm/interop/Function.java | 2 + .../java/org/teavm/platform/Platform.java | 3 +- .../teavm/classlib/java/lang/StringTest.java | 1 - 9 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 core/src/main/java/org/teavm/backend/c/intrinsic/FunctionClassIntrinsic.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/intrinsics/FunctionClassIntrinsic.java diff --git a/core/src/main/java/org/teavm/backend/c/CTarget.java b/core/src/main/java/org/teavm/backend/c/CTarget.java index 58e6fc9bc..2e0298c71 100644 --- a/core/src/main/java/org/teavm/backend/c/CTarget.java +++ b/core/src/main/java/org/teavm/backend/c/CTarget.java @@ -58,6 +58,7 @@ import org.teavm.backend.c.intrinsic.AddressIntrinsic; import org.teavm.backend.c.intrinsic.AllocatorIntrinsic; import org.teavm.backend.c.intrinsic.ConsoleIntrinsic; import org.teavm.backend.c.intrinsic.ExceptionHandlingIntrinsic; +import org.teavm.backend.c.intrinsic.FunctionClassIntrinsic; import org.teavm.backend.c.intrinsic.FunctionIntrinsic; import org.teavm.backend.c.intrinsic.GCIntrinsic; import org.teavm.backend.c.intrinsic.IntegerIntrinsic; @@ -383,6 +384,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { intrinsics.add(new MutatorIntrinsic()); intrinsics.add(new ExceptionHandlingIntrinsic()); intrinsics.add(new FunctionIntrinsic(characteristics, exportDependencyListener.getResolvedMethods())); + intrinsics.add(new FunctionClassIntrinsic()); intrinsics.add(new RuntimeClassIntrinsic()); intrinsics.add(new FiberIntrinsic()); intrinsics.add(new LongIntrinsic()); diff --git a/core/src/main/java/org/teavm/backend/c/intrinsic/FunctionClassIntrinsic.java b/core/src/main/java/org/teavm/backend/c/intrinsic/FunctionClassIntrinsic.java new file mode 100644 index 000000000..696ed9a82 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/c/intrinsic/FunctionClassIntrinsic.java @@ -0,0 +1,36 @@ +/* + * Copyright 2023 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.c.intrinsic; + +import org.teavm.ast.InvocationExpr; +import org.teavm.interop.Function; +import org.teavm.model.MethodReference; + +public class FunctionClassIntrinsic implements Intrinsic { + @Override + public boolean canHandle(MethodReference method) { + return method.getClassName().equals(Function.class.getName()) + && method.getName().equals("isNull"); + } + + @Override + public void apply(IntrinsicContext context, InvocationExpr invocation) { + context.writer().print("("); + context.emit(invocation.getArguments().get(0)); + context.writer().print(" == NULL"); + context.writer().print(")"); + } +} diff --git a/core/src/main/java/org/teavm/backend/c/intrinsic/FunctionIntrinsic.java b/core/src/main/java/org/teavm/backend/c/intrinsic/FunctionIntrinsic.java index ce115b1d9..9253b4809 100644 --- a/core/src/main/java/org/teavm/backend/c/intrinsic/FunctionIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/c/intrinsic/FunctionIntrinsic.java @@ -36,8 +36,8 @@ public class FunctionIntrinsic implements Intrinsic { @Override public boolean canHandle(MethodReference method) { - if (method.getClassName().equals(Function.class.getName()) && method.getName().equals("get")) { - return true; + if (method.getClassName().equals(Function.class.getName())) { + return method.getName().equals("get"); } return characteristics.isFunction(method.getClassName()); } diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java index 936630622..6d8ac9a81 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -61,6 +61,7 @@ import org.teavm.backend.wasm.intrinsics.ConsoleIntrinsic; import org.teavm.backend.wasm.intrinsics.DoubleIntrinsic; import org.teavm.backend.wasm.intrinsics.ExceptionHandlingIntrinsic; import org.teavm.backend.wasm.intrinsics.FloatIntrinsic; +import org.teavm.backend.wasm.intrinsics.FunctionClassIntrinsic; import org.teavm.backend.wasm.intrinsics.FunctionIntrinsic; import org.teavm.backend.wasm.intrinsics.GCIntrinsic; import org.teavm.backend.wasm.intrinsics.IntegerIntrinsic; @@ -509,6 +510,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { context.addIntrinsic(new AddressIntrinsic(classGenerator)); context.addIntrinsic(new StructureIntrinsic(classes, classGenerator)); context.addIntrinsic(new FunctionIntrinsic(classGenerator)); + context.addIntrinsic(new FunctionClassIntrinsic()); var wasmRuntimeIntrinsic = new WasmRuntimeIntrinsic(); context.addIntrinsic(wasmRuntimeIntrinsic); context.addIntrinsic(new AllocatorIntrinsic(classGenerator)); diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/FunctionClassIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/FunctionClassIntrinsic.java new file mode 100644 index 000000000..ee27efc72 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/FunctionClassIntrinsic.java @@ -0,0 +1,39 @@ +/* + * Copyright 2016 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; + +import org.teavm.ast.InvocationExpr; +import org.teavm.backend.wasm.model.expression.WasmExpression; +import org.teavm.backend.wasm.model.expression.WasmInt32Constant; +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.interop.Function; +import org.teavm.model.MethodReference; + +public class FunctionClassIntrinsic implements WasmIntrinsic { + @Override + public boolean isApplicable(MethodReference methodReference) { + return methodReference.getClassName().equals(Function.class.getName()) + && methodReference.getName().equals("isNull"); + } + + @Override + public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) { + return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, + manager.generate(invocation.getArguments().get(0)), new WasmInt32Constant(-1)); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/FunctionIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/FunctionIntrinsic.java index 0326cd3b1..a3a097149 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/FunctionIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/FunctionIntrinsic.java @@ -20,6 +20,7 @@ import org.teavm.backend.wasm.generate.WasmClassGenerator; import org.teavm.backend.wasm.generate.WasmGeneratorUtil; import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.backend.wasm.model.expression.WasmIndirectCall; +import org.teavm.interop.Function; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; @@ -32,6 +33,10 @@ public class FunctionIntrinsic implements WasmIntrinsic { @Override public boolean isApplicable(MethodReference methodReference) { + if (methodReference.getClassName().equals(Function.class.getName()) + && methodReference.getName().equals("isNull")) { + return false; + } return classGenerator.isFunctionClass(methodReference.getClassName()); } diff --git a/interop/core/src/main/java/org/teavm/interop/Function.java b/interop/core/src/main/java/org/teavm/interop/Function.java index eb3813b75..4acc7d5e0 100644 --- a/interop/core/src/main/java/org/teavm/interop/Function.java +++ b/interop/core/src/main/java/org/teavm/interop/Function.java @@ -17,4 +17,6 @@ package org.teavm.interop; public abstract class Function { public static native T get(Class functionType, Class cls, String methodName); + + public static native boolean isNull(Function f); } diff --git a/platform/src/main/java/org/teavm/platform/Platform.java b/platform/src/main/java/org/teavm/platform/Platform.java index cdb040d71..fc70c361f 100644 --- a/platform/src/main/java/org/teavm/platform/Platform.java +++ b/platform/src/main/java/org/teavm/platform/Platform.java @@ -21,6 +21,7 @@ import org.teavm.backend.javascript.spi.InjectedBy; import org.teavm.dependency.PluggableDependency; import org.teavm.interop.Address; import org.teavm.interop.DelegateTo; +import org.teavm.interop.Function; import org.teavm.interop.NoSideEffects; import org.teavm.interop.PlatformMarker; import org.teavm.interop.Platforms; @@ -124,7 +125,7 @@ public final class Platform { @Unmanaged private static void initClassLowLevel(RuntimeClass cls) { - if (cls.init != null) { + if (!Function.isNull(cls.init)) { cls.init.run(); } } diff --git a/tests/src/test/java/org/teavm/classlib/java/lang/StringTest.java b/tests/src/test/java/org/teavm/classlib/java/lang/StringTest.java index 60212d17d..2471ab616 100644 --- a/tests/src/test/java/org/teavm/classlib/java/lang/StringTest.java +++ b/tests/src/test/java/org/teavm/classlib/java/lang/StringTest.java @@ -280,7 +280,6 @@ public class StringTest { } @Test - @SkipPlatform({ TestPlatform.WEBASSEMBLY, TestPlatform.WASI }) public void getUTF8ByteArrayOfLongString() throws UnsupportedEncodingException { char[] chars = new char[8192]; for (int i = 0; i < chars.length;) {