Skip to content

Commit

Permalink
Version 3.8.0-84.0.dev
Browse files Browse the repository at this point in the history
Merge a78235c into dev
  • Loading branch information
Dart CI committed Feb 10, 2025
2 parents 3c9c777 + a78235c commit 5fb003f
Show file tree
Hide file tree
Showing 34 changed files with 2,139 additions and 1,402 deletions.
2 changes: 1 addition & 1 deletion build/rbe/rewrapper_dart.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def list_imports(uri, exec_root, package_config):
]:
continue
# Imports must happen before definitions.
if tokens[0] in ['const', 'class', 'enum']:
if tokens[0] in ['const', 'class', 'enum', 'final']:
break
if 2 <= len(tokens
) and tokens[0] == 'if' and tokens[1] == '(dart.library.io)':
Expand Down
55 changes: 48 additions & 7 deletions pkg/dart2wasm/lib/compile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ sealed class CompilationResult {}
class CompilationSuccess extends CompilationResult {
final Map<String, ({Uint8List moduleBytes, String? sourceMap})> wasmModules;
final String jsRuntime;
final String supportJs;

CompilationSuccess(this.wasmModules, this.jsRuntime);
CompilationSuccess(this.wasmModules, this.jsRuntime, this.supportJs);
}

class CompilationError extends CompilationResult {}
Expand Down Expand Up @@ -238,11 +239,8 @@ Future<CompilationResult> compileToModule(

String? depFile = options.depFile;
if (depFile != null) {
writeDepfile(
compilerOptions.fileSystem,
component.uriToSource.keys,
options.outputFile,
depFile);
writeDepfile(compilerOptions.fileSystem, component.uriToSource.keys,
options.outputFile, depFile);
}

final generateSourceMaps = options.translatorOptions.generateSourceMaps;
Expand All @@ -262,7 +260,50 @@ Future<CompilationResult> compileToModule(
String jsRuntime = jsRuntimeFinalizer.generate(
translator.functions.translatedProcedures,
translator.internalizedStringsForJSRuntime,
translator.options.requireJsStringBuiltin,
mode);

return CompilationSuccess(wasmModules, jsRuntime);
final supportJs = _generateSupportJs(options.translatorOptions);
return CompilationSuccess(wasmModules, jsRuntime, supportJs);
}

String _generateSupportJs(TranslatorOptions options) {
// Copied from
// https://github.com/GoogleChromeLabs/wasm-feature-detect/blob/main/src/detectors/gc/index.js
//
// Uses WasmGC types and will only validate correctly if the engine supports
// WasmGC:
// ```
// (module
// (type $type0 (struct (field $field0 i8)))
// )
// ```
//
// NOTE: Once we support more feature detections we may use
// `package:wasm_builder` to create the module instead of having a fixed one
// here.
const String supportsWasmGC =
'WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,1,5,1,95,1,120,0]))';

// Imports a `js-string` builtin spec function *with wrong signature*. An engine
//
// * *without* knowledge about `js-string` builtin would accept such an import at
// validation time.
//
// * *with* knowledge about `js-string` would refuse it as the signature
// used to import the `cast` function is not according to `js-string` spec
//
// ```
// (module
// (func $wasm:js-string.cast (;0;) (import "wasm:js-string" "cast"))
// )
// ```
const String supportsJsStringBuiltins =
'!WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,1,4,1,96,0,0,2,23,1,14,119,97,115,109,58,106,115,45,115,116,114,105,110,103,4,99,97,115,116,0,0]),{"builtins":["js-string"]})';

final requiredFeatures = [
supportsWasmGC,
if (options.requireJsStringBuiltin) supportsJsStringBuiltins
];
return '(${requiredFeatures.join('&&')})';
}
1 change: 0 additions & 1 deletion pkg/dart2wasm/lib/compiler_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ class WasmCompilerOptions {
Uri mainUri;
String outputFile;
String? depFile;
String? outputJSRuntimeFile;
Uri? dynamicModuleMainUri;
Uri? dynamicInterfaceUri;
Uri? dynamicModuleMetadataFile;
Expand Down
5 changes: 3 additions & 2 deletions pkg/dart2wasm/lib/dart2wasm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,6 @@ final List<Option> options = [
StringMultiOption("delete-tostring-package-uri",
(o, values) => o.deleteToStringPackageUri = values),
StringOption("depfile", (o, value) => o.depFile = value),
StringOption(
"js-runtime-output", (o, value) => o.outputJSRuntimeFile = value),
StringOption(
"dump-kernel-after-cfe", (o, value) => o.dumpKernelAfterCfe = value,
hide: true),
Expand All @@ -99,6 +97,9 @@ final List<Option> options = [
Flag("enable-deferred-loading",
(o, value) => o.translatorOptions.enableDeferredLoading = value,
defaultsTo: _d.translatorOptions.enableDeferredLoading),
Flag("require-js-string-builtin",
(o, value) => o.translatorOptions.requireJsStringBuiltin = value,
defaultsTo: _d.translatorOptions.requireJsStringBuiltin),
Flag("enable-multi-module-stress-test-mode",
(o, value) => o.translatorOptions.enableMultiModuleStressTestMode = value,
defaultsTo: _d.translatorOptions.enableMultiModuleStressTestMode),
Expand Down
6 changes: 4 additions & 2 deletions pkg/dart2wasm/lib/generate_wasm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,11 @@ Future<int> generateWasm(WasmCompilerOptions options,
});
await Future.wait(writeFutures);

final jsFile = options.outputJSRuntimeFile ??
path.setExtension(options.outputFile, '.mjs');
final jsFile = path.setExtension(options.outputFile, '.mjs');
await File(jsFile).writeAsString(result.jsRuntime);

final supportJsFile = path.setExtension(options.outputFile, '.support.js');
await File(supportJsFile).writeAsString(result.supportJs);

return 0;
}
141 changes: 93 additions & 48 deletions pkg/dart2wasm/lib/js/runtime_blob.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,22 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

const jsRuntimeBlobPart1 = r'''
final jsRuntimeBlobTemplate = Template(r'''
// Compiles a dart2wasm-generated main module from `source` which can then
// instantiatable via the `instantiate` method.
//
// `source` needs to be a `Response` object (or promise thereof) e.g. created
// via the `fetch()` JS API.
export async function compileStreaming(source) {
const builtins = {builtins: ['js-string']};
const builtins = {<<BUILTINS_MAP_BODY>>};
return new CompiledApp(
await WebAssembly.compileStreaming(source, builtins), builtins);
}
// Compiles a dart2wasm-generated wasm modules from `bytes` which is then
// instantiatable via the `instantiate` method.
export async function compile(bytes) {
const builtins = {builtins: ['js-string']};
const builtins = {<<BUILTINS_MAP_BODY>>};
return new CompiledApp(await WebAssembly.compile(bytes, builtins), builtins);
}
Expand Down Expand Up @@ -97,59 +96,20 @@ class CompiledApp {
// Imports
const dart2wasm = {
''';

// We break inside the 'dart2wasm' object to enable injection of methods. We
// could use interpolation, but then we'd have to escape characters.
const jsRuntimeBlobPart2 = r'''
<<JS_METHODS>>
};
const baseImports = {
dart2wasm: dart2wasm,
''';

// We break inside of `baseImports` to inject internalized strings.
const jsRuntimeBlobPart3 = r'''
Math: Math,
Date: Date,
Object: Object,
Array: Array,
Reflect: Reflect,
<<IMPORTED_JS_STRINGS_IN_MJS>>
};
const jsStringPolyfill = {
"charCodeAt": (s, i) => s.charCodeAt(i),
"compare": (s1, s2) => {
if (s1 < s2) return -1;
if (s1 > s2) return 1;
return 0;
},
"concat": (s1, s2) => s1 + s2,
"equals": (s1, s2) => s1 === s2,
"fromCharCode": (i) => String.fromCharCode(i),
"length": (s) => s.length,
"substring": (s, a, b) => s.substring(a, b),
"fromCharCodeArray": (a, start, end) => {
if (end <= start) return '';
const read = dartInstance.exports.$wasmI16ArrayGet;
let result = '';
let index = start;
const chunkLength = Math.min(end - index, 500);
let array = new Array(chunkLength);
while (index < end) {
const newChunkLength = Math.min(end - index, 500);
for (let i = 0; i < newChunkLength; i++) {
array[i] = read(a, index++);
}
if (newChunkLength < chunkLength) {
array = array.slice(0, newChunkLength);
}
result += String.fromCharCode(...array);
}
return result;
},
};
<<JS_STRING_POLYFILL_METHODS>>
const deferredLibraryHelper = {
"loadModule": async (moduleName) => {
Expand All @@ -163,7 +123,7 @@ const jsRuntimeBlobPart3 = r'''
return await WebAssembly.instantiate(module, {
...baseImports,
...additionalImports,
"wasm:js-string": jsStringPolyfill,
<<JS_POLYFILL_IMPORT>>
"module0": dartInstance.exports,
});
},
Expand All @@ -173,7 +133,7 @@ const jsRuntimeBlobPart3 = r'''
...baseImports,
...additionalImports,
"deferredLibraryHelper": deferredLibraryHelper,
"wasm:js-string": jsStringPolyfill,
<<JS_POLYFILL_IMPORT>>
});
return new InstantiatedApp(this, dartInstance);
Expand All @@ -191,4 +151,89 @@ class InstantiatedApp {
this.instantiatedModule.exports.$invokeMain(args);
}
}
''');

const String jsPolyFillMethods = r'''
const jsStringPolyfill = {
"charCodeAt": (s, i) => s.charCodeAt(i),
"compare": (s1, s2) => {
if (s1 < s2) return -1;
if (s1 > s2) return 1;
return 0;
},
"concat": (s1, s2) => s1 + s2,
"equals": (s1, s2) => s1 === s2,
"fromCharCode": (i) => String.fromCharCode(i),
"length": (s) => s.length,
"substring": (s, a, b) => s.substring(a, b),
"fromCharCodeArray": (a, start, end) => {
if (end <= start) return '';
const read = dartInstance.exports.$wasmI16ArrayGet;
let result = '';
let index = start;
const chunkLength = Math.min(end - index, 500);
let array = new Array(chunkLength);
while (index < end) {
const newChunkLength = Math.min(end - index, 500);
for (let i = 0; i < newChunkLength; i++) {
array[i] = read(a, index++);
}
if (newChunkLength < chunkLength) {
array = array.slice(0, newChunkLength);
}
result += String.fromCharCode(...array);
}
return result;
},
};
''';

class Template {
static final _templateVariableRegExp = RegExp(r'<<(?<varname>[A-Z_]+)>>');
final List<_TemplatePart> _parts = [];

Template(String stringTemplate) {
int offset = 0;
for (final match in _templateVariableRegExp.allMatches(stringTemplate)) {
_parts.add(
_TemplateStringPart(stringTemplate.substring(offset, match.start)));
_parts.add(_TemplateVariablePart(match.namedGroup('varname')!));
offset = match.end;
}
_parts.add(_TemplateStringPart(
stringTemplate.substring(offset, stringTemplate.length)));
}

String instantiate(Map<String, String> variableValues) {
final sb = StringBuffer();
for (final part in _parts) {
sb.write(part.instantiate(variableValues));
}
return sb.toString();
}
}

abstract class _TemplatePart {
String instantiate(Map<String, String> variableValues);
}

class _TemplateStringPart extends _TemplatePart {
final String string;
_TemplateStringPart(this.string);

@override
String instantiate(Map<String, String> variableValues) => string;
}

class _TemplateVariablePart extends _TemplatePart {
final String variable;
_TemplateVariablePart(this.variable);

@override
String instantiate(Map<String, String> variableValues) {
final value = variableValues[variable];
if (value != null) return value;
throw 'Template contains no value for variable $variable';
}
}
28 changes: 19 additions & 9 deletions pkg/dart2wasm/lib/js/runtime_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,11 @@ class RuntimeFinalizer {

RuntimeFinalizer(this.allJSMethods);

String generate(Iterable<Procedure> translatedProcedures,
List<String> constantStrings, wasm_target.Mode mode) {
String generate(
Iterable<Procedure> translatedProcedures,
List<String> constantStrings,
bool requireJsBuiltin,
wasm_target.Mode mode) {
String escape(String s) => json.encode(s);

Set<Procedure> usedProcedures = {};
Expand Down Expand Up @@ -87,6 +90,11 @@ class RuntimeFinalizer {
}
}

final builtins = [
'builtins: [\'js-string\']',
if (requireJsBuiltin) 'importedStringConstants: \'S\'',
];

String internalizedStrings = '';
if (constantStrings.isNotEmpty) {
internalizedStrings = '''
Expand All @@ -95,13 +103,15 @@ class RuntimeFinalizer {
],
''';
}
return '''
$jsRuntimeBlobPart1
$jsMethods
$jsRuntimeBlobPart2
$internalizedStrings
$jsRuntimeBlobPart3
''';

return jsRuntimeBlobTemplate.instantiate({
'BUILTINS_MAP_BODY': builtins.join(', '),
'JS_METHODS': jsMethods.toString(),
'IMPORTED_JS_STRINGS_IN_MJS': internalizedStrings,
'JS_STRING_POLYFILL_METHODS': requireJsBuiltin ? '' : jsPolyFillMethods,
'JS_POLYFILL_IMPORT':
requireJsBuiltin ? '' : '"wasm:js-string": jsStringPolyfill,'
});
}
}

Expand Down
Loading

0 comments on commit 5fb003f

Please sign in to comment.