1
0
mirror of https://github.com/dart-lang/sdk synced 2024-07-08 12:06:26 +00:00

[js] Move allowInterop functions to dart:js_util.

CoreLibraryReviewExempt: Has core library owners approval.
Change-Id: Iea2f2e707c69c9082e158b48b50dcaf4a7b01067
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/279740
Reviewed-by: Ömer Ağacan <omersa@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Reviewed-by: Srujan Gaddam <srujzs@google.com>
Commit-Queue: Joshua Litt <joshualitt@google.com>
This commit is contained in:
Joshua Litt 2023-02-08 17:16:20 +00:00 committed by Commit Queue
parent 55e0784a62
commit bd79be0e19
24 changed files with 207 additions and 195 deletions

View File

@ -36,7 +36,7 @@ class ExportCreator extends Transformer {
ExportCreator(
this._typeEnvironment, this._diagnosticReporter, this._exportChecker)
: _allowInterop = _typeEnvironment.coreTypes.index
.getTopLevelProcedure('dart:js', 'allowInterop'),
.getTopLevelProcedure('dart:js_util', 'allowInterop'),
_createDartExport = _typeEnvironment.coreTypes.index
.getTopLevelProcedure('dart:js_util', 'createDartExport'),
_createStaticInteropMock = _typeEnvironment.coreTypes.index

View File

@ -89,8 +89,8 @@ class JsUtilOptimizer extends Transformer {
.getTopLevelProcedure('dart:js_util', '_setPropertyUnchecked'),
_jsTarget =
_coreTypes.index.getTopLevelProcedure('dart:_foreign_helper', 'JS'),
_allowInteropTarget =
_coreTypes.index.getTopLevelProcedure('dart:js', 'allowInterop'),
_allowInteropTarget = _coreTypes.index
.getTopLevelProcedure('dart:js_util', 'allowInterop'),
_allowedInteropJsUtilTargets = _allowedInteropJsUtilMembers.map(
(member) =>
_coreTypes.index.getTopLevelProcedure('dart:js_util', member)),

View File

@ -139,8 +139,9 @@ abstract class CommonElements {
late final LibraryEntity internalLibrary =
_env.lookupLibrary(Uris.dart__internal, required: true)!;
/// The dart:js library.
late final LibraryEntity? dartJsLibrary = _env.lookupLibrary(Uris.dart_js);
/// The dart:js_util library.
late final LibraryEntity? dartJsUtilLibrary =
_env.lookupLibrary(Uris.dart_js_util);
/// The package:js library.
late final LibraryEntity? packageJsLibrary =
@ -1039,21 +1040,9 @@ abstract class CommonElements {
: objectClass;
}
// From package:js
late final FunctionEntity? jsAllowInterop1 =
_findLibraryMember(dartJsLibrary, 'allowInterop', required: false);
// From dart:_js_annotations;
late final FunctionEntity? jsAllowInterop2 = _findLibraryMember(
dartJsAnnotationsLibrary, 'allowInterop',
required: false);
/// Returns `true` if [function] is `allowInterop`.
///
/// This function can come from either `package:js` or `dart:_js_annotations`.
bool isJsAllowInterop(FunctionEntity function) {
return function == jsAllowInterop1 || function == jsAllowInterop2;
}
// From dart:js_util
late final FunctionEntity? jsAllowInterop =
_findLibraryMember(dartJsUtilLibrary, 'allowInterop', required: false);
bool isCreateInvocationMirrorHelper(MemberEntity member) {
return member.isTopLevel &&

View File

@ -255,8 +255,8 @@ class Uris {
static final Uri dart__js_shared_embedded_names =
Uri(scheme: 'dart', path: '_js_shared_embedded_names');
/// The URI for 'dart:js'.
static final Uri dart_js = Uri(scheme: 'dart', path: 'js');
/// The URI for 'dart:js_util'.
static final Uri dart_js_util = Uri(scheme: 'dart', path: 'js_util');
/// The URI for 'package:js'.
static final Uri package_js = Uri(scheme: 'package', path: 'js/js.dart');

View File

@ -399,8 +399,7 @@ class BackendImpacts {
late final BackendImpact allowInterop = BackendImpact(
staticUses: [
_commonElements.jsAllowInterop1!,
_commonElements.jsAllowInterop2!,
_commonElements.jsAllowInterop!,
],
features: EnumSet<BackendFeature>.fromValues([
BackendFeature.needToInitializeIsolateAffinityTag,

View File

@ -2066,8 +2066,7 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
List<js.Expression> arguments = visitArguments(node.inputs, start: 0);
if (element == _commonElements.jsAllowInterop1 ||
element == _commonElements.jsAllowInterop2) {
if (element == _commonElements.jsAllowInterop) {
_nativeData.registerAllowInterop();
}

View File

@ -30,21 +30,9 @@ class JsInteropClass {
/*member: JsInteropClass.:*/
external JsInteropClass();
/*member: JsInteropClass.method:
type=[
native:ApplicationCacheErrorEvent,
native:DomError,
native:DomException,
native:ErrorEvent,
/*member: JsInteropClass.method:type=[
native:GenericClass<dynamic>,
native:JsInteropClass,
native:MediaError,
native:NavigatorUserMediaError,
native:OverconstrainedError,
native:PositionError,
native:SensorErrorEvent,
native:SpeechRecognitionError]
*/
native:JsInteropClass]*/
@JS()
external double method();
}

View File

@ -163,8 +163,8 @@ class _JSLowerer extends Transformer {
.getClass('dart:_js_helper', 'JSValue')
.procedures
.firstWhere((p) => p.name.text == 'unbox'),
_allowInteropTarget =
_coreTypes.index.getTopLevelProcedure('dart:js', 'allowInterop'),
_allowInteropTarget = _coreTypes.index
.getTopLevelProcedure('dart:js_util', 'allowInterop'),
_inlineJSTarget =
_coreTypes.index.getTopLevelProcedure('dart:_js_helper', 'JS'),
_wasmExternRefClass =

View File

@ -61,7 +61,6 @@ class WasmTarget extends Target {
'dart:typed_data',
'dart:nativewrappers',
'dart:io',
'dart:js',
'dart:js_util',
'dart:wasm',
'dart:developer',
@ -73,7 +72,6 @@ class WasmTarget extends Target {
'dart:_js_interop',
'dart:collection',
'dart:typed_data',
'dart:js',
'dart:js_util',
'dart:wasm',
];

View File

@ -34,7 +34,7 @@ bool _isJSLibrary(Library library) => _isLibrary(library, [
bool isAllowInterop(Expression node) {
if (node is StaticInvocation) {
var target = node.target;
return _isLibrary(target.enclosingLibrary, ['dart:js']) &&
return _isLibrary(target.enclosingLibrary, ['dart:js_util']) &&
target.name.text == 'allowInterop';
}
return false;

View File

@ -7,7 +7,7 @@ library js;
import 'package:meta/meta.dart';
export 'dart:js' show allowInterop, allowInteropCaptureThis;
export 'dart:js_util' show allowInterop, allowInteropCaptureThis;
/// An annotation that indicates a library, class, or member is implemented
/// directly in JavaScript.

View File

@ -0,0 +1,48 @@
// Copyright (c) 2023, 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.
// Patch file for dart:js_util library.
import 'dart:_foreign_helper' show JS;
import 'dart:_internal' show patch;
import 'dart:_runtime' as dart;
Expando<Function> _interopExpando = Expando<Function>();
@patch
F allowInterop<F extends Function>(F f) {
if (!dart.isDartFunction(f)) return f;
var ret = _interopExpando[f] as F?;
if (ret == null) {
ret = JS<F>(
'',
'function (...args) {'
' return #(#, args);'
'}',
dart.dcall,
f);
_interopExpando[f] = ret;
}
return ret;
}
Expando<Function> _interopCaptureThisExpando = Expando<Function>();
@patch
Function allowInteropCaptureThis(Function f) {
if (!dart.isDartFunction(f)) return f;
var ret = _interopCaptureThisExpando[f];
if (ret == null) {
ret = JS<Function>(
'',
'function(...arguments) {'
' let args = [this];'
' args.push.apply(args, arguments);'
' return #(#, args);'
'}',
dart.dcall,
f);
_interopCaptureThisExpando[f] = ret;
}
return ret;
}

View File

@ -404,43 +404,3 @@ T _putIfAbsent<T>(Object weakMap, Object o, T getValue(Object o)) {
// TODO(vsm): Static cast. Unnecessary?
return JS('', '#', value);
}
Expando<Function> _interopExpando = Expando<Function>();
@patch
F allowInterop<F extends Function>(F f) {
if (!dart.isDartFunction(f)) return f;
var ret = _interopExpando[f] as F?;
if (ret == null) {
ret = JS<F>(
'',
'function (...args) {'
' return #(#, args);'
'}',
dart.dcall,
f);
_interopExpando[f] = ret;
}
return ret;
}
Expando<Function> _interopCaptureThisExpando = Expando<Function>();
@patch
Function allowInteropCaptureThis(Function f) {
if (!dart.isDartFunction(f)) return f;
var ret = _interopCaptureThisExpando[f];
if (ret == null) {
ret = JS<Function>(
'',
'function(...arguments) {'
' let args = [this];'
' args.push.apply(args, arguments);'
' return #(#, args);'
'}',
dart.dcall,
f);
_interopCaptureThisExpando[f] = ret;
}
return ret;
}

View File

@ -0,0 +1,82 @@
// Copyright (c) 2023, 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.
// 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:_internal' show patch;
import 'dart:_js_helper'
show
isJSFunction,
JS_FUNCTION_PROPERTY_NAME,
JS_FUNCTION_PROPERTY_NAME_CAPTURE_THIS;
_convertDartFunctionFast(Function f) {
var existing = JS('', '#.#', f, JS_FUNCTION_PROPERTY_NAME);
if (existing != null) return existing;
var ret = JS(
'JavaScriptFunction',
'''
function(_call, f) {
return function() {
return _call(f, Array.prototype.slice.apply(arguments));
}
}(#, #)
''',
DART_CLOSURE_TO_JS(_callDartFunctionFast),
f);
JS('', '#.# = #', ret, DART_CLOSURE_PROPERTY_NAME, f);
JS('', '#.# = #', f, JS_FUNCTION_PROPERTY_NAME, ret);
return ret;
}
_convertDartFunctionFastCaptureThis(Function f) {
var existing = JS('', '#.#', f, JS_FUNCTION_PROPERTY_NAME_CAPTURE_THIS);
if (existing != null) return existing;
var ret = JS(
'JavaScriptFunction',
'''
function(_call, f) {
return function() {
return _call(f, this,Array.prototype.slice.apply(arguments));
}
}(#, #)
''',
DART_CLOSURE_TO_JS(_callDartFunctionFastCaptureThis),
f);
JS('', '#.# = #', ret, DART_CLOSURE_PROPERTY_NAME, f);
JS('', '#.# = #', f, JS_FUNCTION_PROPERTY_NAME_CAPTURE_THIS, ret);
return ret;
}
_callDartFunctionFast(callback, List arguments) {
return Function.apply(callback, arguments);
}
_callDartFunctionFastCaptureThis(callback, self, List arguments) {
return Function.apply(callback, [self]..addAll(arguments));
}
@patch
F allowInterop<F extends Function>(F f) {
if (isJSFunction(f)) {
// Already supports interop, just use the existing function.
return f;
} else {
return _convertDartFunctionFast(f);
}
}
@patch
Function allowInteropCaptureThis(Function f) {
if (isJSFunction(f)) {
// Behavior when the function is already a JS function is unspecified.
throw ArgumentError(
"Function is already a JS function so cannot capture this.");
return f;
} else {
return _convertDartFunctionFastCaptureThis(f);
}
}

View File

@ -82,6 +82,9 @@ part 'string_helper.dart';
part 'linked_hash_map.dart';
part 'records.dart';
const JS_FUNCTION_PROPERTY_NAME = r'$dart_jsFunction';
const JS_FUNCTION_PROPERTY_NAME_CAPTURE_THIS = r'_$dart_jsFunctionCaptureThis';
/// Marks the internal map in dart2js, so that internal libraries can is-check
/// them.
abstract class InternalMap {}

View File

@ -9,7 +9,12 @@ import 'dart:typed_data' show TypedData;
import 'dart:_foreign_helper' show JS, DART_CLOSURE_TO_JS;
import 'dart:_interceptors' show DART_CLOSURE_PROPERTY_NAME;
import 'dart:_internal' show patch;
import 'dart:_js_helper' show Primitives, getIsolateAffinityTag, isJSFunction;
import 'dart:_js_helper'
show
Primitives,
getIsolateAffinityTag,
isJSFunction,
JS_FUNCTION_PROPERTY_NAME;
import 'dart:_js' show isBrowserObject, convertFromBrowserObject;
@patch
@ -370,8 +375,6 @@ final String _DART_OBJECT_PROPERTY_NAME =
// property added to a JS object referencing its Dart-side JsObject proxy
const _JS_OBJECT_PROPERTY_NAME = r'_$dart_jsObject';
const _JS_FUNCTION_PROPERTY_NAME = r'$dart_jsFunction';
const _JS_FUNCTION_PROPERTY_NAME_CAPTURE_THIS = r'_$dart_jsFunctionCaptureThis';
@pragma('dart2js:tryInline')
JsObject _castToJsObject(o) => JS<JsObject>('', '#', o);
@ -431,7 +434,7 @@ Object? _convertToJS(Object? o) {
return Primitives.lazyAsJsDate(o);
}
if (o is Function) {
return _getJsProxy(o, _JS_FUNCTION_PROPERTY_NAME, (o) {
return _getJsProxy(o, JS_FUNCTION_PROPERTY_NAME, (o) {
var jsFunction = _convertDartFunction(o);
// set a property on the JS closure referencing the Dart closure
_defineProperty(jsFunction, DART_CLOSURE_PROPERTY_NAME, o);
@ -502,71 +505,3 @@ Object _getDartProxy(o, String propertyName, JsObject createProxy(o)) {
}
return dartProxy;
}
_convertDartFunctionFast(Function f) {
var existing = JS('', '#.#', f, _JS_FUNCTION_PROPERTY_NAME);
if (existing != null) return existing;
var ret = JS(
'JavaScriptFunction',
'''
function(_call, f) {
return function() {
return _call(f, Array.prototype.slice.apply(arguments));
}
}(#, #)
''',
DART_CLOSURE_TO_JS(_callDartFunctionFast),
f);
JS('', '#.# = #', ret, DART_CLOSURE_PROPERTY_NAME, f);
JS('', '#.# = #', f, _JS_FUNCTION_PROPERTY_NAME, ret);
return ret;
}
_convertDartFunctionFastCaptureThis(Function f) {
var existing = JS('', '#.#', f, _JS_FUNCTION_PROPERTY_NAME_CAPTURE_THIS);
if (existing != null) return existing;
var ret = JS(
'JavaScriptFunction',
'''
function(_call, f) {
return function() {
return _call(f, this,Array.prototype.slice.apply(arguments));
}
}(#, #)
''',
DART_CLOSURE_TO_JS(_callDartFunctionFastCaptureThis),
f);
JS('', '#.# = #', ret, DART_CLOSURE_PROPERTY_NAME, f);
JS('', '#.# = #', f, _JS_FUNCTION_PROPERTY_NAME_CAPTURE_THIS, ret);
return ret;
}
_callDartFunctionFast(callback, List arguments) {
return Function.apply(callback, arguments);
}
_callDartFunctionFastCaptureThis(callback, self, List arguments) {
return Function.apply(callback, [self]..addAll(arguments));
}
@patch
F allowInterop<F extends Function>(F f) {
if (isJSFunction(f)) {
// Already supports interop, just use the existing function.
return f;
} else {
return _convertDartFunctionFast(f);
}
}
@patch
Function allowInteropCaptureThis(Function f) {
if (isJSFunction(f)) {
// Behavior when the function is already a JS function is unspecified.
throw ArgumentError(
"Function is already a JS function so cannot capture this.");
return f;
} else {
return _convertDartFunctionFastCaptureThis(f);
}
}

View File

@ -8,7 +8,6 @@ import 'dart:_js_helper'
show convertDartClosureToJS, assertInterop, assertInteropArgs;
import 'dart:collection' show HashMap;
import 'dart:async' show Completer;
import 'dart:js_util';
@patch
dynamic jsify(Object object) {

View File

@ -113,10 +113,3 @@ class JsArray<E> extends JsObject with ListMixin<E> {
@patch
void sort([int compare(E a, E b)?]) => throw UnimplementedError();
}
/// This will be lowered to a call to `_wrapDartCallback`.
@patch
F allowInterop<F extends Function>(F f) => throw UnimplementedError();
@patch
Function allowInteropCaptureThis(Function f) => throw UnimplementedError();

View File

@ -150,10 +150,10 @@ typedef _PromiseFailureFunc = void Function(Object? error);
Future<T> promiseToFuture<T>(Object jsPromise) {
Completer<T> completer = Completer<T>();
final success = js.allowInterop<_PromiseSuccessFunc>((r) {
final success = allowInterop<_PromiseSuccessFunc>((r) {
return completer.complete(r as FutureOr<T>?);
});
final error = js.allowInterop<_PromiseFailureFunc>((e) {
final error = allowInterop<_PromiseFailureFunc>((e) {
// Note that `completeError` expects a non-nullable error regardless of
// whether null-safety is enabled, so a `NullRejectionException` is always
// provided if the error is `null` or `undefined`.
@ -257,3 +257,10 @@ Object? dartify(Object? object) {
return convert(object);
}
/// This will be lowered to a a call to `_wrapDartCallback`.
@patch
F allowInterop<F extends Function>(F f) => throw UnimplementedError();
@patch
Function allowInteropCaptureThis(Function f) => throw UnimplementedError();

View File

@ -7,7 +7,7 @@
// https://github.com/dart-lang/sdk/blob/master/pkg/js/lib/js.dart
library _js_annotations;
export 'dart:js' show allowInterop, allowInteropCaptureThis;
export 'dart:js_util' show allowInterop, allowInteropCaptureThis;
class JS {
final String? name;

View File

@ -86,6 +86,8 @@ library dart.js;
import 'dart:collection' show ListMixin;
export 'dart:js_util' show allowInterop, allowInteropCaptureThis;
/// The JavaScript global object, usually `window`.
external JsObject get context;
@ -209,25 +211,3 @@ class JsArray<E> extends JsObject with ListMixin<E> {
external void sort([int compare(E a, E b)?]);
}
/// Returns a wrapper around function [f] that can be called from JavaScript
/// using `package:js` JavaScript interop.
///
/// The calling conventions in Dart2Js differ from JavaScript and so, by
/// default, it is not possible to call a Dart function directly. Wrapping with
/// `allowInterop` creates a function that can be called from JavaScript or
/// Dart. The semantics of the wrapped function are still more strict than
/// JavaScript, and the function will throw if called with too many or too few
/// arguments.
///
/// Calling this method repeatedly on a function will return the same result.
external F allowInterop<F extends Function>(F f);
/// Returns a wrapper around function [f] that can be called from JavaScript
/// using `package:js` JavaScript interop, passing JavaScript `this` as the first
/// argument.
///
/// See [allowInterop].
///
/// When called from Dart, `null` will be passed as the first argument.
external Function allowInteropCaptureThis(Function f);

View File

@ -256,3 +256,25 @@ external T createStaticInteropMock<T extends Object, U extends Object>(
/// Expect.isTrue(counter.stringify(), export.stringify());
/// ```
external Object createDartExport<T extends Object>(T dartObject);
/// Returns a wrapper around function [f] that can be called from JavaScript
/// using `package:js` JavaScript interop.
///
/// The calling conventions in Dart web backends differ from JavaScript and so,
/// by default, it is not possible to call a Dart function directly. Wrapping
/// with `allowInterop` creates a function that can be called from JavaScript or
/// Dart. The semantics of the wrapped function are still more strict than
/// JavaScript, and the function will throw if called with too many or too few
/// arguments.
///
/// Calling this method repeatedly on a function will return the same result.
external F allowInterop<F extends Function>(F f);
/// Returns a wrapper around function [f] that can be called from JavaScript
/// using `package:js` JavaScript interop, passing JavaScript `this` as the
/// first argument.
///
/// See [allowInterop].
///
/// When called from Dart, `null` will be passed as the first argument.
external Function allowInteropCaptureThis(Function f);

View File

@ -334,7 +334,10 @@
},
"js_util": {
"uri": "js_util/js_util.dart",
"patches": "_internal/js_shared/lib/js_util_patch.dart"
"patches": [
"_internal/js_shared/lib/js_util_patch.dart",
"_internal/js_runtime/lib/js_allow_interop_patch.dart"
]
},
"math": {
"uri": "math/math.dart",
@ -506,7 +509,10 @@
},
"js_util": {
"uri": "js_util/js_util.dart",
"patches": "_internal/js_shared/lib/js_util_patch.dart"
"patches": [
"_internal/js_shared/lib/js_util_patch.dart",
"_internal/js_dev_runtime/patch/js_allow_interop_patch.dart"
]
},
"svg": {
"uri": "svg/dart2js/svg_dart2js.dart"

View File

@ -287,7 +287,9 @@ _dart2js_common:
js_util:
uri: "js_util/js_util.dart"
patches: "_internal/js_shared/lib/js_util_patch.dart"
patches:
- "_internal/js_shared/lib/js_util_patch.dart"
- "_internal/js_runtime/lib/js_allow_interop_patch.dart"
math:
uri: "math/math.dart"
@ -457,7 +459,9 @@ dartdevc:
js_util:
uri: "js_util/js_util.dart"
patches: "_internal/js_shared/lib/js_util_patch.dart"
patches:
- "_internal/js_shared/lib/js_util_patch.dart"
- "_internal/js_dev_runtime/patch/js_allow_interop_patch.dart"
svg:
uri: "svg/dart2js/svg_dart2js.dart"