diff --git a/tests/build.gradle.kts b/tests/build.gradle.kts index 2c4ebcfb6..00ac6d8a9 100644 --- a/tests/build.gradle.kts +++ b/tests/build.gradle.kts @@ -59,6 +59,9 @@ tasks.test { systemProperty("teavm.junit.wasm", providers.gradleProperty("teavm.tests.wasm").orElse("true").get()) systemProperty("teavm.junit.wasm.runner", browser) + systemProperty("teavm.junit.wasm-gc", providers.gradleProperty("teavm.tests.wasm-gc").orElse("false").get()) + systemProperty("teavm.junit.wasm-gc.runner", browser) + systemProperty("teavm.junit.wasi", providers.gradleProperty("teavm.tests.wasi").orElse("true").get()) systemProperty("teavm.junit.wasi.runner", providers.gradleProperty("teavm.tests.wasi.runner") .orElse("./run-wasi.sh").get()) diff --git a/tools/browser-runner/src/main/resources/test-server/frame.js b/tools/browser-runner/src/main/resources/test-server/frame.js index af187fb78..b1015e199 100644 --- a/tools/browser-runner/src/main/resources/test-server/frame.js +++ b/tools/browser-runner/src/main/resources/test-server/frame.js @@ -19,7 +19,7 @@ window.addEventListener("message", event => { let request = event.data; switch (request.type) { - case "JAVASCRIPT": + case "JAVASCRIPT": { const files = request.additionalFiles ? [...request.additionalFiles, request.file] : [request.file]; appendFiles(files, 0, () => { launchTest(request.argument, response => { @@ -29,8 +29,8 @@ window.addEventListener("message", event => { event.source.postMessage(wrapResponse({ status: "failed", errorMessage: error }), "*"); }); break; - - case "WASM": + } + case "WASM": { const runtimeFile = request.file.path + "-runtime.js"; appendFiles([{ path: runtimeFile, type: "regular" }], 0, () => { launchWasmTest(request.file, request.argument, response => { @@ -40,6 +40,19 @@ window.addEventListener("message", event => { event.source.postMessage(wrapResponse({ status: "failed", errorMessage: error }), "*"); }); break; + } + + case "WASM_GC": { + const runtimeFile = request.file.path + "-runtime.js"; + appendFiles([{ path: runtimeFile, type: "regular" }], 0, () => { + launchWasmTest(request.file, request.argument, response => { + event.source.postMessage(response, "*"); + }); + }, error => { + event.source.postMessage(wrapResponse({ status: "failed", errorMessage: error} ), "*"); + }); + break; + } } }); diff --git a/tools/junit/src/main/java/org/teavm/junit/PropertyNames.java b/tools/junit/src/main/java/org/teavm/junit/PropertyNames.java index 890573caf..0f0adfcfa 100644 --- a/tools/junit/src/main/java/org/teavm/junit/PropertyNames.java +++ b/tools/junit/src/main/java/org/teavm/junit/PropertyNames.java @@ -25,6 +25,7 @@ final class PropertyNames { static final String WASM_ENABLED = "teavm.junit.wasm"; static final String WASI_ENABLED = "teavm.junit.wasi"; static final String WASI_RUNNER = "teavm.junit.wasi.runner"; + static final String WASM_GC_ENABLED = "teavm.junit.wasm-gc"; static final String C_COMPILER = "teavm.junit.c.compiler"; static final String C_LINE_NUMBERS = "teavm.junit.c.lineNumbers"; static final String MINIFIED = "teavm.junit.minified"; diff --git a/tools/junit/src/main/java/org/teavm/junit/TeaVMTestConfiguration.java b/tools/junit/src/main/java/org/teavm/junit/TeaVMTestConfiguration.java index 912bfcaf5..c27ecff0b 100644 --- a/tools/junit/src/main/java/org/teavm/junit/TeaVMTestConfiguration.java +++ b/tools/junit/src/main/java/org/teavm/junit/TeaVMTestConfiguration.java @@ -17,6 +17,7 @@ package org.teavm.junit; import org.teavm.backend.c.CTarget; import org.teavm.backend.javascript.JavaScriptTarget; +import org.teavm.backend.wasm.WasmGCTarget; import org.teavm.backend.wasm.WasmTarget; import org.teavm.vm.TeaVM; import org.teavm.vm.TeaVMOptimizationLevel; @@ -115,6 +116,38 @@ interface TeaVMTestConfiguration { } }; + TeaVMTestConfiguration WASM_GC_DEFAULT = new TeaVMTestConfiguration<>() { + @Override + public String getSuffix() { + return ""; + } + + @Override + public void apply(TeaVM vm) { + vm.setOptimizationLevel(TeaVMOptimizationLevel.SIMPLE); + } + + @Override + public void apply(WasmGCTarget target) { + } + }; + + TeaVMTestConfiguration WASM_GC_OPTIMIZED = new TeaVMTestConfiguration<>() { + @Override + public String getSuffix() { + return "optimized"; + } + + @Override + public void apply(TeaVM vm) { + vm.setOptimizationLevel(TeaVMOptimizationLevel.FULL); + } + + @Override + public void apply(WasmGCTarget target) { + } + }; + TeaVMTestConfiguration C_DEFAULT = new TeaVMTestConfiguration<>() { @Override public String getSuffix() { diff --git a/tools/junit/src/main/java/org/teavm/junit/TeaVMTestRunner.java b/tools/junit/src/main/java/org/teavm/junit/TeaVMTestRunner.java index 1028149a8..1c4b6b364 100644 --- a/tools/junit/src/main/java/org/teavm/junit/TeaVMTestRunner.java +++ b/tools/junit/src/main/java/org/teavm/junit/TeaVMTestRunner.java @@ -106,6 +106,7 @@ public class TeaVMTestRunner extends Runner implements Filterable { platforms.add(new JSPlatformSupport(classSource, referenceCache)); platforms.add(new WebAssemblyPlatformSupport(classSource, referenceCache)); + platforms.add(new WebAssemblyGCPlatformSupport(classSource, referenceCache)); platforms.add(new WasiPlatformSupport(classSource, referenceCache)); platforms.add(new CPlatformSupport(classSource, referenceCache)); diff --git a/tools/junit/src/main/java/org/teavm/junit/TestPlatform.java b/tools/junit/src/main/java/org/teavm/junit/TestPlatform.java index 1f171aefc..b0e3cdaa7 100644 --- a/tools/junit/src/main/java/org/teavm/junit/TestPlatform.java +++ b/tools/junit/src/main/java/org/teavm/junit/TestPlatform.java @@ -19,5 +19,6 @@ public enum TestPlatform { JAVASCRIPT, WEBASSEMBLY, WASI, + WEBASSEMBLY_GC, C } diff --git a/tools/junit/src/main/java/org/teavm/junit/TestPlatformSupport.java b/tools/junit/src/main/java/org/teavm/junit/TestPlatformSupport.java index 62383bc82..f5d4ede14 100644 --- a/tools/junit/src/main/java/org/teavm/junit/TestPlatformSupport.java +++ b/tools/junit/src/main/java/org/teavm/junit/TestPlatformSupport.java @@ -123,7 +123,7 @@ abstract class TestPlatformSupport { } } - private File getOutputFile(File path, String baseName, String suffix, String extension) { + protected final File getOutputFile(File path, String baseName, String suffix, String extension) { StringBuilder simpleName = new StringBuilder(); simpleName.append(baseName); if (!suffix.isEmpty()) { diff --git a/tools/junit/src/main/java/org/teavm/junit/TestWasmGCEntryPoint.java b/tools/junit/src/main/java/org/teavm/junit/TestWasmGCEntryPoint.java new file mode 100644 index 000000000..dbdb2505c --- /dev/null +++ b/tools/junit/src/main/java/org/teavm/junit/TestWasmGCEntryPoint.java @@ -0,0 +1,25 @@ +/* + * Copyright 2021 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.junit; + +final class TestWasmGCEntryPoint { + private TestWasmGCEntryPoint() { + } + + public static void main(String[] args) throws Throwable { + TestEntryPoint.run(args.length > 0 ? args[0] : null); + } +} diff --git a/tools/junit/src/main/java/org/teavm/junit/WebAssemblyGCPlatformSupport.java b/tools/junit/src/main/java/org/teavm/junit/WebAssemblyGCPlatformSupport.java new file mode 100644 index 000000000..5e31b0d5e --- /dev/null +++ b/tools/junit/src/main/java/org/teavm/junit/WebAssemblyGCPlatformSupport.java @@ -0,0 +1,134 @@ +/* + * 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.junit; + +import static org.teavm.junit.PropertyNames.OPTIMIZED; +import static org.teavm.junit.PropertyNames.SOURCE_DIRS; +import static org.teavm.junit.PropertyNames.WASM_GC_ENABLED; +import static org.teavm.junit.PropertyNames.WASM_RUNNER; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.AnnotatedElement; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.function.Consumer; +import java.util.function.Supplier; +import org.teavm.backend.wasm.WasmGCTarget; +import org.teavm.browserrunner.BrowserRunner; +import org.teavm.model.ClassHolderSource; +import org.teavm.model.MethodReference; +import org.teavm.model.ReferenceCache; +import org.teavm.vm.TeaVM; + +class WebAssemblyGCPlatformSupport extends TestPlatformSupport { + WebAssemblyGCPlatformSupport(ClassHolderSource classSource, ReferenceCache referenceCache) { + super(classSource, referenceCache); + } + + @Override + String getExtension() { + return ".wasm"; + } + + @Override + TestRunStrategy createRunStrategy(File outputDir) { + var runStrategyName = System.getProperty(WASM_RUNNER); + return runStrategyName != null + ? new BrowserRunStrategy(outputDir, "WASM", BrowserRunner.pickBrowser(runStrategyName)) + : null; + } + + @Override + CompileResult compile(Consumer additionalProcessing, String baseName, + TeaVMTestConfiguration configuration, File path, AnnotatedElement element) { + Supplier targetSupplier = () -> { + var target = new WasmGCTarget(); + target.setObfuscated(false); + var sourceDirs = System.getProperty(SOURCE_DIRS); + if (sourceDirs != null) { + var dirs = new ArrayList(); + for (var tokenizer = new StringTokenizer(sourceDirs, Character.toString(File.pathSeparatorChar)); + tokenizer.hasMoreTokens();) { + var dir = new File(tokenizer.nextToken()); + if (dir.isDirectory()) { + dirs.add(dir); + } + } + } + return target; + }; + return compile(configuration, targetSupplier, TestWasmGCEntryPoint.class.getName(), path, + ".wasm", null, additionalProcessing, baseName); + } + + @Override + TestPlatform getPlatform() { + return TestPlatform.WEBASSEMBLY_GC; + } + + @Override + String getPath() { + return "wasm-gc"; + } + + @Override + boolean isEnabled() { + return Boolean.getBoolean(WASM_GC_ENABLED); + } + + @Override + List> getConfigurations() { + List> configurations = new ArrayList<>(); + configurations.add(TeaVMTestConfiguration.WASM_GC_DEFAULT); + if (Boolean.getBoolean(OPTIMIZED)) { + configurations.add(TeaVMTestConfiguration.WASM_GC_OPTIMIZED); + } + return configurations; + } + + @Override + boolean usesFileName() { + return true; + } + + @Override + void additionalOutput(File outputPath, File outputPathForMethod, TeaVMTestConfiguration configuration, + MethodReference reference) { + htmlOutput(outputPath, outputPathForMethod, configuration, reference, "teavm-run-test-wasm.html"); + var testPath = getOutputFile(outputPath, "classTest", configuration.getSuffix(), + getExtension() + "-runtime.js"); + try { + TestUtil.resourceToFile("org/teavm/backend/wasm/wasm-gc-runtime.js", testPath, Map.of()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + void additionalSingleTestOutput(File outputPathForMethod, TeaVMTestConfiguration configuration, + MethodReference reference) { + htmlSingleTestOutput(outputPathForMethod, configuration, "teavm-run-test-wasm.html"); + var testPath = getOutputFile(outputPathForMethod, "test", configuration.getSuffix(), + getExtension() + "-runtime.js"); + try { + TestUtil.resourceToFile("org/teavm/backend/wasm/wasm-gc-runtime.js", testPath, Map.of()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +}