diff --git a/pkg/dart2wasm/lib/functions.dart b/pkg/dart2wasm/lib/functions.dart index cb85cf473ed..56c0ac4dda4 100644 --- a/pkg/dart2wasm/lib/functions.dart +++ b/pkg/dart2wasm/lib/functions.dart @@ -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. diff --git a/pkg/dart2wasm/lib/intrinsics.dart b/pkg/dart2wasm/lib/intrinsics.dart index 5212239734e..c48c06daa26 100644 --- a/pkg/dart2wasm/lib/intrinsics.dart +++ b/pkg/dart2wasm/lib/intrinsics.dart @@ -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; } diff --git a/pkg/dart2wasm/lib/js/callback_specializer.dart b/pkg/dart2wasm/lib/js/callback_specializer.dart index fac118527c5..eaadfc839f3 100644 --- a/pkg/dart2wasm/lib/js/callback_specializer.dart +++ b/pkg/dart2wasm/lib/js/callback_specializer.dart @@ -17,7 +17,7 @@ class CallbackSpecializer { CallbackSpecializer(this._staticTypeContext, this._util); Statement _generateDispatchCase( - FunctionType function, + FunctionType instantiatedFunctionType, Variable callbackVariable, List positionalParameters, int requiredParameterCount, { @@ -25,7 +25,8 @@ class CallbackSpecializer { }) { List 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,35 +434,30 @@ 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([ + StaticInvocation( + _util.wasmFunctionFromFunction, + Arguments( + [ConstantExpression(StaticTearOffConstant(exportedFunction))], + types: [ + exportedFunction.function.computeFunctionType( + Nullability.nonNullable, + ), + ], ), ), - ]), - StaticInvocation( - jsWrapperFunction, - Arguments([ - StaticGet(_util.thisModuleGetter), + StaticInvocation( + _util.jsObjectFromDartObjectTarget, + Arguments([argument]), + ), + if (castClosure != null) StaticInvocation( _util.jsObjectFromDartObjectTarget, - Arguments([argument]), + Arguments([castClosure]), ), - if (castClosure != null) - StaticInvocation( - _util.jsObjectFromDartObjectTarget, - Arguments([castClosure]), - ), - ]), - ), + ]), ), ); } diff --git a/pkg/dart2wasm/lib/js/runtime_blob.dart b/pkg/dart2wasm/lib/js/runtime_blob.dart index f7ba5da12cb..47e3d5c063c 100644 --- a/pkg/dart2wasm/lib/js/runtime_blob.dart +++ b/pkg/dart2wasm/lib/js/runtime_blob.dart @@ -104,7 +104,6 @@ class CompiledApp { <> <> }); - dartInstance.exports.<>(dartInstance); return new InstantiatedApp(this, dartInstance); } @@ -181,7 +180,6 @@ final moduleLoadingHelperTemplate = Template(r''' <> "<>": dartInstance.exports, }); - moduleInstance.exports.<>(moduleInstance); } const moduleLoadingHelper = { "loadDeferredModules": async (moduleNames) => { diff --git a/pkg/dart2wasm/lib/js/runtime_generator.dart b/pkg/dart2wasm/lib/js/runtime_generator.dart index f659e9ccf05..5a494224ea0 100644 --- a/pkg/dart2wasm/lib/js/runtime_generator.dart +++ b/pkg/dart2wasm/lib/js/runtime_generator.dart @@ -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, diff --git a/pkg/dart2wasm/lib/js/util.dart b/pkg/dart2wasm/lib/js/util.dart index d50c8759c40..24d65ddc3cd 100644 --- a/pkg/dart2wasm/lib/js/util.dart +++ b/pkg/dart2wasm/lib/js/util.dart @@ -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 = []; 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); diff --git a/pkg/dart2wasm/lib/namer.dart b/pkg/dart2wasm/lib/namer.dart index 90a47f20349..3764beaa4fe 100644 --- a/pkg/dart2wasm/lib/namer.dart +++ b/pkg/dart2wasm/lib/namer.dart @@ -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 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]. diff --git a/pkg/dart2wasm/lib/translator.dart b/pkg/dart2wasm/lib/translator.dart index b8a1c399bcd..d0c08a63245 100644 --- a/pkg/dart2wasm/lib/translator.dart +++ b/pkg/dart2wasm/lib/translator.dart @@ -414,7 +414,6 @@ class Translator with KernelNodes { final Map _builderToOutput = {}; final Map moduleToBuilder = {}; bool get hasMultipleModules => _moduleOutputData.hasMultipleModules; - final Map _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. diff --git a/pkg/dart2wasm/test/ir_tests/deferred.type_checks_module1.wat b/pkg/dart2wasm/test/ir_tests/deferred.type_checks_module1.wat index d4b97bd1e1b..2638b952546 100644 --- a/pkg/dart2wasm/test/ir_tests/deferred.type_checks_module1.wat +++ b/pkg/dart2wasm/test/ir_tests/deferred.type_checks_module1.wat @@ -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 5 i32.const 14 - call_indirect $$.& (param (ref $Array)) (result (ref $JSExternWrapper)) + call_indirect $$.% (param (ref $Array)) (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 diff --git a/pkg/wasm_builder/lib/src/builder/function.dart b/pkg/wasm_builder/lib/src/builder/function.dart index b476008241a..420f1330cbc 100644 --- a/pkg/wasm_builder/lib/src/builder/function.dart +++ b/pkg/wasm_builder/lib/src/builder/function.dart @@ -41,6 +41,7 @@ class FunctionBuilder extends ir.BaseFunction functionName, ) ..isPure = isPure + ..isJSCalled = isJSCalled ..inlineHint = inlineHint; @override diff --git a/pkg/wasm_builder/lib/src/ir/function.dart b/pkg/wasm_builder/lib/src/ir/function.dart index 6d9b7347e48..f7cc3a48f46 100644 --- a/pkg/wasm_builder/lib/src/ir/function.dart +++ b/pkg/wasm_builder/lib/src/ir/function.dart @@ -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(' '); diff --git a/pkg/wasm_builder/lib/src/ir/module.dart b/pkg/wasm_builder/lib/src/ir/module.dart index f974082c228..e1d3ed1a034 100644 --- a/pkg/wasm_builder/lib/src/ir/module.dart +++ b/pkg/wasm_builder/lib/src/ir/module.dart @@ -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, ); diff --git a/pkg/wasm_builder/lib/src/serialize/sections.dart b/pkg/wasm_builder/lib/src/serialize/sections.dart index 02dfcad7df4..3183647a6b3 100644 --- a/pkg/wasm_builder/lib/src/serialize/sections.dart +++ b/pkg/wasm_builder/lib/src/serialize/sections.dart @@ -14,6 +14,7 @@ const Set _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; diff --git a/sdk/lib/_internal/wasm/js_common/js_helper.dart b/sdk/lib/_internal/wasm/js_common/js_helper.dart index b8052609a1c..b2ebf4900e1 100644 --- a/sdk/lib/_internal/wasm/js_common/js_helper.dart +++ b/sdk/lib/_internal/wasm/js_common/js_helper.dart @@ -766,9 +766,6 @@ external T JS( 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 diff --git a/sdk/lib/_wasm/wasm_types.dart b/sdk/lib/_wasm/wasm_types.dart index 490487fd330..fb7729f0e74 100644 --- a/sdk/lib/_wasm/wasm_types.dart +++ b/sdk/lib/_wasm/wasm_types.dart @@ -696,6 +696,7 @@ class WasmFunction 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.