Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 4ca78ff

Browse files
author
Dart CI
committed
Version 2.16.0-100.0.dev
Merge commit '08aeef030324baae1e01da44fb3e5713124edd3c' into 'dev'
2 parents 6af99b7 + 08aeef0 commit 4ca78ff

File tree

88 files changed

+2472
-1342
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

88 files changed

+2472
-1342
lines changed

pkg/_js_interop_checks/lib/js_interop_checks.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,8 @@ class JsInteropChecks extends RecursiveVisitor {
252252

253253
if (_classHasStaticInteropAnnotation &&
254254
procedure.isInstanceMember &&
255-
!procedure.isFactory) {
255+
!procedure.isFactory &&
256+
!procedure.isSynthetic) {
256257
_diagnosticsReporter.report(
257258
templateJsInteropStaticInteropWithInstanceMembers
258259
.withArguments(procedure.enclosingClass!.name),
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:kernel/ast.dart';
6+
import 'package:kernel/clone.dart';
7+
import 'package:kernel/core_types.dart';
8+
import 'package:kernel/kernel.dart';
9+
import 'package:kernel/src/replacement_visitor.dart';
10+
import 'package:_js_interop_checks/src/js_interop.dart';
11+
12+
class _TypeSubstitutor extends ReplacementVisitor {
13+
final Class _javaScriptObject;
14+
_TypeSubstitutor(this._javaScriptObject);
15+
16+
@override
17+
DartType? visitInterfaceType(InterfaceType type, int variance) {
18+
if (hasStaticInteropAnnotation(type.classNode)) {
19+
return InterfaceType(_javaScriptObject, type.declaredNullability);
20+
}
21+
return super.visitInterfaceType(type, variance);
22+
}
23+
}
24+
25+
/// Erases usage of `@JS` classes that are annotated with `@staticInterop` in
26+
/// favor of `JavaScriptObject`.
27+
class StaticInteropClassEraser extends Transformer {
28+
final Class _javaScriptObject;
29+
final CloneVisitorNotMembers _cloner = CloneVisitorNotMembers();
30+
late final _TypeSubstitutor _typeSubstitutor;
31+
32+
StaticInteropClassEraser(CoreTypes coreTypes)
33+
: _javaScriptObject =
34+
coreTypes.index.getClass('dart:_interceptors', 'JavaScriptObject') {
35+
_typeSubstitutor = _TypeSubstitutor(_javaScriptObject);
36+
}
37+
38+
String _factoryStubName(Procedure factoryTarget) =>
39+
'${factoryTarget.name}|staticInteropFactoryStub';
40+
41+
/// Either finds or creates a static method stub to replace factories with a
42+
/// body in a static interop class.
43+
///
44+
/// Modifies [factoryTarget]'s enclosing class to include the new method.
45+
Procedure _findOrCreateFactoryStub(Procedure factoryTarget) {
46+
assert(factoryTarget.isFactory);
47+
var factoryClass = factoryTarget.enclosingClass!;
48+
assert(hasStaticInteropAnnotation(factoryClass));
49+
var stubName = _factoryStubName(factoryTarget);
50+
var stubs = factoryClass.procedures
51+
.where((procedure) => procedure.name.text == stubName);
52+
if (stubs.isEmpty) {
53+
// Note that the return type of the cloned function is transformed.
54+
var functionNode =
55+
super.visitFunctionNode(_cloner.clone(factoryTarget.function))
56+
as FunctionNode;
57+
var staticMethod = Procedure(
58+
Name(stubName), ProcedureKind.Method, functionNode,
59+
isStatic: true, fileUri: factoryTarget.fileUri)
60+
..parent = factoryClass;
61+
factoryClass.procedures.add(staticMethod);
62+
return staticMethod;
63+
} else {
64+
assert(stubs.length == 1);
65+
return stubs.first;
66+
}
67+
}
68+
69+
@override
70+
TreeNode visitConstructor(Constructor node) {
71+
if (hasStaticInteropAnnotation(node.enclosingClass)) {
72+
// Transform children of the constructor node excluding the return type.
73+
var returnType = node.function.returnType;
74+
var newConstructor = super.visitConstructor(node) as Constructor;
75+
newConstructor.function.returnType = returnType;
76+
return newConstructor;
77+
}
78+
return super.visitConstructor(node);
79+
}
80+
81+
@override
82+
TreeNode visitProcedure(Procedure node) {
83+
// Avoid changing the return types of factories, but rather cast the type of
84+
// the invocation.
85+
if (node.isFactory && hasStaticInteropAnnotation(node.enclosingClass!)) {
86+
if (node.function.body != null && !node.isRedirectingFactory) {
87+
// Bodies of factories may undergo transformation, which may result in
88+
// type invariants breaking. For a motivating example, consider:
89+
//
90+
// ```
91+
// factory Foo.fact() => Foo.cons();
92+
// ```
93+
//
94+
// The invocation of `cons` would have its type erased, but then it
95+
// would no longer match the return type of `fact`, whose return type
96+
// shouldn't get erased as it is a factory. Note that this is only an
97+
// issue when the factory has a body that doesn't simply redirect.
98+
//
99+
// In order to circumvent this, we introduce a new static method that
100+
// clones the factory body and has a return type of
101+
// `JavaScriptObject`. Invocations of the factory are turned into
102+
// invocations of the static method. The original factory is still kept
103+
// in order to make modular compilations work.
104+
_findOrCreateFactoryStub(node);
105+
return node;
106+
} else {
107+
// Transform children of the factory node excluding the return type and
108+
// return type of the signature type.
109+
var returnType = node.function.returnType;
110+
var signatureReturnType = node.signatureType?.returnType;
111+
var newProcedure = super.visitProcedure(node) as Procedure;
112+
newProcedure.function.returnType = returnType;
113+
var signatureType = newProcedure.signatureType;
114+
if (signatureType != null && signatureReturnType != null) {
115+
newProcedure.signatureType = FunctionType(
116+
signatureType.positionalParameters,
117+
signatureReturnType,
118+
signatureType.declaredNullability,
119+
namedParameters: signatureType.namedParameters,
120+
typeParameters: signatureType.typeParameters,
121+
requiredParameterCount: signatureType.requiredParameterCount,
122+
typedefType: signatureType.typedefType);
123+
}
124+
return newProcedure;
125+
}
126+
}
127+
return super.visitProcedure(node);
128+
}
129+
130+
@override
131+
TreeNode visitConstructorInvocation(ConstructorInvocation node) {
132+
if (hasStaticInteropAnnotation(node.target.enclosingClass)) {
133+
// Add a cast so that the result gets typed as `JavaScriptObject`.
134+
var newInvocation = super.visitConstructorInvocation(node) as Expression;
135+
return AsExpression(
136+
newInvocation,
137+
InterfaceType(_javaScriptObject,
138+
node.target.function.returnType.declaredNullability))
139+
..fileOffset = newInvocation.fileOffset;
140+
}
141+
return super.visitConstructorInvocation(node);
142+
}
143+
144+
/// Transform static invocations that correspond only to factories of static
145+
/// interop classes.
146+
@override
147+
TreeNode visitStaticInvocation(StaticInvocation node) {
148+
var targetClass = node.target.enclosingClass;
149+
if (node.target.isFactory &&
150+
targetClass != null &&
151+
hasStaticInteropAnnotation(targetClass)) {
152+
var factoryTarget = node.target;
153+
if (factoryTarget.function.body != null &&
154+
!factoryTarget.isRedirectingFactory) {
155+
// Use or create the static method that replaces this factory instead.
156+
// Note that the static method will not have been created yet in the
157+
// case where we visit the factory later. Also note that a cast is not
158+
// needed since the static method already has its type erased.
159+
var args = super.visitArguments(node.arguments) as Arguments;
160+
return StaticInvocation(_findOrCreateFactoryStub(factoryTarget), args,
161+
isConst: node.isConst)
162+
..fileOffset = node.fileOffset;
163+
} else {
164+
// Add a cast so that the result gets typed as `JavaScriptObject`.
165+
var newInvocation = super.visitStaticInvocation(node) as Expression;
166+
return AsExpression(
167+
newInvocation,
168+
InterfaceType(_javaScriptObject,
169+
node.target.function.returnType.declaredNullability))
170+
..fileOffset = newInvocation.fileOffset;
171+
}
172+
}
173+
return super.visitStaticInvocation(node);
174+
}
175+
176+
@override
177+
DartType visitDartType(DartType type) {
178+
// Variance is not a factor in our type transformation here, so just choose
179+
// `unrelated` as a default.
180+
var substitutedType = type.accept1(_typeSubstitutor, Variance.unrelated);
181+
return substitutedType != null ? substitutedType : type;
182+
}
183+
}

pkg/analysis_server/lib/src/utilities/extensions/range_factory.dart

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,15 @@ extension RangeFactoryExtensions on RangeFactory {
6767
}
6868
}
6969

70-
/// Return a source range that covers the given [node] from the start of
71-
/// any leading comment token (excluding any token considered a trailing
72-
/// comment for the previous node) or the start of the node itself if there
73-
/// are none, up until the end of the trailing comment token or the end of the
74-
/// node itself if there are none.
70+
/// Return a source range that covers the given [node] with any leading and
71+
/// trailing comments.
72+
///
73+
/// The range begins at the start of any leading comment token (excluding any
74+
/// token considered a trailing comment for the previous node) or the start
75+
/// of the node itself if there are none.
76+
///
77+
/// The range ends at the end of the trailing comment token or the end of the
78+
/// node itself if there is not one.
7579
SourceRange nodeWithComments(LineInfo lineInfo, AstNode node) {
7680
// If the node is the first thing in the unit, leading comments are treated
7781
// as headers and should never be included in the range.

pkg/analysis_server/test/src/utilities/extensions/range_factory_test.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ abstract class BaseRangeFactoryTest extends AbstractSingleUnitTest {
7070
? testCode.indexOf(endsBefore)
7171
: testCode.indexOf(endsAfter!) + endsAfter.length;
7272

73-
assert(offset > -1);
74-
assert(end > -1);
73+
expect(offset, greaterThanOrEqualTo(0));
74+
expect(end, greaterThanOrEqualTo(0));
7575

7676
return SourceRange(offset, end - offset);
7777
}

pkg/analyzer/lib/dart/ast/ast.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ import 'package:analyzer/dart/ast/syntactic_entity.dart';
3838
import 'package:analyzer/dart/ast/token.dart';
3939
import 'package:analyzer/dart/element/element.dart';
4040
import 'package:analyzer/dart/element/type.dart';
41-
import 'package:analyzer/src/generated/java_engine.dart';
4241
import 'package:analyzer/src/generated/source.dart' show LineInfo, Source;
4342

4443
/// Two or more string literals that are implicitly concatenated because of
@@ -318,7 +317,9 @@ abstract class AstNode implements SyntacticEntity {
318317

319318
/// Return either this node or the most immediate ancestor of this node for
320319
/// which the [predicate] returns `true`, or `null` if there is no such node.
321-
E? thisOrAncestorMatching<E extends AstNode>(Predicate<AstNode> predicate);
320+
E? thisOrAncestorMatching<E extends AstNode>(
321+
bool Function(AstNode) predicate,
322+
);
322323

323324
/// Return either this node or the most immediate ancestor of this node that
324325
/// has the given type, or `null` if there is no such node.

pkg/analyzer/lib/src/dart/ast/ast.dart

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import 'package:analyzer/dart/element/type.dart';
1616
import 'package:analyzer/src/dart/ast/to_source_visitor.dart';
1717
import 'package:analyzer/src/dart/ast/token.dart';
1818
import 'package:analyzer/src/fasta/token_utils.dart' as util show findPrevious;
19-
import 'package:analyzer/src/generated/java_engine.dart';
2019
import 'package:analyzer/src/generated/source.dart' show LineInfo, Source;
2120
import 'package:analyzer/src/generated/utilities_dart.dart';
2221

@@ -784,7 +783,9 @@ abstract class AstNodeImpl implements AstNode {
784783
}
785784

786785
@override
787-
E? thisOrAncestorMatching<E extends AstNode>(Predicate<AstNode> predicate) {
786+
E? thisOrAncestorMatching<E extends AstNode>(
787+
bool Function(AstNode) predicate,
788+
) {
788789
AstNode? node = this;
789790
while (node != null && !predicate(node)) {
790791
node = node.parent;
@@ -9336,12 +9337,11 @@ class SimpleStringLiteralImpl extends SingleStringLiteralImpl
93369337
Token literal;
93379338

93389339
/// The value of the literal.
9339-
String _value;
9340+
@override
9341+
String value;
93409342

93419343
/// Initialize a newly created simple string literal.
9342-
SimpleStringLiteralImpl(this.literal, this._value) {
9343-
_value = StringUtilities.intern(value);
9344-
}
9344+
SimpleStringLiteralImpl(this.literal, this.value);
93459345

93469346
@override
93479347
Token get beginToken => literal;
@@ -9370,13 +9370,6 @@ class SimpleStringLiteralImpl extends SingleStringLiteralImpl
93709370
@override
93719371
bool get isSynthetic => literal.isSynthetic;
93729372

9373-
@override
9374-
String get value => _value;
9375-
9376-
set value(String string) {
9377-
_value = StringUtilities.intern(_value);
9378-
}
9379-
93809373
StringLexemeHelper get _helper {
93819374
return StringLexemeHelper(literal.lexeme, true, true);
93829375
}
@@ -9579,12 +9572,12 @@ class StringLexemeHelper {
95799572
if (isRaw) {
95809573
start++;
95819574
}
9582-
if (StringUtilities.startsWith3(lexeme, start, 0x27, 0x27, 0x27)) {
9575+
if (lexeme.startsWith("'''", start)) {
95839576
isSingleQuoted = true;
95849577
isMultiline = true;
95859578
start += 3;
95869579
start = _trimInitialWhitespace(start);
9587-
} else if (StringUtilities.startsWith3(lexeme, start, 0x22, 0x22, 0x22)) {
9580+
} else if (lexeme.startsWith('"""', start)) {
95889581
isSingleQuoted = false;
95899582
isMultiline = true;
95909583
start += 3;
@@ -9602,12 +9595,10 @@ class StringLexemeHelper {
96029595
end = lexeme.length;
96039596
if (isLast) {
96049597
if (start + 3 <= end &&
9605-
(StringUtilities.endsWith3(lexeme, 0x22, 0x22, 0x22) ||
9606-
StringUtilities.endsWith3(lexeme, 0x27, 0x27, 0x27))) {
9598+
(lexeme.endsWith("'''") || lexeme.endsWith('"""'))) {
96079599
end -= 3;
96089600
} else if (start + 1 <= end &&
9609-
(StringUtilities.endsWithChar(lexeme, 0x22) ||
9610-
StringUtilities.endsWithChar(lexeme, 0x27))) {
9601+
(lexeme.endsWith("'") || lexeme.endsWith('"'))) {
96119602
end -= 1;
96129603
}
96139604
}

0 commit comments

Comments
 (0)