mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 22:41:41 +00:00
[pkg/_js_interop_checks] use package:lints/recommended.yaml
Change-Id: Ia8024f6f5a2185c1ac6d6dc666c9e246f5131c52 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/282391 Reviewed-by: Srujan Gaddam <srujzs@google.com> Commit-Queue: Devon Carew <devoncarew@google.com>
This commit is contained in:
parent
13521eebab
commit
f39365c8e5
|
@ -1 +1,7 @@
|
|||
include: package:lints/core.yaml
|
||||
include: package:lints/recommended.yaml
|
||||
|
||||
linter:
|
||||
rules:
|
||||
depend_on_referenced_packages: true
|
||||
directives_ordering: true
|
||||
sort_pub_dependencies: true
|
||||
|
|
|
@ -2,11 +2,8 @@
|
|||
// 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.
|
||||
|
||||
// Used for importing CFE utility functions for constructor tear-offs.
|
||||
import 'package:front_end/src/api_prototype/lowering_predicates.dart';
|
||||
import 'package:kernel/core_types.dart';
|
||||
import 'package:kernel/kernel.dart';
|
||||
import 'package:kernel/target/targets.dart';
|
||||
// ignore_for_file: implementation_imports
|
||||
|
||||
import 'package:_fe_analyzer_shared/src/messages/codes.dart'
|
||||
show
|
||||
Message,
|
||||
|
@ -35,6 +32,11 @@ import 'package:_fe_analyzer_shared/src/messages/codes.dart'
|
|||
templateJsInteropStaticInteropTrustTypesUsageNotAllowed,
|
||||
templateJsInteropStaticInteropTrustTypesUsedWithoutStaticInterop;
|
||||
import 'package:_js_interop_checks/src/transformations/export_checker.dart';
|
||||
// Used for importing CFE utility functions for constructor tear-offs.
|
||||
import 'package:front_end/src/api_prototype/lowering_predicates.dart';
|
||||
import 'package:kernel/core_types.dart';
|
||||
import 'package:kernel/kernel.dart';
|
||||
import 'package:kernel/target/targets.dart';
|
||||
|
||||
import 'src/js_interop.dart';
|
||||
|
||||
|
@ -89,7 +91,7 @@ class JsInteropChecks extends RecursiveVisitor {
|
|||
|
||||
/// Native tests to exclude from checks on external.
|
||||
// TODO(rileyporter): Use ExternalName from CFE to exclude native tests.
|
||||
List<Pattern> _allowedNativeTestPatterns = [
|
||||
final List<Pattern> _allowedNativeTestPatterns = [
|
||||
RegExp(r'(?<!generated_)tests/web/native'),
|
||||
RegExp(r'(?<!generated_)tests/web/internal'),
|
||||
'generated_tests/web/native/native_test',
|
||||
|
@ -98,7 +100,7 @@ class JsInteropChecks extends RecursiveVisitor {
|
|||
'generated_tests/web_2/native/native_test',
|
||||
];
|
||||
|
||||
List<Pattern> _allowedTrustTypesTestPatterns = [
|
||||
final List<Pattern> _allowedTrustTypesTestPatterns = [
|
||||
RegExp(r'(?<!generated_)tests/lib/js'),
|
||||
RegExp(r'(?<!generated_)tests/lib_2/js'),
|
||||
];
|
||||
|
@ -130,124 +132,124 @@ class JsInteropChecks extends RecursiveVisitor {
|
|||
}
|
||||
|
||||
@override
|
||||
void defaultMember(Member member) {
|
||||
_checkInstanceMemberJSAnnotation(member);
|
||||
if (!_isJSInteropMember(member)) _checkDisallowedExternal(member);
|
||||
void defaultMember(Member node) {
|
||||
_checkInstanceMemberJSAnnotation(node);
|
||||
if (!_isJSInteropMember(node)) _checkDisallowedExternal(node);
|
||||
// TODO(43530): Disallow having JS interop annotations on non-external
|
||||
// members (class members or otherwise). Currently, they're being ignored.
|
||||
exportChecker.visitMember(member);
|
||||
super.defaultMember(member);
|
||||
exportChecker.visitMember(node);
|
||||
super.defaultMember(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitClass(Class cls) {
|
||||
_classHasJSAnnotation = hasJSInteropAnnotation(cls);
|
||||
_classHasAnonymousAnnotation = hasAnonymousAnnotation(cls);
|
||||
_classHasStaticInteropAnnotation = hasStaticInteropAnnotation(cls);
|
||||
bool classHasTrustTypesAnnotation = hasTrustTypesAnnotation(cls);
|
||||
void visitClass(Class node) {
|
||||
_classHasJSAnnotation = hasJSInteropAnnotation(node);
|
||||
_classHasAnonymousAnnotation = hasAnonymousAnnotation(node);
|
||||
_classHasStaticInteropAnnotation = hasStaticInteropAnnotation(node);
|
||||
bool classHasTrustTypesAnnotation = hasTrustTypesAnnotation(node);
|
||||
if (classHasTrustTypesAnnotation) {
|
||||
if (!_isAllowedTrustTypesUsage(cls)) {
|
||||
if (!_isAllowedTrustTypesUsage(node)) {
|
||||
_diagnosticsReporter.report(
|
||||
templateJsInteropStaticInteropTrustTypesUsageNotAllowed
|
||||
.withArguments(cls.name),
|
||||
cls.fileOffset,
|
||||
cls.name.length,
|
||||
cls.fileUri);
|
||||
.withArguments(node.name),
|
||||
node.fileOffset,
|
||||
node.name.length,
|
||||
node.fileUri);
|
||||
}
|
||||
if (!_classHasStaticInteropAnnotation) {
|
||||
_diagnosticsReporter.report(
|
||||
templateJsInteropStaticInteropTrustTypesUsedWithoutStaticInterop
|
||||
.withArguments(cls.name),
|
||||
cls.fileOffset,
|
||||
cls.name.length,
|
||||
cls.fileUri);
|
||||
.withArguments(node.name),
|
||||
node.fileOffset,
|
||||
node.name.length,
|
||||
node.fileUri);
|
||||
}
|
||||
}
|
||||
var superclass = cls.superclass;
|
||||
var superclass = node.superclass;
|
||||
if (superclass != null && superclass != _coreTypes.objectClass) {
|
||||
var superHasJSAnnotation = hasJSInteropAnnotation(superclass);
|
||||
if (_classHasJSAnnotation && !superHasJSAnnotation) {
|
||||
_diagnosticsReporter.report(
|
||||
templateJsInteropJSClassExtendsDartClass.withArguments(
|
||||
cls.name, superclass.name),
|
||||
cls.fileOffset,
|
||||
cls.name.length,
|
||||
cls.fileUri);
|
||||
node.name, superclass.name),
|
||||
node.fileOffset,
|
||||
node.name.length,
|
||||
node.fileUri);
|
||||
} else if (!_classHasJSAnnotation && superHasJSAnnotation) {
|
||||
_diagnosticsReporter.report(
|
||||
templateJsInteropDartClassExtendsJSClass.withArguments(
|
||||
cls.name, superclass.name),
|
||||
cls.fileOffset,
|
||||
cls.name.length,
|
||||
cls.fileUri);
|
||||
node.name, superclass.name),
|
||||
node.fileOffset,
|
||||
node.name.length,
|
||||
node.fileUri);
|
||||
} else if (_classHasStaticInteropAnnotation &&
|
||||
!hasStaticInteropAnnotation(superclass)) {
|
||||
_diagnosticsReporter.report(
|
||||
templateJsInteropStaticInteropWithNonStaticSupertype.withArguments(
|
||||
cls.name, superclass.name),
|
||||
cls.fileOffset,
|
||||
cls.name.length,
|
||||
cls.fileUri);
|
||||
node.name, superclass.name),
|
||||
node.fileOffset,
|
||||
node.name.length,
|
||||
node.fileUri);
|
||||
} else if (!_classHasStaticInteropAnnotation &&
|
||||
hasStaticInteropAnnotation(superclass)) {
|
||||
_diagnosticsReporter.report(
|
||||
templateJsInteropNonStaticWithStaticInteropSupertype.withArguments(
|
||||
cls.name, superclass.name),
|
||||
cls.fileOffset,
|
||||
cls.name.length,
|
||||
cls.fileUri);
|
||||
node.name, superclass.name),
|
||||
node.fileOffset,
|
||||
node.name.length,
|
||||
node.fileUri);
|
||||
}
|
||||
}
|
||||
if (_classHasStaticInteropAnnotation) {
|
||||
if (!_classHasJSAnnotation) {
|
||||
_diagnosticsReporter.report(
|
||||
templateJsInteropStaticInteropNoJSAnnotation
|
||||
.withArguments(cls.name),
|
||||
cls.fileOffset,
|
||||
cls.name.length,
|
||||
cls.fileUri);
|
||||
.withArguments(node.name),
|
||||
node.fileOffset,
|
||||
node.name.length,
|
||||
node.fileUri);
|
||||
}
|
||||
// Validate that superinterfaces are all annotated as static as well. Note
|
||||
// that mixins are already disallowed and therefore are not checked here.
|
||||
for (var supertype in cls.implementedTypes) {
|
||||
for (var supertype in node.implementedTypes) {
|
||||
if (!hasStaticInteropAnnotation(supertype.classNode)) {
|
||||
_diagnosticsReporter.report(
|
||||
templateJsInteropStaticInteropWithNonStaticSupertype
|
||||
.withArguments(cls.name, supertype.classNode.name),
|
||||
cls.fileOffset,
|
||||
cls.name.length,
|
||||
cls.fileUri);
|
||||
.withArguments(node.name, supertype.classNode.name),
|
||||
node.fileOffset,
|
||||
node.name.length,
|
||||
node.fileUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
// The converse of the above. If the class is not marked as static, it
|
||||
// should not implement a class that is.
|
||||
if (!_classHasStaticInteropAnnotation) {
|
||||
for (var supertype in cls.implementedTypes) {
|
||||
for (var supertype in node.implementedTypes) {
|
||||
if (hasStaticInteropAnnotation(supertype.classNode)) {
|
||||
_diagnosticsReporter.report(
|
||||
templateJsInteropNonStaticWithStaticInteropSupertype
|
||||
.withArguments(cls.name, supertype.classNode.name),
|
||||
cls.fileOffset,
|
||||
cls.name.length,
|
||||
cls.fileUri);
|
||||
.withArguments(node.name, supertype.classNode.name),
|
||||
node.fileOffset,
|
||||
node.name.length,
|
||||
node.fileUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Since this is a breaking check, it is language-versioned.
|
||||
if (cls.enclosingLibrary.languageVersion >= Version(2, 13) &&
|
||||
if (node.enclosingLibrary.languageVersion >= Version(2, 13) &&
|
||||
_classHasJSAnnotation &&
|
||||
!_classHasStaticInteropAnnotation &&
|
||||
!_classHasAnonymousAnnotation &&
|
||||
_libraryIsGlobalNamespace) {
|
||||
var jsClass = getJSName(cls);
|
||||
var jsClass = getJSName(node);
|
||||
if (jsClass.isEmpty) {
|
||||
// No rename, take the name of the class directly.
|
||||
jsClass = cls.name;
|
||||
jsClass = node.name;
|
||||
} else {
|
||||
// Remove any global prefixes. Regex here is greedy and will only return
|
||||
// a value for `className` that doesn't start with 'self.' or 'window.'.
|
||||
var classRegexp = new RegExp(r'^((self|window)\.)*(?<className>.*)$');
|
||||
var classRegexp = RegExp(r'^((self|window)\.)*(?<className>.*)$');
|
||||
var matches = classRegexp.allMatches(jsClass);
|
||||
jsClass = matches.first.namedGroup('className')!;
|
||||
}
|
||||
|
@ -255,29 +257,29 @@ class JsInteropChecks extends RecursiveVisitor {
|
|||
if (nativeClass != null) {
|
||||
_diagnosticsReporter.report(
|
||||
templateJsInteropNativeClassInAnnotation.withArguments(
|
||||
cls.name,
|
||||
node.name,
|
||||
nativeClass.name,
|
||||
nativeClass.enclosingLibrary.importUri.toString()),
|
||||
cls.fileOffset,
|
||||
cls.name.length,
|
||||
cls.fileUri);
|
||||
node.fileOffset,
|
||||
node.name.length,
|
||||
node.fileUri);
|
||||
}
|
||||
}
|
||||
super.visitClass(cls);
|
||||
super.visitClass(node);
|
||||
// Validate `@JSExport` usage after so we know if the members have the
|
||||
// annotation.
|
||||
exportChecker.visitClass(cls);
|
||||
exportChecker.visitClass(node);
|
||||
_classHasAnonymousAnnotation = false;
|
||||
_classHasJSAnnotation = false;
|
||||
}
|
||||
|
||||
@override
|
||||
void visitLibrary(Library lib) {
|
||||
_libraryHasJSAnnotation = hasJSInteropAnnotation(lib);
|
||||
void visitLibrary(Library node) {
|
||||
_libraryHasJSAnnotation = hasJSInteropAnnotation(node);
|
||||
_libraryIsGlobalNamespace = false;
|
||||
if (_libraryHasJSAnnotation) {
|
||||
var libraryAnnotation = getJSName(lib);
|
||||
var globalRegexp = new RegExp(r'^(self|window)(\.(self|window))*$');
|
||||
var libraryAnnotation = getJSName(node);
|
||||
var globalRegexp = RegExp(r'^(self|window)(\.(self|window))*$');
|
||||
if (libraryAnnotation.isEmpty ||
|
||||
globalRegexp.hasMatch(libraryAnnotation)) {
|
||||
_libraryIsGlobalNamespace = true;
|
||||
|
@ -285,52 +287,43 @@ class JsInteropChecks extends RecursiveVisitor {
|
|||
} else {
|
||||
_libraryIsGlobalNamespace = true;
|
||||
}
|
||||
super.visitLibrary(lib);
|
||||
exportChecker.visitLibrary(lib);
|
||||
super.visitLibrary(node);
|
||||
exportChecker.visitLibrary(node);
|
||||
_libraryIsGlobalNamespace = false;
|
||||
_libraryHasJSAnnotation = false;
|
||||
_libraryExtensionsIndex = null;
|
||||
}
|
||||
|
||||
@override
|
||||
void visitProcedure(Procedure procedure) {
|
||||
void visitProcedure(Procedure node) {
|
||||
// TODO(joshualitt): Add a check that only supported operators are allowed
|
||||
// in external extension members / inline classes.
|
||||
_checkInstanceMemberJSAnnotation(procedure);
|
||||
if (_classHasJSAnnotation && !procedure.isExternal) {
|
||||
_checkInstanceMemberJSAnnotation(node);
|
||||
if (_classHasJSAnnotation && !node.isExternal) {
|
||||
// If not one of few exceptions, member is not allowed to exclude
|
||||
// `external` inside of a JS interop class.
|
||||
if (!(procedure.isAbstract ||
|
||||
procedure.isFactory ||
|
||||
procedure.isStatic)) {
|
||||
_diagnosticsReporter.report(
|
||||
messageJsInteropNonExternalMember,
|
||||
procedure.fileOffset,
|
||||
procedure.name.text.length,
|
||||
procedure.fileUri);
|
||||
if (!(node.isAbstract || node.isFactory || node.isStatic)) {
|
||||
_diagnosticsReporter.report(messageJsInteropNonExternalMember,
|
||||
node.fileOffset, node.name.text.length, node.fileUri);
|
||||
}
|
||||
}
|
||||
|
||||
if (!_isJSInteropMember(procedure)) {
|
||||
_checkDisallowedExternal(procedure);
|
||||
if (!_isJSInteropMember(node)) {
|
||||
_checkDisallowedExternal(node);
|
||||
} else {
|
||||
// Check JS interop indexing.
|
||||
if (!procedure.isStatic && procedure.kind == ProcedureKind.Operator) {
|
||||
_diagnosticsReporter.report(
|
||||
messageJsInteropOperatorsNotSupported,
|
||||
procedure.fileOffset,
|
||||
procedure.name.text.length,
|
||||
procedure.fileUri);
|
||||
if (!node.isStatic && node.kind == ProcedureKind.Operator) {
|
||||
_diagnosticsReporter.report(messageJsInteropOperatorsNotSupported,
|
||||
node.fileOffset, node.name.text.length, node.fileUri);
|
||||
}
|
||||
|
||||
// Check JS Interop positional and named parameters.
|
||||
var isAnonymousFactory =
|
||||
_classHasAnonymousAnnotation && procedure.isFactory;
|
||||
var isAnonymousFactory = _classHasAnonymousAnnotation && node.isFactory;
|
||||
if (isAnonymousFactory) {
|
||||
// ignore: unnecessary_null_comparison
|
||||
if (procedure.function != null &&
|
||||
procedure.function.positionalParameters.isNotEmpty) {
|
||||
var firstPositionalParam = procedure.function.positionalParameters[0];
|
||||
if (node.function != null &&
|
||||
node.function.positionalParameters.isNotEmpty) {
|
||||
var firstPositionalParam = node.function.positionalParameters[0];
|
||||
_diagnosticsReporter.report(
|
||||
messageJsInteropAnonymousFactoryPositionalParameters,
|
||||
firstPositionalParam.fileOffset,
|
||||
|
@ -340,97 +333,94 @@ class JsInteropChecks extends RecursiveVisitor {
|
|||
} else {
|
||||
// Only factory constructors for anonymous classes are allowed to have
|
||||
// named parameters.
|
||||
_checkNoNamedParameters(procedure.function);
|
||||
_checkNoNamedParameters(node.function);
|
||||
}
|
||||
|
||||
// JS static methods cannot use a JS name with dots.
|
||||
if (procedure.isStatic && procedure.enclosingClass != null) {
|
||||
String name = getJSName(procedure);
|
||||
if (node.isStatic && node.enclosingClass != null) {
|
||||
String name = getJSName(node);
|
||||
if (name.contains('.')) {
|
||||
_diagnosticsReporter.report(
|
||||
messageJsInteropInvalidStaticClassMemberName,
|
||||
procedure.fileOffset,
|
||||
procedure.name.text.length,
|
||||
procedure.fileUri);
|
||||
node.fileOffset,
|
||||
node.name.text.length,
|
||||
node.fileUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_classHasStaticInteropAnnotation &&
|
||||
procedure.isInstanceMember &&
|
||||
!procedure.isFactory &&
|
||||
!procedure.isSynthetic) {
|
||||
node.isInstanceMember &&
|
||||
!node.isFactory &&
|
||||
!node.isSynthetic) {
|
||||
_diagnosticsReporter.report(
|
||||
templateJsInteropStaticInteropWithInstanceMembers
|
||||
.withArguments(procedure.enclosingClass!.name),
|
||||
procedure.fileOffset,
|
||||
procedure.name.text.length,
|
||||
procedure.fileUri);
|
||||
.withArguments(node.enclosingClass!.name),
|
||||
node.fileOffset,
|
||||
node.name.text.length,
|
||||
node.fileUri);
|
||||
}
|
||||
|
||||
if (procedure.isExternal &&
|
||||
procedure.isExtensionMember &&
|
||||
_isStaticInteropExtensionMember(procedure) &&
|
||||
!_isAllowedCustomStaticInteropImplementation(procedure)) {
|
||||
if (node.isExternal &&
|
||||
node.isExtensionMember &&
|
||||
_isStaticInteropExtensionMember(node) &&
|
||||
!_isAllowedCustomStaticInteropImplementation(node)) {
|
||||
// If the extension has type parameters of its own, it copies those type
|
||||
// parameters to the procedure's type parameters (in the front) as well.
|
||||
// Ignore these for the analysis.
|
||||
var extensionTypeParams =
|
||||
_libraryExtensionsIndex![procedure.reference]!.typeParameters;
|
||||
var procedureTypeParams = List.from(procedure.function.typeParameters);
|
||||
_libraryExtensionsIndex![node.reference]!.typeParameters;
|
||||
var procedureTypeParams = List.from(node.function.typeParameters);
|
||||
procedureTypeParams.removeRange(0, extensionTypeParams.length);
|
||||
if (procedureTypeParams.isNotEmpty ||
|
||||
_typeParameterVisitor.usesTypeParameters(procedure)) {
|
||||
_typeParameterVisitor.usesTypeParameters(node)) {
|
||||
_diagnosticsReporter.report(
|
||||
messageJsInteropStaticInteropExternalExtensionMembersWithTypeParameters,
|
||||
procedure.fileOffset,
|
||||
procedure.name.text.length,
|
||||
procedure.fileUri);
|
||||
node.fileOffset,
|
||||
node.name.text.length,
|
||||
node.fileUri);
|
||||
}
|
||||
}
|
||||
_inTearoff = isTearOffLowering(procedure);
|
||||
super.visitProcedure(procedure);
|
||||
_inTearoff = isTearOffLowering(node);
|
||||
super.visitProcedure(node);
|
||||
_inTearoff = false;
|
||||
}
|
||||
|
||||
@override
|
||||
void visitField(Field field) {
|
||||
if (_classHasStaticInteropAnnotation && field.isInstanceMember) {
|
||||
void visitField(Field node) {
|
||||
if (_classHasStaticInteropAnnotation && node.isInstanceMember) {
|
||||
_diagnosticsReporter.report(
|
||||
templateJsInteropStaticInteropWithInstanceMembers
|
||||
.withArguments(field.enclosingClass!.name),
|
||||
field.fileOffset,
|
||||
field.name.text.length,
|
||||
field.fileUri);
|
||||
.withArguments(node.enclosingClass!.name),
|
||||
node.fileOffset,
|
||||
node.name.text.length,
|
||||
node.fileUri);
|
||||
}
|
||||
super.visitField(field);
|
||||
super.visitField(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitConstructor(Constructor constructor) {
|
||||
_checkInstanceMemberJSAnnotation(constructor);
|
||||
if (!constructor.isSynthetic) {
|
||||
if (_classHasJSAnnotation && !constructor.isExternal) {
|
||||
void visitConstructor(Constructor node) {
|
||||
_checkInstanceMemberJSAnnotation(node);
|
||||
if (!node.isSynthetic) {
|
||||
if (_classHasJSAnnotation && !node.isExternal) {
|
||||
// Non-synthetic constructors must be annotated with `external`.
|
||||
_diagnosticsReporter.report(
|
||||
messageJsInteropNonExternalConstructor,
|
||||
constructor.fileOffset,
|
||||
constructor.name.text.length,
|
||||
constructor.fileUri);
|
||||
_diagnosticsReporter.report(messageJsInteropNonExternalConstructor,
|
||||
node.fileOffset, node.name.text.length, node.fileUri);
|
||||
}
|
||||
if (_classHasStaticInteropAnnotation) {
|
||||
_diagnosticsReporter.report(
|
||||
messageJsInteropStaticInteropGenerativeConstructor,
|
||||
constructor.fileOffset,
|
||||
constructor.name.text.length,
|
||||
constructor.fileUri);
|
||||
node.fileOffset,
|
||||
node.name.text.length,
|
||||
node.fileUri);
|
||||
}
|
||||
}
|
||||
|
||||
if (!_isJSInteropMember(constructor)) {
|
||||
_checkDisallowedExternal(constructor);
|
||||
if (!_isJSInteropMember(node)) {
|
||||
_checkDisallowedExternal(node);
|
||||
} else {
|
||||
_checkNoNamedParameters(constructor.function);
|
||||
_checkNoNamedParameters(node.function);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -619,9 +609,11 @@ class JsInteropChecks extends RecursiveVisitor {
|
|||
assert(member.isExtensionMember);
|
||||
if (_libraryExtensionsIndex == null) {
|
||||
_libraryExtensionsIndex = {};
|
||||
member.enclosingLibrary.extensions.forEach((extension) =>
|
||||
extension.members.forEach((memberDescriptor) =>
|
||||
_libraryExtensionsIndex![memberDescriptor.member] = extension));
|
||||
for (var extension in member.enclosingLibrary.extensions) {
|
||||
for (var memberDescriptor in extension.members) {
|
||||
_libraryExtensionsIndex![memberDescriptor.member] = extension;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var onType = _libraryExtensionsIndex![member.reference]!.onType;
|
||||
|
|
|
@ -185,14 +185,14 @@ List<String> stringAnnotationValues(Expression node) {
|
|||
var value = constant.fieldValues.values.elementAt(0);
|
||||
if (value is StringConstant) values.addAll(value.value.split(','));
|
||||
} else if (argLength > 1) {
|
||||
throw new ArgumentError('Method expects annotation with at most one '
|
||||
throw ArgumentError('Method expects annotation with at most one '
|
||||
'positional argument: $node.');
|
||||
}
|
||||
}
|
||||
} else if (node is ConstructorInvocation) {
|
||||
var argLength = node.arguments.positional.length;
|
||||
if (argLength > 1 || node.arguments.named.isNotEmpty) {
|
||||
throw new ArgumentError('Method expects annotation with at most one '
|
||||
throw ArgumentError('Method expects annotation with at most one '
|
||||
'positional argument: $node.');
|
||||
} else if (argLength == 1) {
|
||||
var value = node.arguments.positional[0];
|
||||
|
@ -233,7 +233,7 @@ List<Library>? calculateTransitiveImportsOfJsInteropIfUsed(
|
|||
if (jsInteropLibrary == null) return null;
|
||||
|
||||
kernel_graph.LibraryGraph graph =
|
||||
new kernel_graph.LibraryGraph(component.libraries);
|
||||
kernel_graph.LibraryGraph(component.libraries);
|
||||
Set<Library> result =
|
||||
kernel_graph.calculateTransitiveDependenciesOf(graph, {jsInteropLibrary});
|
||||
return result.toList();
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
// 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:kernel/ast.dart';
|
||||
import 'package:kernel/target/targets.dart';
|
||||
// ignore_for_file: implementation_imports
|
||||
|
||||
import 'package:_fe_analyzer_shared/src/messages/codes.dart'
|
||||
show
|
||||
Message,
|
||||
|
@ -13,18 +13,20 @@ import 'package:_fe_analyzer_shared/src/messages/codes.dart'
|
|||
templateJsInteropExportMemberCollision,
|
||||
templateJsInteropExportNoExportableMembers;
|
||||
import 'package:_js_interop_checks/src/js_interop.dart' as js_interop;
|
||||
import 'package:kernel/ast.dart';
|
||||
import 'package:kernel/target/targets.dart';
|
||||
|
||||
enum ExportStatus {
|
||||
EXPORT_ERROR,
|
||||
EXPORTABLE,
|
||||
NON_EXPORTABLE,
|
||||
exportError,
|
||||
exportable,
|
||||
nonExportable,
|
||||
}
|
||||
|
||||
class _GetSet {
|
||||
class GetSet {
|
||||
Member? getter;
|
||||
Member? setter;
|
||||
|
||||
_GetSet(this.getter, this.setter);
|
||||
GetSet(this.getter, this.setter);
|
||||
}
|
||||
|
||||
class ExportChecker {
|
||||
|
@ -44,7 +46,7 @@ class ExportChecker {
|
|||
///
|
||||
/// [exports] should be a set of members from the [exportClassToMemberMap]. If
|
||||
/// missing a getter and/or setter, the corresponding field will be `null`.
|
||||
_GetSet getGetterSetter(Set<Member> exports) {
|
||||
GetSet getGetterSetter(Set<Member> exports) {
|
||||
assert(exports.isNotEmpty && exports.length <= 2);
|
||||
Member? getter;
|
||||
Member? setter;
|
||||
|
@ -70,7 +72,7 @@ class ExportChecker {
|
|||
}
|
||||
}
|
||||
|
||||
return _GetSet(getter, setter);
|
||||
return GetSet(getter, setter);
|
||||
}
|
||||
|
||||
/// Calculates the overrides, including inheritance, for [cls].
|
||||
|
@ -95,10 +97,10 @@ class ExportChecker {
|
|||
]) {
|
||||
var memberName = member.name.text;
|
||||
if (member is Procedure && member.isSetter) {
|
||||
memberMap[memberName + '='] = member;
|
||||
memberMap['$memberName='] = member;
|
||||
} else {
|
||||
if (member is Field && !member.isFinal) {
|
||||
memberMap[memberName + '='] = member;
|
||||
memberMap['$memberName='] = member;
|
||||
}
|
||||
memberMap[memberName] = member;
|
||||
}
|
||||
|
@ -122,8 +124,8 @@ class ExportChecker {
|
|||
// when we visited the members and checked their annotations, there's
|
||||
// nothing to do for this class.
|
||||
if (!classHasJSExport &&
|
||||
exportStatus[cls.reference] != ExportStatus.EXPORTABLE) {
|
||||
exportStatus[cls.reference] = ExportStatus.NON_EXPORTABLE;
|
||||
exportStatus[cls.reference] != ExportStatus.exportable) {
|
||||
exportStatus[cls.reference] = ExportStatus.nonExportable;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -134,7 +136,7 @@ class ExportChecker {
|
|||
cls.fileOffset,
|
||||
cls.name.length,
|
||||
cls.location?.file);
|
||||
exportStatus[cls.reference] = ExportStatus.EXPORT_ERROR;
|
||||
exportStatus[cls.reference] = ExportStatus.exportError;
|
||||
}
|
||||
|
||||
_collectOverrides(cls);
|
||||
|
@ -195,7 +197,7 @@ class ExportChecker {
|
|||
cls.fileOffset,
|
||||
cls.name.length,
|
||||
cls.location?.file);
|
||||
exportStatus[cls.reference] = ExportStatus.EXPORT_ERROR;
|
||||
exportStatus[cls.reference] = ExportStatus.exportError;
|
||||
}
|
||||
|
||||
if (exports.isEmpty) {
|
||||
|
@ -204,11 +206,11 @@ class ExportChecker {
|
|||
cls.fileOffset,
|
||||
cls.name.length,
|
||||
cls.location?.file);
|
||||
exportStatus[cls.reference] = ExportStatus.EXPORT_ERROR;
|
||||
exportStatus[cls.reference] = ExportStatus.exportError;
|
||||
}
|
||||
|
||||
exportClassToMemberMap[cls.reference] = exports;
|
||||
exportStatus[cls.reference] ??= ExportStatus.EXPORTABLE;
|
||||
exportStatus[cls.reference] ??= ExportStatus.exportable;
|
||||
}
|
||||
|
||||
/// Check that the [member] can be exportable if it has an annotation, and if
|
||||
|
@ -225,12 +227,12 @@ class ExportChecker {
|
|||
member.name.text.length,
|
||||
member.location?.file);
|
||||
if (cls != null) {
|
||||
exportStatus[cls.reference] = ExportStatus.EXPORT_ERROR;
|
||||
exportStatus[cls.reference] = ExportStatus.exportError;
|
||||
}
|
||||
} else {
|
||||
// Mark as exportable so we know that the class has an exportable member
|
||||
// when we process the class later.
|
||||
if (cls != null) exportStatus[cls.reference] = ExportStatus.EXPORTABLE;
|
||||
if (cls != null) exportStatus[cls.reference] = ExportStatus.exportable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -249,34 +251,34 @@ class ExportChecker {
|
|||
}
|
||||
|
||||
extension ExtensionMemberDescriptorExtension on ExtensionMemberDescriptor {
|
||||
bool get isGetter => this.kind == ExtensionMemberKind.Getter;
|
||||
bool get isSetter => this.kind == ExtensionMemberKind.Setter;
|
||||
bool get isMethod => this.kind == ExtensionMemberKind.Method;
|
||||
bool get isGetter => kind == ExtensionMemberKind.Getter;
|
||||
bool get isSetter => kind == ExtensionMemberKind.Setter;
|
||||
bool get isMethod => kind == ExtensionMemberKind.Method;
|
||||
|
||||
bool get isExternal => (this.member.asProcedure).isExternal;
|
||||
bool get isExternal => (member.asProcedure).isExternal;
|
||||
}
|
||||
|
||||
extension ProcedureExtension on Procedure {
|
||||
// We only care about concrete instance procedures.
|
||||
bool get exportable =>
|
||||
!this.isAbstract &&
|
||||
!this.isStatic &&
|
||||
!this.isExtensionMember &&
|
||||
!this.isFactory &&
|
||||
!this.isExternal &&
|
||||
this.kind != ProcedureKind.Operator;
|
||||
!isAbstract &&
|
||||
!isStatic &&
|
||||
!isExtensionMember &&
|
||||
!isFactory &&
|
||||
!isExternal &&
|
||||
kind != ProcedureKind.Operator;
|
||||
}
|
||||
|
||||
extension FieldExtension on Field {
|
||||
// We only care about concrete instance fields.
|
||||
bool get exportable => !this.isAbstract && !this.isStatic && !this.isExternal;
|
||||
bool get exportable => !isAbstract && !isStatic && !isExternal;
|
||||
}
|
||||
|
||||
extension MemberExtension on Member {
|
||||
// Get the property name that this member will be exported as.
|
||||
String get exportPropertyName {
|
||||
var rename = js_interop.getJSExportName(this);
|
||||
return rename.isEmpty ? this.name.text : rename;
|
||||
return rename.isEmpty ? name.text : rename;
|
||||
}
|
||||
|
||||
bool get exportable =>
|
||||
|
@ -297,8 +299,7 @@ extension MemberExtension on Member {
|
|||
this is Field || (this is Procedure && (this as Procedure).isGetter);
|
||||
|
||||
bool get isSetter =>
|
||||
this.isNonFinalField ||
|
||||
(this is Procedure && (this as Procedure).isSetter);
|
||||
isNonFinalField || (this is Procedure && (this as Procedure).isSetter);
|
||||
|
||||
bool get isMethod =>
|
||||
this is Procedure && (this as Procedure).kind == ProcedureKind.Method;
|
||||
|
|
|
@ -2,6 +2,14 @@
|
|||
// 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.
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
|
||||
import 'package:_fe_analyzer_shared/src/messages/codes.dart'
|
||||
show
|
||||
Message,
|
||||
LocatedMessage,
|
||||
templateJsInteropExportClassNotMarkedExportable;
|
||||
import 'package:_js_interop_checks/src/js_interop.dart' as js_interop;
|
||||
import 'package:front_end/src/fasta/fasta_codes.dart'
|
||||
show
|
||||
templateJsInteropExportInvalidInteropTypeArgument,
|
||||
|
@ -9,12 +17,6 @@ import 'package:front_end/src/fasta/fasta_codes.dart'
|
|||
import 'package:kernel/ast.dart';
|
||||
import 'package:kernel/target/targets.dart';
|
||||
import 'package:kernel/type_environment.dart';
|
||||
import 'package:_fe_analyzer_shared/src/messages/codes.dart'
|
||||
show
|
||||
Message,
|
||||
LocatedMessage,
|
||||
templateJsInteropExportClassNotMarkedExportable;
|
||||
import 'package:_js_interop_checks/src/js_interop.dart' as js_interop;
|
||||
|
||||
import 'export_checker.dart';
|
||||
import 'static_interop_mock_validator.dart';
|
||||
|
@ -126,13 +128,16 @@ class ExportCreator extends Transformer {
|
|||
// This occurs when we deserialize previously compiled modules. Those
|
||||
// modules may contain export classes, so we need to revisit the classes
|
||||
// in those previously compiled modules if they are used.
|
||||
dartClass.procedures
|
||||
.forEach((member) => _exportChecker.visitMember(member));
|
||||
dartClass.fields.forEach((member) => _exportChecker.visitMember(member));
|
||||
for (var member in dartClass.procedures) {
|
||||
_exportChecker.visitMember(member);
|
||||
}
|
||||
for (var member in dartClass.fields) {
|
||||
_exportChecker.visitMember(member);
|
||||
}
|
||||
_exportChecker.visitClass(dartClass);
|
||||
}
|
||||
var exportStatus = _exportChecker.exportStatus[dartClass.reference];
|
||||
if (exportStatus == ExportStatus.NON_EXPORTABLE) {
|
||||
if (exportStatus == ExportStatus.nonExportable) {
|
||||
_diagnosticReporter.report(
|
||||
templateJsInteropExportClassNotMarkedExportable
|
||||
.withArguments(dartClass.name),
|
||||
|
@ -141,7 +146,7 @@ class ExportCreator extends Transformer {
|
|||
node.location?.file);
|
||||
return false;
|
||||
}
|
||||
return exportStatus == ExportStatus.EXPORTABLE;
|
||||
return exportStatus == ExportStatus.exportable;
|
||||
}
|
||||
|
||||
/// Create the object literal using the export map that was computed from the
|
||||
|
|
|
@ -97,15 +97,15 @@ class JsUtilOptimizer extends Transformer {
|
|||
_listEmptyFactory =
|
||||
_coreTypes.index.getProcedure('dart:core', 'List', 'empty'),
|
||||
_staticTypeContext = StatefulStaticTypeContext.stacked(
|
||||
TypeEnvironment(_coreTypes, hierarchy)) {}
|
||||
TypeEnvironment(_coreTypes, hierarchy));
|
||||
|
||||
@override
|
||||
visitLibrary(Library lib) {
|
||||
_inlineExtensionIndex = InlineExtensionIndex(lib);
|
||||
_staticTypeContext.enterLibrary(lib);
|
||||
lib.transformChildren(this);
|
||||
_staticTypeContext.leaveLibrary(lib);
|
||||
return lib;
|
||||
visitLibrary(Library node) {
|
||||
_inlineExtensionIndex = InlineExtensionIndex(node);
|
||||
_staticTypeContext.enterLibrary(node);
|
||||
node.transformChildren(this);
|
||||
_staticTypeContext.leaveLibrary(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -660,26 +660,27 @@ class InlineExtensionIndex {
|
|||
if (_extensionMemberIndex != null) return;
|
||||
_extensionMemberIndex = {};
|
||||
_shouldTrustType = {};
|
||||
_library.extensions
|
||||
.forEach((extension) => extension.members.forEach((descriptor) {
|
||||
var reference = descriptor.member;
|
||||
var onType = extension.onType;
|
||||
Annotatable? cls;
|
||||
if (onType is InterfaceType) {
|
||||
cls = onType.classNode;
|
||||
// For now, `@trustTypes` can only be used on non-inline
|
||||
// classes.
|
||||
if (hasTrustTypesAnnotation(cls)) {
|
||||
_shouldTrustType.add(reference);
|
||||
}
|
||||
} else if (onType is InlineType) {
|
||||
cls = onType.inlineClass;
|
||||
}
|
||||
if (cls == null) return;
|
||||
if (hasJSInteropAnnotation(cls) || hasNativeAnnotation(cls)) {
|
||||
_extensionMemberIndex![reference] = descriptor;
|
||||
}
|
||||
}));
|
||||
for (var extension in _library.extensions) {
|
||||
for (var descriptor in extension.members) {
|
||||
var reference = descriptor.member;
|
||||
var onType = extension.onType;
|
||||
Annotatable? cls;
|
||||
if (onType is InterfaceType) {
|
||||
cls = onType.classNode;
|
||||
// For now, `@trustTypes` can only be used on non-inline
|
||||
// classes.
|
||||
if (hasTrustTypesAnnotation(cls)) {
|
||||
_shouldTrustType.add(reference);
|
||||
}
|
||||
} else if (onType is InlineType) {
|
||||
cls = onType.inlineClass;
|
||||
}
|
||||
if (cls == null) continue;
|
||||
if (hasJSInteropAnnotation(cls) || hasNativeAnnotation(cls)) {
|
||||
_extensionMemberIndex![reference] = descriptor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ExtensionMemberDescriptor? getExtensionDescriptor(Reference reference) {
|
||||
|
@ -700,15 +701,15 @@ class InlineExtensionIndex {
|
|||
if (_inlineMemberIndex != null) return;
|
||||
_inlineMemberIndex = {};
|
||||
_inlineClassIndex = {};
|
||||
_library.inlineClasses.forEach((inlineClass) {
|
||||
for (var inlineClass in _library.inlineClasses) {
|
||||
if (hasJSInteropAnnotation(inlineClass)) {
|
||||
inlineClass.members.forEach((descriptor) {
|
||||
for (var descriptor in inlineClass.members) {
|
||||
var reference = descriptor.member;
|
||||
_inlineMemberIndex![reference] = descriptor;
|
||||
_inlineClassIndex[reference] = inlineClass;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
InlineClassMemberDescriptor? getInlineDescriptor(Reference reference) {
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
// 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.
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
|
||||
import 'package:_js_interop_checks/src/js_interop.dart';
|
||||
import 'package:kernel/ast.dart';
|
||||
import 'package:kernel/clone.dart';
|
||||
import 'package:kernel/core_types.dart';
|
||||
|
@ -9,7 +12,6 @@ import 'package:kernel/kernel.dart';
|
|||
import 'package:kernel/reference_from_index.dart';
|
||||
import 'package:kernel/src/constant_replacer.dart';
|
||||
import 'package:kernel/src/replacement_visitor.dart';
|
||||
import 'package:_js_interop_checks/src/js_interop.dart';
|
||||
|
||||
class _TypeSubstitutor extends ReplacementVisitor {
|
||||
final Class _javaScriptObject;
|
||||
|
@ -36,7 +38,7 @@ class StaticInteropClassEraser extends Transformer {
|
|||
// Visiting core libraries that don't contain `@staticInterop` adds overhead.
|
||||
// To avoid this, we use an allowlist that contains libraries that we know use
|
||||
// `@staticInterop`.
|
||||
late Set<String> _erasableCoreLibraries = {'ui', '_engine'};
|
||||
late final Set<String> _erasableCoreLibraries = {'ui', '_engine'};
|
||||
|
||||
StaticInteropClassEraser(CoreTypes coreTypes, this.referenceFromIndex,
|
||||
{String libraryForJavaScriptObject = 'dart:_interceptors',
|
||||
|
@ -218,9 +220,9 @@ class StaticInteropClassEraser extends Transformer {
|
|||
}
|
||||
|
||||
@override
|
||||
DartType visitDartType(DartType type) {
|
||||
var substitutedType = _getSubstitutedType(type);
|
||||
return substitutedType != null ? substitutedType : type;
|
||||
DartType visitDartType(DartType node) {
|
||||
var substitutedType = _getSubstitutedType(node);
|
||||
return substitutedType ?? node;
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -2,11 +2,8 @@
|
|||
// 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:front_end/src/fasta/fasta_codes.dart'
|
||||
show templateJsInteropStaticInteropMockNotStaticInteropType;
|
||||
import 'package:kernel/ast.dart';
|
||||
import 'package:kernel/target/targets.dart';
|
||||
import 'package:kernel/type_environment.dart';
|
||||
// ignore_for_file: implementation_imports
|
||||
|
||||
import 'package:_fe_analyzer_shared/src/messages/codes.dart'
|
||||
show
|
||||
Message,
|
||||
|
@ -14,6 +11,11 @@ import 'package:_fe_analyzer_shared/src/messages/codes.dart'
|
|||
templateJsInteropStaticInteropMockMissingGetterOrSetter,
|
||||
templateJsInteropStaticInteropMockMissingImplements;
|
||||
import 'package:_js_interop_checks/src/js_interop.dart' as js_interop;
|
||||
import 'package:front_end/src/fasta/fasta_codes.dart'
|
||||
show templateJsInteropStaticInteropMockNotStaticInteropType;
|
||||
import 'package:kernel/ast.dart';
|
||||
import 'package:kernel/target/targets.dart';
|
||||
import 'package:kernel/type_environment.dart';
|
||||
|
||||
import 'export_checker.dart';
|
||||
|
||||
|
@ -289,9 +291,9 @@ class StaticInteropMockValidator {
|
|||
}
|
||||
}
|
||||
}
|
||||
cls.supers.forEach((Supertype supertype) {
|
||||
for (var supertype in cls.supers) {
|
||||
getAllDescriptors(supertype.classNode);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue