1
0
mirror of https://github.com/dart-lang/sdk synced 2024-07-01 07:14:29 +00:00

[dart2wasm/ddc/dart2js] Lower Function.toJS and JSExportedDartFunction.toDart

Lowers these extension methods to some helper functions instead of
allowInterop to improve performance and get consistent semantics.

Specifically:
- Function.toJS no longer gives you the same JSFunction when calling
toJS on the same function multiple times in the JS compilers.
- Adds fast calling syntax for functions with 0-5 args in the JS
compilers.
- Allows additional args to be passed to converted functions that
are omitted in the JS compilers.
- The static type now determines the number of args that can be
passed to the JS function in the JS compilers.
- Fixes an issue in dart2wasm where if too few arguments are
passed, the call may succeed due to conversion of undefined to
null.
- Adds throws when a user tries to wrap a JS function that
returned from Function.toJS in the JS compilers.

Closes https://github.com/dart-lang/sdk/issues/55515
Addresses https://github.com/dart-lang/sdk/issues/48186

CoreLibraryReviewExempt: Changes to docs only in API surface.
Change-Id: I41d864dc5e02b597d9f1c16c88e3c04872f28225
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/368065
Reviewed-by: Nicholas Shahan <nshahan@google.com>
Reviewed-by: Stephen Adams <sra@google.com>
Commit-Queue: Srujan Gaddam <srujzs@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Srujan Gaddam 2024-06-26 23:06:36 +00:00 committed by Commit Queue
parent 4b88698e48
commit 1c534e5fd5
15 changed files with 1114 additions and 109 deletions

View File

@ -64,11 +64,19 @@
`ExternalDartReferenceToObject` and `ObjectToExternalDartReference` are now
extensions on `T` and `ExternalDartReference<T>`, respectively, where `T
extends Object?`. See [#55342][] and [#55536][] for more details.
- Fixes some consistency issues with `Function.toJS` across all compilers.
Specifically, calling `Function.toJS` on the same function gives you a new JS
function (see issue [#55515][]), the max number of args that are passed to the
JS function is determined by the static type of the Dart function, and extra
args are dropped when passed to the JS function in all compilers (see
[#48186][]).
[#55508]: https://github.com/dart-lang/sdk/issues/55508
[#55267]: https://github.com/dart-lang/sdk/issues/55267
[#55342]: https://github.com/dart-lang/sdk/issues/55342
[#55536]: https://github.com/dart-lang/sdk/issues/55536
[#55515]: https://github.com/dart-lang/sdk/issues/55515
[#48186]: https://github.com/dart-lang/sdk/issues/48186
### Tools

View File

@ -41,9 +41,14 @@ class JsUtilOptimizer extends Transformer {
final List<Procedure> _callConstructorUncheckedTargets;
final CloneVisitorNotMembers _cloner = CloneVisitorWithMembers();
final Map<Member, _InvocationBuilder?> _externalInvocationBuilders = {};
final Procedure _functionToJSTarget;
final List<Procedure> _functionToJSTargets;
final Procedure _functionToJSNTarget;
final Procedure _getPropertyTarget;
final Procedure _getPropertyTrustTypeTarget;
final Procedure _globalContextTarget;
final Procedure _jsExportedDartFunctionToDartTarget;
final Procedure _jsFunctionToDart;
final InterfaceType _objectType;
final Procedure _setPropertyTarget;
final Procedure _setPropertyUncheckedTarget;
@ -88,12 +93,25 @@ class JsUtilOptimizer extends Transformer {
5,
(i) => _coreTypes.index.getTopLevelProcedure(
'dart:js_util', '_callConstructorUnchecked$i')),
_functionToJSTarget = _coreTypes.index.getTopLevelProcedure(
'dart:js_interop', 'FunctionToJSExportedDartFunction|get#toJS'),
_functionToJSTargets = List<Procedure>.generate(
6,
(i) => _coreTypes.index
.getTopLevelProcedure('dart:js_util', '_functionToJS$i')),
_functionToJSNTarget = _coreTypes.index
.getTopLevelProcedure('dart:js_util', '_functionToJSN'),
_getPropertyTarget = _coreTypes.index
.getTopLevelProcedure('dart:js_util', 'getProperty'),
_getPropertyTrustTypeTarget = _coreTypes.index
.getTopLevelProcedure('dart:js_util', '_getPropertyTrustType'),
_globalContextTarget = _coreTypes.index.getTopLevelProcedure(
'dart:_js_helper', 'get:staticInteropGlobalContext'),
_jsExportedDartFunctionToDartTarget = _coreTypes.index
.getTopLevelProcedure('dart:js_interop',
'JSExportedDartFunctionToFunction|get#toDart'),
_jsFunctionToDart = _coreTypes.index
.getTopLevelProcedure('dart:js_util', '_jsFunctionToDart'),
_objectType = hierarchy.coreTypes.objectNonNullableRawType,
_setPropertyTarget = _coreTypes.index
.getTopLevelProcedure('dart:js_util', 'setProperty'),
@ -480,6 +498,10 @@ class JsUtilOptimizer extends Transformer {
invocation = _lowerCallConstructor(node);
// TODO(srujzs): Delete the `isPatchedMember` check once
// https://github.com/dart-lang/sdk/issues/53367 is resolved.
} else if (target == _functionToJSTarget) {
invocation = _lowerFunctionToJS(node);
} else if (target == _jsExportedDartFunctionToDartTarget) {
invocation = _lowerJSExportedDartFunctionToDart(node);
} else if (target.isExternal && !JsInteropChecks.isPatchedMember(target)) {
final builder = _externalInvocationBuilders.putIfAbsent(
target, () => _getExternalInvocationBuilder(target));
@ -662,6 +684,45 @@ class JsUtilOptimizer extends Transformer {
..parent = nodeParent;
}
/// For the given `dart:js_interop` `Function.toJS` invocation [node], returns
/// an invocation of `_functionToJSX` with the given `Function` argument,
/// where X is the number of the positional arguments.
///
/// If the number of the positional arguments is larger than 5, returns an
/// invocation of `_functionToJSN` instead.
StaticInvocation _lowerFunctionToJS(StaticInvocation node) {
// JS interop checks assert that the static type is available, and that
// there are no named arguments or type arguments.
final function = node.arguments.positional.single;
final functionType =
function.getStaticType(_staticTypeContext) as FunctionType;
final argumentsLength = functionType.positionalParameters.length;
Procedure target;
Arguments arguments;
if (argumentsLength < _functionToJSTargets.length) {
target = _functionToJSTargets[argumentsLength];
arguments = Arguments([function]);
} else {
target = _functionToJSNTarget;
arguments = Arguments([function, IntLiteral(argumentsLength)]);
}
return StaticInvocation(
target, arguments..fileOffset = node.arguments.fileOffset)
..fileOffset = node.fileOffset
..parent = node.parent;
}
/// For the given `dart:js_interop` `JSExportedDartFunction.toDart` invocation
/// [node], returns an invocation of `_jsFunctionToDart` with the given
/// `JSExportedDartFunction` argument.
StaticInvocation _lowerJSExportedDartFunctionToDart(StaticInvocation node) =>
StaticInvocation(
_jsFunctionToDart,
Arguments([node.arguments.positional[0]])
..fileOffset = node.arguments.fileOffset)
..fileOffset = node.fileOffset
..parent = node.parent;
/// Returns whether the given [node] is guaranteed to be allowed to interop
/// with JS.
///

View File

@ -17,9 +17,6 @@ class CallbackSpecializer {
CallbackSpecializer(
this._staticTypeContext, this._util, this._methodCollector);
bool _needsArgumentsLength(FunctionType type) =>
type.requiredParameterCount < type.positionalParameters.length;
Statement _generateDispatchCase(
FunctionType function,
VariableDeclaration callbackVariable,
@ -54,12 +51,11 @@ class CallbackSpecializer {
/// 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(only for callbacks that take optional parameters),
/// followed by all of the arguments to the Dart callback as JS objects. The
/// trampoline will `dartifyRaw` or box all incoming JS objects and then cast
/// them to their appropriate types, dispatch, and then `jsifyRaw` or box any
/// returned value.
/// then an integer value(double type) indicating the number of arguments
/// passed, followed by all of the arguments to the Dart callback as JS
/// objects. The trampoline will `dartifyRaw` or box all incoming JS objects
/// and then cast them to their appropriate types, dispatch, and then
/// `jsifyRaw` or box any returned value.
///
/// Returns a [String] function name representing the name of the wrapping
/// function.
@ -69,20 +65,18 @@ class CallbackSpecializer {
// 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.
// before being invoked. The second argument will be a `double` indicating
// the number of arguments passed.
int parameterId = 1;
final callbackVariable = VariableDeclaration('callback',
type: _util.nonNullableObjectType, isSynthesized: true);
VariableDeclaration? argumentsLength;
if (_needsArgumentsLength(function)) {
argumentsLength = VariableDeclaration('argumentsLength',
type: _util.coreTypes.doubleNonNullableRawType, isSynthesized: true);
}
final argumentsLength = VariableDeclaration('argumentsLength',
type: _util.coreTypes.doubleNonNullableRawType, isSynthesized: true);
// Initialize variable declarations.
List<VariableDeclaration> positionalParameters = [];
for (int j = 0; j < function.positionalParameters.length; j++) {
final positionalParametersLength = function.positionalParameters.length;
for (int j = 0; j < positionalParametersLength; j++) {
positionalParameters.add(VariableDeclaration('x${parameterId++}',
type: _util.nullableWasmExternRefType, isSynthesized: true));
}
@ -91,26 +85,42 @@ class CallbackSpecializer {
// 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++) {
// If more arguments were passed than there are parameters, ignore the extra
// arguments.
dispatchCases.add(IfStatement(
_util.variableGreaterThanOrEqualToConstant(
argumentsLength, IntConstant(positionalParametersLength)),
_generateDispatchCase(function, callbackVariable, positionalParameters,
positionalParametersLength,
boxExternRef: boxExternRef),
null));
// TODO(srujzs): Consider using a switch instead.
for (int i = positionalParametersLength - 1;
i >= function.requiredParameterCount;
i--) {
dispatchCases.add(IfStatement(
_util.variableCheckConstant(
argumentsLength!, DoubleConstant(i.toDouble())),
argumentsLength, DoubleConstant(i.toDouble())),
_generateDispatchCase(
function, callbackVariable, positionalParameters, i,
boxExternRef: boxExternRef),
null));
}
// Finally handle the case where only required parameters are passed.
dispatchCases.add(_generateDispatchCase(function, callbackVariable,
positionalParameters, function.requiredParameterCount,
boxExternRef: boxExternRef));
// Throw since we have too few arguments. Alternatively, we can continue
// checking lengths and try to call the callback, which will then throw, but
// that's unnecessary extra code. Note that we can't exclude this and assume
// the last dispatch case will catch this. Since arguments that are not
// passed are `undefined` and `undefined` gets converted to `null`, they may
// be treated as valid `null` arguments to the Dart function even though
// they were never passed.
dispatchCases.add(ExpressionStatement(Throw(StringConcatenation([
StringLiteral('Too few arguments passed. '
'Expected ${function.requiredParameterCount} or more, got '),
invokeMethod(argumentsLength, _util.numToIntTarget),
StringLiteral(' instead.')
]))));
Statement functionTrampolineBody = Block(dispatchCases);
// Create a new procedure for the callback trampoline. This procedure will
@ -124,8 +134,9 @@ class CallbackSpecializer {
FunctionNode(functionTrampolineBody,
positionalParameters: [
callbackVariable,
if (argumentsLength != null) argumentsLength
].followedBy(positionalParameters).toList(),
argumentsLength,
...positionalParameters
],
returnType: _util.nullableWasmExternRefType)
..fileOffset = node.fileOffset,
node.fileUri,
@ -144,11 +155,7 @@ class CallbackSpecializer {
jsParameters.add('x$i');
}
String jsParametersString = jsParameters.join(',');
String dartArguments = 'f';
bool needsArguments = _needsArgumentsLength(type);
if (needsArguments) {
dartArguments = '$dartArguments,arguments.length';
}
String dartArguments = 'f,arguments.length';
if (jsParameters.isNotEmpty) {
dartArguments = '$dartArguments,$jsParametersString';
}
@ -171,24 +178,12 @@ class CallbackSpecializer {
// Create JS method.
// Note: We have to use a regular function for the inner closure in some
// cases because we need access to `arguments`.
if (needsArguments) {
_methodCollector.addMethod(
dartProcedure,
jsMethodName,
"f => finalizeWrapper(f, function($jsParametersString) {"
" return dartInstance.exports.$functionTrampolineName($dartArguments) "
"})");
} else {
if (parametersNeedParens(jsParameters)) {
jsParametersString = '($jsParametersString)';
}
_methodCollector.addMethod(
dartProcedure,
jsMethodName,
"f => "
"finalizeWrapper(f,$jsParametersString => "
"dartInstance.exports.$functionTrampolineName($dartArguments))");
}
_methodCollector.addMethod(
dartProcedure,
jsMethodName,
"f => finalizeWrapper(f, function($jsParametersString) {"
" return dartInstance.exports.$functionTrampolineName($dartArguments) "
"})");
return dartProcedure;
}
@ -207,6 +202,9 @@ class CallbackSpecializer {
/// 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.
// TODO(srujzs): It looks like there's no more code that references this
// function anymore in dart2wasm. Should we delete this lowering and related
// code?
Expression allowInterop(StaticInvocation staticInvocation) {
final argument = staticInvocation.arguments.positional.single;
final type = argument.getStaticType(_staticTypeContext) as FunctionType;

View File

@ -16,6 +16,7 @@ class CoreTypesUtil {
final Procedure allowInteropTarget;
final Procedure dartifyRawTarget;
final Procedure functionToJSTarget;
final Procedure greaterThanOrEqualToTarget;
final Procedure inlineJSTarget;
final Procedure isDartFunctionWrappedTarget;
final Procedure jsifyRawTarget;
@ -33,6 +34,8 @@ class CoreTypesUtil {
.getTopLevelProcedure('dart:_js_helper', 'dartifyRaw'),
functionToJSTarget = coreTypes.index.getTopLevelProcedure(
'dart:js_interop', 'FunctionToJSExportedDartFunction|get#toJS'),
greaterThanOrEqualToTarget =
coreTypes.index.getProcedure('dart:core', 'num', '>='),
inlineJSTarget =
coreTypes.index.getTopLevelProcedure('dart:_js_helper', 'JS'),
isDartFunctionWrappedTarget = coreTypes.index
@ -94,6 +97,17 @@ class CoreTypesUtil {
StaticInvocation(coreTypes.identicalProcedure,
Arguments([VariableGet(variable), ConstantExpression(constant)]));
Expression variableGreaterThanOrEqualToConstant(
VariableDeclaration variable, Constant constant) =>
InstanceInvocation(
InstanceAccessKind.Instance,
VariableGet(variable),
greaterThanOrEqualToTarget.name,
Arguments([ConstantExpression(constant)]),
interfaceTarget: greaterThanOrEqualToTarget,
functionType: greaterThanOrEqualToTarget.getterType as FunctionType,
);
/// Cast the [invocation] if needed to conform to the expected [returnType].
Expression castInvocationForReturn(
Expression invocation, DartType returnType) {

View File

@ -1,25 +1,25 @@
library;
import self as self;
import "dart:js_interop" as js_;
import "dart:js_util" as js_;
import "dart:core" as core;
import "dart:js_interop";
static method main() → void {
js_::JSExportedDartFunction /* erasure=_interceptors::JavaScriptFunction */ jsFunction = js_::FunctionToJSExportedDartFunction|get#toJS(() → Null {});
js_::FunctionToJSExportedDartFunction|get#toJS((core::int arg1) → core::int => arg1);
js_::FunctionToJSExportedDartFunction|get#toJS((([core::int? arg1 = #C1]) → core::int? => arg1) as () → void);
js_::FunctionToJSExportedDartFunction|get#toJS((core::int arg1, core::String arg2) → core::String => arg2);
js_::FunctionToJSExportedDartFunction|get#toJS((([core::int? arg1 = #C1, core::String? arg2 = #C1]) → core::String? => arg2) as ([core::int]) → void);
js_::FunctionToJSExportedDartFunction|get#toJS((core::int arg1, core::String arg2, js_::JSArray<js_::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */ arg3) → js_::JSArray<js_::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */ => arg3);
js_::FunctionToJSExportedDartFunction|get#toJS(((core::int arg1, [core::String? arg2 = #C1, js_::JSArray<js_::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSArray<core::Object?>? */ arg3 = #C1]) → js_::JSArray<js_::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSArray<core::Object?>? */ => arg3) as (core::int, [core::String]) → void);
js_::FunctionToJSExportedDartFunction|get#toJS((core::int arg1, core::String arg2, js_::JSArray<js_::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */ arg3, js_::JSObject /* erasure=_interceptors::JSObject */ arg4) → js_::JSObject /* erasure=_interceptors::JSObject */ => arg4);
js_::FunctionToJSExportedDartFunction|get#toJS(((core::int arg1, core::String arg2, js_::JSArray<js_::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */ arg3, [js_::JSObject? /* erasure=_interceptors::JSObject? */ arg4 = #C1]) → js_::JSObject? /* erasure=_interceptors::JSObject? */ => arg4) as (core::int, core::String, js_::JSArray<js_::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */) → void);
js_::FunctionToJSExportedDartFunction|get#toJS((core::int arg1, core::String arg2, js_::JSArray<js_::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */ arg3, js_::JSObject /* erasure=_interceptors::JSObject */ arg4, js_::JSPromise<js_::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSObject */ arg5) → js_::JSPromise<js_::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSObject */ => arg5);
js_::FunctionToJSExportedDartFunction|get#toJS(((core::int arg1, core::String arg2, [js_::JSArray<js_::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSArray<core::Object?>? */ arg3 = #C1, js_::JSObject? /* erasure=_interceptors::JSObject? */ arg4 = #C1, js_::JSPromise<js_::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSObject? */ arg5 = #C1]) → js_::JSPromise<js_::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSObject? */ => arg5) as (core::int, core::String, [js_::JSArray<js_::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSArray<core::Object?>? */, js_::JSObject? /* erasure=_interceptors::JSObject? */]) → void);
js_::FunctionToJSExportedDartFunction|get#toJS((core::int arg1, core::String arg2, js_::JSArray<js_::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */ arg3, js_::JSObject /* erasure=_interceptors::JSObject */ arg4, js_::JSPromise<js_::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSObject */ arg5, js_::JSAny /* erasure=core::Object */ arg6) → js_::JSAny /* erasure=core::Object */ => arg6);
js_::FunctionToJSExportedDartFunction|get#toJS(((core::int arg1, core::String arg2, js_::JSArray<js_::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */ arg3, js_::JSObject /* erasure=_interceptors::JSObject */ arg4, [js_::JSPromise<js_::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSObject? */ arg5 = #C1, js_::JSAny? /* erasure=core::Object? */ arg6 = #C1]) → js_::JSAny? /* erasure=core::Object? */ => arg6) as (core::int, core::String, js_::JSArray<js_::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */, js_::JSObject /* erasure=_interceptors::JSObject */, [js_::JSPromise<js_::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSObject? */]) → void);
js_::JSExportedDartFunctionToFunction|get#toDart(jsFunction);
#lib1::JSExportedDartFunction /* erasure=_interceptors::JavaScriptFunction */ jsFunction = js_::_functionToJS0(() → Null {});
js_::_functionToJS1((core::int arg1) → core::int => arg1);
js_::_functionToJS0((([core::int? arg1 = #C1]) → core::int? => arg1) as () → void);
js_::_functionToJS2((core::int arg1, core::String arg2) → core::String => arg2);
js_::_functionToJS1((([core::int? arg1 = #C1, core::String? arg2 = #C1]) → core::String? => arg2) as ([core::int]) → void);
js_::_functionToJS3((core::int arg1, core::String arg2, #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */ arg3) → #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */ => arg3);
js_::_functionToJS2(((core::int arg1, [core::String? arg2 = #C1, #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSArray<core::Object?>? */ arg3 = #C1]) → #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSArray<core::Object?>? */ => arg3) as (core::int, [core::String]) → void);
js_::_functionToJS4((core::int arg1, core::String arg2, #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */ arg3, #lib1::JSObject /* erasure=_interceptors::JSObject */ arg4) → #lib1::JSObject /* erasure=_interceptors::JSObject */ => arg4);
js_::_functionToJS3(((core::int arg1, core::String arg2, #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */ arg3, [#lib1::JSObject? /* erasure=_interceptors::JSObject? */ arg4 = #C1]) → #lib1::JSObject? /* erasure=_interceptors::JSObject? */ => arg4) as (core::int, core::String, #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */) → void);
js_::_functionToJS5((core::int arg1, core::String arg2, #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */ arg3, #lib1::JSObject /* erasure=_interceptors::JSObject */ arg4, #lib1::JSPromise<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSObject */ arg5) → #lib1::JSPromise<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSObject */ => arg5);
js_::_functionToJS4(((core::int arg1, core::String arg2, [#lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSArray<core::Object?>? */ arg3 = #C1, #lib1::JSObject? /* erasure=_interceptors::JSObject? */ arg4 = #C1, #lib1::JSPromise<#lib1::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSObject? */ arg5 = #C1]) → #lib1::JSPromise<#lib1::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSObject? */ => arg5) as (core::int, core::String, [#lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSArray<core::Object?>? */, #lib1::JSObject? /* erasure=_interceptors::JSObject? */]) → void);
js_::_functionToJSN((core::int arg1, core::String arg2, #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */ arg3, #lib1::JSObject /* erasure=_interceptors::JSObject */ arg4, #lib1::JSPromise<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSObject */ arg5, #lib1::JSAny /* erasure=core::Object */ arg6) → #lib1::JSAny /* erasure=core::Object */ => arg6, 6);
js_::_functionToJS5(((core::int arg1, core::String arg2, #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */ arg3, #lib1::JSObject /* erasure=_interceptors::JSObject */ arg4, [#lib1::JSPromise<#lib1::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSObject? */ arg5 = #C1, #lib1::JSAny? /* erasure=core::Object? */ arg6 = #C1]) → #lib1::JSAny? /* erasure=core::Object? */ => arg6) as (core::int, core::String, #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */, #lib1::JSObject /* erasure=_interceptors::JSObject */, [#lib1::JSPromise<#lib1::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSObject? */]) → void);
js_::_jsFunctionToDart(jsFunction);
}
constants {

View File

@ -1,25 +1,25 @@
library;
import self as self;
import "dart:js_interop" as js_;
import "dart:js_util" as js_;
import "dart:core" as core;
import "dart:js_interop";
static method main() → void {
js_::JSExportedDartFunction /* erasure=_interceptors::JavaScriptFunction */ jsFunction = js_::FunctionToJSExportedDartFunction|get#toJS(() → Null {});
js_::FunctionToJSExportedDartFunction|get#toJS((core::int arg1) → core::int => arg1);
js_::FunctionToJSExportedDartFunction|get#toJS((([core::int? arg1 = #C1]) → core::int? => arg1) as () → void);
js_::FunctionToJSExportedDartFunction|get#toJS((core::int arg1, core::String arg2) → core::String => arg2);
js_::FunctionToJSExportedDartFunction|get#toJS((([core::int? arg1 = #C1, core::String? arg2 = #C1]) → core::String? => arg2) as ([core::int]) → void);
js_::FunctionToJSExportedDartFunction|get#toJS((core::int arg1, core::String arg2, js_::JSArray<js_::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */ arg3) → js_::JSArray<js_::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */ => arg3);
js_::FunctionToJSExportedDartFunction|get#toJS(((core::int arg1, [core::String? arg2 = #C1, js_::JSArray<js_::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSArray<core::Object?>? */ arg3 = #C1]) → js_::JSArray<js_::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSArray<core::Object?>? */ => arg3) as (core::int, [core::String]) → void);
js_::FunctionToJSExportedDartFunction|get#toJS((core::int arg1, core::String arg2, js_::JSArray<js_::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */ arg3, js_::JSObject /* erasure=_interceptors::JSObject */ arg4) → js_::JSObject /* erasure=_interceptors::JSObject */ => arg4);
js_::FunctionToJSExportedDartFunction|get#toJS(((core::int arg1, core::String arg2, js_::JSArray<js_::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */ arg3, [js_::JSObject? /* erasure=_interceptors::JSObject? */ arg4 = #C1]) → js_::JSObject? /* erasure=_interceptors::JSObject? */ => arg4) as (core::int, core::String, js_::JSArray<js_::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */) → void);
js_::FunctionToJSExportedDartFunction|get#toJS((core::int arg1, core::String arg2, js_::JSArray<js_::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */ arg3, js_::JSObject /* erasure=_interceptors::JSObject */ arg4, js_::JSPromise<js_::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSObject */ arg5) → js_::JSPromise<js_::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSObject */ => arg5);
js_::FunctionToJSExportedDartFunction|get#toJS(((core::int arg1, core::String arg2, [js_::JSArray<js_::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSArray<core::Object?>? */ arg3 = #C1, js_::JSObject? /* erasure=_interceptors::JSObject? */ arg4 = #C1, js_::JSPromise<js_::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSObject? */ arg5 = #C1]) → js_::JSPromise<js_::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSObject? */ => arg5) as (core::int, core::String, [js_::JSArray<js_::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSArray<core::Object?>? */, js_::JSObject? /* erasure=_interceptors::JSObject? */]) → void);
js_::FunctionToJSExportedDartFunction|get#toJS((core::int arg1, core::String arg2, js_::JSArray<js_::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */ arg3, js_::JSObject /* erasure=_interceptors::JSObject */ arg4, js_::JSPromise<js_::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSObject */ arg5, js_::JSAny /* erasure=core::Object */ arg6) → js_::JSAny /* erasure=core::Object */ => arg6);
js_::FunctionToJSExportedDartFunction|get#toJS(((core::int arg1, core::String arg2, js_::JSArray<js_::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */ arg3, js_::JSObject /* erasure=_interceptors::JSObject */ arg4, [js_::JSPromise<js_::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSObject? */ arg5 = #C1, js_::JSAny? /* erasure=core::Object? */ arg6 = #C1]) → js_::JSAny? /* erasure=core::Object? */ => arg6) as (core::int, core::String, js_::JSArray<js_::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */, js_::JSObject /* erasure=_interceptors::JSObject */, [js_::JSPromise<js_::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSObject? */]) → void);
js_::JSExportedDartFunctionToFunction|get#toDart(jsFunction);
#lib1::JSExportedDartFunction /* erasure=_interceptors::JavaScriptFunction */ jsFunction = js_::_functionToJS0(() → Null {});
js_::_functionToJS1((core::int arg1) → core::int => arg1);
js_::_functionToJS0((([core::int? arg1 = #C1]) → core::int? => arg1) as () → void);
js_::_functionToJS2((core::int arg1, core::String arg2) → core::String => arg2);
js_::_functionToJS1((([core::int? arg1 = #C1, core::String? arg2 = #C1]) → core::String? => arg2) as ([core::int]) → void);
js_::_functionToJS3((core::int arg1, core::String arg2, #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */ arg3) → #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */ => arg3);
js_::_functionToJS2(((core::int arg1, [core::String? arg2 = #C1, #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSArray<core::Object?>? */ arg3 = #C1]) → #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSArray<core::Object?>? */ => arg3) as (core::int, [core::String]) → void);
js_::_functionToJS4((core::int arg1, core::String arg2, #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */ arg3, #lib1::JSObject /* erasure=_interceptors::JSObject */ arg4) → #lib1::JSObject /* erasure=_interceptors::JSObject */ => arg4);
js_::_functionToJS3(((core::int arg1, core::String arg2, #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */ arg3, [#lib1::JSObject? /* erasure=_interceptors::JSObject? */ arg4 = #C1]) → #lib1::JSObject? /* erasure=_interceptors::JSObject? */ => arg4) as (core::int, core::String, #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */) → void);
js_::_functionToJS5((core::int arg1, core::String arg2, #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */ arg3, #lib1::JSObject /* erasure=_interceptors::JSObject */ arg4, #lib1::JSPromise<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSObject */ arg5) → #lib1::JSPromise<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSObject */ => arg5);
js_::_functionToJS4(((core::int arg1, core::String arg2, [#lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSArray<core::Object?>? */ arg3 = #C1, #lib1::JSObject? /* erasure=_interceptors::JSObject? */ arg4 = #C1, #lib1::JSPromise<#lib1::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSObject? */ arg5 = #C1]) → #lib1::JSPromise<#lib1::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSObject? */ => arg5) as (core::int, core::String, [#lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSArray<core::Object?>? */, #lib1::JSObject? /* erasure=_interceptors::JSObject? */]) → void);
js_::_functionToJSN((core::int arg1, core::String arg2, #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */ arg3, #lib1::JSObject /* erasure=_interceptors::JSObject */ arg4, #lib1::JSPromise<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSObject */ arg5, #lib1::JSAny /* erasure=core::Object */ arg6) → #lib1::JSAny /* erasure=core::Object */ => arg6, 6);
js_::_functionToJS5(((core::int arg1, core::String arg2, #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */ arg3, #lib1::JSObject /* erasure=_interceptors::JSObject */ arg4, [#lib1::JSPromise<#lib1::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSObject? */ arg5 = #C1, #lib1::JSAny? /* erasure=core::Object? */ arg6 = #C1]) → #lib1::JSAny? /* erasure=core::Object? */ => arg6) as (core::int, core::String, #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=_interceptors::JSArray<core::Object?> */, #lib1::JSObject /* erasure=_interceptors::JSObject */, [#lib1::JSPromise<#lib1::JSAny? /* erasure=core::Object? */>? /* erasure=_interceptors::JSObject? */]) → void);
js_::_jsFunctionToDart(jsFunction);
}
constants {

View File

@ -1,25 +1,25 @@
library;
import self as self;
import "dart:js_interop" as js_;
import "dart:js_util" as js_;
import "dart:core" as core;
import "dart:js_interop";
static method main() → void {
js_::JSExportedDartFunction /* erasure=dart._interceptors::JavaScriptFunction */ jsFunction = js_::FunctionToJSExportedDartFunction|get#toJS(() → Null {});
js_::FunctionToJSExportedDartFunction|get#toJS((core::int arg1) → core::int => arg1);
js_::FunctionToJSExportedDartFunction|get#toJS((([core::int? arg1 = #C1]) → core::int? => arg1) as () → void);
js_::FunctionToJSExportedDartFunction|get#toJS((core::int arg1, core::String arg2) → core::String => arg2);
js_::FunctionToJSExportedDartFunction|get#toJS((([core::int? arg1 = #C1, core::String? arg2 = #C1]) → core::String? => arg2) as ([core::int]) → void);
js_::FunctionToJSExportedDartFunction|get#toJS((core::int arg1, core::String arg2, js_::JSArray<js_::JSAny? /* erasure=core::Object? */> /* erasure=dart._interceptors::JSArray<core::Object?> */ arg3) → js_::JSArray<js_::JSAny? /* erasure=core::Object? */> /* erasure=dart._interceptors::JSArray<core::Object?> */ => arg3);
js_::FunctionToJSExportedDartFunction|get#toJS(((core::int arg1, [core::String? arg2 = #C1, js_::JSArray<js_::JSAny? /* erasure=core::Object? */>? /* erasure=dart._interceptors::JSArray<core::Object?>? */ arg3 = #C1]) → js_::JSArray<js_::JSAny? /* erasure=core::Object? */>? /* erasure=dart._interceptors::JSArray<core::Object?>? */ => arg3) as (core::int, [core::String]) → void);
js_::FunctionToJSExportedDartFunction|get#toJS((core::int arg1, core::String arg2, js_::JSArray<js_::JSAny? /* erasure=core::Object? */> /* erasure=dart._interceptors::JSArray<core::Object?> */ arg3, js_::JSObject /* erasure=dart._interceptors::JSObject */ arg4) → js_::JSObject /* erasure=dart._interceptors::JSObject */ => arg4);
js_::FunctionToJSExportedDartFunction|get#toJS(((core::int arg1, core::String arg2, js_::JSArray<js_::JSAny? /* erasure=core::Object? */> /* erasure=dart._interceptors::JSArray<core::Object?> */ arg3, [js_::JSObject? /* erasure=dart._interceptors::JSObject? */ arg4 = #C1]) → js_::JSObject? /* erasure=dart._interceptors::JSObject? */ => arg4) as (core::int, core::String, js_::JSArray<js_::JSAny? /* erasure=core::Object? */> /* erasure=dart._interceptors::JSArray<core::Object?> */) → void);
js_::FunctionToJSExportedDartFunction|get#toJS((core::int arg1, core::String arg2, js_::JSArray<js_::JSAny? /* erasure=core::Object? */> /* erasure=dart._interceptors::JSArray<core::Object?> */ arg3, js_::JSObject /* erasure=dart._interceptors::JSObject */ arg4, js_::JSPromise<js_::JSAny? /* erasure=core::Object? */> /* erasure=dart._interceptors::JSObject */ arg5) → js_::JSPromise<js_::JSAny? /* erasure=core::Object? */> /* erasure=dart._interceptors::JSObject */ => arg5);
js_::FunctionToJSExportedDartFunction|get#toJS(((core::int arg1, core::String arg2, [js_::JSArray<js_::JSAny? /* erasure=core::Object? */>? /* erasure=dart._interceptors::JSArray<core::Object?>? */ arg3 = #C1, js_::JSObject? /* erasure=dart._interceptors::JSObject? */ arg4 = #C1, js_::JSPromise<js_::JSAny? /* erasure=core::Object? */>? /* erasure=dart._interceptors::JSObject? */ arg5 = #C1]) → js_::JSPromise<js_::JSAny? /* erasure=core::Object? */>? /* erasure=dart._interceptors::JSObject? */ => arg5) as (core::int, core::String, [js_::JSArray<js_::JSAny? /* erasure=core::Object? */>? /* erasure=dart._interceptors::JSArray<core::Object?>? */, js_::JSObject? /* erasure=dart._interceptors::JSObject? */]) → void);
js_::FunctionToJSExportedDartFunction|get#toJS((core::int arg1, core::String arg2, js_::JSArray<js_::JSAny? /* erasure=core::Object? */> /* erasure=dart._interceptors::JSArray<core::Object?> */ arg3, js_::JSObject /* erasure=dart._interceptors::JSObject */ arg4, js_::JSPromise<js_::JSAny? /* erasure=core::Object? */> /* erasure=dart._interceptors::JSObject */ arg5, js_::JSAny /* erasure=core::Object */ arg6) → js_::JSAny /* erasure=core::Object */ => arg6);
js_::FunctionToJSExportedDartFunction|get#toJS(((core::int arg1, core::String arg2, js_::JSArray<js_::JSAny? /* erasure=core::Object? */> /* erasure=dart._interceptors::JSArray<core::Object?> */ arg3, js_::JSObject /* erasure=dart._interceptors::JSObject */ arg4, [js_::JSPromise<js_::JSAny? /* erasure=core::Object? */>? /* erasure=dart._interceptors::JSObject? */ arg5 = #C1, js_::JSAny? /* erasure=core::Object? */ arg6 = #C1]) → js_::JSAny? /* erasure=core::Object? */ => arg6) as (core::int, core::String, js_::JSArray<js_::JSAny? /* erasure=core::Object? */> /* erasure=dart._interceptors::JSArray<core::Object?> */, js_::JSObject /* erasure=dart._interceptors::JSObject */, [js_::JSPromise<js_::JSAny? /* erasure=core::Object? */>? /* erasure=dart._interceptors::JSObject? */]) → void);
js_::JSExportedDartFunctionToFunction|get#toDart(jsFunction);
#lib1::JSExportedDartFunction /* erasure=dart._interceptors::JavaScriptFunction */ jsFunction = js_::_functionToJS0(() → Null {});
js_::_functionToJS1((core::int arg1) → core::int => arg1);
js_::_functionToJS0((([core::int? arg1 = #C1]) → core::int? => arg1) as () → void);
js_::_functionToJS2((core::int arg1, core::String arg2) → core::String => arg2);
js_::_functionToJS1((([core::int? arg1 = #C1, core::String? arg2 = #C1]) → core::String? => arg2) as ([core::int]) → void);
js_::_functionToJS3((core::int arg1, core::String arg2, #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=dart._interceptors::JSArray<core::Object?> */ arg3) → #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=dart._interceptors::JSArray<core::Object?> */ => arg3);
js_::_functionToJS2(((core::int arg1, [core::String? arg2 = #C1, #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */>? /* erasure=dart._interceptors::JSArray<core::Object?>? */ arg3 = #C1]) → #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */>? /* erasure=dart._interceptors::JSArray<core::Object?>? */ => arg3) as (core::int, [core::String]) → void);
js_::_functionToJS4((core::int arg1, core::String arg2, #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=dart._interceptors::JSArray<core::Object?> */ arg3, #lib1::JSObject /* erasure=dart._interceptors::JSObject */ arg4) → #lib1::JSObject /* erasure=dart._interceptors::JSObject */ => arg4);
js_::_functionToJS3(((core::int arg1, core::String arg2, #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=dart._interceptors::JSArray<core::Object?> */ arg3, [#lib1::JSObject? /* erasure=dart._interceptors::JSObject? */ arg4 = #C1]) → #lib1::JSObject? /* erasure=dart._interceptors::JSObject? */ => arg4) as (core::int, core::String, #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=dart._interceptors::JSArray<core::Object?> */) → void);
js_::_functionToJS5((core::int arg1, core::String arg2, #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=dart._interceptors::JSArray<core::Object?> */ arg3, #lib1::JSObject /* erasure=dart._interceptors::JSObject */ arg4, #lib1::JSPromise<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=dart._interceptors::JSObject */ arg5) → #lib1::JSPromise<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=dart._interceptors::JSObject */ => arg5);
js_::_functionToJS4(((core::int arg1, core::String arg2, [#lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */>? /* erasure=dart._interceptors::JSArray<core::Object?>? */ arg3 = #C1, #lib1::JSObject? /* erasure=dart._interceptors::JSObject? */ arg4 = #C1, #lib1::JSPromise<#lib1::JSAny? /* erasure=core::Object? */>? /* erasure=dart._interceptors::JSObject? */ arg5 = #C1]) → #lib1::JSPromise<#lib1::JSAny? /* erasure=core::Object? */>? /* erasure=dart._interceptors::JSObject? */ => arg5) as (core::int, core::String, [#lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */>? /* erasure=dart._interceptors::JSArray<core::Object?>? */, #lib1::JSObject? /* erasure=dart._interceptors::JSObject? */]) → void);
js_::_functionToJSN((core::int arg1, core::String arg2, #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=dart._interceptors::JSArray<core::Object?> */ arg3, #lib1::JSObject /* erasure=dart._interceptors::JSObject */ arg4, #lib1::JSPromise<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=dart._interceptors::JSObject */ arg5, #lib1::JSAny /* erasure=core::Object */ arg6) → #lib1::JSAny /* erasure=core::Object */ => arg6, 6);
js_::_functionToJS5(((core::int arg1, core::String arg2, #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=dart._interceptors::JSArray<core::Object?> */ arg3, #lib1::JSObject /* erasure=dart._interceptors::JSObject */ arg4, [#lib1::JSPromise<#lib1::JSAny? /* erasure=core::Object? */>? /* erasure=dart._interceptors::JSObject? */ arg5 = #C1, #lib1::JSAny? /* erasure=core::Object? */ arg6 = #C1]) → #lib1::JSAny? /* erasure=core::Object? */ => arg6) as (core::int, core::String, #lib1::JSArray<#lib1::JSAny? /* erasure=core::Object? */> /* erasure=dart._interceptors::JSArray<core::Object?> */, #lib1::JSObject /* erasure=dart._interceptors::JSObject */, [#lib1::JSPromise<#lib1::JSAny? /* erasure=core::Object? */>? /* erasure=dart._interceptors::JSObject? */]) → void);
js_::_jsFunctionToDart(jsFunction);
}
constants {

View File

@ -4,6 +4,7 @@
// Patch file for dart:js_util library.
import 'dart:_foreign_helper' show JS;
import 'dart:_interceptors' show JavaScriptFunction;
import 'dart:_internal' show patch;
import 'dart:_runtime' as dart;
@ -46,3 +47,254 @@ Function allowInteropCaptureThis(Function f) {
}
return ret;
}
// TODO(srujzs): In dart2js, this is guaranteed to be unique per isolate. DDC
// doesn't have a mechanism to guarantee that, so use a Symbol instead to match
// the unique-per-runtime semantics of [allowInterop].
final _functionToJSPropertyName = r'_$dart_dartClosure';
final _functionToJSProperty = JS('!', "Symbol($_functionToJSPropertyName)");
JavaScriptFunction _functionToJS0(Function f) {
// This can only happen if a user casted a JavaScriptFunction to Function.
// Such a cast is an error in dart2wasm, so we should make this behavior an
// error as well.
if (!dart.isDartFunction(f)) {
throw ArgumentError('Attempting to rewrap a JS function.');
}
final ret = JS<JavaScriptFunction>(
'!',
'''
function () {
return #(#);
}
''',
_callDartFunctionFast0,
f);
JS('', '#[#] = #', ret, _functionToJSProperty, f);
return ret;
}
JavaScriptFunction _functionToJS1(Function f) {
if (!dart.isDartFunction(f)) {
throw ArgumentError('Attempting to rewrap a JS function.');
}
final ret = JS<JavaScriptFunction>(
'!',
'''
function (arg1) {
return #(#, arg1, arguments.length);
}
''',
_callDartFunctionFast1,
f);
JS('', '#[#] = #', ret, _functionToJSProperty, f);
return ret;
}
JavaScriptFunction _functionToJS2(Function f) {
if (!dart.isDartFunction(f)) {
throw ArgumentError('Attempting to rewrap a JS function.');
}
final ret = JS<JavaScriptFunction>(
'!',
'''
function (arg1, arg2) {
return #(#, arg1, arg2, arguments.length);
}
''',
_callDartFunctionFast2,
f);
JS('', '#[#] = #', ret, _functionToJSProperty, f);
return ret;
}
JavaScriptFunction _functionToJS3(Function f) {
if (!dart.isDartFunction(f)) {
throw ArgumentError('Attempting to rewrap a JS function.');
}
final ret = JS<JavaScriptFunction>(
'!',
'''
function (arg1, arg2, arg3) {
return #(#, arg1, arg2, arg3, arguments.length);
}
''',
_callDartFunctionFast3,
f);
JS('', '#[#] = #', ret, _functionToJSProperty, f);
return ret;
}
JavaScriptFunction _functionToJS4(Function f) {
if (!dart.isDartFunction(f)) {
throw ArgumentError('Attempting to rewrap a JS function.');
}
final ret = JS<JavaScriptFunction>(
'!',
'''
function (arg1, arg2, arg3, arg4) {
return #(#, arg1, arg2, arg3, arg4, arguments.length);
}
''',
_callDartFunctionFast4,
f);
JS('', '#[#] = #', ret, _functionToJSProperty, f);
return ret;
}
JavaScriptFunction _functionToJS5(Function f) {
if (!dart.isDartFunction(f)) {
throw ArgumentError('Attempting to rewrap a JS function.');
}
final ret = JS<JavaScriptFunction>(
'!',
'''
function (arg1, arg2, arg3, arg4, arg5) {
return #(#, arg1, arg2, arg3, arg4, arg5, arguments.length);
}
''',
_callDartFunctionFast5,
f);
JS('', '#[#] = #', ret, _functionToJSProperty, f);
return ret;
}
JavaScriptFunction _functionToJSN(Function f, int maxLength) {
if (!dart.isDartFunction(f)) {
throw ArgumentError('Attempting to rewrap a JS function.');
}
final ret = JS<JavaScriptFunction>(
'!',
'''
function (...args) {
return #(#, Array.prototype.slice.call(args, 0,
Math.min(args.length, #)));
}
''',
dart.dcall,
f,
maxLength);
JS('', '#[#] = #', ret, _functionToJSProperty, f);
return ret;
}
_callDartFunctionFast0(callback) => JS('', '#()', callback);
_callDartFunctionFast1(callback, arg1, int length) {
if (length >= 1) {
final error =
dart.validateFunctionToJSArgs(callback, JS<List>('!', '[#]', arg1));
if (error != null) return error;
return JS('', '#(#)', callback, arg1);
} else {
final error = dart.validateFunctionToJSArgs(callback, JS<List>('!', '[]'));
if (error != null) return error;
return JS('', '#()', callback);
}
}
_callDartFunctionFast2(callback, arg1, arg2, int length) {
if (length >= 2) {
final error = dart.validateFunctionToJSArgs(
callback, JS<List>('!', '[#, #]', arg1, arg2));
if (error != null) return error;
return JS('', '#(#, #)', callback, arg1, arg2);
} else if (length == 1) {
final error =
dart.validateFunctionToJSArgs(callback, JS<List>('!', '[#]', arg1));
if (error != null) return error;
return JS('', '#(#)', callback, arg1);
} else {
final error = dart.validateFunctionToJSArgs(callback, JS<List>('!', '[]'));
if (error != null) return error;
return JS('', '#()', callback);
}
}
_callDartFunctionFast3(callback, arg1, arg2, arg3, int length) {
if (length >= 3) {
final error = dart.validateFunctionToJSArgs(
callback, JS<List>('!', '[#, #, #]', arg1, arg2, arg3));
if (error != null) return error;
return JS('', '#(#, #, #)', callback, arg1, arg2, arg3);
} else if (length == 2) {
final error = dart.validateFunctionToJSArgs(
callback, JS<List>('!', '[#, #]', arg1, arg2));
if (error != null) return error;
return JS('', '#(#, #)', callback, arg1, arg2);
} else if (length == 1) {
final error =
dart.validateFunctionToJSArgs(callback, JS<List>('!', '[#]', arg1));
if (error != null) return error;
return JS('', '#(#)', callback, arg1);
} else {
final error = dart.validateFunctionToJSArgs(callback, JS<List>('!', '[]'));
if (error != null) return error;
return JS('', '#()', callback);
}
}
_callDartFunctionFast4(callback, arg1, arg2, arg3, arg4, int length) {
if (length >= 4) {
final error = dart.validateFunctionToJSArgs(
callback, JS<List>('!', '[#, #, #, #]', arg1, arg2, arg3, arg4));
if (error != null) return error;
return JS('', '#(#, #, #, #)', callback, arg1, arg2, arg3, arg4);
} else if (length == 3) {
final error = dart.validateFunctionToJSArgs(
callback, JS<List>('!', '[#, #, #]', arg1, arg2, arg3));
if (error != null) return error;
return JS('', '#(#, #, #)', callback, arg1, arg2, arg3);
} else if (length == 2) {
final error = dart.validateFunctionToJSArgs(
callback, JS<List>('!', '[#, #]', arg1, arg2));
if (error != null) return error;
return JS('', '#(#, #)', callback, arg1, arg2);
} else if (length == 1) {
final error =
dart.validateFunctionToJSArgs(callback, JS<List>('!', '[#]', arg1));
if (error != null) return error;
return JS('', '#(#)', callback, arg1);
} else {
final error = dart.validateFunctionToJSArgs(callback, JS<List>('!', '[]'));
if (error != null) return error;
return JS('', '#()', callback);
}
}
_callDartFunctionFast5(callback, arg1, arg2, arg3, arg4, arg5, int length) {
if (length >= 5) {
final error = dart.validateFunctionToJSArgs(callback,
JS<List>('!', '[#, #, #, #, #]', arg1, arg2, arg3, arg4, arg5));
if (error != null) return error;
return JS('', '#(#, #, #, #, #)', callback, arg1, arg2, arg3, arg4, arg5);
} else if (length == 4) {
final error = dart.validateFunctionToJSArgs(
callback, JS<List>('!', '[#, #, #, #]', arg1, arg2, arg3, arg4));
if (error != null) return error;
return JS('', '#(#, #, #, #)', callback, arg1, arg2, arg3, arg4);
} else if (length == 3) {
final error = dart.validateFunctionToJSArgs(
callback, JS<List>('!', '[#, #, #]', arg1, arg2, arg3));
if (error != null) return error;
return JS('', '#(#, #, #)', callback, arg1, arg2, arg3);
} else if (length == 2) {
final error = dart.validateFunctionToJSArgs(
callback, JS<List>('!', '[#, #]', arg1, arg2));
if (error != null) return error;
return JS('', '#(#, #)', callback, arg1, arg2);
} else if (length == 1) {
final error =
dart.validateFunctionToJSArgs(callback, JS<List>('!', '[#]', arg1));
if (error != null) return error;
return JS('', '#(#)', callback, arg1);
} else {
final error = dart.validateFunctionToJSArgs(callback, JS<List>('!', '[]'));
if (error != null) return error;
return JS('', '#()', callback);
}
}
Function _jsFunctionToDart(JavaScriptFunction f) {
return JS('Function', '#[#]', f, _functionToJSProperty);
}

View File

@ -428,6 +428,26 @@ _checkAndCall(f, ftype, obj, typeArgs, args, named, displayName) {
return callNSM(errorMessage);
}
/// Given a Dart function [f] that was wrapped in a `Function.toJS` call, and
/// the corresponding [args] used to call it, validates that the arity and types
/// of [args] are correct.
///
/// Returns null if it's valid call and a [noSuchMethod] invocation with the
/// specific error otherwise.
validateFunctionToJSArgs(f, List args) {
var errorMessage = _argumentErrors(
JS<Object>('', '#[#]', f, JS_GET_NAME(JsGetName.SIGNATURE_NAME)),
args,
null);
if (errorMessage != null) {
return noSuchMethod(
f,
InvocationImpl(JS('', 'f.name'), args,
isMethod: true, failureMessage: errorMessage));
}
return null;
}
dcall(f, args, [@undefined named]) => _checkAndCall(
f, null, JS('', 'void 0'), null, args, named, JS('', 'f.name'));

View File

@ -5,7 +5,7 @@
// Patch file for dart:js_util library.
import 'dart:_foreign_helper' show JS, DART_CLOSURE_TO_JS;
import 'dart:_interceptors' show DART_CLOSURE_PROPERTY_NAME;
import 'dart:_interceptors' show DART_CLOSURE_PROPERTY_NAME, JavaScriptFunction;
import 'dart:_internal' show patch;
import 'dart:_js_helper'
show
@ -80,3 +80,187 @@ Function allowInteropCaptureThis(Function f) {
return _convertDartFunctionFastCaptureThis(f);
}
}
JavaScriptFunction _functionToJS0(Function f) {
// This can only happen if a user casted a JavaScriptFunction to Function.
// Such a cast is an error in dart2wasm, so we should make this behavior an
// error as well.
if (isJSFunction(f)) {
throw ArgumentError('Attempting to rewrap a JS function.');
}
final result = JS(
'JavaScriptFunction',
'''
function(_call, f) {
return function() {
return _call(f);
}
}(#, #)
''',
DART_CLOSURE_TO_JS(_callDartFunctionFast0),
f);
JS('', '#.# = #', result, DART_CLOSURE_PROPERTY_NAME, f);
return result;
}
JavaScriptFunction _functionToJS1(Function f) {
if (isJSFunction(f)) {
throw ArgumentError('Attempting to rewrap a JS function.');
}
final result = JS(
'JavaScriptFunction',
'''
function(_call, f) {
return function(arg1) {
return _call(f, arg1, arguments.length);
}
}(#, #)
''',
DART_CLOSURE_TO_JS(_callDartFunctionFast1),
f);
JS('', '#.# = #', result, DART_CLOSURE_PROPERTY_NAME, f);
return result;
}
JavaScriptFunction _functionToJS2(Function f) {
if (isJSFunction(f)) {
throw ArgumentError('Attempting to rewrap a JS function.');
}
final result = JS(
'JavaScriptFunction',
'''
function(_call, f) {
return function(arg1, arg2) {
return _call(f, arg1, arg2, arguments.length);
}
}(#, #)
''',
DART_CLOSURE_TO_JS(_callDartFunctionFast2),
f);
JS('', '#.# = #', result, DART_CLOSURE_PROPERTY_NAME, f);
return result;
}
JavaScriptFunction _functionToJS3(Function f) {
if (isJSFunction(f)) {
throw ArgumentError('Attempting to rewrap a JS function.');
}
final result = JS(
'JavaScriptFunction',
'''
function(_call, f) {
return function(arg1, arg2, arg3) {
return _call(f, arg1, arg2, arg3, arguments.length);
}
}(#, #)
''',
DART_CLOSURE_TO_JS(_callDartFunctionFast3),
f);
JS('', '#.# = #', result, DART_CLOSURE_PROPERTY_NAME, f);
return result;
}
JavaScriptFunction _functionToJS4(Function f) {
if (isJSFunction(f)) {
throw ArgumentError('Attempting to rewrap a JS function.');
}
final result = JS(
'JavaScriptFunction',
'''
function(_call, f) {
return function(arg1, arg2, arg3, arg4) {
return _call(f, arg1, arg2, arg3, arg4, arguments.length);
}
}(#, #)
''',
DART_CLOSURE_TO_JS(_callDartFunctionFast4),
f);
JS('', '#.# = #', result, DART_CLOSURE_PROPERTY_NAME, f);
return result;
}
JavaScriptFunction _functionToJS5(Function f) {
if (isJSFunction(f)) {
throw ArgumentError('Attempting to rewrap a JS function.');
}
final result = JS(
'JavaScriptFunction',
'''
function(_call, f) {
return function(arg1, arg2, arg3, arg4, arg5) {
return _call(f, arg1, arg2, arg3, arg4, arg5, arguments.length);
}
}(#, #)
''',
DART_CLOSURE_TO_JS(_callDartFunctionFast5),
f);
JS('', '#.# = #', result, DART_CLOSURE_PROPERTY_NAME, f);
return result;
}
JavaScriptFunction _functionToJSN(Function f, int maxLength) {
if (isJSFunction(f)) {
throw ArgumentError('Attempting to rewrap a JS function.');
}
final result = JS(
'JavaScriptFunction',
'''
function(_call, f, maxLength) {
return function () {
return _call(f, Array.prototype.slice.call(arguments, 0,
Math.min(arguments.length, maxLength)));
}
}(#, #, #)
''',
DART_CLOSURE_TO_JS(_callDartFunctionFastN),
f,
maxLength);
JS('', '#.# = #', result, DART_CLOSURE_PROPERTY_NAME, f);
return result;
}
_callDartFunctionFast0(Function callback) => callback();
_callDartFunctionFast1(Function callback, arg1, int length) {
if (length >= 1) return callback(arg1);
return callback();
}
_callDartFunctionFast2(Function callback, arg1, arg2, int length) {
if (length >= 2) return callback(arg1, arg2);
if (length == 1) return callback(arg1);
return callback();
}
_callDartFunctionFast3(Function callback, arg1, arg2, arg3, int length) {
if (length >= 3) return callback(arg1, arg2, arg3);
if (length == 2) return callback(arg1, arg2);
if (length == 1) return callback(arg1);
return callback();
}
_callDartFunctionFast4(Function callback, arg1, arg2, arg3, arg4, int length) {
if (length >= 4) return callback(arg1, arg2, arg3, arg4);
if (length == 3) return callback(arg1, arg2, arg3);
if (length == 2) return callback(arg1, arg2);
if (length == 1) return callback(arg1);
return callback();
}
_callDartFunctionFast5(
Function callback, arg1, arg2, arg3, arg4, arg5, int length) {
if (length >= 5) return callback(arg1, arg2, arg3, arg4, arg5);
if (length == 4) return callback(arg1, arg2, arg3, arg4);
if (length == 3) return callback(arg1, arg2, arg3);
if (length == 2) return callback(arg1, arg2);
if (length == 1) return callback(arg1);
return callback();
}
_callDartFunctionFastN(Function callback, List arguments) {
return Function.apply(callback, arguments);
}
Function _jsFunctionToDart(JavaScriptFunction f) {
return JS('Function', '#.#', f, DART_CLOSURE_PROPERTY_NAME);
}

View File

@ -65,19 +65,18 @@ extension NullableObjectUtilExtension on Object? {
// JSExportedDartFunction <-> Function
@patch
extension JSExportedDartFunctionToFunction on JSExportedDartFunction {
// TODO(srujzs): We should unwrap rather than allow arbitrary JS functions
// to be called in Dart.
@patch
@pragma('dart2js:prefer-inline')
Function get toDart => this._jsFunction;
Function get toDart => throw UnimplementedError(
"'toDart' should never directly be called. Calls to 'toDart' should have "
'been transformed by the interop transformer.');
}
@patch
extension FunctionToJSExportedDartFunction on Function {
@patch
@pragma('dart2js:prefer-inline')
JSExportedDartFunction get toJS =>
js_util.allowInterop(this) as JSExportedDartFunction;
JSExportedDartFunction get toJS => throw UnimplementedError(
"'toJS' should never directly be called. Calls to 'toJS' should have "
'been transformed by the interop transformer.');
}
// Embedded global property for wrapped Dart objects passed via JS interop.

View File

@ -477,7 +477,8 @@ extension JSFunctionUtilExtension on JSFunction {
extension JSExportedDartFunctionToFunction on JSExportedDartFunction {
/// The Dart [Function] that this [JSExportedDartFunction] wrapped.
///
/// Must be a wrapped Dart [Function].
/// Must be a function that was wrapped with
/// [FunctionToJSExportedDartFunction.toJS].
external Function get toDart;
}
@ -490,6 +491,15 @@ extension FunctionToJSExportedDartFunction on Function {
/// compile. See
/// https://dart.dev/interop/js-interop/js-types#requirements-on-external-declarations-and-function-tojs
/// for more details on what types are allowed.
///
/// The max number of arguments that are passed to this [Function] from the
/// wrapper JavaScript function is determined by this [Function]'s static
/// type. Any extra arguments passed to the JavaScript function after the max
/// number of arguments are discarded like they are with regular JavaScript
/// functions.
///
/// Calling this on the same [Function] again will always result in a new
/// JavaScript function.
external JSExportedDartFunction get toJS;
}

View File

@ -0,0 +1,453 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// 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.
/// Requirements=checked-implicit-downcasts
import 'dart:js_interop';
import 'package:expect/expect.dart';
const isDDC = const bool.fromEnvironment('dart.library._ddc_only');
const isDart2JS = const bool.fromEnvironment('dart.tool.dart2js');
@JS('call')
external String _call(JSFunction f, JSArray<JSAny?> args);
String call(JSFunction f, List<Object?> args) =>
_call(f, args.map((e) => e?.jsify()).toList().toJS);
@JS()
external void eval(String code);
// Zero.
String zeroArgs() => '0';
// One.
String oneRequired(String arg1) => arg1;
String oneOptional([String arg1 = 'default']) => '$arg1';
// Two.
String twoRequired(String arg1, String? arg2) => '$arg1$arg2';
String oneRequiredOneOptional(String arg1, [String? arg2 = 'default']) =>
'$arg1$arg2';
String twoOptional([String arg1 = 'default', String? arg2 = 'default']) =>
'$arg1$arg2';
// Three.
String threeRequired(String arg1, String? arg2, String arg3) =>
'$arg1$arg2$arg3';
String twoRequiredOneOptional(String arg1, String? arg2,
[String arg3 = 'default']) =>
'$arg1$arg2$arg3';
String oneRequiredTwoOptional(String arg1,
[String? arg2 = 'default', String arg3 = 'default']) =>
'$arg1$arg2$arg3';
String threeOptional(
[String arg1 = 'default',
String? arg2 = 'default',
String arg3 = 'default']) =>
'$arg1$arg2$arg3';
// Four.
String fourRequired(String arg1, String? arg2, String arg3, String arg4) =>
'$arg1$arg2$arg3$arg4';
String threeRequiredOneOptional(String arg1, String? arg2, String arg3,
[String arg4 = 'default']) =>
'$arg1$arg2$arg3$arg4';
String twoRequiredTwoOptional(String arg1, String? arg2,
[String arg3 = 'default', String arg4 = 'default']) =>
'$arg1$arg2$arg3$arg4';
String oneRequiredThreeOptional(String arg1,
[String? arg2 = 'default',
String arg3 = 'default',
String arg4 = 'default']) =>
'$arg1$arg2$arg3$arg4';
String fourOptional(
[String arg1 = 'default',
String? arg2 = 'default',
String arg3 = 'default',
String arg4 = 'default']) =>
'$arg1$arg2$arg3$arg4';
// Five.
String fiveRequired(
String arg1, String? arg2, String arg3, String arg4, String arg5) =>
'$arg1$arg2$arg3$arg4$arg5';
String fourRequiredOneOptional(
String arg1, String? arg2, String arg3, String arg4,
[String arg5 = 'default']) =>
'$arg1$arg2$arg3$arg4$arg5';
String threeRequiredTwoOptional(String arg1, String? arg2, String arg3,
[String arg4 = 'default', String arg5 = 'default']) =>
'$arg1$arg2$arg3$arg4$arg5';
String twoRequiredThreeOptional(String arg1, String? arg2,
[String arg3 = 'default',
String arg4 = 'default',
String arg5 = 'default']) =>
'$arg1$arg2$arg3$arg4$arg5';
String oneRequiredFourOptional(String arg1,
[String? arg2 = 'default',
String arg3 = 'default',
String arg4 = 'default',
String arg5 = 'default']) =>
'$arg1$arg2$arg3$arg4$arg5';
String fiveOptional(
[String arg1 = 'default',
String? arg2 = 'default',
String arg3 = 'default',
String arg4 = 'default',
String arg5 = 'default']) =>
'$arg1$arg2$arg3$arg4$arg5';
// Six.
String sixRequired(String arg1, String? arg2, String arg3, String arg4,
String arg5, String arg6) =>
'$arg1$arg2$arg3$arg4$arg5$arg6';
String fiveRequiredOneOptional(
String arg1, String? arg2, String arg3, String arg4, String arg5,
[String arg6 = 'default']) =>
'$arg1$arg2$arg3$arg4$arg5$arg6';
String fourRequiredTwoOptional(
String arg1, String? arg2, String arg3, String arg4,
[String arg5 = 'default', String arg6 = 'default']) =>
'$arg1$arg2$arg3$arg4$arg5$arg6';
String threeRequiredThreeOptional(String arg1, String? arg2, String arg3,
[String arg4 = 'default',
String arg5 = 'default',
String arg6 = 'default']) =>
'$arg1$arg2$arg3$arg4$arg5$arg6';
String twoRequiredFourOptional(String arg1, String? arg2,
[String arg3 = 'default',
String arg4 = 'default',
String arg5 = 'default',
String arg6 = 'default']) =>
'$arg1$arg2$arg3$arg4$arg5$arg6';
String oneRequiredFiveOptional(String arg1,
[String? arg2 = 'default',
String arg3 = 'default',
String arg4 = 'default',
String arg5 = 'default',
String arg6 = 'default']) =>
'$arg1$arg2$arg3$arg4$arg5$arg6';
String sixOptional(
[String arg1 = 'default',
String? arg2 = 'default',
String arg3 = 'default',
String arg4 = 'default',
String arg5 = 'default',
String arg6 = 'default']) =>
'$arg1$arg2$arg3$arg4$arg5$arg6';
void testZero() {
// Arity tests.
Expect.equals(call(zeroArgs.toJS, []), '0');
Expect.equals(call(zeroArgs.toJS, ['extra']), '0');
Expect.equals(call(zeroArgs.toJS, [1.0]), '0');
// Conversion round-trip test.
final tearOff = zeroArgs;
Expect.equals(tearOff, tearOff.toJS.toDart);
// Avoid rewrapping test.
if (isDDC || isDart2JS) {
Expect.throws(() => (zeroArgs.toJS as String Function()).toJS);
}
}
void testOne() {
// Type tests.
Expect.throws(() => call(oneRequired.toJS, [0]));
Expect.throwsWhen(hasSoundNullSafety, () => call(oneOptional.toJS, [null]));
Expect.throwsWhen(
hasSoundNullSafety, () => call(oneOptional.toJS, ['undefined']));
// Arity tests.
Expect.throws(() => call(oneRequired.toJS, []));
Expect.equals(call(oneRequired.toJS, ['a']), 'a');
Expect.equals(call(oneRequired.toJS, ['a', 'extra']), 'a');
Expect.equals(call(oneOptional.toJS, []), 'default');
Expect.equals(call(oneOptional.toJS, ['a']), 'a');
Expect.equals(call(oneOptional.toJS, ['a', 'extra']), 'a');
// Function subtyping tests.
Expect.equals(call((oneOptional as String Function()).toJS, []), 'default');
// Throws away the additional args due to the static typing.
Expect.equals(
call((oneOptional as String Function()).toJS, ['a']), 'default');
// Conversion round-trip test.
final tearOff = oneRequired;
Expect.equals(tearOff, tearOff.toJS.toDart);
// Avoid rewrapping test.
if (isDDC || isDart2JS) {
Expect.throws(() => (oneOptional.toJS as String Function()).toJS);
}
}
void testTwo() {
// Type tests.
Expect.throws(() => call(twoOptional.toJS, [false, 'b']));
Expect.throws(() => call(twoOptional.toJS, ['a', 1.0]));
Expect.throwsWhen(hasSoundNullSafety,
() => call(oneRequiredOneOptional.toJS, ['undefined', 'b']));
Expect.throws(() => call(oneRequiredOneOptional.toJS, ['a', true]));
Expect.throws(() => call(twoRequired.toJS, [0, 'b']));
Expect.throws(() => call(twoRequired.toJS, ['a', 0]));
// Arity tests.
Expect.throws(() => call(twoRequired.toJS, []));
Expect.throws(() => call(twoRequired.toJS, ['a']));
Expect.equals(call(twoRequired.toJS, ['a', 'b']), 'ab');
Expect.equals(call(twoRequired.toJS, ['a', 'b', 'extra']), 'ab');
Expect.throws(() => call(oneRequiredOneOptional.toJS, []));
Expect.equals(call(oneRequiredOneOptional.toJS, ['a']), 'adefault');
Expect.equals(call(oneRequiredOneOptional.toJS, ['a', 'b']), 'ab');
Expect.equals(call(oneRequiredOneOptional.toJS, ['a', 'b', 'extra']), 'ab');
Expect.equals(call(twoOptional.toJS, []), 'defaultdefault');
Expect.equals(call(twoOptional.toJS, ['a']), 'adefault');
Expect.equals(call(twoOptional.toJS, ['a', 'b']), 'ab');
Expect.equals(call(twoOptional.toJS, ['a', 'b', 'extra']), 'ab');
// Function subtyping tests.
// TODO(55881): dart2wasm's type conversions are based on the static type,
// whereas DDC and dart2js only do type checks based on the runtime type. We
// can't replicate dart2Wasm's behavior in DDC and dart2js as it would require
// a new Dart trampoline for every function, so there's a discrepancy when we
// use a static type with different parameter types.
var closure = () =>
call((twoRequired as String Function(String, String)).toJS, ['a', null]);
if (isDDC || isDart2JS) {
Expect.equals(closure(), 'anull');
} else {
Expect.throws(closure);
}
Expect.throws(
() => call((oneRequiredOneOptional as String Function(String)).toJS, []));
Expect.equals(
call((oneRequiredOneOptional as String Function(String)).toJS, ['a']),
'adefault');
Expect.equals(
call((oneRequiredOneOptional as String Function(String)).toJS, ['a', 0]),
'adefault');
Expect.equals(
call((twoOptional as String Function()).toJS, []), 'defaultdefault');
Expect.equals(
call((twoOptional as String Function()).toJS, ['a']), 'defaultdefault');
Expect.equals(call((twoOptional as String Function([String])).toJS, []),
'defaultdefault');
Expect.equals(
call((twoOptional as String Function([String])).toJS, ['a']), 'adefault');
Expect.equals(
call((twoOptional as String Function([String])).toJS, ['a', false]),
'adefault');
// `undefined` tests.
Expect.equals(call(twoRequired.toJS, ['a', 'undefined']), 'anull');
// TODO(55884): DDC lowers function with defaults to use the JS default
// argument syntax, which means passing `undefined` results in DDC replacing
// it with the default, instead of keeping it as `undefined`.
Expect.equals(call(oneRequiredOneOptional.toJS, ['a', 'undefined']),
isDDC ? 'adefault' : 'anull');
Expect.equals(
call(twoOptional.toJS, ['a', 'undefined']), isDDC ? 'adefault' : 'anull');
// Conversion round-trip test.
final tearOff = twoRequired;
Expect.equals(tearOff, tearOff.toJS.toDart);
// Avoid rewrapping test.
if (isDDC || isDart2JS) {
Expect.throws(
() => (oneRequiredOneOptional.toJS as String Function()).toJS);
}
}
// To avoid making this test unreadably long, the remaining tests choose a small
// subset of all possible tests for general validation.
void testThree() {
// Type tests.
Expect.throws(() => call(threeRequired.toJS, [0, 'b', 'c']));
Expect.throws(() => call(oneRequiredTwoOptional.toJS, ['a', false]));
// Arity tests.
Expect.equals(call(twoRequiredOneOptional.toJS, ['a', 'b']), 'abdefault');
Expect.throws(() => call(oneRequiredTwoOptional.toJS, []));
// Function subtyping tests.
var closure = () => call(
(twoRequiredOneOptional as String Function(String, String)).toJS,
['a', null, 'c']);
if (isDDC || isDart2JS) {
Expect.equals(closure(), 'anulldefault');
} else {
Expect.throws(closure);
}
Expect.equals(
call((threeOptional as String Function([String])).toJS, ['a', 0, true]),
'adefaultdefault');
// `undefined` tests.
Expect.equals(call(threeOptional.toJS, ['a', 'undefined']),
isDDC ? 'adefaultdefault' : 'anulldefault');
// Conversion round-trip test.
final tearOff = threeRequired;
Expect.equals(tearOff, tearOff.toJS.toDart);
// Avoid rewrapping test.
if (isDDC || isDart2JS) {
Expect.throws(
() => (twoRequiredOneOptional.toJS as String Function()).toJS);
}
}
void testFour() {
// Type tests.
Expect.throws(
() => call(threeRequiredOneOptional.toJS, ['a', 'b', 'c', true]));
Expect.throws(() => call(oneRequiredThreeOptional.toJS, [false]));
// Arity tests.
Expect.equals(call(fourRequired.toJS, ['a', 'b', 'c', 'd', false]), 'abcd');
Expect.equals(call(fourOptional.toJS, ['a']), 'adefaultdefaultdefault');
// Function subtyping tests.
final closure = () => call(
(threeRequiredOneOptional as String Function(String, String, String))
.toJS,
['a', null, 'c']);
if (isDDC || isDart2JS) {
Expect.equals(closure(), 'anullcdefault');
} else {
Expect.throws(closure);
}
Expect.equals(
call(
(twoRequiredTwoOptional as String Function(String, String?, [String]))
.toJS,
['a', null]),
'anulldefaultdefault');
// `undefined` tests.
Expect.equals(call(oneRequiredThreeOptional.toJS, ['a', 'undefined', 'c']),
isDDC ? 'adefaultcdefault' : 'anullcdefault');
// Conversion round-trip test.
final tearOff = fourRequired;
Expect.equals(tearOff, tearOff.toJS.toDart);
// Avoid rewrapping test.
if (isDDC || isDart2JS) {
Expect.throws(
() => (oneRequiredThreeOptional.toJS as String Function()).toJS);
}
}
void testFive() {
// Type tests.
Expect.throws(() => call(twoRequiredThreeOptional.toJS, ['a', 0]));
Expect.throws(() => call(fiveOptional.toJS, [false]));
// Arity tests.
Expect.equals(call(fiveRequired.toJS, ['a', 'b', 'c', 'd', 'e', 0]), 'abcde');
Expect.equals(call(fourRequiredOneOptional.toJS, ['a', null, 'c', 'd']),
'anullcddefault');
// Function subtyping tests.
final closure = () => call(
(threeRequiredTwoOptional as String Function(String, String, String,
[String]))
.toJS,
['a', null, 'c']);
if (isDDC || isDart2JS) {
Expect.equals(closure(), 'anullcdefaultdefault');
} else {
Expect.throws(closure);
}
Expect.equals(
call(
(twoRequiredThreeOptional as String Function(String, String?,
[String]))
.toJS,
['a', null, 'c']),
'anullcdefaultdefault');
// `undefined` tests.
Expect.equals(
call(oneRequiredFourOptional.toJS, ['a', 'undefined', 'c', 'd', 'e']),
isDDC ? 'adefaultcde' : 'anullcde');
// Conversion round-trip test.
final tearOff = fiveRequired;
Expect.equals(tearOff, tearOff.toJS.toDart);
// Avoid rewrapping test.
if (isDDC || isDart2JS) {
Expect.throws(
() => (twoRequiredThreeOptional.toJS as String Function()).toJS);
}
}
// DDC and dart2js should use either a `dcall` or `Function.apply` for this.
void testSix() {
// Type tests.
Expect.throws(() => call(sixRequired.toJS, ['a', 'b', 0.0, 'd', 'e', 'f']));
Expect.throws(() => call(threeRequiredThreeOptional.toJS, ['undefined']));
// Arity tests.
// Verify that we appropriately truncate arguments even though we don't have
// a special lowering for six arguments in DDC and dart2js.
Expect.equals(
call(fourRequiredTwoOptional.toJS, ['a', 'b', 'c', 'd', 'e', 'f', 0]),
'abcdef');
Expect.throws(() => call(twoRequiredFourOptional.toJS, []));
// Function subtyping tests.
var closure = () => call(
(fiveRequiredOneOptional as String Function(
String, String, String, String, String))
.toJS,
['a', null, 'c', 'd', 'e', 'f']);
if (isDDC || isDart2JS) {
Expect.equals(closure(), 'anullcdedefault');
} else {
Expect.throws(closure);
}
Expect.equals(
call((oneRequiredFiveOptional as String Function(String, [String?])).toJS,
['a', 'b', 0, 0.0, false]),
'abdefaultdefaultdefaultdefault');
// `undefined` tests.
Expect.equals(call(sixOptional.toJS, ['a', 'undefined', 'c', 'd', 'e']),
isDDC ? 'adefaultcdedefault' : 'anullcdedefault');
// Conversion round-trip test.
final tearOff = sixRequired;
Expect.equals(tearOff, tearOff.toJS.toDart);
// Avoid rewrapping test.
if (isDDC || isDart2JS) {
Expect.throws(() => (sixOptional.toJS as String Function()).toJS);
}
}
void main() {
eval('''
self.call = function(f, args) {
var convert = function(arg) {
return arg == 'undefined' ? undefined : arg;
};
return f.apply(null, args.map((e) => convert(e)));
};
''');
testZero();
testOne();
testTwo();
testThree();
testFour();
testFive();
testSix();
}

View File

@ -156,15 +156,20 @@ void syncTests() {
expect(confuse(fun) is JSFunction, true);
// [JSExportedDartFunction] <-> [Function]
edf = (JSString a, JSString b) {
final dartFunction = (JSString a, JSString b) {
return (a.toDart + b.toDart).toJS;
}.toJS;
};
edf = dartFunction.toJS;
expect(doFun('foo'.toJS, 'bar'.toJS).toDart, 'foobar');
expect(
(edf.toDart as JSString Function(JSString, JSString))(
'foo'.toJS, 'bar'.toJS)
.toDart,
'foobar');
Expect.equals(edf.toDart, dartFunction);
Expect.isTrue(identical(edf.toDart, dartFunction));
// Two wrappers should not be the same.
Expect.notEquals(edf, dartFunction.toJS);
// Converting a non-function should throw.
Expect.throws(() => ('foo'.toJS as JSExportedDartFunction).toDart);

View File

@ -90,6 +90,7 @@ js/static_interop_test/isa/functional_test: SkipByDesign # Issue 42085. CSP poli
js/static_interop_test/isa/library_renaming_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code
js/static_interop_test/js_array_proxy_or_ref_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code
js/static_interop_test/js_array_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code
js/static_interop_test/js_function_arity_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code
js/static_interop_test/js_function_conversions_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code
js/static_interop_test/js_typed_array_test: SkipByDesign # CSP policy disallows injected JS code
js/static_interop_test/jsobject_type_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code