wasm gc: add support to Gradle plugin

This commit is contained in:
Alexey Andreev 2024-09-19 08:24:50 +02:00
parent 10805ed0dd
commit 9fa88a15da
10 changed files with 208 additions and 0 deletions

View File

@ -39,6 +39,9 @@ teavm {
outputDir = layout.buildDirectory.dir("libs/wasi").get().asFile outputDir = layout.buildDirectory.dir("libs/wasi").get().asFile
relativePathInOutputDir = "" relativePathInOutputDir = ""
} }
wasmGC {
addedToWebApp = true
}
all { all {
mainClass = "org.teavm.samples.pi.PiCalculator" mainClass = "org.teavm.samples.pi.PiCalculator"
} }

View File

@ -24,6 +24,7 @@
<ul> <ul>
<li><a href="js.html">JavaScript</a></li> <li><a href="js.html">JavaScript</a></li>
<li><a href="wasm.html">WebAssembly</a></li> <li><a href="wasm.html">WebAssembly</a></li>
<li><a href="wasm-gc.html">WebAssembly GC</a></li>
</ul> </ul>
</body> </body>
</html> </html>

View File

@ -0,0 +1,62 @@
<!--
Copyright 2014 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.
-->
<!DOCTYPE html>
<html>
<head>
<title>PI calculator example</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<script type="text/javascript" charset="utf-8" src="teavm/stdout.js"></script>
<script type="text/javascript" charset="utf-8" src="wasm-gc/pi.wasm-runtime.js"></script>
<style>
#stdout {
font-family: monospace;
white-space: pre-wrap;
margin-top: 1em;
}
</style>
<script type="application/javascript">
let runner = null;
function init() {
TeaVM.wasm.load("wasm-gc/pi.wasm", {
installImports(o) {
function putwchar(ch) {
$rt_putStdoutCustom(String.fromCharCode(ch));
}
o.teavm.putcharStderr = putwchar;
o.teavm.putcharStdout = putwchar;
},
}).then(teavm => {
this.instance = teavm.instance;
runner = n => teavm.main([n.toString()]);
document.getElementById("run").disabled = false;
})
}
function calculate() {
var count = parseInt(document.getElementById("digit-count").value);
runner(count);
}
init();
</script>
</head>
<body>
<div>
Digit count:
<input type="text" id="digit-count" value="1000">
<button onclick="calculate()" id="run" disabled>Run</button>
</div>
<div id="stdout"></div>
</body>
</html>

View File

@ -19,5 +19,6 @@ public enum TeaVMTargetType {
JAVASCRIPT, JAVASCRIPT,
WEBASSEMBLY, WEBASSEMBLY,
WEBASSEMBLY_WASI, WEBASSEMBLY_WASI,
WEBASSEMBLY_GC,
C C
} }

View File

@ -38,6 +38,7 @@ import org.teavm.backend.c.generate.ShorteningFileNameProvider;
import org.teavm.backend.c.generate.SimpleFileNameProvider; import org.teavm.backend.c.generate.SimpleFileNameProvider;
import org.teavm.backend.javascript.JSModuleType; import org.teavm.backend.javascript.JSModuleType;
import org.teavm.backend.javascript.JavaScriptTarget; import org.teavm.backend.javascript.JavaScriptTarget;
import org.teavm.backend.wasm.WasmGCTarget;
import org.teavm.backend.wasm.WasmRuntimeType; import org.teavm.backend.wasm.WasmRuntimeType;
import org.teavm.backend.wasm.WasmTarget; import org.teavm.backend.wasm.WasmTarget;
import org.teavm.backend.wasm.render.WasmBinaryVersion; import org.teavm.backend.wasm.render.WasmBinaryVersion;
@ -336,6 +337,8 @@ public class TeaVMTool {
return prepareWebAssemblyDefaultTarget(); return prepareWebAssemblyDefaultTarget();
case WEBASSEMBLY_WASI: case WEBASSEMBLY_WASI:
return prepareWebAssemblyWasiTarget(); return prepareWebAssemblyWasiTarget();
case WEBASSEMBLY_GC:
return prepareWebAssemblyGCTarget();
case C: case C:
return prepareCTarget(); return prepareCTarget();
} }
@ -381,6 +384,13 @@ public class TeaVMTool {
return target; return target;
} }
private WasmGCTarget prepareWebAssemblyGCTarget() {
var target = new WasmGCTarget();
target.setObfuscated(obfuscated);
target.setStrict(strict);
return target;
}
private CTarget prepareCTarget() { private CTarget prepareCTarget() {
cTarget = new CTarget(new CNameProvider()); cTarget = new CTarget(new CNameProvider());
cTarget.setMinHeapSize(minHeapSize); cTarget.setMinHeapSize(minHeapSize);
@ -529,6 +539,7 @@ public class TeaVMTool {
return "classes.js"; return "classes.js";
case WEBASSEMBLY: case WEBASSEMBLY:
case WEBASSEMBLY_WASI: case WEBASSEMBLY_WASI:
case WEBASSEMBLY_GC:
return "classes.wasm"; return "classes.wasm";
case C: case C:
return "classes.c"; return "classes.c";

View File

@ -30,11 +30,13 @@ import org.teavm.gradle.api.TeaVMExtension;
import org.teavm.gradle.api.TeaVMJSConfiguration; import org.teavm.gradle.api.TeaVMJSConfiguration;
import org.teavm.gradle.api.TeaVMWasiConfiguration; import org.teavm.gradle.api.TeaVMWasiConfiguration;
import org.teavm.gradle.api.TeaVMWasmConfiguration; import org.teavm.gradle.api.TeaVMWasmConfiguration;
import org.teavm.gradle.api.TeaVMWasmGCConfiguration;
class TeaVMExtensionImpl extends TeaVMBaseExtensionImpl implements TeaVMExtension { class TeaVMExtensionImpl extends TeaVMBaseExtensionImpl implements TeaVMExtension {
private TeaVMJSConfiguration js; private TeaVMJSConfiguration js;
private TeaVMWasmConfiguration wasm; private TeaVMWasmConfiguration wasm;
private TeaVMWasiConfiguration wasi; private TeaVMWasiConfiguration wasi;
private TeaVMWasmGCConfiguration wasmGC;
private TeaVMCConfiguration c; private TeaVMCConfiguration c;
private TeaVMCommonConfiguration all; private TeaVMCommonConfiguration all;
@ -43,11 +45,13 @@ class TeaVMExtensionImpl extends TeaVMBaseExtensionImpl implements TeaVMExtensio
js = objectFactory.newInstance(JsConfigImpl.class); js = objectFactory.newInstance(JsConfigImpl.class);
wasm = objectFactory.newInstance(TeaVMWasmConfiguration.class); wasm = objectFactory.newInstance(TeaVMWasmConfiguration.class);
wasi = objectFactory.newInstance(TeaVMWasiConfiguration.class); wasi = objectFactory.newInstance(TeaVMWasiConfiguration.class);
wasmGC = objectFactory.newInstance(TeaVMWasmGCConfiguration.class);
c = objectFactory.newInstance(TeaVMCConfiguration.class); c = objectFactory.newInstance(TeaVMCConfiguration.class);
all = objectFactory.newInstance(TeaVMCommonConfiguration.class); all = objectFactory.newInstance(TeaVMCommonConfiguration.class);
inherit(js, all); inherit(js, all);
inherit(wasm, all); inherit(wasm, all);
inherit(wasi, all); inherit(wasi, all);
inherit(wasmGC, all);
inherit(c, all); inherit(c, all);
setupDefaults(); setupDefaults();
} }
@ -56,6 +60,7 @@ class TeaVMExtensionImpl extends TeaVMBaseExtensionImpl implements TeaVMExtensio
setupJsDefaults(); setupJsDefaults();
setupWasmDefaults(); setupWasmDefaults();
setupWasiDefaults(); setupWasiDefaults();
setupWasmGCDefaults();
setupCDefaults(); setupCDefaults();
setupAllDefaults(); setupAllDefaults();
} }
@ -95,6 +100,17 @@ class TeaVMExtensionImpl extends TeaVMBaseExtensionImpl implements TeaVMExtensio
wasm.getExceptionsUsed().convention(property("wasm.exceptionsUsed").map(Boolean::parseBoolean).orElse(true)); wasm.getExceptionsUsed().convention(property("wasm.exceptionsUsed").map(Boolean::parseBoolean).orElse(true));
} }
private void setupWasmGCDefaults() {
wasmGC.getRelativePathInOutputDir().convention("wasm-gc");
wasmGC.getOptimization().convention(property("wasm-gc.optimization").map(OptimizationLevel::valueOf)
.orElse(OptimizationLevel.AGGRESSIVE));
wasmGC.getTargetFileName().convention(project.provider(() -> project.getName() + ".wasm"));
wasmGC.getAddedToWebApp().convention(property("wasm-gc.addedToWebApp")
.map(Boolean::parseBoolean).orElse(false));
wasmGC.getStrict().convention(property("wasm-gc.strict").map(Boolean::parseBoolean).orElse(true));
wasmGC.getObfuscated().convention(property("wasm-gc.obfuscated").map(Boolean::parseBoolean).orElse(true));
}
private void setupWasiDefaults() { private void setupWasiDefaults() {
wasi.getRelativePathInOutputDir().convention("wasi"); wasi.getRelativePathInOutputDir().convention("wasi");
wasi.getMinHeapSize().convention(1); wasi.getMinHeapSize().convention(1);
@ -170,6 +186,21 @@ class TeaVMExtensionImpl extends TeaVMBaseExtensionImpl implements TeaVMExtensio
action.rehydrate(getWasi(), action.getOwner(), action.getThisObject()).call(); action.rehydrate(getWasi(), action.getOwner(), action.getThisObject()).call();
} }
@Override
public TeaVMWasmGCConfiguration getWasmGC() {
return wasmGC;
}
@Override
public void wasmGC(Action<TeaVMWasmGCConfiguration> action) {
action.execute(getWasmGC());
}
@Override
public void wasmGC(Closure<?> action) {
action.rehydrate(getWasmGC(), action.getOwner(), action.getThisObject()).call();
}
@Override @Override
public TeaVMCConfiguration getC() { public TeaVMCConfiguration getC() {
return c; return c;

View File

@ -40,6 +40,7 @@ import org.teavm.gradle.config.ArtifactCoordinates;
import org.teavm.gradle.tasks.GenerateCTask; import org.teavm.gradle.tasks.GenerateCTask;
import org.teavm.gradle.tasks.GenerateJavaScriptTask; import org.teavm.gradle.tasks.GenerateJavaScriptTask;
import org.teavm.gradle.tasks.GenerateWasiTask; import org.teavm.gradle.tasks.GenerateWasiTask;
import org.teavm.gradle.tasks.GenerateWasmGCTask;
import org.teavm.gradle.tasks.GenerateWasmTask; import org.teavm.gradle.tasks.GenerateWasmTask;
import org.teavm.gradle.tasks.JavaScriptDevServerTask; import org.teavm.gradle.tasks.JavaScriptDevServerTask;
import org.teavm.gradle.tasks.StopJavaScriptDevServerTask; import org.teavm.gradle.tasks.StopJavaScriptDevServerTask;
@ -53,6 +54,7 @@ public class TeaVMPlugin implements Plugin<Project> {
public static final String STOP_JS_DEV_SERVER_TASK_NAME = "stopJavaScriptDevServer"; public static final String STOP_JS_DEV_SERVER_TASK_NAME = "stopJavaScriptDevServer";
public static final String WASM_TASK_NAME = "generateWasm"; public static final String WASM_TASK_NAME = "generateWasm";
public static final String WASI_TASK_NAME = "generateWasi"; public static final String WASI_TASK_NAME = "generateWasi";
public static final String WASM_GC_TASK_NAME = "generateWasmGC";
public static final String C_TASK_NAME = "generateC"; public static final String C_TASK_NAME = "generateC";
public static final String CONFIGURATION_NAME = "teavm"; public static final String CONFIGURATION_NAME = "teavm";
public static final String CLASSPATH_CONFIGURATION_NAME = "teavmClasspath"; public static final String CLASSPATH_CONFIGURATION_NAME = "teavmClasspath";
@ -118,6 +120,7 @@ public class TeaVMPlugin implements Plugin<Project> {
registerStopJsDevServerTask(project); registerStopJsDevServerTask(project);
registerWasmTask(project, compilerConfig); registerWasmTask(project, compilerConfig);
registerWasiTask(project, compilerConfig); registerWasiTask(project, compilerConfig);
registerWasmGCTask(project, compilerConfig);
registerCTask(project, compilerConfig); registerCTask(project, compilerConfig);
} }
@ -210,6 +213,17 @@ public class TeaVMPlugin implements Plugin<Project> {
}); });
} }
private void registerWasmGCTask(Project project, Configuration configuration) {
var extension = project.getExtensions().getByType(TeaVMExtension.class);
project.getTasks().create(WASM_GC_TASK_NAME, GenerateWasmGCTask.class, task -> {
var wasmGC = extension.getWasmGC();
applyToTask(wasmGC, task, configuration);
task.getTargetFileName().convention(wasmGC.getTargetFileName());
task.getObfuscated().convention(wasmGC.getObfuscated());
task.getStrict().convention(wasmGC.getStrict());
});
}
private void registerCTask(Project project, Configuration configuration) { private void registerCTask(Project project, Configuration configuration) {
var extension = project.getExtensions().getByType(TeaVMExtension.class); var extension = project.getExtensions().getByType(TeaVMExtension.class);
project.getTasks().create(C_TASK_NAME, GenerateCTask.class, task -> { project.getTasks().create(C_TASK_NAME, GenerateCTask.class, task -> {
@ -236,6 +250,7 @@ public class TeaVMPlugin implements Plugin<Project> {
if (task.getName().equals(WarPlugin.WAR_TASK_NAME)) { if (task.getName().equals(WarPlugin.WAR_TASK_NAME)) {
var jsAddedToWebApp = extension.getJs().getAddedToWebApp().get(); var jsAddedToWebApp = extension.getJs().getAddedToWebApp().get();
var wasmAddedToWebApp = extension.getWasm().getAddedToWebApp().get(); var wasmAddedToWebApp = extension.getWasm().getAddedToWebApp().get();
var wasmGCAddedToWebApp = extension.getWasmGC().getAddedToWebApp().get();
if (jsAddedToWebApp) { if (jsAddedToWebApp) {
task.dependsOn(project.getTasks().named(JS_TASK_NAME)); task.dependsOn(project.getTasks().named(JS_TASK_NAME));
var outDir = extension.getJs().getOutputDir(); var outDir = extension.getJs().getOutputDir();
@ -255,6 +270,15 @@ public class TeaVMPlugin implements Plugin<Project> {
spec.from(project.files(outDir.map(dir -> new File(dir.getAsFile(), relPath.get())))); spec.from(project.files(outDir.map(dir -> new File(dir.getAsFile(), relPath.get()))));
})); }));
} }
if (wasmGCAddedToWebApp) {
task.dependsOn(project.getTasks().named(WASM_GC_TASK_NAME));
var outDir = extension.getWasmGC().getOutputDir();
var relPath = extension.getWasmGC().getRelativePathInOutputDir();
task.with(project.copySpec(spec -> {
spec.into(relPath);
spec.from(project.files(outDir.map(dir -> new File(dir.getAsFile(), relPath.get()))));
}));
}
} }
}); });
} }

View File

@ -38,6 +38,12 @@ public interface TeaVMExtension extends TeaVMBaseExtension {
void wasi(@DelegatesTo(TeaVMWasiConfiguration.class) Closure<?> action); void wasi(@DelegatesTo(TeaVMWasiConfiguration.class) Closure<?> action);
TeaVMWasmGCConfiguration getWasmGC();
void wasmGC(Action<TeaVMWasmGCConfiguration> action);
void wasmGC(@DelegatesTo(TeaVMWasmGCConfiguration.class) Closure<?> action);
TeaVMCConfiguration getC(); TeaVMCConfiguration getC();
void c(Action<TeaVMCConfiguration> action); void c(Action<TeaVMCConfiguration> action);

View File

@ -0,0 +1,26 @@
/*
* 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.gradle.api;
import org.gradle.api.provider.Property;
public interface TeaVMWasmGCConfiguration extends TeaVMCommonConfiguration, TeaVMWebConfiguration {
Property<Boolean> getObfuscated();
Property<Boolean> getStrict();
Property<String> getTargetFileName();
}

View File

@ -0,0 +1,43 @@
/*
* 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.gradle.tasks;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.Input;
import org.teavm.tooling.TeaVMTargetType;
import org.teavm.tooling.builder.BuildStrategy;
public abstract class GenerateWasmGCTask extends TeaVMTask {
private static final int MB = 1024 * 1024;
public GenerateWasmGCTask() {
getStrict().convention(true);
getObfuscated().convention(true);
}
@Input
public abstract Property<Boolean> getStrict();
@Input
public abstract Property<Boolean> getObfuscated();
@Override
protected void setupBuilder(BuildStrategy builder) {
builder.setStrict(getStrict().get());
builder.setObfuscated(getObfuscated().get());
builder.setTargetType(TeaVMTargetType.WEBASSEMBLY_GC);
}
}