[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',
|
'dart2wasm_platform.dill',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
String get wasmStandalonePlatformDill => _dillPathFor(
|
||||||
|
'dart2wasm_standalone_platform.dill',
|
||||||
|
);
|
||||||
|
|
||||||
String _dillPathFor(String dillName) => path.absolute(
|
String _dillPathFor(String dillName) => path.absolute(
|
||||||
runFromBuildRoot
|
runFromBuildRoot
|
||||||
? sdkPath
|
? 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,
|
negatable: false,
|
||||||
help: enableAssertsOption.help,
|
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(
|
..addOption(
|
||||||
'shared-memory',
|
'shared-memory',
|
||||||
help:
|
help:
|
||||||
@@ -1046,10 +1054,14 @@ class CompileWasmCommand extends CompileSubcommandCommand {
|
|||||||
|
|
||||||
final generateSourceMap = args.flag('source-maps');
|
final generateSourceMap = args.flag('source-maps');
|
||||||
final enabledExperiments = args.enabledExperiments;
|
final enabledExperiments = args.enabledExperiments;
|
||||||
|
final standalone = args.flag('standalone');
|
||||||
|
final platform = standalone
|
||||||
|
? sdk.wasmStandalonePlatformDill
|
||||||
|
: sdk.wasmPlatformDill;
|
||||||
final dart2wasmCommand = [
|
final dart2wasmCommand = [
|
||||||
sdk.dartAotRuntime,
|
sdk.dartAotRuntime,
|
||||||
sdk.dart2wasmSnapshot,
|
sdk.dart2wasmSnapshot,
|
||||||
'--platform=${sdk.wasmPlatformDill}',
|
'--platform=$platform',
|
||||||
if (verbose) '--verbose',
|
if (verbose) '--verbose',
|
||||||
if (packages != null) '--packages=$packages',
|
if (packages != null) '--packages=$packages',
|
||||||
if (args.flag('print-wasm')) '--print-wasm',
|
if (args.flag('print-wasm')) '--print-wasm',
|
||||||
@@ -1060,6 +1072,7 @@ class CompileWasmCommand extends CompileSubcommandCommand {
|
|||||||
if (args.flag('minify')) '--minify',
|
if (args.flag('minify')) '--minify',
|
||||||
if (!args.flag('strip-wasm')) '--no-strip-wasm',
|
if (!args.flag('strip-wasm')) '--no-strip-wasm',
|
||||||
if (args.flag('enable-deferred-loading')) '--enable-deferred-loading',
|
if (args.flag('enable-deferred-loading')) '--enable-deferred-loading',
|
||||||
|
if (standalone) '--standalone',
|
||||||
if (args.option(recordedUsesOption.flag) != null)
|
if (args.option(recordedUsesOption.flag) != null)
|
||||||
'--recorded-uses=${args.option(recordedUsesOption.flag)}',
|
'--recorded-uses=${args.option(recordedUsesOption.flag)}',
|
||||||
for (final define in defines) '-D$define',
|
for (final define in defines) '-D$define',
|
||||||
@@ -1091,10 +1104,23 @@ class CompileWasmCommand extends CompileSubcommandCommand {
|
|||||||
|
|
||||||
if (isDryRun) return 0;
|
if (isDryRun) return 0;
|
||||||
|
|
||||||
final mjsFile = '$outputFileBasename.mjs';
|
if (standalone) {
|
||||||
log.stdout(
|
log.stdout(
|
||||||
"Generated wasm module '$outputFile', and JS init file '$mjsFile'.",
|
"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;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -883,6 +883,35 @@ void main() {
|
|||||||
);
|
);
|
||||||
}, skip: isRunningOnIA32);
|
}, 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 {
|
test('Compile JS with sound null safety', () async {
|
||||||
final p = project(mainSrc: '''void main() {}''');
|
final p = project(mainSrc: '''void main() {}''');
|
||||||
final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
|
final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
|
||||||
|
|||||||
@@ -670,12 +670,15 @@ copy("copy_dart2wasm_platform") {
|
|||||||
"../:dart2wasm_platform",
|
"../:dart2wasm_platform",
|
||||||
"../utils/dart2wasm:compile_dart2wasm_js_compatibility_platform",
|
"../utils/dart2wasm:compile_dart2wasm_js_compatibility_platform",
|
||||||
"../utils/dart2wasm:compile_dart2wasm_platform",
|
"../utils/dart2wasm:compile_dart2wasm_platform",
|
||||||
|
"../utils/dart2wasm:compile_dart2wasm_standalone_platform",
|
||||||
]
|
]
|
||||||
sources = [
|
sources = [
|
||||||
"$root_out_dir/dart2wasm_js_compatibility_outline.dill",
|
"$root_out_dir/dart2wasm_js_compatibility_outline.dill",
|
||||||
"$root_out_dir/dart2wasm_js_compatibility_platform.dill",
|
"$root_out_dir/dart2wasm_js_compatibility_platform.dill",
|
||||||
"$root_out_dir/dart2wasm_outline.dill",
|
"$root_out_dir/dart2wasm_outline.dill",
|
||||||
"$root_out_dir/dart2wasm_platform.dill",
|
"$root_out_dir/dart2wasm_platform.dill",
|
||||||
|
"$root_out_dir/dart2wasm_standalone_outline.dill",
|
||||||
|
"$root_out_dir/dart2wasm_standalone_platform.dill",
|
||||||
]
|
]
|
||||||
outputs =
|
outputs =
|
||||||
[ "$root_out_dir/$dart_sdk_output/lib/_internal/{{source_file_part}}" ]
|
[ "$root_out_dir/$dart_sdk_output/lib/_internal/{{source_file_part}}" ]
|
||||||
|
|||||||
Reference in New Issue
Block a user