Skip to content

Commit 6417d99

Browse files
committed
[GR-54677] Support new exception handling proposal
PullRequest: graal/22458
2 parents aab6528 + 880aa24 commit 6417d99

File tree

18 files changed

+516
-93
lines changed

18 files changed

+516
-93
lines changed

web-image/README.md

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,76 @@
11
# Web Image
22

3+
Web Image is an experimental backend for Native Image that produces a
4+
WebAssembly module from Java code.
5+
6+
## Prerequisites
7+
8+
Being part of Native Image, see the [Quick Start Guide](../docs/reference-manual/native-image/contribute/DevelopingNativeImage.md)
9+
for all the prerequisites for building Native Image.
10+
11+
In addition, Web Image also uses `wasm-as` from
12+
[binaryen](https://github.com/WebAssembly/binaryen) as its assembler.
13+
Version 119 of `wasm-as` has to be available on your `PATH`.
14+
15+
<details>
16+
<summary>Installation</summary>
17+
18+
Binaryen provides pre-built releases for all major platforms on
19+
[GitHub](https://github.com/WebAssembly/binaryen/releases).
20+
21+
On MacOS, it is recommended to install through [Homebrew](https://brew.sh/), as
22+
the pre-built binaries are not signed and may be quarantined by MacOS.
23+
24+
```bash
25+
brew install binaryen
26+
```
27+
</details>
28+
329
## Building
430

31+
To build Web Image, run the `mx build` command in the `web-image` suite:
32+
533
```bash
34+
cd /path/to/graal
35+
cd web-image
636
mx build
737
```
838

939
## Usage
1040

11-
Use this as you would regular the regular `native-image` tool, but with an
41+
Use this as you would use the regular `native-image` tool, but with an
1242
additional `--tool:svm-wasm` flag to enable the WebAssembly backend:
1343

1444
```bash
1545
mx native-image --tool:svm-wasm -cp ... HelloWorld
1646
```
1747

1848
This produces `helloworld.js` and `helloworld.js.wasm` in your working
19-
directory. The JavaScript file is a wrapper that loads and runs the WebAssembly
20-
code and can be run with [Node.js](https://nodejs.org/en) 22 or later:
49+
directory. The `--tool:svm-wasm` flag should be the first argument if possible.
50+
If any experimental options specific to the Wasm backend are used, they can
51+
only be added after the `--tool:svm-wasm` flag.
2152

22-
The `--tool:svm-wasm` flag should be the first argument if possible. If any
23-
experimental options specific to the Wasm backend are used, they can only be
24-
added after the `--tool:svm-wasm` flag.
53+
The JavaScript file is a wrapper that loads and runs the WebAssembly
54+
code and can be run with [Node.js](https://nodejs.org/en) 22 or later:
2555

2656
```bash
27-
$ node helloworld.js
57+
# --experimental-wasm-exnref is only required for Node versions before 25
58+
$ node --experimental-wasm-exnref helloworld.js
2859
Hello World
2960
```
3061

62+
## WebAssembly Features
63+
64+
The WebAssembly code generated by Web Image makes use of various WebAssembly
65+
features from WebAssembly 3.0:
66+
67+
- [Garbage Collection](https://github.com/WebAssembly/gc)
68+
- [Exception Handling](https://github.com/WebAssembly/exception-handling/blob/master/proposals/exception-handling/Exceptions.md)
69+
- [Typed Function References](https://github.com/WebAssembly/function-references/blob/main/proposals/function-references/Overview.md)
70+
71+
Support for [WebAssembly 2.0](https://www.w3.org/TR/wasm-core-2/#release-20) is
72+
generally assumed.
73+
3174
## Contributors
3275

3376
- Aleksandar Prokopec

web-image/mx.web-image/mx_web_image.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949

5050
_suite = mx.suite("web-image")
5151

52-
_web_image_js_engine_name = os.getenv("NODE_EXE", "node")
52+
_web_image_js_engine_name = [os.getenv("NODE_EXE", "node"), "--experimental-wasm-exnref"]
5353

5454
# Name of GraalVm component defining the web-image macro
5555
web_image_component = "web-image"
@@ -63,7 +63,7 @@
6363
"web-image:WEBIMAGE_CLOSURE_SUPPORT",
6464
"web-image:WEBIMAGE_GOOGLE_CLOSURE",
6565
]
66-
# Hosted options defined in the web-image-enterprise suite
66+
# Hosted options defined in the web-image suite
6767
# This list has to be kept in sync with the code (the 'webimageoptions' gate tag checks this)
6868
# See also WebImageConfiguration.hosted_options
6969
web_image_hosted_options = [
@@ -92,6 +92,7 @@
9292
"GrowthTriggerThreshold=",
9393
"HeapGrowthFactor=",
9494
"ImageHeapObjectsPerFunction=",
95+
"LegacyExceptions",
9596
"JSComments=",
9697
"JSRuntime=",
9798
"LogFilter=",
@@ -489,7 +490,7 @@ def __init__(self):
489490
def apply(self, config):
490491
vm_args, main_class, main_class_args = config
491492

492-
vm_args += ["-Dwebimage.test.js=" + _web_image_js_engine_name]
493+
vm_args += ["-Dwebimage.test.js=" + ",".join(_web_image_js_engine_name)]
493494
vm_args += ["-Dwebimage.test.launcher=" + vm_web_image_path()]
494495
vm_args += ["-Dwebimage.test.flags=" + ",".join(get_launcher_flags(WebImageConfiguration.test_cases))]
495496
# If any of the arguments contains spaces and double quotes, on Windows it will add its own quotes around

web-image/src/com.oracle.svm.hosted.webimage.test/src/com/oracle/svm/hosted/webimage/test/util/WebImageTestOptions.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
import jdk.graal.compiler.debug.GraalError;
3232

3333
public class WebImageTestOptions {
34-
public static final String JS_CMD = System.getProperty("webimage.test.js");
34+
public static final List<String> JS_CMD = Arrays.asList(System.getProperty("webimage.test.js", ",").split(","));
3535
private static final String LAUNCHER = System.getProperty("webimage.test.launcher");
3636

3737
/**

web-image/src/com.oracle.svm.hosted.webimage.test/src/com/oracle/svm/hosted/webimage/test/util/WebImageTestUtil.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,7 @@ public static RunResult executeTestProgram(Class<?> c, String[] args, int expect
191191
}
192192

193193
public static RunResult runJS(String cmd, String[] arguments, int expectExitCode) {
194-
List<String> invokeCmd = new ArrayList<>();
195-
invokeCmd.add(WebImageTestOptions.JS_CMD);
194+
List<String> invokeCmd = new ArrayList<>(WebImageTestOptions.JS_CMD);
196195
invokeCmd.add(cmd);
197196

198197
if (arguments != null) {

web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/NativeImageWasmGeneratorRunner.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@
5454
import com.oracle.svm.hosted.webimage.options.WebImageOptions.CompilerBackend;
5555
import com.oracle.svm.hosted.webimage.util.BenchmarkLogger;
5656
import com.oracle.svm.hosted.webimage.wasm.WebImageWasmLMJavaMainSupport;
57-
import com.oracle.svm.hosted.webimage.wasm.codegen.BinaryenCompat;
5857
import com.oracle.svm.hosted.webimage.wasmgc.WebImageWasmGCJavaMainSupport;
5958
import com.oracle.svm.webimage.WebImageJSJavaMainSupport;
6059
import com.oracle.svm.webimage.WebImageJavaMainSupport;
@@ -169,11 +168,6 @@ public int build(ImageClassLoader classLoader) {
169168
// For the Wasm backends, turn off closure compiler
170169
optionProvider.getHostedValues().put(WebImageOptions.ClosureCompiler, false);
171170

172-
if (backend == CompilerBackend.WASMGC && !optionProvider.getHostedValues().containsKey(BinaryenCompat.Options.UseBinaryen)) {
173-
// For WasmGC backend, use binaryen by default
174-
optionProvider.getHostedValues().put(BinaryenCompat.Options.UseBinaryen, true);
175-
}
176-
177171
if (!optionProvider.getHostedValues().containsKey(WebImageOptions.NamingConvention)) {
178172
// The naming convention does not affect the binary image (unless debug information
179173
// is embedded) and the REDUCED mode makes the text file a lot easier to read

web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/codegen/reconstruction/stackifier/StackifierData.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ public class StackifierData implements ReconstructionData {
5858
*/
5959
private HIRBlock[] blocks;
6060

61+
private ControlFlowGraph cfg;
62+
6163
/**
6264
* Mapping from a basic block to its index in {@link #blocks}.
6365
*/
@@ -92,6 +94,10 @@ public HIRBlock[] getBlocks() {
9294
return blocks;
9395
}
9496

97+
public ControlFlowGraph getCfg() {
98+
return cfg;
99+
}
100+
95101
public EconomicMap<HIRBlock, Scope> getEnclosingScope() {
96102
return enclosingScope;
97103
}
@@ -134,6 +140,7 @@ public void setLabeledBlockEnd(EconomicMap<HIRBlock, LabeledBlock> labeledBlockE
134140

135141
public void setSortedBlocks(HIRBlock[] sortedBlocks, ControlFlowGraph cfg) {
136142
this.blocks = sortedBlocks;
143+
this.cfg = cfg;
137144
this.blockIndexSortOrder = new BlockMap<>(cfg);
138145
for (int i = 0; i < sortedBlocks.length; ++i) {
139146
this.blockIndexSortOrder.put(sortedBlocks[i], i);

web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasm/WebImageWasmOptions.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ public class WebImageWasmOptions {
4949
"Has no effect on code size, the binary format does not have comments.")//
5050
public static final EnumOptionKey<CommentVerbosity> WasmComments = new EnumOptionKey<>(CommentVerbosity.NORMAL);
5151

52+
@Option(help = "Enable the legacy exception proposal using try-catch instead of try_table") //
53+
public static final HostedOptionKey<Boolean> LegacyExceptions = new HostedOptionKey<>(false);
54+
5255
@Option(help = "Assemble the Wasm binary file with debug names.")//
5356
public static final HostedOptionKey<Boolean> DebugNames = new HostedOptionKey<>(false) {
5457

web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasm/ast/Instruction.java

Lines changed: 74 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -148,14 +148,33 @@ public abstract static class WasmBlock extends Instruction {
148148
* If set to null, the block has no named identifier
149149
*/
150150
protected final WasmId.Label label;
151+
protected final WasmValType result;
151152

152-
public WasmBlock(WasmId.Label label) {
153+
protected WasmBlock(WasmId.Label label, WasmValType result) {
153154
this.label = label;
155+
this.result = result;
154156
}
155157

156158
public WasmId.Label getLabel() {
157159
return label;
158160
}
161+
162+
public WasmValType getResult() {
163+
return result;
164+
}
165+
166+
public boolean hasResult() {
167+
return result != null;
168+
}
169+
170+
@Override
171+
protected String toInnerString() {
172+
if (hasResult()) {
173+
return "() -> " + result;
174+
} else {
175+
return "() -> ()";
176+
}
177+
}
159178
}
160179

161180
/**
@@ -165,7 +184,11 @@ public static final class Block extends WasmBlock {
165184
public final Instructions instructions = new Instructions();
166185

167186
public Block(WasmId.Label label) {
168-
super(label);
187+
this(label, null);
188+
}
189+
190+
public Block(WasmId.Label label, WasmValType result) {
191+
super(label, result);
169192
}
170193
}
171194

@@ -176,7 +199,7 @@ public static final class Loop extends WasmBlock {
176199
public final Instructions instructions = new Instructions();
177200

178201
public Loop(WasmId.Label label) {
179-
super(label);
202+
super(label, null);
180203
}
181204
}
182205

@@ -190,7 +213,7 @@ public static final class If extends WasmBlock {
190213
public final Instruction condition;
191214

192215
public If(WasmId.Label label, Instruction condition) {
193-
super(label);
216+
super(label, null);
194217
this.condition = condition;
195218
}
196219

@@ -200,10 +223,50 @@ public boolean hasElse() {
200223
}
201224

202225
/**
203-
* The WASM try block from the exception handling proposal.
226+
* The WASM try_table from the exception handling proposal.
204227
* <p>
205228
* Ref: https://github.com/WebAssembly/exception-handling
206229
*/
230+
public static final class TryTable extends WasmBlock {
231+
/**
232+
* A catch clause for a certain tag.
233+
* <p>
234+
* When an exception is caught, the block branches to the label of the appropriate clause.
235+
*/
236+
public static final class Catch {
237+
public final WasmId.Tag tag;
238+
public final WasmId.Label label;
239+
240+
private Catch(WasmId.Tag tag, WasmId.Label label) {
241+
this.tag = tag;
242+
this.label = label;
243+
}
244+
245+
@Override
246+
public String toString() {
247+
return "Catch{tag=" + tag + ", label=" + label + '}';
248+
}
249+
}
250+
251+
public final Instructions instructions = new Instructions();
252+
public final List<Catch> catchBlocks = new ArrayList<>();
253+
254+
public TryTable(WasmId.Label label) {
255+
super(label, null);
256+
}
257+
258+
public void addCatch(WasmId.Tag tag, WasmId.Label catchLabel) {
259+
var catchBlock = new Catch(tag, catchLabel);
260+
catchBlocks.add(catchBlock);
261+
}
262+
}
263+
264+
/**
265+
* The WASM try block from the legacy exception handling proposal.
266+
* <p>
267+
* Ref:
268+
* https://github.com/WebAssembly/exception-handling/blob/master/proposals/exception-handling/legacy/Exceptions.md
269+
*/
207270
public static final class Try extends WasmBlock {
208271

209272
/**
@@ -222,7 +285,7 @@ private Catch(WasmId.Tag tag) {
222285
public final List<Catch> catchBlocks = new ArrayList<>();
223286

224287
public Try(WasmId.Label label) {
225-
super(label);
288+
super(label, null);
226289
}
227290

228291
public Instructions addCatch(WasmId.Tag tag) {
@@ -546,6 +609,11 @@ public static Const forWord(WordBase word) {
546609
// TODO GR-42105 Use forInt
547610
return forLong(word.rawValue());
548611
}
612+
613+
@Override
614+
protected String toInnerString() {
615+
return literal.type + ", " + literal.asText();
616+
}
549617
}
550618

551619
/**

0 commit comments

Comments
 (0)