[dart2wasm] Simplify handling of JS interop callbacks
Right now a JS interop callback works like this: * Each wasm module that gets instantiated will be given it's module instance (JS calls Dart to set it) via `setThisModule` * When Dart code calls JS and gives it a callback to invoke, it gave it this module instance. It will also make the callback wasm function weakly exported. * The JS trampoline code, when invoked, would then call the weakly exported wasm function from the module instance. We simplify this now by making the Dart code simply give the wasm function reference to JS, then JS can later on invoke it. No need to weakly export a function and call back via `module.exports.<weaklyExportedCallback>` To ensure binaryen is aware that the wasm function may be called from JS, we annotate it via the `(@binaryen.js.called)` annotation. Change-Id: I828dd0cf8d3b36db338792c4e277a4bb94c76faf Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/511080 Commit-Queue: Martin Kustermann <kustermann@google.com> Reviewed-by: Srujan Gaddam <srujzs@google.com>
This commit is contained in:
committed by
dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent
87ff0ea65d
commit
2d78883f27
@@ -171,6 +171,9 @@ class FunctionCollector {
|
||||
final function = module.functions.define(ftype, getFunctionName(target))
|
||||
..isPure = hasPureAnnotation && !target.isCheckedEntryReference
|
||||
..inlineHint = inlineHint;
|
||||
if (util.hasPragma(translator.coreTypes, member, 'wasm:js-trampoline')) {
|
||||
function.isJSCalled = true;
|
||||
}
|
||||
if (exportName != null) {
|
||||
// Add weak exports to the module as we now know they're used. Strong
|
||||
// exports have already been added.
|
||||
|
||||
@@ -10,7 +10,6 @@ import 'abi.dart' show kWasmAbiEnumIndex;
|
||||
import 'class_info.dart';
|
||||
import 'code_generator.dart';
|
||||
import 'dynamic_dispatchers.dart';
|
||||
import 'js/util.dart';
|
||||
import 'translator.dart';
|
||||
import 'types.dart';
|
||||
import 'util.dart';
|
||||
@@ -1136,15 +1135,6 @@ class Intrinsifier {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
if (target.enclosingLibrary.name == 'dart._js_helper') {
|
||||
if (target.name.text == 'thisModule') {
|
||||
final global = translator.getThisModuleGlobal(b.moduleBuilder);
|
||||
b.global_get(global);
|
||||
return global.type.type;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1540,12 +1530,7 @@ class Intrinsifier {
|
||||
final constant = argument.constant;
|
||||
if (constant is! StaticTearOffConstant) throw error;
|
||||
final target = constant.target;
|
||||
if (!hasWasmWeakExportPragma(codeGen.translator.coreTypes, target) &&
|
||||
!(JsInteropMemberData.fromMember(
|
||||
target,
|
||||
codeGen.translator.coreTypes,
|
||||
)?.isWeakExport ??
|
||||
true)) {
|
||||
if (!hasWasmWeakExportPragma(codeGen.translator.coreTypes, target)) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ class CallbackSpecializer {
|
||||
CallbackSpecializer(this._staticTypeContext, this._util);
|
||||
|
||||
Statement _generateDispatchCase(
|
||||
FunctionType function,
|
||||
FunctionType instantiatedFunctionType,
|
||||
Variable callbackVariable,
|
||||
List<Variable> positionalParameters,
|
||||
int requiredParameterCount, {
|
||||
@@ -25,7 +25,8 @@ class CallbackSpecializer {
|
||||
}) {
|
||||
List<Expression> callbackArguments = [];
|
||||
for (int i = 0; i < requiredParameterCount; i++) {
|
||||
DartType callbackParameterType = function.positionalParameters[i];
|
||||
DartType callbackParameterType =
|
||||
instantiatedFunctionType.positionalParameters[i];
|
||||
Expression expression;
|
||||
VariableGet v = VariableGet(positionalParameters[i]);
|
||||
if (_util.isJSValueType(callbackParameterType) && boxExternRef) {
|
||||
@@ -50,10 +51,7 @@ class CallbackSpecializer {
|
||||
FunctionAccessKind.FunctionType,
|
||||
VariableGet(callbackVariable),
|
||||
Arguments(callbackArguments),
|
||||
// Instantiate any type parameters to bounds as they would otherwise
|
||||
// be free type variables in this context.
|
||||
functionType:
|
||||
const _InstantiateToBounds().substituteType(function) as FunctionType,
|
||||
functionType: instantiatedFunctionType,
|
||||
);
|
||||
|
||||
final temp = Variable(
|
||||
@@ -100,7 +98,7 @@ class CallbackSpecializer {
|
||||
// needed.
|
||||
final callbackVariable = Variable(
|
||||
'callback',
|
||||
type: _util.nonNullableObjectType,
|
||||
type: _util.nonNullableWasmExternRefType,
|
||||
isSynthesized: true,
|
||||
);
|
||||
final argumentsLengthWasmI32 = Variable(
|
||||
@@ -151,6 +149,28 @@ class CallbackSpecializer {
|
||||
),
|
||||
);
|
||||
|
||||
final instantiatedFunctionType =
|
||||
const _InstantiateToBounds().substituteType(function) as FunctionType;
|
||||
|
||||
// Convert `WasmExternRef` argument to Dart Function
|
||||
final callbackFunctionVar = Variable(
|
||||
'callbackFunction',
|
||||
type: instantiatedFunctionType,
|
||||
initializer: StaticInvocation(
|
||||
_util.unsafeCastOpaqueTarget,
|
||||
Arguments(
|
||||
[
|
||||
StaticInvocation(
|
||||
_util.wasmInternalizeNonNullable,
|
||||
Arguments([VariableGet(callbackVariable)]),
|
||||
),
|
||||
],
|
||||
types: [instantiatedFunctionType],
|
||||
),
|
||||
),
|
||||
);
|
||||
body.add(VariableStatement(VariableDeclaration(callbackFunctionVar)));
|
||||
|
||||
body.add(VariableStatement(VariableDeclaration(argumentsLength)));
|
||||
|
||||
if (castClosureArguments.isNotEmpty) {
|
||||
@@ -185,8 +205,8 @@ class CallbackSpecializer {
|
||||
IntConstant(positionalParametersLength),
|
||||
),
|
||||
_generateDispatchCase(
|
||||
function,
|
||||
callbackVariable,
|
||||
instantiatedFunctionType,
|
||||
callbackFunctionVar,
|
||||
positionalParameters,
|
||||
positionalParametersLength,
|
||||
boxExternRef: boxExternRef,
|
||||
@@ -204,8 +224,8 @@ class CallbackSpecializer {
|
||||
IfStatement(
|
||||
_util.variableCheckConstant(argumentsLength, IntConstant(i)),
|
||||
_generateDispatchCase(
|
||||
function,
|
||||
callbackVariable,
|
||||
instantiatedFunctionType,
|
||||
callbackFunctionVar,
|
||||
positionalParameters,
|
||||
i,
|
||||
boxExternRef: boxExternRef,
|
||||
@@ -306,8 +326,8 @@ class CallbackSpecializer {
|
||||
null,
|
||||
positionalParameters: [
|
||||
Variable(
|
||||
'thisModule',
|
||||
type: _util.nonNullableWasmExternRefType,
|
||||
'wasmFunction',
|
||||
type: _util.nonNullableWasmFuncRefType,
|
||||
isSynthesized: true,
|
||||
),
|
||||
Variable(
|
||||
@@ -331,7 +351,6 @@ class CallbackSpecializer {
|
||||
numJsParameters: jsParametersLength,
|
||||
captureThis: captureThis,
|
||||
needsCastClosure: needsCastClosure,
|
||||
trampoline: functionTrampoline,
|
||||
).applyToMember(dartProcedure, _util.coreTypes);
|
||||
|
||||
return (dartProcedure, functionTrampoline);
|
||||
@@ -415,24 +434,20 @@ class CallbackSpecializer {
|
||||
captureThis: captureThis,
|
||||
);
|
||||
return _createJSValue(
|
||||
BlockExpression(
|
||||
Block([
|
||||
// This ensures TFA will retain the function which the
|
||||
// JS code will call. The backend in return will export
|
||||
// the function due to `@pragma('wasm:weak-export', ...)`
|
||||
ExpressionStatement(
|
||||
StaticInvocation(
|
||||
_util.exportWasmFunctionTarget,
|
||||
Arguments([
|
||||
ConstantExpression(StaticTearOffConstant(exportedFunction)),
|
||||
]),
|
||||
),
|
||||
),
|
||||
]),
|
||||
StaticInvocation(
|
||||
jsWrapperFunction,
|
||||
Arguments([
|
||||
StaticGet(_util.thisModuleGetter),
|
||||
StaticInvocation(
|
||||
_util.wasmFunctionFromFunction,
|
||||
Arguments(
|
||||
[ConstantExpression(StaticTearOffConstant(exportedFunction))],
|
||||
types: [
|
||||
exportedFunction.function.computeFunctionType(
|
||||
Nullability.nonNullable,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
StaticInvocation(
|
||||
_util.jsObjectFromDartObjectTarget,
|
||||
Arguments([argument]),
|
||||
@@ -444,7 +459,6 @@ class CallbackSpecializer {
|
||||
),
|
||||
]),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,6 @@ class CompiledApp {
|
||||
<<MODULE_LOADING_IMPORT>>
|
||||
<<JS_POLYFILL_IMPORT>>
|
||||
});
|
||||
dartInstance.exports.<<THIS_MODULE_SETTER_NAME>>(dartInstance);
|
||||
|
||||
return new InstantiatedApp(this, dartInstance);
|
||||
}
|
||||
@@ -181,7 +180,6 @@ final moduleLoadingHelperTemplate = Template(r'''
|
||||
<<JS_POLYFILL_IMPORT>>
|
||||
"<<MAIN_MODULE_NAME>>": dartInstance.exports,
|
||||
});
|
||||
moduleInstance.exports.<<THIS_MODULE_SETTER_NAME>>(moduleInstance);
|
||||
}
|
||||
const moduleLoadingHelper = {
|
||||
"loadDeferredModules": async (moduleNames) => {
|
||||
|
||||
@@ -68,13 +68,11 @@ class RuntimeFinalizer {
|
||||
case JsCodeData(:final jsCode):
|
||||
final importName = _interopMemberNamer.getImportName(p)!.itemName;
|
||||
usedJSMethods.add((importName: importName, jsCode: jsCode));
|
||||
case JsTrampolineWrapperData(:final trampoline):
|
||||
case JsTrampolineWrapperData():
|
||||
final importName = _interopMemberNamer.getImportName(p)!.itemName;
|
||||
usedJSMethods.add((
|
||||
importName: importName,
|
||||
jsCode: annotationInfo.jsCode(
|
||||
_interopMemberNamer.getExportName(trampoline)!,
|
||||
),
|
||||
jsCode: annotationInfo.jsCode(),
|
||||
));
|
||||
case JsTrampolineData():
|
||||
// do nothing
|
||||
@@ -161,7 +159,6 @@ class RuntimeFinalizer {
|
||||
? moduleLoadingHelperTemplate.instantiate({
|
||||
...jsStringBuiltinPolyfillImportVars,
|
||||
'MAIN_MODULE_NAME': mainModuleName,
|
||||
'THIS_MODULE_SETTER_NAME': _interopMemberNamer.thisModuleSetterName,
|
||||
})
|
||||
: '';
|
||||
|
||||
@@ -170,7 +167,6 @@ class RuntimeFinalizer {
|
||||
...moduleLoadingImportVars,
|
||||
'BUILTINS_MAP_BODY': builtins.join(', '),
|
||||
'JS_METHODS': jsMethods,
|
||||
'THIS_MODULE_SETTER_NAME': _interopMemberNamer.thisModuleSetterName,
|
||||
'INTERNAL_IMPORTS_MODULE_NAME':
|
||||
_interopMemberNamer.interopHelperModuleName,
|
||||
'IMPORTED_JS_STRINGS_IN_MJS': internalizedStrings,
|
||||
|
||||
@@ -26,7 +26,6 @@ sealed class JsInteropMemberData {
|
||||
String get pragmaName;
|
||||
Constant? get toPragmaValue;
|
||||
bool get isImport;
|
||||
bool get isWeakExport => !isImport;
|
||||
void applyToMember(Member member, CoreTypes coreTypes) {
|
||||
addPragma(member, pragmaName, coreTypes, value: toPragmaValue);
|
||||
}
|
||||
@@ -112,7 +111,6 @@ class JsTrampolineData extends JsInteropMemberData {
|
||||
class JsTrampolineWrapperData extends JsInteropMemberData {
|
||||
static const String _pragmaName = 'wasm:js-trampoline-wrapper';
|
||||
final int numJsParameters;
|
||||
final Procedure trampoline;
|
||||
final bool captureThis;
|
||||
final bool needsCastClosure;
|
||||
|
||||
@@ -124,19 +122,16 @@ class JsTrampolineWrapperData extends JsInteropMemberData {
|
||||
|
||||
JsTrampolineWrapperData({
|
||||
required this.numJsParameters,
|
||||
required this.trampoline,
|
||||
required this.captureThis,
|
||||
required this.needsCastClosure,
|
||||
});
|
||||
|
||||
factory JsTrampolineWrapperData.fromPragmaValue(ListConstant constant) {
|
||||
final trampoline = (constant.entries[0] as StaticTearOffConstant).target;
|
||||
final numJsParameters = (constant.entries[1] as IntConstant).value;
|
||||
final captureThis = (constant.entries[2] as BoolConstant).value;
|
||||
final needsCastClosure = (constant.entries[3] as BoolConstant).value;
|
||||
final numJsParameters = (constant.entries[0] as IntConstant).value;
|
||||
final captureThis = (constant.entries[1] as BoolConstant).value;
|
||||
final needsCastClosure = (constant.entries[2] as BoolConstant).value;
|
||||
return JsTrampolineWrapperData(
|
||||
numJsParameters: numJsParameters,
|
||||
trampoline: trampoline,
|
||||
captureThis: captureThis,
|
||||
needsCastClosure: needsCastClosure,
|
||||
);
|
||||
@@ -144,13 +139,12 @@ class JsTrampolineWrapperData extends JsInteropMemberData {
|
||||
|
||||
@override
|
||||
ListConstant get toPragmaValue => ListConstant(DynamicType(), [
|
||||
StaticTearOffConstant(trampoline),
|
||||
IntConstant(numJsParameters),
|
||||
BoolConstant(captureThis),
|
||||
BoolConstant(needsCastClosure),
|
||||
]);
|
||||
|
||||
String jsCode(String trampolineExportName) {
|
||||
String jsCode() {
|
||||
final jsParameters = <String>[];
|
||||
for (int i = 0; i < numJsParameters; i++) {
|
||||
jsParameters.add('x$i');
|
||||
@@ -163,10 +157,10 @@ class JsTrampolineWrapperData extends JsInteropMemberData {
|
||||
? 'arguments.length + 1'
|
||||
: 'arguments.length';
|
||||
String dartArguments = 'f,$argumentsLength';
|
||||
String jsMethodParams = '(module,f)';
|
||||
String jsMethodParams = '(wasmFunction,f)';
|
||||
if (needsCastClosure) {
|
||||
dartArguments = '$dartArguments,castClosure';
|
||||
jsMethodParams = '(module,f,castClosure)';
|
||||
jsMethodParams = '(wasmFunction,f,castClosure)';
|
||||
}
|
||||
if (captureThis) dartArguments = '$dartArguments,this';
|
||||
if (jsParameters.isNotEmpty) {
|
||||
@@ -176,7 +170,7 @@ class JsTrampolineWrapperData extends JsInteropMemberData {
|
||||
// Note: We have to use a regular function for the inner closure in some
|
||||
// cases because we need access to `arguments`.
|
||||
return "$jsMethodParams => finalizeWrapper(f, function($jsWrapperParams) {"
|
||||
" return module.exports.$trampolineExportName($dartArguments) })";
|
||||
" return wasmFunction($dartArguments) })";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,6 +215,8 @@ class CoreTypesUtil {
|
||||
final Class wasmArrayRefClass;
|
||||
final Procedure wrapDartFunctionTarget;
|
||||
final Procedure exportWasmFunctionTarget;
|
||||
final Procedure wasmInternalizeNonNullable;
|
||||
final Procedure unsafeCastOpaqueTarget;
|
||||
final Member wasmExternRefNullRef;
|
||||
final Class wasmI32Class;
|
||||
final Procedure wasmI32ToIntSigned;
|
||||
@@ -258,7 +254,8 @@ class CoreTypesUtil {
|
||||
final Procedure jsifyJSArrayBufferImpl; // JS ByteBuffer
|
||||
final Procedure jsArrayBufferFromDartByteBuffer; // Wasm ByteBuffer
|
||||
final Procedure jsifyFunction;
|
||||
final Procedure thisModuleGetter;
|
||||
final Class wasmFuncRefClass;
|
||||
final Procedure wasmFunctionFromFunction;
|
||||
|
||||
// Classes used in type tests for the converters.
|
||||
final Class jsInt8ArrayImplClass;
|
||||
@@ -503,6 +500,14 @@ class CoreTypesUtil {
|
||||
'get:nullRef',
|
||||
),
|
||||
wasmVoidClass = coreTypes.index.getClass('dart:_wasm', 'WasmVoid'),
|
||||
wasmInternalizeNonNullable = coreTypes.index.getTopLevelProcedure(
|
||||
'dart:_wasm',
|
||||
'_internalizeNonNullable',
|
||||
),
|
||||
unsafeCastOpaqueTarget = coreTypes.index.getTopLevelProcedure(
|
||||
'dart:_internal',
|
||||
'unsafeCastOpaque',
|
||||
),
|
||||
wasmArrayClass = coreTypes.index.getClass('dart:_wasm', 'WasmArray'),
|
||||
wasmArrayRefClass = coreTypes.index.getClass(
|
||||
'dart:_wasm',
|
||||
@@ -643,9 +648,11 @@ class CoreTypesUtil {
|
||||
'dart:_js_helper',
|
||||
'jsifyFunction',
|
||||
),
|
||||
thisModuleGetter = coreTypes.index.getTopLevelProcedure(
|
||||
'dart:_js_helper',
|
||||
'get:thisModule',
|
||||
wasmFuncRefClass = coreTypes.index.getClass('dart:_wasm', 'WasmFuncRef'),
|
||||
wasmFunctionFromFunction = coreTypes.index.getProcedure(
|
||||
'dart:_wasm',
|
||||
'WasmFunction',
|
||||
'fromFunction',
|
||||
),
|
||||
jsInt8ArrayImplClass = coreTypes.index.getClass(
|
||||
'dart:_js_types',
|
||||
@@ -735,6 +742,9 @@ class CoreTypesUtil {
|
||||
DartType get nonNullableWasmExternRefType =>
|
||||
wasmExternRefClass.getThisType(coreTypes, Nullability.nonNullable);
|
||||
|
||||
DartType get nonNullableWasmFuncRefType =>
|
||||
wasmFuncRefClass.getThisType(coreTypes, Nullability.nonNullable);
|
||||
|
||||
DartType get nullableJSValueType =>
|
||||
InterfaceType(jsValueClass, Nullability.nullable);
|
||||
|
||||
|
||||
@@ -104,23 +104,14 @@ class _ExternalMemberNamer {
|
||||
/// Their names can therefore be minified.
|
||||
class _InteropHelperMemberNamer {
|
||||
final Namer _interopHelperNamer;
|
||||
final Namer _exportNamer;
|
||||
final CoreTypes coreTypes;
|
||||
final String interopModuleName;
|
||||
final String thisModuleSetterName;
|
||||
final Map<Member, String> interopMemberNames = {};
|
||||
|
||||
_InteropHelperMemberNamer(
|
||||
this._exportNamer,
|
||||
this.coreTypes,
|
||||
TranslatorOptions options,
|
||||
) : interopModuleName = options.minify ? '_' : 'dart2wasm',
|
||||
_InteropHelperMemberNamer(this.coreTypes, TranslatorOptions options)
|
||||
: interopModuleName = options.minify ? '_' : 'dart2wasm',
|
||||
_interopHelperNamer = Namer(
|
||||
minify: options.minify || options.minifyInteropNames,
|
||||
),
|
||||
thisModuleSetterName = _exportNamer.getName(
|
||||
'\$setThisModule',
|
||||
jsSafeName: true,
|
||||
);
|
||||
|
||||
ImportName? getImportName(Member member) {
|
||||
@@ -137,18 +128,6 @@ class _InteropHelperMemberNamer {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String? getExportName(Member member) {
|
||||
final annotationInfo = JsInteropMemberData.fromMember(member, coreTypes);
|
||||
if (annotationInfo == null) return null;
|
||||
if (annotationInfo.isWeakExport) {
|
||||
return interopMemberNames[member] ??= _exportNamer.getName(
|
||||
member.name.text,
|
||||
jsSafeName: true,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Manages naming for [Member]s associated with JS interop.
|
||||
@@ -165,16 +144,10 @@ class InteropMemberNamer {
|
||||
Namer exportNamer,
|
||||
TranslatorOptions options,
|
||||
) : _externalMemberNamer = _ExternalMemberNamer(coreTypes, exportNamer),
|
||||
_interopHelperMemberNamer = _InteropHelperMemberNamer(
|
||||
exportNamer,
|
||||
coreTypes,
|
||||
options,
|
||||
);
|
||||
_interopHelperMemberNamer = _InteropHelperMemberNamer(coreTypes, options);
|
||||
|
||||
String get interopHelperModuleName =>
|
||||
_interopHelperMemberNamer.interopModuleName;
|
||||
String get thisModuleSetterName =>
|
||||
_interopHelperMemberNamer.thisModuleSetterName;
|
||||
|
||||
/// Returns the import name for the given member.
|
||||
///
|
||||
@@ -190,9 +163,7 @@ class InteropMemberNamer {
|
||||
/// Returns null if the member is not an export. Checks both external and
|
||||
/// interop helper exports.
|
||||
String? getExportName(Member member) {
|
||||
final externalName = _externalMemberNamer.getExportName(member);
|
||||
if (externalName != null) return externalName;
|
||||
return _interopHelperMemberNamer.getExportName(member);
|
||||
return _externalMemberNamer.getExportName(member);
|
||||
}
|
||||
|
||||
/// Registers the export name for the given member with the [Namer].
|
||||
|
||||
@@ -414,7 +414,6 @@ class Translator with KernelNodes {
|
||||
final Map<w.ModuleBuilder, ModuleMetadata> _builderToOutput = {};
|
||||
final Map<w.Module, w.ModuleBuilder> moduleToBuilder = {};
|
||||
bool get hasMultipleModules => _moduleOutputData.hasMultipleModules;
|
||||
final Map<w.ModuleBuilder, w.Global> _thisModuleGlobals = {};
|
||||
|
||||
w.ModuleBuilder moduleForReference(Reference reference) {
|
||||
final module = _moduleOutputData.moduleForReference(reference);
|
||||
@@ -506,35 +505,6 @@ class Translator with KernelNodes {
|
||||
}
|
||||
}
|
||||
|
||||
w.Global getThisModuleGlobal(w.ModuleBuilder module) {
|
||||
return _thisModuleGlobals.putIfAbsent(module, () {
|
||||
final global = module.globals.define(
|
||||
w.GlobalType(w.RefType.extern(nullable: true)),
|
||||
'thisModule',
|
||||
);
|
||||
final gb = global.initializer;
|
||||
gb.ref_null(w.HeapType.extern);
|
||||
gb.end();
|
||||
|
||||
final thisModuleSetter = module.functions.define(
|
||||
typesBuilder.defineFunction(const [
|
||||
w.RefType.extern(nullable: false),
|
||||
], const []),
|
||||
"setThisModule",
|
||||
);
|
||||
module.exports.export(
|
||||
interopMemberNamer.thisModuleSetterName,
|
||||
thisModuleSetter,
|
||||
);
|
||||
final fb = thisModuleSetter.body;
|
||||
fb.local_get(thisModuleSetter.locals[0]);
|
||||
fb.global_set(global);
|
||||
fb.end();
|
||||
|
||||
return global;
|
||||
});
|
||||
}
|
||||
|
||||
void drainCompletionQueue() {
|
||||
while (!compilationQueue.isEmpty) {
|
||||
final task = compilationQueue.pop();
|
||||
@@ -607,11 +577,6 @@ class Translator with KernelNodes {
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure non-empty modules expose `$setThisModule` function.
|
||||
for (final moduleBuilder in _outputToBuilder.values) {
|
||||
getThisModuleGlobal(moduleBuilder);
|
||||
}
|
||||
|
||||
// This getter will be null if we pass e.g. `--use-load-ids` as the
|
||||
// runtime code will then be pruned to call out to embedder instead of
|
||||
// consulting the load mapping bundled in the app.
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
(type $_Environment <...>)
|
||||
(type $_InterfaceType <...>)
|
||||
(type $_Type <...>)
|
||||
(global $"\")\"_9" (import "$" "0") (ref $JSExternWrapper))
|
||||
(global $_InterfaceType (import "$" ".") (ref $_InterfaceType))
|
||||
(table $$.% (import "$" "%") 765 funcref)
|
||||
(table $$.& (import "$" "&") 20 funcref)
|
||||
(global $"\")\"_9" (import "$" "/") (ref $JSExternWrapper))
|
||||
(global $_InterfaceType (import "$" "-") (ref $_InterfaceType))
|
||||
(table $$.$ (import "$" "$") 765 funcref)
|
||||
(table $$.% (import "$" "%") 20 funcref)
|
||||
(global $"\">.takeT(\"" (ref $JSExternWrapper) <...>)
|
||||
(global $"\"Foo<\"" (ref $JSExternWrapper) <...>)
|
||||
(elem $$.& <...>)
|
||||
(elem $$.% <...>)
|
||||
(elem $$.$ <...>)
|
||||
(@binaryen.inline 0)
|
||||
(func $"Foo.takeT (body)" (param $var0 (ref $Foo)) (param $var1 (ref $#Top))
|
||||
(local $var2 (ref $_InterfaceType))
|
||||
@@ -26,9 +26,9 @@
|
||||
global.get $"\")\"_9"
|
||||
array.new_fixed $Array<Object?> 5
|
||||
i32.const 14
|
||||
call_indirect $$.& (param (ref $Array<Object?>)) (result (ref $JSExternWrapper))
|
||||
call_indirect $$.% (param (ref $Array<Object?>)) (result (ref $JSExternWrapper))
|
||||
i32.const 18
|
||||
call_indirect $$.& (param (ref null $#Top))
|
||||
call_indirect $$.% (param (ref null $#Top))
|
||||
global.get $_InterfaceType
|
||||
local.set $var2
|
||||
block $label0 (result i32)
|
||||
@@ -45,14 +45,14 @@
|
||||
struct.get $Foo $field0
|
||||
i32.const 344
|
||||
i32.add
|
||||
call_indirect $$.% (param (ref $#Top)) (result (ref $Array<_Type>))
|
||||
call_indirect $$.$ (param (ref $#Top)) (result (ref $Array<_Type>))
|
||||
i32.const 0
|
||||
array.get $Array<_Type>
|
||||
ref.null none
|
||||
local.get $var2
|
||||
ref.null none
|
||||
i32.const 19
|
||||
call_indirect $$.& (param (ref $_Type) (ref null $_Environment) (ref $_Type) (ref null $_Environment)) (result i32)
|
||||
call_indirect $$.% (param (ref $_Type) (ref null $_Environment) (ref $_Type) (ref null $_Environment)) (result i32)
|
||||
i32.const 1
|
||||
i32.ne
|
||||
br_if $label0
|
||||
@@ -62,12 +62,12 @@
|
||||
i32.eqz
|
||||
if
|
||||
i32.const 2
|
||||
call_indirect $$.&
|
||||
call_indirect $$.%
|
||||
unreachable
|
||||
end
|
||||
local.get $var0
|
||||
i32.const 18
|
||||
call_indirect $$.& (param (ref null $#Top))
|
||||
call_indirect $$.% (param (ref null $#Top))
|
||||
)
|
||||
(func $"Foo.takeT (checked entry)" (param $var0 (ref $Foo)) (param $var1 (ref $#Top))
|
||||
(local $var2 i32)
|
||||
@@ -110,7 +110,7 @@
|
||||
ref.as_non_null
|
||||
local.get $var1
|
||||
i32.const 4
|
||||
call_indirect $$.& (param (ref $_Type) (ref $#Top)) (result i32)
|
||||
call_indirect $$.% (param (ref $_Type) (ref $#Top)) (result i32)
|
||||
br $label0
|
||||
end
|
||||
br $label1
|
||||
@@ -127,7 +127,7 @@
|
||||
ref.as_non_null
|
||||
local.get $var1
|
||||
i32.const 5
|
||||
call_indirect $$.& (param (ref $_Type) (ref $#Top)) (result i32)
|
||||
call_indirect $$.% (param (ref $_Type) (ref $#Top)) (result i32)
|
||||
br $label0
|
||||
end
|
||||
br $label1
|
||||
@@ -140,7 +140,7 @@
|
||||
ref.as_non_null
|
||||
local.get $var1
|
||||
i32.const 6
|
||||
call_indirect $$.& (param (ref $_Type) (ref $#Top)) (result i32)
|
||||
call_indirect $$.% (param (ref $_Type) (ref $#Top)) (result i32)
|
||||
br $label0
|
||||
end
|
||||
end $label1
|
||||
@@ -152,12 +152,12 @@
|
||||
struct.get $_Type $field0
|
||||
i32.const 467
|
||||
i32.add
|
||||
call_indirect $$.% (param (ref $_Type) (ref $#Top)) (result i32)
|
||||
call_indirect $$.$ (param (ref $_Type) (ref $#Top)) (result i32)
|
||||
end $label0
|
||||
i32.eqz
|
||||
if
|
||||
i32.const 2
|
||||
call_indirect $$.&
|
||||
call_indirect $$.%
|
||||
unreachable
|
||||
end
|
||||
local.get $var0
|
||||
|
||||
@@ -41,6 +41,7 @@ class FunctionBuilder extends ir.BaseFunction
|
||||
functionName,
|
||||
)
|
||||
..isPure = isPure
|
||||
..isJSCalled = isJSCalled
|
||||
..inlineHint = inlineHint;
|
||||
|
||||
@override
|
||||
|
||||
@@ -40,10 +40,16 @@ abstract class BaseFunction with Indexable, Exportable {
|
||||
|
||||
/// Whether this function is pure and has no effect.
|
||||
///
|
||||
/// If marked as spure, we'll emit metadata in the
|
||||
/// If marked as pure, we'll emit metadata in the
|
||||
/// `binaryen.removable.if.unused` custom section.
|
||||
bool isPure = false;
|
||||
|
||||
/// Whether this function is called from JS.
|
||||
///
|
||||
/// If marked as isJSCalled, we'll emit metadata in the
|
||||
/// `binaryen.js.called` custom section.
|
||||
bool isJSCalled = false;
|
||||
|
||||
/// Inline hint for this function.
|
||||
///
|
||||
/// If set, we'll emit metadata in the `binaryen.inline` custom section.
|
||||
@@ -122,6 +128,9 @@ class DefinedFunction extends BaseFunction implements Serializable {
|
||||
if (isPure) {
|
||||
p.writeln('(@binaryen.removable.if.unused)');
|
||||
}
|
||||
if (isJSCalled) {
|
||||
p.writeln('(@binaryen.js.called)');
|
||||
}
|
||||
if (inlineHint != null) {
|
||||
p.writeln('(@binaryen.inline $inlineHint)');
|
||||
}
|
||||
@@ -195,12 +204,11 @@ class ImportedFunction extends BaseFunction implements Import {
|
||||
}
|
||||
|
||||
void printTo(IrPrinter p) {
|
||||
assert(!isJSCalled);
|
||||
assert(inlineHint == null);
|
||||
if (isPure) {
|
||||
p.writeln('(@binaryen.removable.if.unused)');
|
||||
}
|
||||
if (inlineHint != null) {
|
||||
p.writeln('(@binaryen.inline $inlineHint)');
|
||||
}
|
||||
p.write('(func ');
|
||||
p.writeFunctionReference(this);
|
||||
p.write(' ');
|
||||
|
||||
@@ -134,6 +134,7 @@ class Module implements Serializable {
|
||||
).serialize(s);
|
||||
BinaryenRemovableIfUnusedSection(functions).serialize(s);
|
||||
BinaryenInlineHintSection(functions).serialize(s);
|
||||
BinaryenJSCalledSection(functions).serialize(s);
|
||||
SourceMapSection(sourceMapUrl).serialize(s);
|
||||
for (final customSection in _extraCustomSections) {
|
||||
customSection.serialize(s);
|
||||
@@ -300,6 +301,10 @@ class Module implements Serializable {
|
||||
?.single,
|
||||
functions,
|
||||
);
|
||||
BinaryenJSCalledSection.deserialize(
|
||||
customSections.remove(BinaryenJSCalledSection.customSectionName)?.single,
|
||||
functions,
|
||||
);
|
||||
final sourceMapUrl = SourceMapSection.deserialize(
|
||||
customSections.remove(SourceMapSection.customSectionName)?.single,
|
||||
);
|
||||
|
||||
@@ -14,6 +14,7 @@ const Set<String> _reservedCustomSectionNames = {
|
||||
SourceMapSection.customSectionName,
|
||||
BinaryenRemovableIfUnusedSection.customSectionName,
|
||||
BinaryenInlineHintSection.customSectionName,
|
||||
BinaryenJSCalledSection.customSectionName,
|
||||
};
|
||||
|
||||
abstract class Section implements Serializable {
|
||||
@@ -1209,6 +1210,55 @@ class BinaryenInlineHintSection extends CustomSection {
|
||||
}
|
||||
}
|
||||
|
||||
class BinaryenJSCalledSection extends CustomSection {
|
||||
static const String customSectionName = 'binaryen.js.called';
|
||||
|
||||
final ir.Functions functions;
|
||||
|
||||
BinaryenJSCalledSection(this.functions) : super([]);
|
||||
|
||||
@override
|
||||
void serializeContents(Serializer s) {
|
||||
final functionsToAnnotate = [
|
||||
...functions.imported.where((f) => f.isJSCalled),
|
||||
...functions.defined.where((f) => f.isJSCalled),
|
||||
];
|
||||
if (functionsToAnnotate.isNotEmpty) {
|
||||
s.writeName(customSectionName);
|
||||
s.writeUnsigned(functionsToAnnotate.length);
|
||||
for (final function in functionsToAnnotate) {
|
||||
s.writeUnsigned(function.index);
|
||||
s.writeUnsigned(1); // Number of hints
|
||||
s.writeUnsigned(0); // Offset (0 == function-level)
|
||||
s.writeUnsigned(0); // hint length (always 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void deserialize(Deserializer? d, ir.Functions functions) {
|
||||
if (d == null) return;
|
||||
|
||||
final count = d.readUnsigned();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final functionIndex = d.readUnsigned();
|
||||
final numHints = d.readUnsigned();
|
||||
for (int j = 0; j < numHints; j++) {
|
||||
final offset = d.readUnsigned(); // Offset (0 == function-level)
|
||||
if (offset != 0) {
|
||||
throw UnsupportedError(
|
||||
'Only function-level ($customSectionName) annotation supported.',
|
||||
);
|
||||
}
|
||||
final data = d.readUnsigned(); // always 0
|
||||
if (data != 0) {
|
||||
throw StateError('Expected 0 but got $data');
|
||||
}
|
||||
functions[functionIndex].isJSCalled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ExtraCustomSection extends CustomSection {
|
||||
final String name;
|
||||
final Uint8List bytes;
|
||||
|
||||
@@ -766,9 +766,6 @@ external T JS<T>(
|
||||
arg19,
|
||||
]);
|
||||
|
||||
@pragma("wasm:intrinsic")
|
||||
external WasmExternRef get thisModule;
|
||||
|
||||
/// Represents a JS `null` or `undefined` thrown from JS and caught in Wasm.
|
||||
///
|
||||
/// The class name is copied from the dart2js class for the same thing, for
|
||||
|
||||
@@ -696,6 +696,7 @@ class WasmFunction<F extends Function> extends WasmFuncRef {
|
||||
/// The argument must directly name a static function with no optional
|
||||
/// parameters and no type parameters.
|
||||
@pragma("wasm:intrinsic")
|
||||
@pragma("wasm:entry-point")
|
||||
external factory WasmFunction.fromFunction(F f);
|
||||
|
||||
/// Downcast `funcref` to a typed function reference.
|
||||
|
||||
Reference in New Issue
Block a user