Skip to content

Commit 6faa5f3

Browse files
askeksaCommit Bot
authored and
Commit Bot
committed
[dart2wasm] Initial commit for the Dart-to-WasmGC compiler.
This is work in progress. Several language features are still unimplemented or only partially implemented. Instructions for running the compiler and its output can be found in pkg/dart2wasm/dart2wasm.md. These procedures are preliminary and expected to change. The best version of d8 to use for this version of dart2wasm is 10.0.40, as explained here: https://dart-review.googlesource.com/c/sdk/+/232097 This commit also adds a dart2wasm-hostasserts-linux-x64-d8 testing configuration to run the compiler over the test suite. The history of the prototype that this is based on can be seen here: https://github.com/askeksa-google/sdk/tree/wasm_prototype Issue: #32894 Change-Id: I910b6ff239ef9c5f66863e4ca97b39b8202cce85 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/175728 Reviewed-by: Joshua Litt <[email protected]> Commit-Queue: Aske Simon Christensen <[email protected]>
1 parent 5e4f36e commit 6faa5f3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+15752
-1
lines changed

.dart_tool/package_config.json

+12
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,12 @@
228228
"packageUri": "lib/",
229229
"languageVersion": "2.12"
230230
},
231+
{
232+
"name": "dart2wasm",
233+
"rootUri": "../pkg/dart2wasm",
234+
"packageUri": "lib/",
235+
"languageVersion": "2.12"
236+
},
231237
{
232238
"name": "dart_internal",
233239
"rootUri": "../pkg/dart_internal",
@@ -780,6 +786,12 @@
780786
"packageUri": "lib/",
781787
"languageVersion": "2.12"
782788
},
789+
{
790+
"name": "wasm_builder",
791+
"rootUri": "../pkg/wasm_builder",
792+
"packageUri": "lib/",
793+
"languageVersion": "2.12"
794+
},
783795
{
784796
"name": "watcher",
785797
"rootUri": "../third_party/pkg/watcher",

.packages

+2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ dart2js_info:pkg/dart2js_info/lib
3333
dart2js_runtime_metrics:pkg/dart2js_runtime_metrics/lib
3434
dart2js_tools:pkg/dart2js_tools/lib
3535
dart2native:pkg/dart2native/lib
36+
dart2wasm:pkg/dart2wasm/lib
3637
dart_internal:pkg/dart_internal/lib
3738
dart_style:third_party/pkg_tested/dart_style/lib
3839
dartdev:pkg/dartdev/lib
@@ -117,6 +118,7 @@ vector_math:third_party/pkg/vector_math/lib
117118
vm:pkg/vm/lib
118119
vm_service:pkg/vm_service/lib
119120
vm_snapshot_analysis:pkg/vm_snapshot_analysis/lib
121+
wasm_builder:pkg/wasm_builder/lib
120122
watcher:third_party/pkg/watcher/lib
121123
webdriver:third_party/pkg/webdriver/lib
122124
webkit_inspection_protocol:third_party/pkg/webkit_inspection_protocol/lib

pkg/dart2wasm/bin/dart2wasm.dart

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:io';
6+
import 'dart:typed_data';
7+
8+
import 'package:front_end/src/api_unstable/vm.dart'
9+
show printDiagnosticMessage, resolveInputUri;
10+
11+
import 'package:dart2wasm/compile.dart';
12+
import 'package:dart2wasm/translator.dart';
13+
14+
final Map<String, void Function(TranslatorOptions, bool)> boolOptionMap = {
15+
"export-all": (o, value) => o.exportAll = value,
16+
"inlining": (o, value) => o.inlining = value,
17+
"lazy-constants": (o, value) => o.lazyConstants = value,
18+
"local-nullability": (o, value) => o.localNullability = value,
19+
"name-section": (o, value) => o.nameSection = value,
20+
"nominal-types": (o, value) => o.nominalTypes = value,
21+
"parameter-nullability": (o, value) => o.parameterNullability = value,
22+
"polymorphic-specialization": (o, value) =>
23+
o.polymorphicSpecialization = value,
24+
"print-kernel": (o, value) => o.printKernel = value,
25+
"print-wasm": (o, value) => o.printWasm = value,
26+
"runtime-types": (o, value) => o.runtimeTypes = value,
27+
"string-data-segments": (o, value) => o.stringDataSegments = value,
28+
};
29+
final Map<String, void Function(TranslatorOptions, int)> intOptionMap = {
30+
"watch": (o, value) => (o.watchPoints ??= []).add(value),
31+
};
32+
33+
Never usage(String message) {
34+
print("Usage: dart2wasm [<options>] <infile.dart> <outfile.wasm>");
35+
print("");
36+
print("Options:");
37+
print(" --dart-sdk=<path>");
38+
print("");
39+
for (String option in boolOptionMap.keys) {
40+
print(" --[no-]$option");
41+
}
42+
print("");
43+
for (String option in intOptionMap.keys) {
44+
print(" --$option <value>");
45+
}
46+
print("");
47+
48+
throw message;
49+
}
50+
51+
Future<int> main(List<String> args) async {
52+
Uri sdkPath = Platform.script.resolve("../../../sdk");
53+
TranslatorOptions options = TranslatorOptions();
54+
List<String> nonOptions = [];
55+
void Function(TranslatorOptions, int)? intOptionFun = null;
56+
for (String arg in args) {
57+
if (intOptionFun != null) {
58+
intOptionFun(options, int.parse(arg));
59+
intOptionFun = null;
60+
} else if (arg.startsWith("--dart-sdk=")) {
61+
String path = arg.substring("--dart-sdk=".length);
62+
sdkPath = Uri.file(Directory(path).absolute.path);
63+
} else if (arg.startsWith("--no-")) {
64+
var optionFun = boolOptionMap[arg.substring(5)];
65+
if (optionFun == null) usage("Unknown option $arg");
66+
optionFun(options, false);
67+
} else if (arg.startsWith("--")) {
68+
var optionFun = boolOptionMap[arg.substring(2)];
69+
if (optionFun != null) {
70+
optionFun(options, true);
71+
} else {
72+
intOptionFun = intOptionMap[arg.substring(2)];
73+
if (intOptionFun == null) usage("Unknown option $arg");
74+
}
75+
} else {
76+
nonOptions.add(arg);
77+
}
78+
}
79+
if (intOptionFun != null) {
80+
usage("Missing argument to ${args.last}");
81+
}
82+
83+
if (nonOptions.length != 2) usage("Requires two file arguments");
84+
String input = nonOptions[0];
85+
String output = nonOptions[1];
86+
Uri mainUri = resolveInputUri(input);
87+
88+
Uint8List? module = await compileToModule(mainUri, sdkPath, options,
89+
(message) => printDiagnosticMessage(message, print));
90+
91+
if (module == null) {
92+
exitCode = 1;
93+
return exitCode;
94+
}
95+
96+
await File(output).writeAsBytes(module);
97+
98+
return 0;
99+
}

pkg/dart2wasm/bin/run_wasm.js

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
//
5+
// Runner V8 script for testing dart2wasm, takes ".wasm" file as argument.
6+
// Run as follows:
7+
//
8+
// $> d8 --experimental-wasm-gc --wasm-gc-js-interop run_wasm.js -- <file_name>.wasm
9+
10+
function stringFromDartString(string) {
11+
var length = inst.exports.$stringLength(string);
12+
var array = new Array(length);
13+
for (var i = 0; i < length; i++) {
14+
array[i] = inst.exports.$stringRead(string, i);
15+
}
16+
return String.fromCharCode(...array);
17+
}
18+
19+
function stringToDartString(string) {
20+
var length = string.length;
21+
var range = 0;
22+
for (var i = 0; i < length; i++) {
23+
range |= string.codePointAt(i);
24+
}
25+
if (range < 256) {
26+
var dartString = inst.exports.$stringAllocate1(length);
27+
for (var i = 0; i < length; i++) {
28+
inst.exports.$stringWrite1(dartString, i, string.codePointAt(i));
29+
}
30+
return dartString;
31+
} else {
32+
var dartString = inst.exports.$stringAllocate2(length);
33+
for (var i = 0; i < length; i++) {
34+
inst.exports.$stringWrite2(dartString, i, string.codePointAt(i));
35+
}
36+
return dartString;
37+
}
38+
}
39+
40+
// Imports for printing and event loop
41+
var dart2wasm = {
42+
printToConsole: function(string) {
43+
console.log(stringFromDartString(string))
44+
},
45+
scheduleCallback: function(milliseconds, closure) {
46+
setTimeout(function() {
47+
inst.exports.$call0(closure);
48+
}, milliseconds);
49+
}
50+
};
51+
52+
// Create a Wasm module from the binary wasm file.
53+
var bytes = readbuffer(arguments[0]);
54+
var module = new WebAssembly.Module(bytes);
55+
56+
// Instantiate the Wasm module, importing from the global scope.
57+
var importObject = (typeof window !== 'undefined')
58+
? window
59+
: Realm.global(Realm.current());
60+
var inst = new WebAssembly.Instance(module, importObject);
61+
62+
var result = inst.exports.main();
63+
if (result) console.log(result);

pkg/dart2wasm/dart2wasm.md

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
## Running dart2wasm
2+
3+
You don't need to build the Dart SDK to run dart2wasm, as long as you have a Dart SDK installed.
4+
5+
To compile a Dart file to Wasm, run:
6+
7+
`dart --enable-asserts pkg/dart2wasm/bin/dart2wasm.dart` *options* *infile*`.dart` *outfile*`.wasm`
8+
9+
where *options* include:
10+
11+
| Option | Default | Description |
12+
| --------------------------------------- | ------- | ----------- |
13+
| `--dart-sdk=`*path* | relative to script | The location of the `sdk` directory inside the Dart SDK, containing the core library sources.
14+
| `--`[`no-`]`export-all` | no | Export all functions; otherwise, just export `main`.
15+
| `--`[`no-`]`inlining` | no | Inline small functions.
16+
| `--`[`no-`]`lazy-constants` | no | Instantiate constants lazily.
17+
| `--`[`no-`]`local-nullability` | no | Use non-nullable types for non-nullable locals and temporaries.
18+
| `--`[`no-`]`name-section` | yes | Emit Name Section with function names.
19+
| `--`[`no-`]`nominal-types` | no | Emit experimental nominal types.
20+
| `--`[`no-`]`parameter-nullability` | yes | Use non-nullable types for non-nullable parameters and return values.
21+
| `--`[`no-`]`polymorphic-specialization` | no | Do virtual calls by switching on the class ID instead of using `call_indirect`.
22+
| `--`[`no-`]`print-kernel` | no | Print IR for each function before compiling it.
23+
| `--`[`no-`]`print-wasm` | no | Print Wasm instructions of each compiled function.
24+
| `--`[`no-`]`runtime-types` | yes | Use RTTs for allocations and casts.
25+
| `--`[`no-`]`string-data-segments` | no | Use experimental array init from data segment for string constants.
26+
| `--watch` *offset* | | Print stack trace leading to the byte at offset *offset* in the `.wasm` output file. Can be specified multiple times.
27+
28+
The resulting `.wasm` file can be run with:
29+
30+
`d8 --experimental-wasm-gc --wasm-gc-js-interop pkg/dart2wasm/bin/run_wasm.js -- `*outfile*`.wasm`
31+
32+
## Imports and exports
33+
34+
To import a function, declare it as a global, external function and mark it with a `wasm:import` pragma indicating the imported name (which must be two identifiers separated by a dot):
35+
```dart
36+
@pragma("wasm:import", "foo.bar")
37+
external void fooBar(Object object);
38+
```
39+
which will call `foo.bar` on the host side:
40+
```javascript
41+
var foo = {
42+
bar: function(object) { /* implementation here */ }
43+
};
44+
```
45+
To export a function, mark it with a `wasm:export` pragma:
46+
```dart
47+
@pragma("wasm:export")
48+
void foo(double x) { /* implementation here */ }
49+
50+
@pragma("wasm:export", "baz")
51+
void bar(double x) { /* implementation here */ }
52+
```
53+
With the Wasm module instance in `inst`, these can be called as:
54+
```javascript
55+
inst.exports.foo(1);
56+
inst.exports.baz(2);
57+
```
58+
59+
### Types to use for interop
60+
61+
In the signatures of imported and exported functions, use the following types:
62+
63+
- For numbers, use `double`.
64+
- For Dart objects, use the corresponding Dart type. The fields of the underlying representation can be accessed on the JS side as `.$field0`, `.$field1` etc., but there is currently no defined way of finding the field index of a particular Dart field, so this mechanism is mainly useful for special objects with known layout.
65+
- For JS objects, use the `WasmAnyRef` type (or `WasmAnyRef?` as applicable) from the `dart:wasm` package. These can be passed around and stored as opaque values on the Dart side.

0 commit comments

Comments
 (0)