Files
sdk/pkg/dev_compiler/lib/src/kernel/js_interop.dart
T
Nicholas Shahan c5218f2d4e [ddc] Refactor visitFunctionTearOff
Stop passing `null` as the member parameter of `_emitPropertyGet`.

Avoids the use of one method with multiple code paths and returns
to handle any situation because it becomes very hard to reason
about what original source code leads to each path.

Issue: https://github.com/dart-lang/sdk/issues/54463
Change-Id: I1255817c7cf76578b256650789f6530b456b2f03
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/357207
Reviewed-by: Srujan Gaddam <srujzs@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Reviewed-by: Bob Nystrom <rnystrom@google.com>
Commit-Queue: Nicholas Shahan <nshahan@google.com>
2024-04-02 19:57:25 +00:00

192 lines
7.3 KiB
Dart

// Copyright (c) 2017, 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.
import 'package:_js_interop_checks/src/js_interop.dart';
import 'package:kernel/kernel.dart';
import 'kernel_helpers.dart';
/// Returns true if [library] is one of the [candidates].
/// The latter should be a list, e.g.,: ['dart:js', 'dart:_js_annotations'].
bool _isLibrary(Library library, List<String> candidates) {
var uri = library.importUri;
var scheme = uri.scheme;
var path = uri.pathSegments[0];
for (var candidate in candidates) {
var pair = candidate.split(':');
if (scheme == pair[0] && (path == pair[1] || pair[1] == '*')) {
return true;
}
}
return false;
}
/// Returns true if [library] represents any library from `package:js`,
/// `dart:_foreign_helper`, `dart:_js_annotations`, or `dart:_js_helper`, or
/// `dart:js_interop`.
bool _isJSLibrary(Library library) => _isLibrary(library, [
// While the annotations no longer live in `package:js`, this is needed to
// support older versions of the package.
'package:js',
'dart:_foreign_helper',
'dart:_js_annotations',
'dart:_js_helper',
// This is to allow `dart:js_interop`'s `@JS` to work with
// `@staticInterop`.
'dart:js_interop',
]);
/// Whether [node] is a direct call to `allowInterop`.
bool isAllowInterop(Expression node) {
if (node is StaticInvocation) {
var target = node.target;
return _isLibrary(target.enclosingLibrary, ['dart:js_util']) &&
target.name.text == 'allowInterop';
}
return false;
}
bool isJsMember(Member member) {
return member.isExternal &&
!_isLibrary(member.enclosingLibrary, ['dart:*']) &&
!isNative(member) &&
// Non-JS interop external members might exist e.g. the ones generated by
// Angular, so we should check to see that this member actually uses JS
// interop.
usesJSInterop(member);
}
bool _annotationIsFromJSLibrary(String expectedName, Expression value) {
var c = getAnnotationClass(value);
return c != null &&
c.name == expectedName &&
_isJSLibrary(c.enclosingLibrary);
}
/// Whether [value] is a `@rest` annotation (to be used on function parameters
/// to have them compiled as `...` rest params in ES6 outputs).
bool isJsRestAnnotation(Expression value) =>
_annotationIsFromJSLibrary('_Rest', value);
// TODO(jmesserly): Move JsPeerInterface to package:js (see issue #135).
// TODO(jacobr): The 'JS' annotation is the new, publicly accessible one.
// The 'JsName' annotation is the old one using internally by dart2js and
// html libraries. These two concepts will probably merge eventually.
bool isJSAnnotation(Expression value) =>
_annotationIsFromJSLibrary('JS', value) || isJSName(value);
/// Returns [true] if [value] is the `JS` annotation from
/// `package:js`, `dart:_js_annotations`, or `dart:js_interop`.
bool isJSInteropAnnotation(Expression value) =>
_annotationIsFromJSLibrary('JS', value);
bool _isJSAnonymousAnnotation(Expression value) =>
_annotationIsFromJSLibrary('_Anonymous', value);
bool _isStaticInteropAnnotation(Expression value) =>
_annotationIsFromJSLibrary('_StaticInterop', value);
/// Whether [value] is a `@JSExportName` (internal annotation used in SDK
/// instead of `@JS` from `dart:_js_annotations`).
bool isJSExportNameAnnotation(Expression value) =>
isBuiltinAnnotation(value, '_foreign_helper', 'JSExportName');
/// Whether [i] is a `spread` invocation (to be used on function arguments
/// to have them compiled as `...` spread args in ES6 outputs).
bool isJSSpreadInvocation(Procedure target) =>
target.name.text == 'spread' && _isJSLibrary(target.enclosingLibrary);
bool isJSName(Expression value) =>
isBuiltinAnnotation(value, '_js_helper', 'JSName');
bool isJsPeerInterface(Expression value) =>
isBuiltinAnnotation(value, '_js_helper', 'JsPeerInterface');
bool isNativeAnnotation(Expression value) =>
isBuiltinAnnotation(value, '_js_helper', 'Native');
bool isJSAnonymousType(Class namedClass) {
var hasJSInterop = hasJSInteropAnnotation(namedClass);
var isAnonymous =
findAnnotation(namedClass, _isJSAnonymousAnnotation) != null;
return hasJSInterop && isAnonymous;
}
bool isStaticInteropType(Class namedClass) {
var hasJSInterop = hasJSInteropAnnotation(namedClass);
var isStaticInterop =
findAnnotation(namedClass, _isStaticInteropAnnotation) != null;
return hasJSInterop && isStaticInterop;
}
bool isUndefinedAnnotation(Expression value) =>
isBuiltinAnnotation(value, '_js_helper', '_Undefined');
/// Returns true iff the annotatable has an `@JS(...)` annotation from
/// `package:js`, `dart:_js_annotations`, or `dart:js_interop`.
///
/// Note: usually [usesJSInterop] should be used instead of this.
//
// TODO(jmesserly): I think almost all uses of this should be replaced with
// [usesJSInterop], which also checks that the library is marked with `@JS`.
//
// Right now we have inconsistencies: sometimes we'll respect `@JS` on the
// class itself, other places we require it on the library. Also members are
// inconsistent: sometimes they need to have `@JS` on them, other times they
// need to be `external` in an `@JS` class.
//
// TODO(srujzs): We should replace many of these helpers with the ones defined
// in pkg:_js_interop_checks.
bool hasJSInteropAnnotation(Annotatable a) =>
a.annotations.any(isJSInteropAnnotation);
/// Returns true iff this element is a JS interop member.
///
/// JS annotations are required explicitly on classes. Other elements, such as
/// class members and top-level functions/accessors, should be marked `external`
/// and should have directly or indirectly a `JS` annotation. It is sufficient
/// if the annotation is in the procedure itself or an enclosing element like
/// the class or library.
bool usesJSInterop(NamedNode n) {
if (n is Member && n.isExternal) {
return hasJSInteropAnnotation(n) ||
(n.enclosingClass == null
? hasJSInteropAnnotation(n.enclosingLibrary)
: hasJSInteropAnnotation(n.enclosingClass!));
} else if (n is Class) {
return n.annotations.any(isJSInteropAnnotation);
}
return false;
}
/// Returns `true` when [member] is a JavaScript interop member using the
/// package:js style of interop.
///
/// Will return `false` when the newer static interop style is used or when
/// [member] is not a JavaScript interop member at all.
bool isNonStaticJsInterop(Member member) {
return member.isExternal &&
!member.isExtensionMember &&
!member.isExtensionTypeMember &&
_usesNonStaticInteropAnnotation(member);
}
/// Returns `true` when [member] is a JavaScript interop member using the
/// package:js style of interop annotation.
///
/// This does not mean [member] has the annotation directly because it can
/// be applied on the class or the library level.
///
/// Will return `false` when the newer static interop style annotation is
/// found or when [member] is not a JavaScript interop member at all.
bool _usesNonStaticInteropAnnotation(Member member) {
var enclosingClass = member.enclosingClass;
if (enclosingClass != null) {
return hasPackageJSAnnotation(enclosingClass) &&
!hasStaticInteropAnnotation(enclosingClass);
}
return hasPackageJSAnnotation(member) ||
hasPackageJSAnnotation(member.enclosingLibrary);
}