mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 08:24:39 +00:00
[dart2wasm] Specialize callback wrappers.
Change-Id: I25dd953af21b14f64d9a61fbf51095a74a800e33 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/273080 Reviewed-by: Srujan Gaddam <srujzs@google.com> Commit-Queue: Joshua Litt <joshualitt@google.com>
This commit is contained in:
parent
ae5dc9e2a9
commit
d844eee251
|
@ -71,6 +71,12 @@ export const instantiate = async (modulePromise, importObjectPromise) => {
|
|||
// A special symbol attached to functions that wrap Dart functions.
|
||||
const jsWrappedDartFunctionSymbol = Symbol("JSWrappedDartFunction");
|
||||
|
||||
function finalizeWrapper(dartFunction, wrapped) {
|
||||
wrapped.dartFunction = dartFunction;
|
||||
wrapped[jsWrappedDartFunctionSymbol] = true;
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
// Calls a constructor with a variable number of arguments.
|
||||
function callConstructorVarArgs(constructor, args) {
|
||||
// Apply bind to the constructor. We pass `null` as the first argument
|
||||
|
@ -157,30 +163,6 @@ export const instantiate = async (modulePromise, importObjectPromise) => {
|
|||
},
|
||||
stringFromDartString: stringFromDartString,
|
||||
stringToDartString: stringToDartString,
|
||||
wrapDartFunction: function(dartFunction, exportFunctionName, argCount) {
|
||||
const wrapped = function (...args) {
|
||||
// Compute the last defined argument so we can support default
|
||||
// arguments in callbacks.
|
||||
var lastDefinedArg = -1;
|
||||
for (var i = args.length - 1; i >= 0; i--) {
|
||||
if (args[i] !== undefined) {
|
||||
lastDefinedArg = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Pad `undefined` for optional arguments that aren't passed so
|
||||
// that the trampoline can replace these values with defaults.
|
||||
while (args.length < argCount) {
|
||||
args.push(undefined);
|
||||
}
|
||||
return dartInstance.exports[`${exportFunctionName}`](
|
||||
dartFunction, lastDefinedArg, ...args.map(dartInstance.exports.$dartifyRaw));
|
||||
};
|
||||
wrapped.dartFunction = dartFunction;
|
||||
wrapped[jsWrappedDartFunctionSymbol] = true;
|
||||
return wrapped;
|
||||
},
|
||||
objectLength: function(o) {
|
||||
return o.length;
|
||||
},
|
||||
|
|
|
@ -16,6 +16,8 @@ import 'package:kernel/class_hierarchy.dart';
|
|||
import 'package:kernel/core_types.dart';
|
||||
import 'package:kernel/type_environment.dart';
|
||||
|
||||
enum _AnnotationType { import, export }
|
||||
|
||||
enum _MethodType {
|
||||
jsObjectLiteralConstructor,
|
||||
constructor,
|
||||
|
@ -24,6 +26,9 @@ enum _MethodType {
|
|||
setter,
|
||||
}
|
||||
|
||||
bool parametersNeedParens(List<String> parameters) =>
|
||||
parameters.isEmpty || parameters.length > 1;
|
||||
|
||||
class _MethodLoweringConfig {
|
||||
final Procedure procedure;
|
||||
final _MethodType type;
|
||||
|
@ -47,7 +52,6 @@ class _MethodLoweringConfig {
|
|||
String callArguments;
|
||||
String functionParameters;
|
||||
String object;
|
||||
bool argumentsNeedParens = parameters.isEmpty || parameters.length > 1;
|
||||
if (isConstructor) {
|
||||
object = '';
|
||||
callArguments = parameters.join(',');
|
||||
|
@ -86,7 +90,7 @@ class _MethodLoweringConfig {
|
|||
bodyString = '$object.$jsString = $callArguments';
|
||||
break;
|
||||
}
|
||||
if (argumentsNeedParens) {
|
||||
if (parametersNeedParens(parameters)) {
|
||||
return '($functionParameters) => $bodyString';
|
||||
} else {
|
||||
return '$functionParameters => $bodyString';
|
||||
|
@ -95,10 +99,10 @@ class _MethodLoweringConfig {
|
|||
}
|
||||
|
||||
/// Lowers static interop to JS, generating specialized JS methods as required.
|
||||
/// TODO(joshualitt): Generate specialized JS callback trampolines.
|
||||
class _JSLowerer extends Transformer {
|
||||
final Procedure _dartifyRawTarget;
|
||||
final Procedure _jsifyRawTarget;
|
||||
final Procedure _isDartFunctionWrappedTarget;
|
||||
final Procedure _wrapDartFunctionTarget;
|
||||
final Procedure _allowInteropTarget;
|
||||
final Procedure _numToInt;
|
||||
|
@ -110,7 +114,7 @@ class _JSLowerer extends Transformer {
|
|||
// _MethodLoweringConfigs until after we run the TFA, and then only generating
|
||||
// js methods for the dart stubs that remain.
|
||||
final List<String> jsMethods = [];
|
||||
int _jsTrampolineN = 1;
|
||||
int _methodN = 1;
|
||||
late Library _library;
|
||||
late String _libraryJSString;
|
||||
|
||||
|
@ -123,6 +127,8 @@ class _JSLowerer extends Transformer {
|
|||
.getTopLevelProcedure('dart:_js_helper', 'dartifyRaw'),
|
||||
_jsifyRawTarget = _coreTypes.index
|
||||
.getTopLevelProcedure('dart:_js_helper', 'jsifyRaw'),
|
||||
_isDartFunctionWrappedTarget = _coreTypes.index
|
||||
.getTopLevelProcedure('dart:_js_helper', '_isDartFunctionWrapped'),
|
||||
_wrapDartFunctionTarget = _coreTypes.index
|
||||
.getTopLevelProcedure('dart:_js_helper', '_wrapDartFunction'),
|
||||
_allowInteropTarget =
|
||||
|
@ -162,7 +168,7 @@ class _JSLowerer extends Transformer {
|
|||
}
|
||||
|
||||
@override
|
||||
StaticInvocation visitStaticInvocation(StaticInvocation node) {
|
||||
Expression visitStaticInvocation(StaticInvocation node) {
|
||||
node = super.visitStaticInvocation(node) as StaticInvocation;
|
||||
if (node.target == _allowInteropTarget) {
|
||||
Expression argument = node.arguments.positional.single;
|
||||
|
@ -271,8 +277,9 @@ class _JSLowerer extends Transformer {
|
|||
return _extensionMemberIndex!;
|
||||
}
|
||||
|
||||
DartType get _nullableObjectType =>
|
||||
_coreTypes.objectRawType(Nullability.nullable);
|
||||
// We could generate something more human readable, but for now we just
|
||||
// generate something short and unique.
|
||||
String generateMethodName() => '_${_methodN++}';
|
||||
|
||||
DartType get _nonNullableObjectType =>
|
||||
_coreTypes.objectRawType(Nullability.nonNullable);
|
||||
|
@ -280,158 +287,229 @@ class _JSLowerer extends Transformer {
|
|||
DartType get _nullableWasmExternRefType =>
|
||||
_wasmExternRefClass.getThisType(_coreTypes, Nullability.nullable);
|
||||
|
||||
DartType get _nonNullableWasmExternRefType =>
|
||||
_wasmExternRefClass.getThisType(_coreTypes, Nullability.nonNullable);
|
||||
|
||||
Expression _variableCheckConstant(
|
||||
VariableDeclaration variable, Constant constant) =>
|
||||
StaticInvocation(_coreTypes.identicalProcedure,
|
||||
Arguments([VariableGet(variable), ConstantExpression(constant)]));
|
||||
|
||||
List<Expression> _generateCallbackArguments(
|
||||
FunctionType function, List<VariableDeclaration> positionalParameters,
|
||||
[int? requiredParameterCount]) {
|
||||
List<Expression> callbackArguments = [];
|
||||
int length = requiredParameterCount ?? function.positionalParameters.length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
callbackArguments.add(AsExpression(VariableGet(positionalParameters[i]),
|
||||
function.positionalParameters[i]));
|
||||
Procedure _addInteropProcedure(String name, String pragmaOptionString,
|
||||
FunctionNode function, Uri fileUri, _AnnotationType type,
|
||||
{required bool isExternal}) {
|
||||
String pragmaName;
|
||||
switch (type) {
|
||||
case _AnnotationType.import:
|
||||
pragmaName = 'import';
|
||||
break;
|
||||
case _AnnotationType.export:
|
||||
pragmaName = 'export';
|
||||
break;
|
||||
}
|
||||
return callbackArguments;
|
||||
final procedure = Procedure(
|
||||
Name(name, _library), ProcedureKind.Method, function,
|
||||
isStatic: true, isExternal: isExternal, fileUri: fileUri)
|
||||
..isNonNullableByDefault = true;
|
||||
procedure.addAnnotation(
|
||||
ConstantExpression(InstanceConstant(_pragmaClass.reference, [], {
|
||||
_pragmaName.fieldReference: StringConstant('wasm:$pragmaName'),
|
||||
_pragmaOptions.fieldReference: StringConstant('$pragmaOptionString')
|
||||
})));
|
||||
_library.addProcedure(procedure);
|
||||
return procedure;
|
||||
}
|
||||
|
||||
Statement _generateDispatchCase(
|
||||
FunctionType function,
|
||||
VariableDeclaration callbackVariable,
|
||||
List<VariableDeclaration> positionalParameters,
|
||||
[int? requiredParameterCount]) =>
|
||||
ReturnStatement(StaticInvocation(
|
||||
_jsifyRawTarget,
|
||||
Arguments([
|
||||
FunctionInvocation(
|
||||
FunctionAccessKind.FunctionType,
|
||||
AsExpression(VariableGet(callbackVariable), function),
|
||||
Arguments(_generateCallbackArguments(
|
||||
function, positionalParameters, requiredParameterCount)),
|
||||
functionType: function),
|
||||
])));
|
||||
|
||||
/// Builds the body of a function trampoline. To support default arguments, we
|
||||
/// find the last defined argument in JS, that is the last argument which was
|
||||
/// explicitly passed by the user, and then we dispatch to a Dart function
|
||||
/// with the right number of arguments.
|
||||
Statement _createFunctionTrampolineBody(
|
||||
FunctionType function,
|
||||
VariableDeclaration callbackVariable,
|
||||
VariableDeclaration lastDefinedArgument,
|
||||
List<VariableDeclaration> positionalParameters) {
|
||||
// Handle cases where some or all arguments are undefined.
|
||||
// TODO(joshualitt): Consider using a switch instead.
|
||||
List<Statement> dispatchCases = [];
|
||||
for (int i = function.requiredParameterCount - 1;
|
||||
i < function.positionalParameters.length;
|
||||
i++) {
|
||||
// In this case, [i] is the last defined argument which can range from
|
||||
// -1(no arguments defined), to an actual index in the positional
|
||||
// parameters. [_generateDispatchCase] must also take the required
|
||||
// parameter count, which is always the index of the last defined argument
|
||||
// + 1, i.e. the total number of defined arguments.
|
||||
int requiredParameterCount = i + 1;
|
||||
dispatchCases.add(IfStatement(
|
||||
_variableCheckConstant(
|
||||
lastDefinedArgument, DoubleConstant(i.toDouble())),
|
||||
_generateDispatchCase(function, callbackVariable,
|
||||
positionalParameters, requiredParameterCount),
|
||||
null));
|
||||
List<VariableDeclaration> positionalParameters,
|
||||
int requiredParameterCount) {
|
||||
List<Expression> callbackArguments = [];
|
||||
for (int i = 0; i < requiredParameterCount; i++) {
|
||||
callbackArguments.add(AsExpression(
|
||||
StaticInvocation(_dartifyRawTarget,
|
||||
Arguments([VariableGet(positionalParameters[i])])),
|
||||
function.positionalParameters[i]));
|
||||
}
|
||||
|
||||
// Finally handle the case where all arguments are defined.
|
||||
dispatchCases.add(_generateDispatchCase(
|
||||
function, callbackVariable, positionalParameters));
|
||||
|
||||
return Block(dispatchCases);
|
||||
return ReturnStatement(StaticInvocation(
|
||||
_jsifyRawTarget,
|
||||
Arguments([
|
||||
FunctionInvocation(
|
||||
FunctionAccessKind.FunctionType,
|
||||
AsExpression(VariableGet(callbackVariable), function),
|
||||
Arguments(callbackArguments),
|
||||
functionType: function),
|
||||
])));
|
||||
}
|
||||
|
||||
bool _needsArgumentsLength(FunctionType type) =>
|
||||
type.requiredParameterCount < type.positionalParameters.length;
|
||||
|
||||
/// Creates a callback trampoline for the given [function]. This callback
|
||||
/// trampoline expects a Dart callback as its first argument, then an integer
|
||||
/// value(double type) indicating the position of the last defined argument,
|
||||
/// followed by all of the arguments to the Dart callback as Dart objects. We
|
||||
/// will always pad the argument list up to the maximum number of positional
|
||||
/// arguments with `undefined` values. The trampoline will cast all incoming
|
||||
/// Dart objects to the appropriate types, dispatch, and then `jsifyRaw` any
|
||||
/// returned value. [_createFunctionTrampoline] Returns a [String] function
|
||||
/// name representing the name of the wrapping function.
|
||||
/// TODO(joshualitt): Share callback trampolines if the [FunctionType]
|
||||
/// matches.
|
||||
/// TODO(joshualitt): Simplify the trampoline in JS for the case where there
|
||||
/// are no default arguments.
|
||||
/// value(double type) indicating the position of the last defined
|
||||
/// argument(only for callbacks that take optional parameters), followed by
|
||||
/// all of the arguments to the Dart callback as JS objects. The trampoline
|
||||
/// will `dartifyRaw` all incoming JS objects and then cast them to their
|
||||
/// approriate types, dispatch, and then `jsifyRaw` any returned value.
|
||||
/// [_createFunctionTrampoline] Returns a [String] function name representing
|
||||
/// the name of the wrapping function.
|
||||
String _createFunctionTrampoline(Procedure node, FunctionType function) {
|
||||
int fileOffset = node.fileOffset;
|
||||
|
||||
// Create arguments for each positional parameter in the function. These
|
||||
// arguments will be converted in JS to Dart objects. The generated wrapper
|
||||
// will cast each argument to the correct type. The first argument to this
|
||||
// function will be the Dart callback, which will be cast to the supplied
|
||||
// [FunctionType] before being invoked. The second argument will be the
|
||||
// last defined argument which is necessary to support default arguments in
|
||||
// callbacks.
|
||||
// arguments will be JS objects. The generated wrapper will cast each
|
||||
// argument to the correct type. The first argument to this function will
|
||||
// be the Dart callback, which will be cast to the supplied [FunctionType]
|
||||
// before being invoked. If the callback takes optional parameters then, the
|
||||
// second argument will be a `double` indicating the last defined argument.
|
||||
int parameterId = 1;
|
||||
final callbackVariable =
|
||||
VariableDeclaration('callback', type: _nonNullableObjectType);
|
||||
final lastDefinedArgument = VariableDeclaration('lastDefinedArgument',
|
||||
type: _coreTypes.doubleNonNullableRawType);
|
||||
VariableDeclaration? argumentsLength;
|
||||
if (_needsArgumentsLength(function)) {
|
||||
argumentsLength = VariableDeclaration('argumentsLength',
|
||||
type: _coreTypes.doubleNonNullableRawType);
|
||||
}
|
||||
|
||||
// Initialize variable declarations.
|
||||
List<VariableDeclaration> positionalParameters = [];
|
||||
for (int j = 0; j < function.positionalParameters.length; j++) {
|
||||
positionalParameters
|
||||
.add(VariableDeclaration('x$j', type: _nullableObjectType));
|
||||
positionalParameters.add(VariableDeclaration('x${parameterId++}',
|
||||
type: _nullableWasmExternRefType));
|
||||
}
|
||||
|
||||
Statement functionTrampolineBody = _createFunctionTrampolineBody(
|
||||
function, callbackVariable, lastDefinedArgument, positionalParameters);
|
||||
// Build the body of a function trampoline. To support default arguments, we
|
||||
// find the last defined argument in JS, that is the last argument which was
|
||||
// explicitly passed by the user, and then we dispatch to a Dart function
|
||||
// with the right number of arguments.
|
||||
//
|
||||
// First we handle cases where some or all arguments are undefined.
|
||||
// TODO(joshualitt): Consider using a switch instead.
|
||||
List<Statement> dispatchCases = [];
|
||||
for (int i = function.requiredParameterCount + 1;
|
||||
i <= function.positionalParameters.length;
|
||||
i++) {
|
||||
dispatchCases.add(IfStatement(
|
||||
_variableCheckConstant(
|
||||
argumentsLength!, DoubleConstant(i.toDouble())),
|
||||
_generateDispatchCase(
|
||||
function, callbackVariable, positionalParameters, i),
|
||||
null));
|
||||
}
|
||||
|
||||
// Finally handle the case where only required parameters are passed.
|
||||
dispatchCases.add(_generateDispatchCase(function, callbackVariable,
|
||||
positionalParameters, function.requiredParameterCount));
|
||||
Statement functionTrampolineBody = Block(dispatchCases);
|
||||
|
||||
// Create a new procedure for the callback trampoline. This procedure will
|
||||
// be exported from Wasm to JS so it can be called from JS. The argument
|
||||
// returned from the supplied callback will be converted with `jsifyRaw` to
|
||||
// a native JS value before being returned to JS.
|
||||
final String libraryName = _library.name ?? 'Unnamed';
|
||||
final functionTrampolineName =
|
||||
'|_functionTrampoline${_jsTrampolineN++}For$libraryName';
|
||||
final functionTrampolineImportName = '\$$functionTrampolineName';
|
||||
final functionTrampoline = Procedure(
|
||||
Name(functionTrampolineName, _library),
|
||||
ProcedureKind.Method,
|
||||
final functionTrampolineName = generateMethodName();
|
||||
_addInteropProcedure(
|
||||
functionTrampolineName,
|
||||
functionTrampolineName,
|
||||
FunctionNode(functionTrampolineBody,
|
||||
positionalParameters: [callbackVariable, lastDefinedArgument]
|
||||
.followedBy(positionalParameters)
|
||||
.toList(),
|
||||
positionalParameters: [
|
||||
callbackVariable,
|
||||
if (argumentsLength != null) argumentsLength
|
||||
].followedBy(positionalParameters).toList(),
|
||||
returnType: _nullableWasmExternRefType)
|
||||
..fileOffset = fileOffset,
|
||||
isStatic: true,
|
||||
fileUri: node.fileUri)
|
||||
..fileOffset = fileOffset
|
||||
..isNonNullableByDefault = true;
|
||||
functionTrampoline.addAnnotation(
|
||||
ConstantExpression(InstanceConstant(_pragmaClass.reference, [], {
|
||||
_pragmaName.fieldReference: StringConstant('wasm:export'),
|
||||
_pragmaOptions.fieldReference:
|
||||
StringConstant(functionTrampolineImportName)
|
||||
})));
|
||||
_library.addProcedure(functionTrampoline);
|
||||
return functionTrampolineImportName;
|
||||
..fileOffset = node.fileOffset,
|
||||
node.fileUri,
|
||||
_AnnotationType.export,
|
||||
isExternal: false);
|
||||
return functionTrampolineName;
|
||||
}
|
||||
|
||||
/// Lowers a [StaticInvocation] of `allowInterop` to
|
||||
/// [_createFunctionTrampoline] followed by `_wrapDartFunction`.
|
||||
StaticInvocation _allowInterop(
|
||||
/// Returns a JS method that wraps a Dart callback in a JS wrapper.
|
||||
Procedure _getJSWrapperFunction(
|
||||
FunctionType type, String functionTrampolineName, Uri fileUri) {
|
||||
List<String> jsParameters = [];
|
||||
for (int i = 0; i < type.positionalParameters.length; i++) {
|
||||
jsParameters.add('x$i');
|
||||
}
|
||||
String jsParametersString = jsParameters.join(',');
|
||||
String dartArguments = 'f';
|
||||
bool needsArguments = _needsArgumentsLength(type);
|
||||
if (needsArguments) {
|
||||
dartArguments = '$dartArguments,arguments.length';
|
||||
}
|
||||
if (jsParameters.isNotEmpty) {
|
||||
dartArguments = '$dartArguments,$jsParametersString';
|
||||
}
|
||||
|
||||
// Create JS method.
|
||||
// Note: We have to use a regular function for the inner closure in some
|
||||
// cases because we need access to `arguments`.
|
||||
final jsMethodName = functionTrampolineName;
|
||||
if (needsArguments) {
|
||||
jsMethods.add("$jsMethodName: f => "
|
||||
"finalizeWrapper(f, function($jsParametersString) {"
|
||||
" return dartInstance.exports.$functionTrampolineName($dartArguments) "
|
||||
"})");
|
||||
} else {
|
||||
if (parametersNeedParens(jsParameters)) {
|
||||
jsParametersString = '($jsParametersString)';
|
||||
}
|
||||
jsMethods.add("$jsMethodName: f => "
|
||||
"finalizeWrapper(f,$jsParametersString => "
|
||||
"dartInstance.exports.$functionTrampolineName($dartArguments))");
|
||||
}
|
||||
|
||||
// Create Dart procedure stub.
|
||||
return _addInteropProcedure(
|
||||
'|$jsMethodName',
|
||||
'dart2wasm.$jsMethodName',
|
||||
FunctionNode(null,
|
||||
positionalParameters: [
|
||||
VariableDeclaration('dartFunction',
|
||||
type: _nonNullableWasmExternRefType)
|
||||
],
|
||||
returnType: _nonNullableWasmExternRefType),
|
||||
fileUri,
|
||||
_AnnotationType.import,
|
||||
isExternal: true);
|
||||
}
|
||||
|
||||
/// Lowers an invocation of `allowInterop<type>(foo)` to:
|
||||
///
|
||||
/// let #var = foo in
|
||||
/// _isDartFunctionWrapped<type>(#var) ?
|
||||
/// #var :
|
||||
/// _wrapDartFunction<type>(#var, jsWrapperFunction(#var));
|
||||
///
|
||||
/// The use of two functions here is necessary because we do not allow
|
||||
/// `WasmExternRef` to be an argument or return type for a tear off.
|
||||
///
|
||||
/// Note: _wrapDartFunction tracks wrapped Dart functions in a map. When
|
||||
/// these Dart functions flow to JS, they are replaced by their wrappers. If
|
||||
/// the wrapper should ever flow back into Dart then it will be replaced by
|
||||
/// the original Dart function.
|
||||
Expression _allowInterop(
|
||||
Procedure node, FunctionType type, Expression argument) {
|
||||
String functionTrampolineName = _createFunctionTrampoline(node, type);
|
||||
return StaticInvocation(
|
||||
_wrapDartFunctionTarget,
|
||||
Arguments([
|
||||
argument,
|
||||
StringLiteral(functionTrampolineName),
|
||||
ConstantExpression(IntConstant(type.positionalParameters.length))
|
||||
], types: [
|
||||
type
|
||||
]));
|
||||
Procedure jsWrapperFunction =
|
||||
_getJSWrapperFunction(type, functionTrampolineName, node.fileUri);
|
||||
VariableDeclaration v =
|
||||
VariableDeclaration('#var', initializer: argument, type: type);
|
||||
return Let(
|
||||
v,
|
||||
ConditionalExpression(
|
||||
StaticInvocation(_isDartFunctionWrappedTarget,
|
||||
Arguments([VariableGet(v)], types: [type])),
|
||||
VariableGet(v),
|
||||
StaticInvocation(
|
||||
_wrapDartFunctionTarget,
|
||||
Arguments([
|
||||
VariableGet(v),
|
||||
StaticInvocation(
|
||||
jsWrapperFunction, Arguments([VariableGet(v)])),
|
||||
], types: [
|
||||
type
|
||||
])),
|
||||
type));
|
||||
}
|
||||
|
||||
// Specializes a JS method for a given [_MethodLoweringConfig] and returns an
|
||||
|
@ -449,24 +527,16 @@ class _JSLowerer extends Transformer {
|
|||
}
|
||||
|
||||
// Create Dart procedure stub for JS method.
|
||||
final jsMethodName = '_${_jsTrampolineN++}';
|
||||
final dartProcedureName = '|$jsMethodName';
|
||||
final dartProcedure = Procedure(
|
||||
Name(dartProcedureName, _library),
|
||||
ProcedureKind.Method,
|
||||
String jsMethodName = generateMethodName();
|
||||
final dartProcedure = _addInteropProcedure(
|
||||
'|$jsMethodName',
|
||||
'dart2wasm.$jsMethodName',
|
||||
FunctionNode(null,
|
||||
positionalParameters: dartPositionalParameters,
|
||||
returnType: _nullableWasmExternRefType),
|
||||
isExternal: true,
|
||||
isStatic: true,
|
||||
fileUri: config.fileUri)
|
||||
..isNonNullableByDefault = true;
|
||||
dartProcedure.addAnnotation(
|
||||
ConstantExpression(InstanceConstant(_pragmaClass.reference, [], {
|
||||
_pragmaName.fieldReference: StringConstant('wasm:import'),
|
||||
_pragmaOptions.fieldReference: StringConstant('dart2wasm.$jsMethodName')
|
||||
})));
|
||||
_library.addProcedure(dartProcedure);
|
||||
config.fileUri,
|
||||
_AnnotationType.import,
|
||||
isExternal: true);
|
||||
|
||||
// Create JS method
|
||||
jsMethods.add("$jsMethodName: ${config.generateJS(jsParameterStrings)}");
|
||||
|
|
|
@ -520,20 +520,13 @@ List<int> jsIntTypedArrayToDartIntTypedData(
|
|||
List<Object?> toDartList(WasmExternRef? ref) => List<Object?>.generate(
|
||||
objectLength(ref).round(), (int n) => dartifyRaw(objectReadIndex(ref, n)));
|
||||
|
||||
@pragma("wasm:import", "dart2wasm.wrapDartFunction")
|
||||
external WasmExternRef? _wrapDartFunctionRaw(WasmExternRef? dartFunction,
|
||||
WasmExternRef? trampolineName, WasmExternRef? argCount);
|
||||
// These two trivial helpers are needed to work around an issue with tearing off
|
||||
// functions that take / return [WasmExternRef].
|
||||
bool _isDartFunctionWrapped<F extends Function>(F f) =>
|
||||
functionToJSWrapper.containsKey(f);
|
||||
|
||||
F _wrapDartFunction<F extends Function>(
|
||||
F f, String trampolineName, int argCount) {
|
||||
if (functionToJSWrapper.containsKey(f)) {
|
||||
return f;
|
||||
}
|
||||
JSValue wrappedFunction = JSValue(_wrapDartFunctionRaw(
|
||||
f.toJS().toExternRef(),
|
||||
trampolineName.toJS().toExternRef(),
|
||||
argCount.toDouble().toJS().toExternRef())!);
|
||||
functionToJSWrapper[f] = wrappedFunction;
|
||||
F _wrapDartFunction<F extends Function>(F f, WasmExternRef ref) {
|
||||
functionToJSWrapper[f] = JSValue(ref);
|
||||
return f;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue