[dart2wasm] Document standalone platform and include it in SDK
Given that the standalone target for dart2wasm is feature-complete now, it makes sense to include it in released SDKs. This adds the platform and outline files to built SDKs and exposes the `--standalone` flag in `dart compile wasm`. It also documents the standalone target in `pkg/dart2wasm/doc`, which should be helpful as a starting point to use these compiled modules. Change-Id: I5bd86e9670f03f2955e31789095dd5c462bf149e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/506920 Reviewed-by: Slava Egorov <vegorov@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com> Commit-Queue: Martin Kustermann <kustermann@google.com>
This commit is contained in:
committed by
dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent
7749454698
commit
567bf3337b
@@ -144,6 +144,10 @@ class Sdk {
|
||||
'dart2wasm_platform.dill',
|
||||
);
|
||||
|
||||
String get wasmStandalonePlatformDill => _dillPathFor(
|
||||
'dart2wasm_standalone_platform.dill',
|
||||
);
|
||||
|
||||
String _dillPathFor(String dillName) => path.absolute(
|
||||
runFromBuildRoot
|
||||
? sdkPath
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
# Standalone mode
|
||||
|
||||
By default, dart2wasm generates WebAssembly modules meant to run on platforms
|
||||
with JavaScript support (mainly, browsers).
|
||||
Both the SDK and application code can rely on `dart:js_interop` libraries to
|
||||
access external JavaScript APIs.
|
||||
|
||||
For WebAssembly targets without a JavaScript engine (like wasmtime or similar
|
||||
runtimes), dart2wasm supports a standalone target too. This target is enabled
|
||||
with the `--standalone` compiler flag, and makes `dart:js_interop` unavailable.
|
||||
|
||||
Even in standalone mode, the SDK needs host imports for functionality like
|
||||
timers, stack traces, regular expressions, `dart:math` or number formatting.
|
||||
These imports are referenced in [this file](https://github.com/dart-lang/sdk/blob/main/sdk/lib/_internal/wasm/standalone/embedder.dart).
|
||||
Note that these imports are not stable, and might change in future Dart
|
||||
versions.
|
||||
|
||||
Additional definitions can be imported via [imports and exports](./imports_and_exports.md).
|
||||
|
||||
## Running standalone modules
|
||||
|
||||
There are two plausible ways to run standalone modules:
|
||||
|
||||
1. By using APIs from a WebAssembly engine to define definitions for imports in
|
||||
native code. There is no example for this in the SDK, but test definitions
|
||||
for browser tests (`dart2WasmStandaloneHtml` in `pkg/test_runner/lib/src/browser.dart`)
|
||||
might help as a starting point.
|
||||
2. By re-implementing a subset of imports in WebAssembly, and then using tools
|
||||
like `wasm-merge` from Binaryen to link two modules into a module with fewer
|
||||
or no dependencies. Implementing Dart imports by delegating to WASI
|
||||
definitions would allow running these modules without custom native imports,
|
||||
for example.
|
||||
|
||||
Examples for both options are discussed in the [tracking issue](https://github.com/dart-lang/sdk/issues/53884).
|
||||
@@ -894,6 +894,14 @@ class CompileWasmCommand extends CompileSubcommandCommand {
|
||||
negatable: false,
|
||||
help: enableAssertsOption.help,
|
||||
)
|
||||
..addFlag(
|
||||
'standalone',
|
||||
help:
|
||||
'Compile to a WebAssembly module without JavaScript interop. '
|
||||
'Dart-specific host imports are necessary to load these modules.',
|
||||
negatable: false,
|
||||
hide: !verbose,
|
||||
)
|
||||
..addOption(
|
||||
'shared-memory',
|
||||
help:
|
||||
@@ -1046,10 +1054,14 @@ class CompileWasmCommand extends CompileSubcommandCommand {
|
||||
|
||||
final generateSourceMap = args.flag('source-maps');
|
||||
final enabledExperiments = args.enabledExperiments;
|
||||
final standalone = args.flag('standalone');
|
||||
final platform = standalone
|
||||
? sdk.wasmStandalonePlatformDill
|
||||
: sdk.wasmPlatformDill;
|
||||
final dart2wasmCommand = [
|
||||
sdk.dartAotRuntime,
|
||||
sdk.dart2wasmSnapshot,
|
||||
'--platform=${sdk.wasmPlatformDill}',
|
||||
'--platform=$platform',
|
||||
if (verbose) '--verbose',
|
||||
if (packages != null) '--packages=$packages',
|
||||
if (args.flag('print-wasm')) '--print-wasm',
|
||||
@@ -1060,6 +1072,7 @@ class CompileWasmCommand extends CompileSubcommandCommand {
|
||||
if (args.flag('minify')) '--minify',
|
||||
if (!args.flag('strip-wasm')) '--no-strip-wasm',
|
||||
if (args.flag('enable-deferred-loading')) '--enable-deferred-loading',
|
||||
if (standalone) '--standalone',
|
||||
if (args.option(recordedUsesOption.flag) != null)
|
||||
'--recorded-uses=${args.option(recordedUsesOption.flag)}',
|
||||
for (final define in defines) '-D$define',
|
||||
@@ -1091,10 +1104,23 @@ class CompileWasmCommand extends CompileSubcommandCommand {
|
||||
|
||||
if (isDryRun) return 0;
|
||||
|
||||
final mjsFile = '$outputFileBasename.mjs';
|
||||
log.stdout(
|
||||
"Generated wasm module '$outputFile', and JS init file '$mjsFile'.",
|
||||
);
|
||||
if (standalone) {
|
||||
log.stdout(
|
||||
"Generated wasm module '$outputFile'. See "
|
||||
'https://github.com/dart-lang/sdk/blob/main/pkg/dart2wasm/docs/standalone.md '
|
||||
'for usage instructions.',
|
||||
);
|
||||
log.stdout(
|
||||
'The standalone option is experimental, and used imports may change'
|
||||
'across Dart SDK releases.',
|
||||
);
|
||||
} else {
|
||||
final mjsFile = '$outputFileBasename.mjs';
|
||||
log.stdout(
|
||||
"Generated wasm module '$outputFile', and JS init file '$mjsFile'.",
|
||||
);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -883,6 +883,35 @@ void main() {
|
||||
);
|
||||
}, skip: isRunningOnIA32);
|
||||
|
||||
test('Compile wasm standalone', () async {
|
||||
final p = project(
|
||||
mainSrc: '''
|
||||
void main() {
|
||||
print('hello world');
|
||||
}
|
||||
''',
|
||||
);
|
||||
final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
|
||||
final outFile = path.canonicalize(path.join(p.dirPath, 'standalone.wasm'));
|
||||
|
||||
final result = await p.run([
|
||||
'compile',
|
||||
'wasm',
|
||||
'--standalone',
|
||||
'-o',
|
||||
outFile,
|
||||
inFile,
|
||||
]);
|
||||
|
||||
expect(result.stdout, isNot(contains('.mjs')));
|
||||
expect(result.stdout, contains('standalone option is experimental'));
|
||||
expect(
|
||||
File(outFile).existsSync(),
|
||||
true,
|
||||
reason: 'Expected output file to exist: $outFile',
|
||||
);
|
||||
}, skip: isRunningOnIA32);
|
||||
|
||||
test('Compile JS with sound null safety', () async {
|
||||
final p = project(mainSrc: '''void main() {}''');
|
||||
final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
|
||||
|
||||
@@ -670,12 +670,15 @@ copy("copy_dart2wasm_platform") {
|
||||
"../:dart2wasm_platform",
|
||||
"../utils/dart2wasm:compile_dart2wasm_js_compatibility_platform",
|
||||
"../utils/dart2wasm:compile_dart2wasm_platform",
|
||||
"../utils/dart2wasm:compile_dart2wasm_standalone_platform",
|
||||
]
|
||||
sources = [
|
||||
"$root_out_dir/dart2wasm_js_compatibility_outline.dill",
|
||||
"$root_out_dir/dart2wasm_js_compatibility_platform.dill",
|
||||
"$root_out_dir/dart2wasm_outline.dill",
|
||||
"$root_out_dir/dart2wasm_platform.dill",
|
||||
"$root_out_dir/dart2wasm_standalone_outline.dill",
|
||||
"$root_out_dir/dart2wasm_standalone_platform.dill",
|
||||
]
|
||||
outputs =
|
||||
[ "$root_out_dir/$dart_sdk_output/lib/_internal/{{source_file_part}}" ]
|
||||
|
||||
Reference in New Issue
Block a user