Version 2.19.0-389.0.dev
Merge 4306e077756f4bef3cf1b3ed26b6bc5e1c08e243 into dev
diff --git a/WATCHLISTS b/WATCHLISTS
index be0e2b3..49b1558 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -34,6 +34,14 @@
'^tests/web'
)
},
+ 'dart2wasm': {
+ 'filepath': (
+ '^pkg/dart2wasm|'
+ '^pkg/wasm_builder|'
+ '^sdk/lib/_internal/vm_shared|'
+ '^sdk/lib/_internal/wasm'
+ )
+ },
'dartdevc': {
'filepath': (
'^pkg/dev_compiler|'
@@ -76,6 +84,7 @@
'WATCHLISTS': {
'dart2js': [ 'dart2js-team+reviews@google.com' ],
+ 'dart2wasm': [ 'dart2wasm-team+reviews@google.com' ],
'dartdevc': [ 'dart-dc-team+reviews@google.com' ],
'experimental_features': [ 'scheglov@google.com' ],
'front_end': [ 'dart-fe-team+reviews@google.com' ],
diff --git a/pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer.dart b/pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer.dart
index 7f51503..e3a006c 100644
--- a/pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer.dart
@@ -29,6 +29,12 @@
NamedType(this.name, this.type);
}
+/// Information supplied by the client to [TypeAnalyzer.analyzeObjectPattern],
+/// [TypeAnalyzer.analyzeRecordPattern], or
+/// [TypeAnalyzer.analyzeRecordPatternSchema] about a single field in a record
+/// or object pattern.
+///
+/// The client is free to `implement` or `extend` this class.
class RecordPatternField<Node extends Object, Pattern extends Object> {
/// The client specific node from which this object was created. It can be
/// used for error reporting.
diff --git a/pkg/_fe_analyzer_shared/test/mini_ast.dart b/pkg/_fe_analyzer_shared/test/mini_ast.dart
index 3567b7e..d71ed9e 100644
--- a/pkg/_fe_analyzer_shared/test/mini_ast.dart
+++ b/pkg/_fe_analyzer_shared/test/mini_ast.dart
@@ -240,7 +240,7 @@
Pattern objectPattern({
required ObjectPatternRequiredType requiredType,
- required List<shared.RecordPatternField<Node, Pattern>> fields,
+ required List<RecordPatternField> fields,
}) {
return _ObjectPattern(
requiredType: requiredType,
@@ -249,7 +249,7 @@
);
}
-Pattern recordPattern(List<SharedRecordPatternField> fields) =>
+Pattern recordPattern(List<RecordPatternField> fields) =>
_RecordPattern(fields, location: computeLocation());
Pattern relationalPattern(
@@ -297,8 +297,6 @@
_VariablePattern(type == null ? null : Type(type), null, expectInferredType,
isFinal: isFinal, location: computeLocation());
-typedef SharedRecordPatternField = shared.RecordPatternField<Node, Pattern>;
-
mixin CaseHead implements CaseHeads, Node {
@override
List<CaseHead> get _caseHeads => [this];
@@ -1130,6 +1128,14 @@
void preVisit(
PreVisitor visitor, VariableBinder<Node, Var, Type> variableBinder);
+ RecordPatternField recordField([String? name]) {
+ return RecordPatternField(
+ name: name,
+ pattern: this,
+ location: computeLocation(),
+ );
+ }
+
@override
String toString() => _debugString(needsKeywordOrType: true);
@@ -1175,10 +1181,20 @@
PromotableLValue._({required super.location}) : super._();
}
-/// TODO(scheglov) This node is used temporary to model 'node'.
-class RecordPatternField implements Node {
+/// A field in object and record patterns.
+class RecordPatternField extends Node
+ implements shared.RecordPatternField<Node, Pattern> {
+ final String? name;
+ final Pattern pattern;
+
+ RecordPatternField({
+ required this.name,
+ required this.pattern,
+ required super.location,
+ }) : super._();
+
@override
- dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
+ Node get node => this;
}
/// Representation of a statement in the pseudo-Dart language used for flow
@@ -3216,7 +3232,7 @@
@override
Type resolveObjectPatternPropertyGet({
required Type receiverType,
- required SharedRecordPatternField field,
+ required shared.RecordPatternField<Node, Pattern> field,
}) {
return _harness.getMember(receiverType, field.name!)._type;
}
@@ -3390,7 +3406,7 @@
class _ObjectPattern extends Pattern {
final ObjectPatternRequiredType requiredType;
- final List<SharedRecordPatternField> fields;
+ final List<RecordPatternField> fields;
_ObjectPattern({
required this.requiredType,
@@ -3526,7 +3542,7 @@
}
class _RecordPattern extends Pattern {
- final List<SharedRecordPatternField> fields;
+ final List<RecordPatternField> fields;
_RecordPattern(this.fields, {required super.location}) : super._();
diff --git a/pkg/_fe_analyzer_shared/test/type_inference/type_inference_test.dart b/pkg/_fe_analyzer_shared/test/type_inference/type_inference_test.dart
index d3bb183..c0286d1 100644
--- a/pkg/_fe_analyzer_shared/test/type_inference/type_inference_test.dart
+++ b/pkg/_fe_analyzer_shared/test/type_inference/type_inference_test.dart
@@ -4,8 +4,6 @@
import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer.dart'
hide RecordPatternField, NamedType, RecordType;
-import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer.dart'
- as shared;
import 'package:test/test.dart';
import '../mini_ast.dart';
@@ -1838,11 +1836,7 @@
objectPattern(
requiredType: ObjectPatternRequiredType.name('B'),
fields: [
- shared.RecordPatternField(
- node: RecordPatternField(),
- name: 'foo',
- pattern: Var('foo').pattern(),
- ),
+ Var('foo').pattern().recordField('foo'),
],
),
[],
@@ -1861,11 +1855,7 @@
objectPattern(
requiredType: ObjectPatternRequiredType.type('num'),
fields: [
- shared.RecordPatternField(
- node: RecordPatternField(),
- name: 'foo',
- pattern: Var('foo').pattern(),
- ),
+ Var('foo').pattern().recordField('foo'),
],
),
expr('int').checkContext('num'),
@@ -1882,11 +1872,7 @@
objectPattern(
requiredType: ObjectPatternRequiredType.type('int'),
fields: [
- shared.RecordPatternField(
- node: RecordPatternField(),
- name: 'foo',
- pattern: Var('foo').pattern(),
- ),
+ Var('foo').pattern().recordField('foo'),
],
)..errorId = 'PATTERN',
expr('num').checkContext('int'),
@@ -1910,16 +1896,8 @@
ifCase(
expr('dynamic').checkContext('?'),
recordPattern([
- shared.RecordPatternField(
- node: RecordPatternField(),
- name: null,
- pattern: Var('a').pattern(type: 'int'),
- ),
- shared.RecordPatternField(
- node: RecordPatternField(),
- name: null,
- pattern: Var('b').pattern(),
- ),
+ Var('a').pattern(type: 'int').recordField(),
+ Var('b').pattern().recordField(),
]),
[],
).checkIr(
@@ -1943,16 +1921,8 @@
h.run([
match(
recordPattern([
- shared.RecordPatternField(
- node: RecordPatternField(),
- name: null,
- pattern: Var('a').pattern(type: 'int'),
- ),
- shared.RecordPatternField(
- node: RecordPatternField(),
- name: null,
- pattern: Var('b').pattern(),
- ),
+ Var('a').pattern(type: 'int').recordField(),
+ Var('b').pattern().recordField(),
]),
expr('(int, String)').checkContext('(int, ?)'),
).checkIr(
@@ -1970,17 +1940,9 @@
h.run([
(match(
recordPattern([
- shared.RecordPatternField(
- node: RecordPatternField(),
- name: null,
- pattern: Var('a').pattern(type: 'int')
- ..errorId = 'VAR(a)',
- ),
- shared.RecordPatternField(
- node: RecordPatternField(),
- name: null,
- pattern: Var('b').pattern(),
- ),
+ (Var('a').pattern(type: 'int')..errorId = 'VAR(a)')
+ .recordField(),
+ Var('b').pattern().recordField(),
])
..errorId = 'PATTERN',
expr('(int,)').checkContext('(int, ?)'),
@@ -2005,16 +1967,8 @@
ifCase(
expr('(int,)').checkContext('?'),
recordPattern([
- shared.RecordPatternField(
- node: RecordPatternField(),
- name: null,
- pattern: Var('a').pattern(),
- ),
- shared.RecordPatternField(
- node: RecordPatternField(),
- name: null,
- pattern: Var('b').pattern(),
- ),
+ Var('a').pattern().recordField(),
+ Var('b').pattern().recordField(),
]),
[],
).checkIr('ifCase(expr((int)), recordPattern(varPattern(a, '
@@ -2029,11 +1983,7 @@
ifCase(
expr('(int, String)').checkContext('?'),
recordPattern([
- shared.RecordPatternField(
- node: RecordPatternField(),
- name: null,
- pattern: Var('a').pattern(),
- ),
+ Var('a').pattern().recordField(),
]),
[],
).checkIr('ifCase(expr((int, String)), '
@@ -2051,16 +2001,8 @@
ifCase(
expr('X').checkContext('?'),
recordPattern([
- shared.RecordPatternField(
- node: RecordPatternField(),
- name: null,
- pattern: Var('a').pattern(type: 'int'),
- ),
- shared.RecordPatternField(
- node: RecordPatternField(),
- name: null,
- pattern: Var('b').pattern(),
- ),
+ Var('a').pattern(type: 'int').recordField(),
+ Var('b').pattern().recordField(),
]),
[],
).checkIr('ifCase(expr(X), recordPattern(varPattern(a, '
@@ -2079,16 +2021,8 @@
ifCase(
expr('dynamic').checkContext('?'),
recordPattern([
- shared.RecordPatternField(
- node: RecordPatternField(),
- name: 'a',
- pattern: Var('a').pattern(type: 'int'),
- ),
- shared.RecordPatternField(
- node: RecordPatternField(),
- name: 'b',
- pattern: Var('b').pattern(),
- ),
+ Var('a').pattern(type: 'int').recordField('a'),
+ Var('b').pattern().recordField('b'),
]),
[],
).checkIr('ifCase(expr(dynamic), recordPattern(varPattern(a, '
@@ -2110,16 +2044,8 @@
h.run([
match(
recordPattern([
- shared.RecordPatternField(
- node: RecordPatternField(),
- name: 'a',
- pattern: Var('a').pattern(type: 'int'),
- ),
- shared.RecordPatternField(
- node: RecordPatternField(),
- name: 'b',
- pattern: Var('b').pattern(),
- ),
+ Var('a').pattern(type: 'int').recordField('a'),
+ Var('b').pattern().recordField('b'),
]),
expr('({int a, String b})').checkContext('({int a, ? b})'),
).checkIr('match(expr(({int a, String b})), '
@@ -2136,17 +2062,9 @@
h.run([
(match(
recordPattern([
- shared.RecordPatternField(
- node: RecordPatternField(),
- name: 'a',
- pattern: Var('a').pattern(type: 'int')
- ..errorId = 'VAR(a)',
- ),
- shared.RecordPatternField(
- node: RecordPatternField(),
- name: 'b',
- pattern: Var('b').pattern(),
- ),
+ (Var('a').pattern(type: 'int')..errorId = 'VAR(a)')
+ .recordField('a'),
+ Var('b').pattern().recordField('b'),
])
..errorId = 'PATTERN',
expr('({int a})').checkContext('({int a, ? b})'),
@@ -2171,16 +2089,8 @@
ifCase(
expr('({int a})').checkContext('?'),
recordPattern([
- shared.RecordPatternField(
- node: RecordPatternField(),
- name: 'a',
- pattern: Var('a').pattern(),
- ),
- shared.RecordPatternField(
- node: RecordPatternField(),
- name: 'b',
- pattern: Var('b').pattern(),
- ),
+ Var('a').pattern().recordField('a'),
+ Var('b').pattern().recordField('b'),
]),
[],
).checkIr('ifCase(expr(({int a})), '
@@ -2196,11 +2106,7 @@
ifCase(
expr('({int a, String b})').checkContext('?'),
recordPattern([
- shared.RecordPatternField(
- node: RecordPatternField(),
- name: 'a',
- pattern: Var('a').pattern(),
- ),
+ Var('a').pattern().recordField('a'),
]),
[],
).checkIr('ifCase(expr(({int a, String b})), '
@@ -2218,23 +2124,15 @@
ifCase(
expr('X').checkContext('?'),
recordPattern([
- shared.RecordPatternField(
- node: RecordPatternField(),
- name: null,
- pattern: Var('a').pattern(type: 'int'),
- ),
- shared.RecordPatternField(
- node: RecordPatternField(),
- name: null,
- pattern: Var('b').pattern(),
- ),
+ Var('a').pattern(type: 'int').recordField('a'),
+ Var('b').pattern().recordField('b'),
]),
[],
).checkIr('ifCase(expr(X), recordPattern(varPattern(a, '
'matchedType: Object?, staticType: int), varPattern(b, '
'matchedType: Object?, staticType: Object?), '
'matchedType: X, requiredType: '
- '(Object?, Object?)), true, block(), noop)'),
+ '({Object? a, Object? b})), true, block(), noop)'),
]);
});
});
diff --git a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
index 5203057..5069192 100644
--- a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
+++ b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
@@ -1802,7 +1802,9 @@
LintCode.empty_statements:
status: hasFix
LintCode.enable_null_safety:
- status: needsEvaluation
+ status: noFix
+ notes: |-
+ The correction is to run the migration tool.
LintCode.eol_at_end_of_file:
status: hasFix
LintCode.exhaustive_cases:
@@ -1826,7 +1828,7 @@
LintCode.leading_newlines_in_multiline_strings:
status: hasFix
LintCode.library_annotations:
- status: needsEvaluation
+ status: needsFix
LintCode.library_names:
status: needsEvaluation
LintCode.library_prefixes:
diff --git a/pkg/analyzer/lib/src/error/dead_code_verifier.dart b/pkg/analyzer/lib/src/error/dead_code_verifier.dart
index 318fa4d..19c161b 100644
--- a/pkg/analyzer/lib/src/error/dead_code_verifier.dart
+++ b/pkg/analyzer/lib/src/error/dead_code_verifier.dart
@@ -6,6 +6,7 @@
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/error/listener.dart';
@@ -16,6 +17,7 @@
import 'package:analyzer/src/dart/resolver/scope.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/constant.dart';
+import 'package:collection/collection.dart';
typedef _CatchClausesVerifierReporter = void Function(
CatchClause first,
@@ -581,6 +583,17 @@
_catchClausesVerifiers.removeLast();
}
+ void verifyCascadeExpression(CascadeExpression node) {
+ var first = node.cascadeSections.firstOrNull;
+ if (first is PropertyAccess) {
+ _verifyUnassignedSimpleIdentifier(node, node.target, first.operator);
+ } else if (first is MethodInvocation) {
+ _verifyUnassignedSimpleIdentifier(node, node.target, first.operator);
+ } else if (first is IndexExpression) {
+ _verifyUnassignedSimpleIdentifier(node, node.target, first.period);
+ }
+ }
+
void verifyCatchClause(CatchClause node) {
var verifier = _catchClausesVerifiers.last;
if (verifier._done) return;
@@ -588,6 +601,18 @@
verifier.nextCatchClause(node);
}
+ void verifyIndexExpression(IndexExpression node) {
+ _verifyUnassignedSimpleIdentifier(node, node.target, node.question);
+ }
+
+ void verifyMethodInvocation(MethodInvocation node) {
+ _verifyUnassignedSimpleIdentifier(node, node.target, node.operator);
+ }
+
+ void verifyPropertyAccess(PropertyAccess node) {
+ _verifyUnassignedSimpleIdentifier(node, node.target, node.operator);
+ }
+
void visitNode(AstNode node) {
// Comments are visited after bodies of functions.
// So, they look unreachable, but this does not make sense.
@@ -636,6 +661,38 @@
}
}
}
+
+ void _verifyUnassignedSimpleIdentifier(
+ AstNode node, Expression? target, Token? operator) {
+ var flowAnalysis = _flowAnalysis;
+ if (flowAnalysis == null) return;
+
+ var operatorType = operator?.type;
+ if (operatorType != TokenType.QUESTION &&
+ operatorType != TokenType.QUESTION_PERIOD &&
+ operatorType != TokenType.QUESTION_PERIOD_PERIOD) {
+ return;
+ }
+ if (target?.staticType?.nullabilitySuffix != NullabilitySuffix.question) {
+ return;
+ }
+
+ target = target?.unParenthesized;
+ if (target is SimpleIdentifier) {
+ var element = target.staticElement;
+ if (element is PromotableElement &&
+ flowAnalysis.isDefinitelyUnassigned(target, element)) {
+ var parent = node.parent;
+ while (parent is MethodInvocation ||
+ parent is PropertyAccess ||
+ parent is IndexExpression) {
+ node = parent!;
+ parent = node.parent;
+ }
+ _errorReporter.reportErrorForNode(HintCode.DEAD_CODE, node);
+ }
+ }
+ }
}
/// A visitor that finds a [BreakStatement] for a specified [DoStatement].
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index f820cd7..1a8e0f3 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -1710,6 +1710,7 @@
nullShortingTermination(node);
_insertImplicitCallReference(node, contextType: contextType);
+ nullSafetyDeadCodeVerifier.verifyCascadeExpression(node);
}
@override
@@ -2449,6 +2450,7 @@
nullShortingTermination(node);
_insertImplicitCallReference(replacement, contextType: contextType);
+ nullSafetyDeadCodeVerifier.verifyIndexExpression(node);
}
@override
@@ -2614,6 +2616,7 @@
checkForArgumentTypesNotAssignableInList(
node.argumentList, whyNotPromotedList);
_insertImplicitCallReference(replacement, contextType: contextType);
+ nullSafetyDeadCodeVerifier.verifyMethodInvocation(node);
}
@override
@@ -2794,6 +2797,7 @@
nullShortingTermination(node);
_insertImplicitCallReference(replacement, contextType: contextType);
+ nullSafetyDeadCodeVerifier.verifyPropertyAccess(node);
}
@override
diff --git a/pkg/analyzer/test/src/dart/resolution/non_nullable_test.dart b/pkg/analyzer/test/src/dart/resolution/non_nullable_test.dart
index 7eb4066..270c475 100644
--- a/pkg/analyzer/test/src/dart/resolution/non_nullable_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/non_nullable_test.dart
@@ -80,8 +80,7 @@
test_local_getterNullAwareAccess_interfaceType() async {
await assertNoErrorsInCode(r'''
-main() {
- int? x;
+f(int? x) {
return x?.isEven;
}
''');
@@ -131,8 +130,7 @@
bool x() => true;
}
-main() {
- C? c;
+f(C? c) {
return c?.x();
}
''');
diff --git a/pkg/analyzer/test/src/diagnostics/dead_code_test.dart b/pkg/analyzer/test/src/diagnostics/dead_code_test.dart
index ed92c67..cb7279a 100644
--- a/pkg/analyzer/test/src/diagnostics/dead_code_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/dead_code_test.dart
@@ -958,6 +958,17 @@
]);
}
+ test_assigned_methodInvocation() async {
+ await assertErrorsInCode(r'''
+void f() {
+ int? i = 1;
+ i?.truncate();
+}
+''', [
+ error(StaticWarningCode.INVALID_NULL_AWARE_OPERATOR, 28, 2),
+ ]);
+ }
+
test_doWhile() async {
await assertErrorsInCode(r'''
void f(bool c) {
@@ -1322,6 +1333,26 @@
]);
}
+ test_notUnassigned_propertyAccess() async {
+ await assertNoErrorsInCode(r'''
+void f(int? i) {
+ (i)?.sign;
+}
+''');
+ }
+
+ test_potentiallyAssigned_propertyAccess() async {
+ await assertNoErrorsInCode(r'''
+void f(bool b) {
+ int? i;
+ if (b) {
+ i = 1;
+ }
+ (i)?.sign;
+}
+''');
+ }
+
test_returnTypeNever_function() async {
await assertErrorsInCode(r'''
Never foo() => throw 0;
@@ -1393,6 +1424,116 @@
error(HintCode.DEAD_CODE, 87, 14),
]);
}
+
+ test_unassigned_cascadeExpression_indexExpression() async {
+ await assertErrorsInCode(r'''
+void f() {
+ List<int>? l;
+ l?..[0]..length;
+}
+''', [
+ error(HintCode.DEAD_CODE, 29, 15),
+ ]);
+ }
+
+ test_unassigned_cascadeExpression_methodInvocation() async {
+ await assertErrorsInCode(r'''
+void f() {
+ int? i;
+ i?..toInt()..isEven;
+}
+''', [
+ error(HintCode.DEAD_CODE, 23, 19),
+ ]);
+ }
+
+ test_unassigned_cascadeExpression_propertyAccess() async {
+ await assertErrorsInCode(r'''
+void f() {
+ int? i;
+ i?..sign..isEven;
+}
+''', [
+ error(HintCode.DEAD_CODE, 23, 16),
+ ]);
+ }
+
+ test_unassigned_indexExpression() async {
+ await assertErrorsInCode(r'''
+void f() {
+ List<int>? l;
+ l?[0];
+}
+''', [
+ error(HintCode.DEAD_CODE, 29, 5),
+ ]);
+ }
+
+ test_unassigned_indexExpression_indexExpression() async {
+ await assertErrorsInCode(r'''
+void f() {
+ List<List<int>>? l;
+ l?[0][0];
+}
+''', [
+ error(HintCode.DEAD_CODE, 35, 8),
+ ]);
+ }
+
+ test_unassigned_methodInvocation() async {
+ await assertErrorsInCode(r'''
+void f() {
+ int? i;
+ i?.truncate();
+}
+''', [
+ error(HintCode.DEAD_CODE, 23, 13),
+ ]);
+ }
+
+ test_unassigned_methodInvocation_methodInvocation() async {
+ await assertErrorsInCode(r'''
+void f() {
+ int? i;
+ i?.truncate().truncate();
+}
+''', [
+ error(HintCode.DEAD_CODE, 23, 24),
+ ]);
+ }
+
+ test_unassigned_methodInvocation_propertyAccess() async {
+ await assertErrorsInCode(r'''
+void f() {
+ int? i;
+ i?.truncate().sign;
+}
+''', [
+ error(HintCode.DEAD_CODE, 23, 18),
+ ]);
+ }
+
+ test_unassigned_propertyAccess() async {
+ await assertErrorsInCode(r'''
+void f() {
+ int? i;
+ (i)?.sign;
+}
+''', [
+ error(HintCode.DEAD_CODE, 23, 9),
+ ]);
+ }
+
+ test_unassigned_propertyAccess_propertyAccess() async {
+ await assertErrorsInCode(r'''
+void f() {
+ int? i;
+ (i)?.sign.sign;
+}
+''', [
+ error(HintCode.DEAD_CODE, 23, 14),
+ ]);
+ }
}
@reflectiveTest
diff --git a/pkg/analyzer/test/src/diagnostics/use_of_nullable_value_test.dart b/pkg/analyzer/test/src/diagnostics/use_of_nullable_value_test.dart
index add8a1e..c5f975b 100644
--- a/pkg/analyzer/test/src/diagnostics/use_of_nullable_value_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/use_of_nullable_value_test.dart
@@ -811,7 +811,7 @@
test_cascade_nonNullable() async {
await assertNoErrorsInCode(r'''
-m() {
+f() {
int x = 0;
x..isEven;
}
@@ -820,20 +820,18 @@
test_cascade_nullable_indexed_assignment() async {
await assertErrorsInCode(r'''
-m() {
- List<int>? x;
+f(List<int>? x) {
x..[0] = 1;
}
''', [
error(CompileTimeErrorCode.UNCHECKED_METHOD_INVOCATION_OF_NULLABLE_VALUE,
- 27, 1),
+ 23, 1),
]);
}
test_cascade_nullable_indexed_assignment_null_aware() async {
await assertNoErrorsInCode(r'''
-m() {
- List<int>? x;
+f(List<int>? x) {
x?..[0] = 1;
}
''');
@@ -853,8 +851,7 @@
test_cascade_nullable_method_invocation_null_aware() async {
await assertNoErrorsInCode(r'''
-m() {
- int? x;
+f(int? x) {
x?..abs();
}
''');
@@ -862,20 +859,18 @@
test_cascade_nullable_property_access() async {
await assertErrorsInCode(r'''
-m() {
- int? x;
+f(int? x) {
x..isEven;
}
''', [
error(CompileTimeErrorCode.UNCHECKED_PROPERTY_ACCESS_OF_NULLABLE_VALUE,
- 21, 6),
+ 17, 6),
]);
}
test_cascade_nullable_property_access_null_aware() async {
await assertNoErrorsInCode(r'''
-m() {
- int? x;
+m(int? x) {
x?..isEven;
}
''');
@@ -1137,8 +1132,7 @@
test_member_questionDot_nullable() async {
await assertNoErrorsInCode(r'''
-m() {
- int? x;
+f(int? x) {
x?.isEven;
}
''');
@@ -1199,8 +1193,7 @@
test_method_questionDot_nullable() async {
await assertNoErrorsInCode(r'''
-m() {
- int? x;
+m(int? x) {
x?.round();
}
''');
diff --git a/pkg/dartdev/test/commands/compile_test.dart b/pkg/dartdev/test/commands/compile_test.dart
index ec601f4..64dafce 100644
--- a/pkg/dartdev/test/commands/compile_test.dart
+++ b/pkg/dartdev/test/commands/compile_test.dart
@@ -44,8 +44,8 @@
],
);
- expect(result.stderr, contains(soundNullSafetyMessage));
- expect(result.stdout, isEmpty);
+ expect(result.stdout, contains(soundNullSafetyMessage));
+ expect(result.stderr, isEmpty);
expect(result.exitCode, 0);
expect(File(outFile).existsSync(), true,
reason: 'File not found: $outFile');
@@ -85,8 +85,8 @@
],
);
- expect(result.stderr, contains(soundNullSafetyMessage));
- expect(result.stdout, isEmpty);
+ expect(result.stdout, contains(soundNullSafetyMessage));
+ expect(result.stderr, isEmpty);
expect(result.exitCode, 0);
expect(File(outFile).existsSync(), true,
reason: 'File not found: $outFile');
diff --git a/pkg/front_end/lib/src/fasta/builder/builder_mixins.dart b/pkg/front_end/lib/src/fasta/builder/builder_mixins.dart
new file mode 100644
index 0000000..954c274
--- /dev/null
+++ b/pkg/front_end/lib/src/fasta/builder/builder_mixins.dart
@@ -0,0 +1,162 @@
+// Copyright (c) 2022, 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:kernel/ast.dart';
+import 'package:kernel/class_hierarchy.dart';
+
+import '../messages.dart';
+import '../problems.dart';
+import '../scope.dart';
+import '../source/source_library_builder.dart';
+import 'builder.dart';
+import 'declaration_builder.dart';
+import 'field_builder.dart';
+import 'library_builder.dart';
+import 'member_builder.dart';
+import 'nullability_builder.dart';
+import 'type_builder.dart';
+import 'type_variable_builder.dart';
+
+/// Shared implementation between extension and view builders.
+mixin DeclarationBuilderMixin implements DeclarationBuilder {
+ /// Type parameters declared.
+ ///
+ /// This is `null` if the declaration is not generic.
+ List<TypeVariableBuilder>? get typeParameters;
+
+ /// Lookup a static member of this declaration.
+ @override
+ Builder? findStaticBuilder(
+ String name, int charOffset, Uri fileUri, LibraryBuilder accessingLibrary,
+ {bool isSetter = false}) {
+ if (accessingLibrary.nameOriginBuilder.origin !=
+ libraryBuilder.nameOriginBuilder.origin &&
+ name.startsWith("_")) {
+ return null;
+ }
+ Builder? declaration = isSetter
+ ? scope.lookupSetter(name, charOffset, fileUri, isInstanceScope: false)
+ : scope.lookup(name, charOffset, fileUri, isInstanceScope: false);
+ // TODO(johnniwinther): Handle patched extensions/views.
+ return declaration;
+ }
+
+ @override
+ DartType buildAliasedType(
+ LibraryBuilder library,
+ NullabilityBuilder nullabilityBuilder,
+ List<TypeBuilder>? arguments,
+ TypeUse typeUse,
+ Uri fileUri,
+ int charOffset,
+ ClassHierarchyBase? hierarchy,
+ {required bool hasExplicitTypeArguments}) {
+ return buildAliasedTypeWithBuiltArguments(
+ library,
+ nullabilityBuilder.build(library),
+ _buildAliasedTypeArguments(library, arguments, hierarchy),
+ typeUse,
+ fileUri,
+ charOffset,
+ hasExplicitTypeArguments: hasExplicitTypeArguments);
+ }
+
+ @override
+ int get typeVariablesCount => typeParameters?.length ?? 0;
+
+ List<DartType> _buildAliasedTypeArguments(LibraryBuilder library,
+ List<TypeBuilder>? arguments, ClassHierarchyBase? hierarchy) {
+ if (arguments == null && typeParameters == null) {
+ return <DartType>[];
+ }
+
+ if (arguments == null && typeParameters != null) {
+ List<DartType> result =
+ new List<DartType>.generate(typeParameters!.length, (int i) {
+ return typeParameters![i].defaultType!.buildAliased(
+ library, TypeUse.defaultTypeAsTypeArgument, hierarchy);
+ }, growable: true);
+ if (library is SourceLibraryBuilder) {
+ library.inferredTypes.addAll(result);
+ }
+ return result;
+ }
+
+ if (arguments != null && arguments.length != typeVariablesCount) {
+ // That should be caught and reported as a compile-time error earlier.
+ return unhandled(
+ templateTypeArgumentMismatch
+ .withArguments(typeVariablesCount)
+ .problemMessage,
+ "buildTypeArguments",
+ -1,
+ null);
+ }
+
+ assert(arguments!.length == typeVariablesCount);
+ List<DartType> result =
+ new List<DartType>.generate(arguments!.length, (int i) {
+ return arguments[i]
+ .buildAliased(library, TypeUse.typeArgument, hierarchy);
+ }, growable: true);
+ return result;
+ }
+
+ void forEach(void f(String name, Builder builder)) {
+ scope
+ .filteredNameIterator(
+ includeDuplicates: false, includeAugmentations: false)
+ .forEach(f);
+ }
+
+ @override
+ InterfaceType? get thisType => null;
+
+ @override
+ Builder? lookupLocalMember(String name,
+ {bool setter = false, bool required = false}) {
+ // TODO(johnniwinther): Support patching on extensions/views.
+ Builder? builder = scope.lookupLocalMember(name, setter: setter);
+ if (required && builder == null) {
+ internalProblem(
+ templateInternalProblemNotFoundIn.withArguments(
+ name, fullNameForErrors),
+ -1,
+ null);
+ }
+ return builder;
+ }
+
+ Builder? lookupLocalMemberByName(Name name,
+ {bool setter = false, bool required = false}) {
+ Builder? builder =
+ lookupLocalMember(name.text, setter: setter, required: required);
+ if (builder == null && setter) {
+ // When looking up setters, we include assignable fields.
+ builder = lookupLocalMember(name.text, setter: false, required: required);
+ if (builder is! FieldBuilder || !builder.isAssignable) {
+ builder = null;
+ }
+ }
+ if (builder != null) {
+ if (name.isPrivate && libraryBuilder.library != name.library) {
+ builder = null;
+ } else if (builder is FieldBuilder &&
+ !builder.isStatic &&
+ !builder.isExternal) {
+ // Non-external extension instance fields are invalid.
+ builder = null;
+ } else if (builder.isDuplicate) {
+ // Duplicates are not visible in the instance scope.
+ builder = null;
+ } else if (builder is MemberBuilder && builder.isConflictingSetter) {
+ // Conflicting setters are not visible in the instance scope.
+ // TODO(johnniwinther): Should we return an [AmbiguousBuilder] here and
+ // above?
+ builder = null;
+ }
+ }
+ return builder;
+ }
+}
diff --git a/pkg/front_end/lib/src/fasta/builder/declaration_builder.dart b/pkg/front_end/lib/src/fasta/builder/declaration_builder.dart
index cec4048..a0d0db4 100644
--- a/pkg/front_end/lib/src/fasta/builder/declaration_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/declaration_builder.dart
@@ -6,7 +6,6 @@
import '../messages.dart';
import '../scope.dart';
-
import 'builder.dart';
import 'library_builder.dart';
import 'metadata_builder.dart';
diff --git a/pkg/front_end/lib/src/fasta/builder/extension_builder.dart b/pkg/front_end/lib/src/fasta/builder/extension_builder.dart
index f31ee90..f45c315 100644
--- a/pkg/front_end/lib/src/fasta/builder/extension_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/extension_builder.dart
@@ -3,20 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:kernel/ast.dart';
-import 'package:kernel/class_hierarchy.dart';
-
-import '../fasta_codes.dart'
- show templateInternalProblemNotFoundIn, templateTypeArgumentMismatch;
-import '../problems.dart';
import '../scope.dart';
import '../source/source_library_builder.dart';
import 'builder.dart';
+import 'builder_mixins.dart';
import 'declaration_builder.dart';
-import 'field_builder.dart';
import 'library_builder.dart';
-import 'member_builder.dart';
import 'metadata_builder.dart';
-import 'nullability_builder.dart';
import 'type_builder.dart';
import 'type_variable_builder.dart';
@@ -50,54 +43,12 @@
}
abstract class ExtensionBuilderImpl extends DeclarationBuilderImpl
+ with DeclarationBuilderMixin
implements ExtensionBuilder {
ExtensionBuilderImpl(List<MetadataBuilder>? metadata, int modifiers,
String name, LibraryBuilder parent, int charOffset, Scope scope)
: super(metadata, modifiers, name, parent, charOffset, scope);
- /// Lookup a static member of this declaration.
- @override
- Builder? findStaticBuilder(
- String name, int charOffset, Uri fileUri, LibraryBuilder accessingLibrary,
- {bool isSetter = false}) {
- if (accessingLibrary.nameOriginBuilder.origin !=
- libraryBuilder.nameOriginBuilder.origin &&
- name.startsWith("_")) {
- return null;
- }
- Builder? declaration = isSetter
- ? scope.lookupSetter(name, charOffset, fileUri, isInstanceScope: false)
- : scope.lookup(name, charOffset, fileUri, isInstanceScope: false);
- // TODO(johnniwinther): Handle patched extensions.
- return declaration;
- }
-
- @override
- DartType buildAliasedType(
- LibraryBuilder library,
- NullabilityBuilder nullabilityBuilder,
- List<TypeBuilder>? arguments,
- TypeUse typeUse,
- Uri fileUri,
- int charOffset,
- ClassHierarchyBase? hierarchy,
- {required bool hasExplicitTypeArguments}) {
- if (library is SourceLibraryBuilder &&
- library.libraryFeatures.extensionTypes.isEnabled) {
- return buildAliasedTypeWithBuiltArguments(
- library,
- nullabilityBuilder.build(library),
- _buildAliasedTypeArguments(library, arguments, hierarchy),
- typeUse,
- fileUri,
- charOffset,
- hasExplicitTypeArguments: hasExplicitTypeArguments);
- } else {
- throw new UnsupportedError("ExtensionBuilder.buildType is not supported"
- "in library '${library.importUri}'.");
- }
- }
-
@override
DartType buildAliasedTypeWithBuiltArguments(
LibraryBuilder library,
@@ -118,108 +69,8 @@
}
@override
- int get typeVariablesCount => typeParameters?.length ?? 0;
-
- List<DartType> _buildAliasedTypeArguments(LibraryBuilder library,
- List<TypeBuilder>? arguments, ClassHierarchyBase? hierarchy) {
- if (arguments == null && typeParameters == null) {
- return <DartType>[];
- }
-
- if (arguments == null && typeParameters != null) {
- List<DartType> result =
- new List<DartType>.generate(typeParameters!.length, (int i) {
- return typeParameters![i].defaultType!.buildAliased(
- library, TypeUse.defaultTypeAsTypeArgument, hierarchy);
- }, growable: true);
- if (library is SourceLibraryBuilder) {
- library.inferredTypes.addAll(result);
- }
- return result;
- }
-
- if (arguments != null && arguments.length != typeVariablesCount) {
- // That should be caught and reported as a compile-time error earlier.
- return unhandled(
- templateTypeArgumentMismatch
- .withArguments(typeVariablesCount)
- .problemMessage,
- "buildTypeArguments",
- -1,
- null);
- }
-
- assert(arguments!.length == typeVariablesCount);
- List<DartType> result =
- new List<DartType>.generate(arguments!.length, (int i) {
- return arguments[i]
- .buildAliased(library, TypeUse.typeArgument, hierarchy);
- }, growable: true);
- return result;
- }
-
- @override
- void forEach(void f(String name, Builder builder)) {
- scope
- .filteredNameIterator(
- includeDuplicates: false, includeAugmentations: false)
- .forEach(f);
- }
-
- @override
bool get isExtension => true;
@override
- InterfaceType? get thisType => null;
-
- @override
- Builder? lookupLocalMember(String name,
- {bool setter = false, bool required = false}) {
- // TODO(johnniwinther): Support patching on extensions.
- Builder? builder = scope.lookupLocalMember(name, setter: setter);
- if (required && builder == null) {
- internalProblem(
- templateInternalProblemNotFoundIn.withArguments(
- name, fullNameForErrors),
- -1,
- null);
- }
- return builder;
- }
-
- @override
- Builder? lookupLocalMemberByName(Name name,
- {bool setter = false, bool required = false}) {
- Builder? builder =
- lookupLocalMember(name.text, setter: setter, required: required);
- if (builder == null && setter) {
- // When looking up setters, we include assignable fields.
- builder = lookupLocalMember(name.text, setter: false, required: required);
- if (builder is! FieldBuilder || !builder.isAssignable) {
- builder = null;
- }
- }
- if (builder != null) {
- if (name.isPrivate && libraryBuilder.library != name.library) {
- builder = null;
- } else if (builder is FieldBuilder &&
- !builder.isStatic &&
- !builder.isExternal) {
- // Non-external extension instance fields are invalid.
- builder = null;
- } else if (builder.isDuplicate) {
- // Duplicates are not visible in the instance scope.
- builder = null;
- } else if (builder is MemberBuilder && builder.isConflictingSetter) {
- // Conflicting setters are not visible in the instance scope.
- // TODO(johnniwinther): Should we return an [AmbiguousBuilder] here and
- // above?
- builder = null;
- }
- }
- return builder;
- }
-
- @override
String get debugName => "ExtensionBuilder";
}
diff --git a/pkg/front_end/lib/src/fasta/builder/view_builder.dart b/pkg/front_end/lib/src/fasta/builder/view_builder.dart
new file mode 100644
index 0000000..b20df5d
--- /dev/null
+++ b/pkg/front_end/lib/src/fasta/builder/view_builder.dart
@@ -0,0 +1,66 @@
+// Copyright (c) 2022, 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:kernel/ast.dart';
+
+import '../scope.dart';
+import 'builder.dart';
+import 'builder_mixins.dart';
+import 'declaration_builder.dart';
+import 'library_builder.dart';
+import 'metadata_builder.dart';
+import 'type_builder.dart';
+import 'type_variable_builder.dart';
+
+abstract class ViewBuilder implements DeclarationBuilder {
+ /// Type parameters declared on the view.
+ ///
+ /// This is `null` if the view is not generic.
+ List<TypeVariableBuilder>? get typeParameters;
+
+ /// The type of the underlying representation.
+ DartType get representationType;
+
+ /// Return the [View] built by this builder.
+ View get view;
+
+ /// Looks up extension member by [name] taking privacy into account.
+ ///
+ /// If [setter] is `true` the sought member is a setter or assignable field.
+ /// If [required] is `true` and no member is found an internal problem is
+ /// reported.
+ ///
+ /// If the extension member is a duplicate, `null` is returned.
+ // TODO(johnniwinther): Support [AmbiguousBuilder] here and in instance
+ // member lookup to avoid reporting that the member doesn't exist when it is
+ // duplicate.
+ Builder? lookupLocalMemberByName(Name name,
+ {bool setter = false, bool required = false});
+
+ /// Calls [f] for each member declared in this extension.
+ void forEach(void f(String name, Builder builder));
+}
+
+abstract class ViewBuilderImpl extends DeclarationBuilderImpl
+ with DeclarationBuilderMixin
+ implements ViewBuilder {
+ ViewBuilderImpl(List<MetadataBuilder>? metadata, int modifiers, String name,
+ LibraryBuilder parent, int charOffset, Scope scope)
+ : super(metadata, modifiers, name, parent, charOffset, scope);
+
+ @override
+ DartType buildAliasedTypeWithBuiltArguments(
+ LibraryBuilder library,
+ Nullability nullability,
+ List<DartType> arguments,
+ TypeUse typeUse,
+ Uri fileUri,
+ int charOffset,
+ {required bool hasExplicitTypeArguments}) {
+ return new ViewType(view, nullability, arguments);
+ }
+
+ @override
+ String get debugName => "ViewBuilder";
+}
diff --git a/pkg/front_end/lib/src/fasta/kernel/type_algorithms.dart b/pkg/front_end/lib/src/fasta/kernel/type_algorithms.dart
index 76dd95c..a674477 100644
--- a/pkg/front_end/lib/src/fasta/kernel/type_algorithms.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/type_algorithms.dart
@@ -24,6 +24,7 @@
import '../builder/type_declaration_builder.dart';
import '../builder/type_variable_builder.dart';
+import '../builder/view_builder.dart';
import '../dill/dill_class_builder.dart' show DillClassBuilder;
import '../dill/dill_type_alias_builder.dart' show DillTypeAliasBuilder;
@@ -45,6 +46,7 @@
import '../source/source_class_builder.dart';
import '../source/source_extension_builder.dart';
import '../source/source_type_alias_builder.dart';
+import '../source/source_view_builder.dart';
/// Initial value for "variance" that is to be computed by the compiler.
const int pendingVariance = -1;
@@ -841,6 +843,8 @@
}
} else if (declaration is ExtensionBuilder) {
visitTypeVariables(declaration.typeParameters);
+ } else if (declaration is ViewBuilder) {
+ visitTypeVariables(declaration.typeParameters);
} else if (declaration is TypeVariableBuilder) {
// Do nothing. The type variable is handled by its parent declaration.
} else if (declaration is BuiltinTypeDeclarationBuilder) {
@@ -932,6 +936,9 @@
} else if (declaration is SourceExtensionBuilder) {
return _findRawTypeCyclesFromTypeVariables(
declaration, declaration.typeParameters);
+ } else if (declaration is SourceViewBuilder) {
+ return _findRawTypeCyclesFromTypeVariables(
+ declaration, declaration.typeParameters);
} else {
unhandled('$declaration (${declaration.runtimeType})', 'findRawTypeCycles',
declaration.charOffset, declaration.fileUri);
@@ -1017,6 +1024,8 @@
issues.addAll(getInboundReferenceIssues(declaration.typeVariables));
} else if (declaration is SourceExtensionBuilder) {
issues.addAll(getInboundReferenceIssues(declaration.typeParameters));
+ } else if (declaration is SourceViewBuilder) {
+ issues.addAll(getInboundReferenceIssues(declaration.typeParameters));
} else {
unhandled(
'$declaration (${declaration.runtimeType})',
diff --git a/pkg/front_end/lib/src/fasta/source/outline_builder.dart b/pkg/front_end/lib/src/fasta/source/outline_builder.dart
index e812158..1de5a1b 100644
--- a/pkg/front_end/lib/src/fasta/source/outline_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/outline_builder.dart
@@ -876,11 +876,6 @@
List<TypeVariableBuilder>? typeVariables =
pop() as List<TypeVariableBuilder>?;
push(typeVariables ?? NullValue.TypeVariables);
- libraryBuilder.currentTypeParameterScopeBuilder
- .markAsClassDeclaration(name.lexeme, name.charOffset, typeVariables);
- libraryBuilder.setCurrentClassName(name.lexeme);
- inAbstractClass = abstractToken != null;
- push(abstractToken != null ? abstractMask : 0);
if (macroToken != null) {
if (reportIfNotEnabled(
libraryFeatures.macros, macroToken.charOffset, macroToken.length)) {
@@ -899,6 +894,16 @@
sealedToken = null;
}
}
+ if (viewToken != null) {
+ libraryBuilder.currentTypeParameterScopeBuilder
+ .markAsViewDeclaration(name.lexeme, name.charOffset, typeVariables);
+ } else {
+ libraryBuilder.currentTypeParameterScopeBuilder
+ .markAsClassDeclaration(name.lexeme, name.charOffset, typeVariables);
+ }
+ libraryBuilder.setCurrentClassName(name.lexeme);
+ inAbstractClass = abstractToken != null;
+ push(abstractToken != null ? abstractMask : 0);
push(macroToken ?? NullValue.Token);
push(viewToken ?? NullValue.Token);
push(sealedToken ?? NullValue.Token);
@@ -1243,20 +1248,37 @@
}
}
- libraryBuilder.addClass(
+ if (viewToken != null) {
+ libraryBuilder.addViewDeclaration(
metadata,
modifiers,
name as String,
typeVariables,
- supertype,
- mixinApplication,
- interfaces,
+ /*supertype,
+ mixinApplication,
+ interfaces,*/
startCharOffset,
nameOffset,
endToken.charOffset,
- supertypeOffset,
- isMacro: macroToken != null,
- isAugmentation: augmentToken != null);
+ /*supertypeOffset,
+ isAugmentation: augmentToken != null*/
+ );
+ } else {
+ libraryBuilder.addClass(
+ metadata,
+ modifiers,
+ name as String,
+ typeVariables,
+ supertype,
+ mixinApplication,
+ interfaces,
+ startCharOffset,
+ nameOffset,
+ endToken.charOffset,
+ supertypeOffset,
+ isMacro: macroToken != null,
+ isAugmentation: augmentToken != null);
+ }
}
libraryBuilder.setCurrentClassName(null);
popDeclarationContext(DeclarationContext.Class);
diff --git a/pkg/front_end/lib/src/fasta/source/source_builder_mixins.dart b/pkg/front_end/lib/src/fasta/source/source_builder_mixins.dart
new file mode 100644
index 0000000..6563dd0
--- /dev/null
+++ b/pkg/front_end/lib/src/fasta/source/source_builder_mixins.dart
@@ -0,0 +1,179 @@
+// Copyright (c) 2019, 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:kernel/ast.dart';
+import 'package:kernel/class_hierarchy.dart';
+import 'package:kernel/type_environment.dart';
+
+import '../builder/builder.dart';
+import '../builder/builder_mixins.dart';
+import '../builder/class_builder.dart';
+import '../builder/declaration_builder.dart';
+import '../builder/library_builder.dart';
+import '../builder/metadata_builder.dart';
+import '../builder/procedure_builder.dart';
+import '../fasta_codes.dart'
+ show templateExtensionMemberConflictsWithObjectMember;
+import '../kernel/kernel_helper.dart';
+import '../problems.dart';
+import '../scope.dart';
+import '../util/helpers.dart';
+import 'source_field_builder.dart';
+import 'source_library_builder.dart';
+import 'source_member_builder.dart';
+import 'source_procedure_builder.dart';
+
+mixin SourceDeclarationBuilderMixin
+ implements DeclarationBuilder, DeclarationBuilderMixin {
+ @override
+ SourceLibraryBuilder get libraryBuilder;
+
+ @override
+ Uri get fileUri;
+
+ /// Returns the [Annotatable] node that holds the annotations declared on
+ /// this declaration or its augmentations.
+ Annotatable get annotatable;
+
+ /// Builds the [Extension] for this extension build and inserts the members
+ /// into the [Library] of [libraryBuilder].
+ ///
+ /// [addMembersToLibrary] is `true` if the extension members should be added
+ /// to the library. This is `false` if the extension is in conflict with
+ /// another library member. In this case, the extension member should not be
+ /// added to the library to avoid name clashes with other members in the
+ /// library.
+ void buildInternal(LibraryBuilder coreLibrary,
+ {required bool addMembersToLibrary}) {
+ SourceLibraryBuilder.checkMemberConflicts(libraryBuilder, scope,
+ checkForInstanceVsStaticConflict: true,
+ checkForMethodVsSetterConflict: true);
+
+ ClassBuilder objectClassBuilder =
+ coreLibrary.lookupLocalMember('Object', required: true) as ClassBuilder;
+
+ void buildBuilders(String name, Builder declaration) {
+ Builder? objectGetter = objectClassBuilder.lookupLocalMember(name);
+ Builder? objectSetter =
+ objectClassBuilder.lookupLocalMember(name, setter: true);
+ if (objectGetter != null && !objectGetter.isStatic ||
+ objectSetter != null && !objectSetter.isStatic) {
+ addProblem(
+ // TODO(johnniwinther): Use a different error message for views.
+ templateExtensionMemberConflictsWithObjectMember
+ .withArguments(name),
+ declaration.charOffset,
+ name.length);
+ }
+ if (declaration.parent != this) {
+ if (fileUri != declaration.parent!.fileUri) {
+ unexpected("$fileUri", "${declaration.parent!.fileUri}", charOffset,
+ fileUri);
+ } else {
+ unexpected(fullNameForErrors, declaration.parent!.fullNameForErrors,
+ charOffset, fileUri);
+ }
+ } else if (declaration is SourceMemberBuilder) {
+ SourceMemberBuilder memberBuilder = declaration;
+ memberBuilder
+ .buildOutlineNodes((Member member, BuiltMemberKind memberKind) {
+ _buildMember(memberBuilder, member, memberKind,
+ addMembersToLibrary: addMembersToLibrary);
+ });
+ } else {
+ unhandled("${declaration.runtimeType}", "buildBuilders",
+ declaration.charOffset, declaration.fileUri);
+ }
+ }
+
+ scope.unfilteredNameIterator.forEach(buildBuilders);
+ }
+
+ int buildBodyNodes({required bool addMembersToLibrary}) {
+ int count = 0;
+ Iterator<SourceMemberBuilder> iterator =
+ scope.filteredIterator<SourceMemberBuilder>(
+ parent: this, includeDuplicates: false, includeAugmentations: true);
+ while (iterator.moveNext()) {
+ SourceMemberBuilder declaration = iterator.current;
+ count +=
+ declaration.buildBodyNodes((Member member, BuiltMemberKind kind) {
+ _buildMember(declaration, member, kind,
+ addMembersToLibrary: addMembersToLibrary);
+ });
+ }
+ return count;
+ }
+
+ void checkTypesInOutline(TypeEnvironment typeEnvironment) {
+ forEach((String name, Builder builder) {
+ if (builder is SourceFieldBuilder) {
+ // Check fields.
+ libraryBuilder.checkTypesInField(builder, typeEnvironment);
+ } else if (builder is SourceProcedureBuilder) {
+ // Check procedures
+ libraryBuilder.checkTypesInFunctionBuilder(builder, typeEnvironment);
+ if (builder.isGetter) {
+ Builder? setterDeclaration =
+ scope.lookupLocalMember(builder.name, setter: true);
+ if (setterDeclaration != null) {
+ libraryBuilder.checkGetterSetterTypes(builder,
+ setterDeclaration as ProcedureBuilder, typeEnvironment);
+ }
+ }
+ } else {
+ assert(false, "Unexpected member: $builder.");
+ }
+ });
+ }
+
+ void buildOutlineExpressions(
+ ClassHierarchy classHierarchy,
+ List<DelayedActionPerformer> delayedActionPerformers,
+ List<DelayedDefaultValueCloner> delayedDefaultValueCloners) {
+ MetadataBuilder.buildAnnotations(annotatable, metadata, libraryBuilder,
+ this, null, fileUri, libraryBuilder.scope);
+ if (typeParameters != null) {
+ for (int i = 0; i < typeParameters!.length; i++) {
+ typeParameters![i].buildOutlineExpressions(libraryBuilder, this, null,
+ classHierarchy, delayedActionPerformers, scope.parent!);
+ }
+ }
+
+ Iterator<SourceMemberBuilder> iterator =
+ scope.filteredIterator<SourceMemberBuilder>(
+ parent: this, includeDuplicates: false, includeAugmentations: true);
+ while (iterator.moveNext()) {
+ iterator.current.buildOutlineExpressions(
+ classHierarchy, delayedActionPerformers, delayedDefaultValueCloners);
+ }
+ }
+
+ void _buildMember(SourceMemberBuilder memberBuilder, Member member,
+ BuiltMemberKind memberKind,
+ {required bool addMembersToLibrary}) {
+ if (addMembersToLibrary &&
+ !memberBuilder.isPatch &&
+ !memberBuilder.isDuplicate &&
+ !memberBuilder.isConflictingSetter) {
+ Reference memberReference;
+ if (member is Field) {
+ libraryBuilder.library.addField(member);
+ memberReference = member.fieldReference;
+ } else if (member is Procedure) {
+ libraryBuilder.library.addProcedure(member);
+ memberReference = member.reference;
+ } else {
+ unhandled("${member.runtimeType}", "buildBuilders", member.fileOffset,
+ member.fileUri);
+ }
+ addMemberDescriptorInternal(
+ memberBuilder, member, memberKind, memberReference);
+ }
+ }
+
+ /// Adds a descriptor for [member] to this declaration.
+ void addMemberDescriptorInternal(SourceMemberBuilder memberBuilder,
+ Member member, BuiltMemberKind memberKind, Reference memberReference);
+}
diff --git a/pkg/front_end/lib/src/fasta/source/source_extension_builder.dart b/pkg/front_end/lib/src/fasta/source/source_extension_builder.dart
index 8faedba..059e1cc 100644
--- a/pkg/front_end/lib/src/fasta/source/source_extension_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_extension_builder.dart
@@ -3,38 +3,31 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:kernel/ast.dart';
-import 'package:kernel/class_hierarchy.dart';
-import 'package:kernel/type_environment.dart';
import '../../base/common.dart';
import '../builder/builder.dart';
-import '../builder/class_builder.dart';
import '../builder/extension_builder.dart';
import '../builder/library_builder.dart';
import '../builder/metadata_builder.dart';
-import '../builder/procedure_builder.dart';
import '../builder/type_builder.dart';
import '../builder/type_variable_builder.dart';
import '../fasta_codes.dart'
show
messagePatchDeclarationMismatch,
messagePatchDeclarationOrigin,
- noLength,
- templateExtensionMemberConflictsWithObjectMember;
-import '../kernel/kernel_helper.dart';
+ noLength;
import '../operator.dart';
import '../problems.dart';
import '../scope.dart';
-import '../util/helpers.dart';
import 'name_scheme.dart';
-import 'source_field_builder.dart';
+import 'source_builder_mixins.dart';
import 'source_library_builder.dart';
import 'source_member_builder.dart';
-import 'source_procedure_builder.dart';
const String extensionThisName = '#this';
-class SourceExtensionBuilder extends ExtensionBuilderImpl {
+class SourceExtensionBuilder extends ExtensionBuilderImpl
+ with SourceDeclarationBuilderMixin {
final Extension _extension;
SourceExtensionBuilder? _origin;
@@ -80,12 +73,12 @@
extensionName.attachExtension(_extension);
}
- bool get isUnnamedExtension => extensionName.isUnnamedExtension;
-
@override
SourceLibraryBuilder get libraryBuilder =>
super.libraryBuilder as SourceLibraryBuilder;
+ bool get isUnnamedExtension => extensionName.isUnnamedExtension;
+
@override
SourceExtensionBuilder get origin => _origin ?? this;
@@ -97,6 +90,9 @@
@override
Extension get extension => isPatch ? origin._extension : _extension;
+ @override
+ Annotatable get annotatable => extension;
+
/// Builds the [Extension] for this extension build and inserts the members
/// into the [Library] of [libraryBuilder].
///
@@ -111,108 +107,52 @@
extensionTypeShowHideClauseBuilder.buildAndStoreTypes(
_extension, libraryBuilder);
- SourceLibraryBuilder.checkMemberConflicts(libraryBuilder, scope,
- checkForInstanceVsStaticConflict: true,
- checkForMethodVsSetterConflict: true);
-
- ClassBuilder objectClassBuilder =
- coreLibrary.lookupLocalMember('Object', required: true) as ClassBuilder;
-
- void buildBuilders(String name, Builder declaration) {
- Builder? objectGetter = objectClassBuilder.lookupLocalMember(name);
- Builder? objectSetter =
- objectClassBuilder.lookupLocalMember(name, setter: true);
- if (objectGetter != null && !objectGetter.isStatic ||
- objectSetter != null && !objectSetter.isStatic) {
- addProblem(
- templateExtensionMemberConflictsWithObjectMember
- .withArguments(name),
- declaration.charOffset,
- name.length);
- }
- if (declaration.parent != this) {
- if (fileUri != declaration.parent!.fileUri) {
- unexpected("$fileUri", "${declaration.parent!.fileUri}", charOffset,
- fileUri);
- } else {
- unexpected(fullNameForErrors, declaration.parent!.fullNameForErrors,
- charOffset, fileUri);
- }
- } else if (declaration is SourceMemberBuilder) {
- SourceMemberBuilder memberBuilder = declaration;
- memberBuilder
- .buildOutlineNodes((Member member, BuiltMemberKind memberKind) {
- _buildMember(memberBuilder, member, memberKind,
- addMembersToLibrary: addMembersToLibrary);
- });
- } else {
- unhandled("${declaration.runtimeType}", "buildBuilders",
- declaration.charOffset, declaration.fileUri);
- }
- }
-
- scope.unfilteredNameIterator.forEach(buildBuilders);
+ buildInternal(coreLibrary, addMembersToLibrary: addMembersToLibrary);
return _extension;
}
- void _buildMember(SourceMemberBuilder memberBuilder, Member member,
- BuiltMemberKind memberKind,
- {required bool addMembersToLibrary}) {
- if (addMembersToLibrary &&
- !memberBuilder.isPatch &&
- !memberBuilder.isDuplicate &&
- !memberBuilder.isConflictingSetter) {
- ExtensionMemberKind kind;
- String name = memberBuilder.name;
- switch (memberKind) {
- case BuiltMemberKind.Constructor:
- case BuiltMemberKind.RedirectingFactory:
- case BuiltMemberKind.Field:
- case BuiltMemberKind.Method:
- unhandled("${member.runtimeType}:${memberKind}", "buildMembers",
- memberBuilder.charOffset, memberBuilder.fileUri);
- case BuiltMemberKind.ExtensionField:
- case BuiltMemberKind.LateIsSetField:
- kind = ExtensionMemberKind.Field;
- break;
- case BuiltMemberKind.ExtensionMethod:
- kind = ExtensionMemberKind.Method;
- break;
- case BuiltMemberKind.ExtensionGetter:
- case BuiltMemberKind.LateGetter:
- kind = ExtensionMemberKind.Getter;
- break;
- case BuiltMemberKind.ExtensionSetter:
- case BuiltMemberKind.LateSetter:
- kind = ExtensionMemberKind.Setter;
- break;
- case BuiltMemberKind.ExtensionOperator:
- kind = ExtensionMemberKind.Operator;
- break;
- case BuiltMemberKind.ExtensionTearOff:
- kind = ExtensionMemberKind.TearOff;
- break;
- }
- // ignore: unnecessary_null_comparison
- assert(kind != null);
- Reference memberReference;
- if (member is Field) {
- libraryBuilder.library.addField(member);
- memberReference = member.fieldReference;
- } else if (member is Procedure) {
- libraryBuilder.library.addProcedure(member);
- memberReference = member.reference;
- } else {
- unhandled("${member.runtimeType}", "buildBuilders", member.fileOffset,
- member.fileUri);
- }
- extension.members.add(new ExtensionMemberDescriptor(
- name: new Name(name, libraryBuilder.library),
- member: memberReference,
- isStatic: memberBuilder.isStatic,
- kind: kind));
+ @override
+ void addMemberDescriptorInternal(SourceMemberBuilder memberBuilder,
+ Member member, BuiltMemberKind memberKind, Reference memberReference) {
+ String name = memberBuilder.name;
+ ExtensionMemberKind kind;
+ switch (memberKind) {
+ case BuiltMemberKind.Constructor:
+ case BuiltMemberKind.RedirectingFactory:
+ case BuiltMemberKind.Field:
+ case BuiltMemberKind.Method:
+ unhandled("${member.runtimeType}:${memberKind}", "buildMembers",
+ memberBuilder.charOffset, memberBuilder.fileUri);
+ case BuiltMemberKind.ExtensionField:
+ case BuiltMemberKind.LateIsSetField:
+ kind = ExtensionMemberKind.Field;
+ break;
+ case BuiltMemberKind.ExtensionMethod:
+ kind = ExtensionMemberKind.Method;
+ break;
+ case BuiltMemberKind.ExtensionGetter:
+ case BuiltMemberKind.LateGetter:
+ kind = ExtensionMemberKind.Getter;
+ break;
+ case BuiltMemberKind.ExtensionSetter:
+ case BuiltMemberKind.LateSetter:
+ kind = ExtensionMemberKind.Setter;
+ break;
+ case BuiltMemberKind.ExtensionOperator:
+ kind = ExtensionMemberKind.Operator;
+ break;
+ case BuiltMemberKind.ExtensionTearOff:
+ kind = ExtensionMemberKind.TearOff;
+ break;
}
+ // ignore: unnecessary_null_comparison
+ assert(kind != null);
+ extension.members.add(new ExtensionMemberDescriptor(
+ name: new Name(name, libraryBuilder.library),
+ member: memberReference,
+ isStatic: memberBuilder.isStatic,
+ kind: kind));
}
@override
@@ -222,6 +162,9 @@
if (retainDataForTesting) {
patchForTesting = patch;
}
+ // TODO(johnniwinther): Check that type parameters and on-type match
+ // with origin declaration.
+
scope.forEachLocalMember((String name, Builder member) {
Builder? memberPatch =
patch.scope.lookupLocalMember(name, setter: false);
@@ -236,9 +179,6 @@
member.applyPatch(memberPatch);
}
});
-
- // TODO(johnniwinther): Check that type parameters and on-type match
- // with origin declaration.
} else {
libraryBuilder.addProblem(messagePatchDeclarationMismatch,
patch.charOffset, noLength, patch.fileUri, context: [
@@ -247,66 +187,6 @@
]);
}
}
-
- int buildBodyNodes({required bool addMembersToLibrary}) {
- int count = 0;
- Iterator<SourceMemberBuilder> iterator =
- scope.filteredIterator<SourceMemberBuilder>(
- parent: this, includeDuplicates: false, includeAugmentations: true);
- while (iterator.moveNext()) {
- SourceMemberBuilder declaration = iterator.current;
- count +=
- declaration.buildBodyNodes((Member member, BuiltMemberKind kind) {
- _buildMember(declaration, member, kind,
- addMembersToLibrary: addMembersToLibrary);
- });
- }
- return count;
- }
-
- void checkTypesInOutline(TypeEnvironment typeEnvironment) {
- forEach((String name, Builder builder) {
- if (builder is SourceFieldBuilder) {
- // Check fields.
- libraryBuilder.checkTypesInField(builder, typeEnvironment);
- } else if (builder is SourceProcedureBuilder) {
- // Check procedures
- libraryBuilder.checkTypesInFunctionBuilder(builder, typeEnvironment);
- if (builder.isGetter) {
- Builder? setterDeclaration =
- scope.lookupLocalMember(builder.name, setter: true);
- if (setterDeclaration != null) {
- libraryBuilder.checkGetterSetterTypes(builder,
- setterDeclaration as ProcedureBuilder, typeEnvironment);
- }
- }
- } else {
- assert(false, "Unexpected member: $builder.");
- }
- });
- }
-
- void buildOutlineExpressions(
- ClassHierarchy classHierarchy,
- List<DelayedActionPerformer> delayedActionPerformers,
- List<DelayedDefaultValueCloner> delayedDefaultValueCloners) {
- MetadataBuilder.buildAnnotations(isPatch ? origin.extension : extension,
- metadata, libraryBuilder, this, null, fileUri, libraryBuilder.scope);
- if (typeParameters != null) {
- for (int i = 0; i < typeParameters!.length; i++) {
- typeParameters![i].buildOutlineExpressions(libraryBuilder, this, null,
- classHierarchy, delayedActionPerformers, scope.parent!);
- }
- }
-
- Iterator<SourceMemberBuilder> iterator =
- scope.filteredIterator<SourceMemberBuilder>(
- parent: this, includeDuplicates: false, includeAugmentations: true);
- while (iterator.moveNext()) {
- iterator.current.buildOutlineExpressions(
- classHierarchy, delayedActionPerformers, delayedDefaultValueCloners);
- }
- }
}
class ExtensionTypeShowHideClauseBuilder {
diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
index 6c7ccff..31c3b72 100644
--- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
@@ -55,6 +55,7 @@
import '../builder/type_builder.dart';
import '../builder/type_declaration_builder.dart';
import '../builder/type_variable_builder.dart';
+import '../builder/view_builder.dart';
import '../builder/void_type_declaration_builder.dart';
import '../combinator.dart' show CombinatorBuilder;
import '../configuration.dart' show Configuration;
@@ -117,6 +118,7 @@
import 'source_member_builder.dart';
import 'source_procedure_builder.dart';
import 'source_type_alias_builder.dart';
+import 'source_view_builder.dart';
class SourceLibraryBuilder extends LibraryBuilderImpl {
static const String MALFORMED_URI_SCHEME = "org-dartlang-malformed-uri";
@@ -2160,6 +2162,77 @@
getterReference: referenceFrom?.reference);
}
+ void addViewDeclaration(
+ List<MetadataBuilder>? metadata,
+ int modifiers,
+ String name,
+ List<TypeVariableBuilder>? typeVariables,
+ int startOffset,
+ int nameOffset,
+ int endOffset) {
+ // Nested declaration began in `OutlineBuilder.beginExtensionDeclaration`.
+ TypeParameterScopeBuilder declaration =
+ endNestedDeclaration(TypeParameterScopeKind.viewDeclaration, name)
+ ..resolveNamedTypes(typeVariables, this);
+ assert(declaration.parent == _libraryTypeParameterScopeBuilder);
+ Map<String, Builder> members = declaration.members!;
+ Map<String, MemberBuilder> constructors = declaration.constructors!;
+ Map<String, MemberBuilder> setters = declaration.setters!;
+
+ Scope classScope = new Scope(
+ local: members,
+ setters: setters,
+ parent: scope.withTypeVariables(typeVariables),
+ debugName: "extension $name",
+ isModifiable: false);
+
+ Extension? referenceFrom = referencesFromIndexed?.lookupExtension(name);
+
+ ViewBuilder viewBuilder = new SourceViewBuilder(
+ metadata,
+ modifiers,
+ declaration.name,
+ typeVariables,
+ classScope,
+ this,
+ startOffset,
+ nameOffset,
+ endOffset,
+ referenceFrom);
+ constructorReferences.clear();
+ Map<String, TypeVariableBuilder>? typeVariablesByName =
+ checkTypeVariables(typeVariables, viewBuilder);
+ void setParent(String name, MemberBuilder? member) {
+ while (member != null) {
+ member.parent = viewBuilder;
+ member = member.next as MemberBuilder?;
+ }
+ }
+
+ void setParentAndCheckConflicts(String name, Builder member) {
+ if (typeVariablesByName != null) {
+ TypeVariableBuilder? tv = typeVariablesByName[name];
+ if (tv != null) {
+ viewBuilder.addProblem(
+ templateConflictsWithTypeVariable.withArguments(name),
+ member.charOffset,
+ name.length,
+ context: [
+ messageConflictsWithTypeVariableCause.withLocation(
+ tv.fileUri!, tv.charOffset, name.length)
+ ]);
+ }
+ }
+ setParent(name, member as MemberBuilder);
+ }
+
+ members.forEach(setParentAndCheckConflicts);
+ constructors.forEach(setParentAndCheckConflicts);
+ setters.forEach(setParentAndCheckConflicts);
+ addBuilder(viewBuilder.name, viewBuilder, nameOffset,
+ getterReference: referenceFrom?.reference);
+ }
+
TypeBuilder? _applyMixins(
TypeBuilder? supertype,
MixinApplicationBuilder? mixinApplications,
@@ -3113,6 +3186,9 @@
} else if (declaration is SourceExtensionBuilder) {
declaration.buildOutlineExpressions(classHierarchy,
delayedActionPerformers, delayedDefaultValueCloners);
+ } else if (declaration is SourceViewBuilder) {
+ declaration.buildOutlineExpressions(classHierarchy,
+ delayedActionPerformers, delayedDefaultValueCloners);
} else if (declaration is SourceMemberBuilder) {
declaration.buildOutlineExpressions(classHierarchy,
delayedActionPerformers, delayedDefaultValueCloners);
@@ -3153,6 +3229,12 @@
}
library.addExtension(extension);
}
+ } else if (declaration is SourceViewBuilder) {
+ View view = declaration.build(coreLibrary,
+ addMembersToLibrary: !declaration.isDuplicate);
+ if (!declaration.isPatch && !declaration.isDuplicate) {
+ library.addView(view);
+ }
} else if (declaration is SourceMemberBuilder) {
declaration
.buildOutlineNodes((Member member, BuiltMemberKind memberKind) {
@@ -3822,13 +3904,11 @@
count += computeDefaultTypesForVariables(declaration.typeVariables,
inErrorRecovery: issues.isNotEmpty);
- Iterator<MemberBuilder> constructorIterator =
- declaration.constructorScope.filteredIterator(
- parent: declaration,
- includeDuplicates: false,
- includeAugmentations: true);
- while (constructorIterator.moveNext()) {
- MemberBuilder member = constructorIterator.current;
+ Iterator<MemberBuilder> iterator = declaration.constructorScope
+ .filteredIterator(
+ includeDuplicates: false, includeAugmentations: true);
+ while (iterator.moveNext()) {
+ MemberBuilder member = iterator.current;
List<FormalParameterBuilder>? formals;
if (member is SourceFactoryBuilder) {
assert(member.isFactory,
@@ -3871,7 +3951,7 @@
}
}
}
- } else if (declaration is TypeAliasBuilder) {
+ } else if (declaration is SourceTypeAliasBuilder) {
List<NonSimplicityIssue> issues = getNonSimplicityIssuesForDeclaration(
declaration,
performErrorRecovery: true);
@@ -3906,12 +3986,7 @@
count += computeDefaultTypesForVariables(declaration.typeParameters,
inErrorRecovery: issues.isNotEmpty);
- Iterator<Builder> memberIterator = declaration.scope.filteredIterator(
- parent: declaration,
- includeDuplicates: false,
- includeAugmentations: true);
- while (memberIterator.moveNext()) {
- Builder member = memberIterator.current;
+ declaration.forEach((String name, Builder member) {
if (member is SourceProcedureBuilder) {
processSourceProcedureBuilder(member);
} else if (member is SourceFieldBuilder) {
@@ -3923,7 +3998,28 @@
throw new StateError(
"Unexpected extension member $member (${member.runtimeType}).");
}
- }
+ });
+ } else if (declaration is SourceViewBuilder) {
+ List<NonSimplicityIssue> issues = getNonSimplicityIssuesForDeclaration(
+ declaration,
+ performErrorRecovery: true);
+ reportIssues(issues);
+ count += computeDefaultTypesForVariables(declaration.typeParameters,
+ inErrorRecovery: issues.isNotEmpty);
+
+ declaration.forEach((String name, Builder member) {
+ if (member is SourceProcedureBuilder) {
+ processSourceProcedureBuilder(member);
+ } else if (member is SourceFieldBuilder) {
+ if (member.type is! OmittedTypeBuilder) {
+ _recursivelyReportGenericFunctionTypesAsBoundsForType(
+ member.type);
+ }
+ } else {
+ throw new StateError(
+ "Unexpected extension member $member (${member.runtimeType}).");
+ }
+ });
} else if (declaration is SourceFieldBuilder) {
if (declaration.type is! OmittedTypeBuilder) {
List<NonSimplicityIssue> issues =
@@ -4016,6 +4112,9 @@
} else if (builder is SourceExtensionBuilder) {
count +=
builder.buildBodyNodes(addMembersToLibrary: !builder.isDuplicate);
+ } else if (builder is SourceViewBuilder) {
+ count +=
+ builder.buildBodyNodes(addMembersToLibrary: !builder.isDuplicate);
} else if (builder is SourceClassBuilder) {
count += builder.buildBodyNodes();
} else if (builder is SourceTypeAliasBuilder) {
@@ -4486,6 +4585,8 @@
declaration.checkTypesInOutline(typeEnvironment);
} else if (declaration is SourceExtensionBuilder) {
declaration.checkTypesInOutline(typeEnvironment);
+ } else if (declaration is SourceViewBuilder) {
+ declaration.checkTypesInOutline(typeEnvironment);
} else if (declaration is SourceTypeAliasBuilder) {
// Do nothing.
} else {
@@ -4922,6 +5023,7 @@
unnamedMixinApplication,
namedMixinApplication,
extensionDeclaration,
+ viewDeclaration,
typedef,
staticMethod,
instanceMethod,
@@ -5062,6 +5164,18 @@
_typeVariables = typeVariables;
}
+ /// Registers that this builder is preparing for a view declaration with the
+ /// given [name] and [typeVariables] located [charOffset].
+ void markAsViewDeclaration(
+ String name, int charOffset, List<TypeVariableBuilder>? typeVariables) {
+ assert(_kind == TypeParameterScopeKind.classOrNamedMixinApplication,
+ "Unexpected declaration kind: $_kind");
+ _kind = TypeParameterScopeKind.viewDeclaration;
+ _name = name;
+ _charOffset = charOffset;
+ _typeVariables = typeVariables;
+ }
+
/// Returns `true` if this scope builder is for an unnamed extension
/// declaration.
bool get isUnnamedExtension => extensionName?.isUnnamedExtension ?? false;
diff --git a/pkg/front_end/lib/src/fasta/source/source_view_builder.dart b/pkg/front_end/lib/src/fasta/source/source_view_builder.dart
new file mode 100644
index 0000000..e9424e2
--- /dev/null
+++ b/pkg/front_end/lib/src/fasta/source/source_view_builder.dart
@@ -0,0 +1,170 @@
+// Copyright (c) 2022, 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:front_end/src/fasta/source/source_builder_mixins.dart';
+import 'package:kernel/ast.dart';
+
+import '../../base/common.dart';
+import '../builder/builder.dart';
+import '../builder/library_builder.dart';
+import '../builder/metadata_builder.dart';
+import '../builder/type_variable_builder.dart';
+import '../builder/view_builder.dart';
+import '../fasta_codes.dart'
+ show
+ messagePatchDeclarationMismatch,
+ messagePatchDeclarationOrigin,
+ noLength;
+import '../problems.dart';
+import '../scope.dart';
+import 'source_library_builder.dart';
+import 'source_member_builder.dart';
+
+class SourceViewBuilder extends ViewBuilderImpl
+ with SourceDeclarationBuilderMixin {
+ final View _view;
+
+ SourceViewBuilder? _origin;
+ SourceViewBuilder? patchForTesting;
+
+ MergedClassMemberScope? _mergedScope;
+
+ @override
+ final List<TypeVariableBuilder>? typeParameters;
+
+ SourceViewBuilder(
+ List<MetadataBuilder>? metadata,
+ int modifiers,
+ String name,
+ this.typeParameters,
+ Scope scope,
+ SourceLibraryBuilder parent,
+ int startOffset,
+ int nameOffset,
+ int endOffset,
+ Extension? referenceFrom)
+ : _view = new View(
+ name: name,
+ fileUri: parent.fileUri,
+ typeParameters:
+ TypeVariableBuilder.typeParametersFromBuilders(typeParameters),
+ reference: referenceFrom?.reference)
+ ..fileOffset = nameOffset,
+ super(metadata, modifiers, name, parent, nameOffset, scope);
+
+ @override
+ SourceLibraryBuilder get libraryBuilder =>
+ super.libraryBuilder as SourceLibraryBuilder;
+
+ @override
+ SourceViewBuilder get origin => _origin ?? this;
+
+ // TODO(johnniwinther): Add merged scope for views.
+ MergedClassMemberScope get mergedScope => _mergedScope ??= isPatch
+ ? origin.mergedScope
+ : throw new UnimplementedError("SourceViewBuilder.mergedScope");
+
+ @override
+ View get view => isPatch ? origin._view : _view;
+
+ @override
+ Annotatable get annotatable => view;
+
+ /// Builds the [View] for this view builder and inserts the members
+ /// into the [Library] of [libraryBuilder].
+ ///
+ /// [addMembersToLibrary] is `true` if the view members should be added
+ /// to the library. This is `false` if the view is in conflict with
+ /// another library member. In this case, the view member should not be
+ /// added to the library to avoid name clashes with other members in the
+ /// library.
+ View build(LibraryBuilder coreLibrary, {required bool addMembersToLibrary}) {
+ // TODO(johnniwinther): Find and build the representation type.
+ _view.representationType = const DynamicType();
+
+ buildInternal(coreLibrary, addMembersToLibrary: addMembersToLibrary);
+
+ return _view;
+ }
+
+ @override
+ void addMemberDescriptorInternal(SourceMemberBuilder memberBuilder,
+ Member member, BuiltMemberKind memberKind, Reference memberReference) {
+ String name = memberBuilder.name;
+ ViewMemberKind kind;
+ switch (memberKind) {
+ case BuiltMemberKind.Constructor:
+ case BuiltMemberKind.RedirectingFactory:
+ case BuiltMemberKind.Field:
+ case BuiltMemberKind.Method:
+ unhandled("${member.runtimeType}:${memberKind}", "buildMembers",
+ memberBuilder.charOffset, memberBuilder.fileUri);
+ case BuiltMemberKind.ExtensionField:
+ case BuiltMemberKind.LateIsSetField:
+ kind = ViewMemberKind.Field;
+ break;
+ case BuiltMemberKind.ExtensionMethod:
+ kind = ViewMemberKind.Method;
+ break;
+ case BuiltMemberKind.ExtensionGetter:
+ case BuiltMemberKind.LateGetter:
+ kind = ViewMemberKind.Getter;
+ break;
+ case BuiltMemberKind.ExtensionSetter:
+ case BuiltMemberKind.LateSetter:
+ kind = ViewMemberKind.Setter;
+ break;
+ case BuiltMemberKind.ExtensionOperator:
+ kind = ViewMemberKind.Operator;
+ break;
+ case BuiltMemberKind.ExtensionTearOff:
+ kind = ViewMemberKind.TearOff;
+ break;
+ }
+ // ignore: unnecessary_null_comparison
+ assert(kind != null);
+ view.members.add(new ViewMemberDescriptor(
+ name: new Name(name, libraryBuilder.library),
+ member: memberReference,
+ isStatic: memberBuilder.isStatic,
+ kind: kind));
+ }
+
+ @override
+ void applyPatch(Builder patch) {
+ if (patch is SourceViewBuilder) {
+ patch._origin = this;
+ if (retainDataForTesting) {
+ patchForTesting = patch;
+ }
+ scope.forEachLocalMember((String name, Builder member) {
+ Builder? memberPatch =
+ patch.scope.lookupLocalMember(name, setter: false);
+ if (memberPatch != null) {
+ member.applyPatch(memberPatch);
+ }
+ });
+ scope.forEachLocalSetter((String name, Builder member) {
+ Builder? memberPatch =
+ patch.scope.lookupLocalMember(name, setter: true);
+ if (memberPatch != null) {
+ member.applyPatch(memberPatch);
+ }
+ });
+
+ // TODO(johnniwinther): Check that type parameters and on-type match
+ // with origin declaration.
+ } else {
+ libraryBuilder.addProblem(messagePatchDeclarationMismatch,
+ patch.charOffset, noLength, patch.fileUri, context: [
+ messagePatchDeclarationOrigin.withLocation(
+ fileUri, charOffset, noLength)
+ ]);
+ }
+ }
+
+ // TODO(johnniwinther): Implement representationType.
+ @override
+ DartType get representationType => throw new UnimplementedError();
+}
diff --git a/pkg/front_end/testcases/views/view_class_declaration.dart b/pkg/front_end/testcases/views/view_class_declaration.dart
index 041a686..277547e 100644
--- a/pkg/front_end/testcases/views/view_class_declaration.dart
+++ b/pkg/front_end/testcases/views/view_class_declaration.dart
@@ -4,4 +4,7 @@
mixin Mixin {}
view class Class1 {}
-view class Class2 = Object with Mixin;
\ No newline at end of file
+view class Class2 = Object with Mixin;
+view class Class3<T> {}
+
+method(Class1 c1, Class3<int> c3) {}
\ No newline at end of file
diff --git a/pkg/front_end/testcases/views/view_class_declaration.dart.strong.expect b/pkg/front_end/testcases/views/view_class_declaration.dart.strong.expect
index e7e05fa..d89018f 100644
--- a/pkg/front_end/testcases/views/view_class_declaration.dart.strong.expect
+++ b/pkg/front_end/testcases/views/view_class_declaration.dart.strong.expect
@@ -4,13 +4,13 @@
abstract class Mixin extends core::Object /*isMixinDeclaration*/ {
}
-class Class1 extends core::Object {
- synthetic constructor •() → self::Class1
- : super core::Object::•()
- ;
-}
class Class2 = core::Object with self::Mixin /*hasConstConstructor*/ {
const synthetic constructor •() → self::Class2
: super core::Object::•()
;
}
+view Class1 /* representationType = dynamic */ {
+}
+view Class3<T extends core::Object? = dynamic> /* representationType = dynamic */ {
+}
+static method method(self::Class1 c1, self::Class3<core::int> c3) → dynamic {}
diff --git a/pkg/front_end/testcases/views/view_class_declaration.dart.strong.transformed.expect b/pkg/front_end/testcases/views/view_class_declaration.dart.strong.transformed.expect
index ad230b2..42a6490 100644
--- a/pkg/front_end/testcases/views/view_class_declaration.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/views/view_class_declaration.dart.strong.transformed.expect
@@ -4,13 +4,13 @@
abstract class Mixin extends core::Object /*isMixinDeclaration*/ {
}
-class Class1 extends core::Object {
- synthetic constructor •() → self::Class1
- : super core::Object::•()
- ;
-}
class Class2 extends core::Object implements self::Mixin /*isEliminatedMixin,hasConstConstructor*/ {
const synthetic constructor •() → self::Class2
: super core::Object::•()
;
}
+view Class1 /* representationType = dynamic */ {
+}
+view Class3<T extends core::Object? = dynamic> /* representationType = dynamic */ {
+}
+static method method(self::Class1 c1, self::Class3<core::int> c3) → dynamic {}
diff --git a/pkg/front_end/testcases/views/view_class_declaration.dart.textual_outline.expect b/pkg/front_end/testcases/views/view_class_declaration.dart.textual_outline.expect
index eb7f7f0..bbb1ac9 100644
--- a/pkg/front_end/testcases/views/view_class_declaration.dart.textual_outline.expect
+++ b/pkg/front_end/testcases/views/view_class_declaration.dart.textual_outline.expect
@@ -3,3 +3,6 @@
class Class1 {}
view
class Class2 = Object with Mixin;
+view
+class Class3<T> {}
+method(Class1 c1, Class3<int> c3) {}
diff --git a/pkg/front_end/testcases/views/view_class_declaration.dart.weak.expect b/pkg/front_end/testcases/views/view_class_declaration.dart.weak.expect
index e7e05fa..d89018f 100644
--- a/pkg/front_end/testcases/views/view_class_declaration.dart.weak.expect
+++ b/pkg/front_end/testcases/views/view_class_declaration.dart.weak.expect
@@ -4,13 +4,13 @@
abstract class Mixin extends core::Object /*isMixinDeclaration*/ {
}
-class Class1 extends core::Object {
- synthetic constructor •() → self::Class1
- : super core::Object::•()
- ;
-}
class Class2 = core::Object with self::Mixin /*hasConstConstructor*/ {
const synthetic constructor •() → self::Class2
: super core::Object::•()
;
}
+view Class1 /* representationType = dynamic */ {
+}
+view Class3<T extends core::Object? = dynamic> /* representationType = dynamic */ {
+}
+static method method(self::Class1 c1, self::Class3<core::int> c3) → dynamic {}
diff --git a/pkg/front_end/testcases/views/view_class_declaration.dart.weak.modular.expect b/pkg/front_end/testcases/views/view_class_declaration.dart.weak.modular.expect
index e7e05fa..d89018f 100644
--- a/pkg/front_end/testcases/views/view_class_declaration.dart.weak.modular.expect
+++ b/pkg/front_end/testcases/views/view_class_declaration.dart.weak.modular.expect
@@ -4,13 +4,13 @@
abstract class Mixin extends core::Object /*isMixinDeclaration*/ {
}
-class Class1 extends core::Object {
- synthetic constructor •() → self::Class1
- : super core::Object::•()
- ;
-}
class Class2 = core::Object with self::Mixin /*hasConstConstructor*/ {
const synthetic constructor •() → self::Class2
: super core::Object::•()
;
}
+view Class1 /* representationType = dynamic */ {
+}
+view Class3<T extends core::Object? = dynamic> /* representationType = dynamic */ {
+}
+static method method(self::Class1 c1, self::Class3<core::int> c3) → dynamic {}
diff --git a/pkg/front_end/testcases/views/view_class_declaration.dart.weak.outline.expect b/pkg/front_end/testcases/views/view_class_declaration.dart.weak.outline.expect
index 6c64557..df84fe7 100644
--- a/pkg/front_end/testcases/views/view_class_declaration.dart.weak.outline.expect
+++ b/pkg/front_end/testcases/views/view_class_declaration.dart.weak.outline.expect
@@ -4,12 +4,14 @@
abstract class Mixin extends core::Object /*isMixinDeclaration*/ {
}
-class Class1 extends core::Object {
- synthetic constructor •() → self::Class1
- ;
-}
class Class2 = core::Object with self::Mixin /*hasConstConstructor*/ {
const synthetic constructor •() → self::Class2
: super core::Object::•()
;
}
+view Class1 /* representationType = dynamic */ {
+}
+view Class3<T extends core::Object? = dynamic> /* representationType = dynamic */ {
+}
+static method method(self::Class1 c1, self::Class3<core::int> c3) → dynamic
+ ;
diff --git a/pkg/front_end/testcases/views/view_class_declaration.dart.weak.transformed.expect b/pkg/front_end/testcases/views/view_class_declaration.dart.weak.transformed.expect
index ad230b2..42a6490 100644
--- a/pkg/front_end/testcases/views/view_class_declaration.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/views/view_class_declaration.dart.weak.transformed.expect
@@ -4,13 +4,13 @@
abstract class Mixin extends core::Object /*isMixinDeclaration*/ {
}
-class Class1 extends core::Object {
- synthetic constructor •() → self::Class1
- : super core::Object::•()
- ;
-}
class Class2 extends core::Object implements self::Mixin /*isEliminatedMixin,hasConstConstructor*/ {
const synthetic constructor •() → self::Class2
: super core::Object::•()
;
}
+view Class1 /* representationType = dynamic */ {
+}
+view Class3<T extends core::Object? = dynamic> /* representationType = dynamic */ {
+}
+static method method(self::Class1 c1, self::Class3<core::int> c3) → dynamic {}
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index cb17399..35b86f9 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -12470,10 +12470,7 @@
viewReference, typeArguments, declaredNullability);
@override
- Nullability get nullability {
- return uniteNullabilities(
- declaredNullability, view.representationType.nullability);
- }
+ Nullability get nullability => declaredNullability;
@override
DartType get resolveTypeParameterType =>
diff --git a/pkg/kernel/lib/text/ast_to_text.dart b/pkg/kernel/lib/text/ast_to_text.dart
index 233eeec..019fbe4 100644
--- a/pkg/kernel/lib/text/ast_to_text.dart
+++ b/pkg/kernel/lib/text/ast_to_text.dart
@@ -343,6 +343,10 @@
return node.name;
}
+ String getViewName(View node) {
+ return node.name;
+ }
+
String getClassReference(Class node) {
// ignore: unnecessary_null_comparison
if (node == null) return '<No Class>';
@@ -359,6 +363,14 @@
return '$library::$name';
}
+ String getViewReference(View node) {
+ // ignore: unnecessary_null_comparison
+ if (node == null) return '<No View>';
+ String name = getViewName(node);
+ String library = getLibraryReference(node.enclosingLibrary);
+ return '$library::$name';
+ }
+
String getTypedefReference(Typedef node) {
// ignore: unnecessary_null_comparison
if (node == null) return '<No Typedef>';
@@ -504,6 +516,7 @@
library.typedefs.forEach(writeNode);
library.classes.forEach(writeNode);
library.extensions.forEach(writeNode);
+ library.views.forEach(writeNode);
library.fields.forEach(writeNode);
library.procedures.forEach(writeNode);
}
@@ -984,6 +997,22 @@
throw "Neither node nor canonical name found";
}
+ void writeViewReferenceFromReference(Reference reference) {
+ writeWord(getViewReferenceFromReference(reference));
+ }
+
+ String getViewReferenceFromReference(Reference reference) {
+ // ignore: unnecessary_null_comparison
+ if (reference == null) return '<No Extension>';
+ if (reference.node != null) {
+ return getViewReference(reference.asView);
+ }
+ if (reference.canonicalName != null) {
+ return getCanonicalNameString(reference.canonicalName!);
+ }
+ throw "Neither node nor canonical name found";
+ }
+
void writeMemberReferenceFromReference(Reference? reference) {
writeWord(getMemberReferenceFromReference(reference));
}
@@ -1447,6 +1476,71 @@
}
@override
+ void visitView(View node) {
+ writeAnnotationList(node.annotations);
+ writeIndentation();
+ writeWord('view');
+ writeWord(getViewName(node));
+ writeTypeParameterList(node.typeParameters);
+ writeWord('/* representationType =');
+ writeType(node.representationType);
+ writeWord('*/');
+
+ String endLineString = ' {';
+ if (node.enclosingLibrary.fileUri != node.fileUri) {
+ endLineString += ' // from ${node.fileUri}';
+ }
+
+ endLine(endLineString);
+ ++indentation;
+ node.members.forEach((ViewMemberDescriptor descriptor) {
+ writeIndentation();
+ writeModifier(descriptor.isStatic, 'static');
+ switch (descriptor.kind) {
+ case ViewMemberKind.Constructor:
+ writeWord('constructor');
+ break;
+ case ViewMemberKind.Factory:
+ writeWord('factory');
+ break;
+ case ViewMemberKind.Method:
+ writeWord('method');
+ break;
+ case ViewMemberKind.Getter:
+ writeWord('get');
+ break;
+ case ViewMemberKind.Setter:
+ writeWord('set');
+ break;
+ case ViewMemberKind.Operator:
+ writeWord('operator');
+ break;
+ case ViewMemberKind.Field:
+ writeWord('field');
+ break;
+ case ViewMemberKind.TearOff:
+ writeWord('tearoff');
+ break;
+ }
+ writeName(descriptor.name);
+ writeSpaced('=');
+ Member member = descriptor.member.asMember;
+ if (member is Procedure) {
+ if (member.isGetter) {
+ writeWord('get');
+ } else if (member.isSetter) {
+ writeWord('set');
+ }
+ }
+ writeMemberReferenceFromReference(descriptor.member);
+ endLine(';');
+ });
+ --indentation;
+ writeIndentation();
+ endLine('}');
+ }
+
+ @override
void visitTypedef(Typedef node) {
writeAnnotationList(node.annotations);
writeIndentation();
@@ -2678,6 +2772,18 @@
}
@override
+ void visitViewType(ViewType node) {
+ writeViewReferenceFromReference(node.viewReference);
+ if (node.typeArguments.isNotEmpty) {
+ writeSymbol('<');
+ writeList(node.typeArguments, writeType);
+ writeSymbol('>');
+ state = Printer.WORD;
+ }
+ writeNullability(node.declaredNullability);
+ }
+
+ @override
void visitFutureOrType(FutureOrType node) {
writeWord('FutureOr');
writeSymbol('<');
diff --git a/pkg/vm_service/lib/src/vm_service.dart b/pkg/vm_service/lib/src/vm_service.dart
index 782a241..9f30e1c 100644
--- a/pkg/vm_service/lib/src/vm_service.dart
+++ b/pkg/vm_service/lib/src/vm_service.dart
@@ -7120,15 +7120,18 @@
/// An object that is part of a retaining path.
ObjRef? value;
- /// The offset of the retaining object in a containing list.
+ /// If `value` is a List, `parentListIndex` is the index where the previous
+ /// object on the retaining path is located.
@optional
int? parentListIndex;
- /// The key mapping to the retaining object in a containing map.
+ /// If `value` is a Map, `parentMapKey` is the key mapping to the previous
+ /// object on the retaining path.
@optional
ObjRef? parentMapKey;
- /// The name of the field containing the retaining object within an object.
+ /// If `value` is a non-List, non-Map object, `parentField` is the name of the
+ /// field containing the previous object on the retaining path.
@optional
String? parentField;
diff --git a/runtime/observatory/lib/src/elements/helpers/any_ref.dart b/runtime/observatory/lib/src/elements/helpers/any_ref.dart
index ac5e1e3..222f40d 100644
--- a/runtime/observatory/lib/src/elements/helpers/any_ref.dart
+++ b/runtime/observatory/lib/src/elements/helpers/any_ref.dart
@@ -93,6 +93,8 @@
}
} else if (ref is M.Sentinel) {
return new SentinelValueElement(ref, queue: queue).element;
+ } else if (ref is num || ref is String) {
+ return new SpanElement()..text = ref.toString();
}
throw new Exception('Unknown ref type (${ref.runtimeType})');
}
diff --git a/runtime/observatory/lib/src/elements/instance_ref.dart b/runtime/observatory/lib/src/elements/instance_ref.dart
index 819a499..01ba033 100644
--- a/runtime/observatory/lib/src/elements/instance_ref.dart
+++ b/runtime/observatory/lib/src/elements/instance_ref.dart
@@ -128,6 +128,7 @@
case M.InstanceKind.functionType:
case M.InstanceKind.typeRef:
case M.InstanceKind.typeParameter:
+ case M.InstanceKind.recordType:
return [
new AnchorElement(href: Uris.inspect(_isolate, object: _instance))
..text = _instance.name
@@ -194,12 +195,10 @@
]
];
case M.InstanceKind.mirrorReference:
- return [
- new AnchorElement(href: Uris.inspect(_isolate, object: _instance))
- ..classes = ['emphasize']
- ..text = _instance.clazz!.name
- ];
case M.InstanceKind.weakProperty:
+ case M.InstanceKind.finalizer:
+ case M.InstanceKind.weakReference:
+ case M.InstanceKind.record:
return [
new AnchorElement(href: Uris.inspect(_isolate, object: _instance))
..classes = ['emphasize']
@@ -216,6 +215,7 @@
case M.InstanceKind.mirrorReference:
case M.InstanceKind.stackTrace:
case M.InstanceKind.weakProperty:
+ case M.InstanceKind.recordType:
return true;
case M.InstanceKind.list:
case M.InstanceKind.map:
@@ -339,6 +339,18 @@
queue: _r.queue)
.element,
];
+ case M.InstanceKind.recordType:
+ final fields = _loadedInstance!.fields!.toList();
+ return [
+ for (int i = 0; i < fields.length; ++i) ...[
+ new SpanElement()..text = '${fields[i].name} = ',
+ new InstanceRefElement(
+ _isolate, fields[i].value!.asValue!, _objects,
+ queue: _r.queue)
+ .element,
+ if (i + 1 != fields.length) new BRElement(),
+ ]
+ ];
default:
return [];
}
diff --git a/runtime/observatory/lib/src/elements/instance_view.dart b/runtime/observatory/lib/src/elements/instance_view.dart
index 2063c28..4c4d690 100644
--- a/runtime/observatory/lib/src/elements/instance_view.dart
+++ b/runtime/observatory/lib/src/elements/instance_view.dart
@@ -335,9 +335,12 @@
..content = <Element>[
new DivElement()
..classes = ['memberList']
- ..children = fields
- .map<Element>((f) => member(f.decl, f.value))
- .toList()
+ ..children = fields.map<Element>((f) {
+ final name = _instance.kind == M.InstanceKind.record
+ ? f.name
+ : f.decl;
+ return member(name, f.value);
+ }).toList()
])
.element
]
diff --git a/runtime/observatory/lib/src/models/objects/instance.dart b/runtime/observatory/lib/src/models/objects/instance.dart
index 8102095..1b2d58e 100644
--- a/runtime/observatory/lib/src/models/objects/instance.dart
+++ b/runtime/observatory/lib/src/models/objects/instance.dart
@@ -126,6 +126,18 @@
/// An instance of the Dart class RawReceivePort
receivePort,
+
+ /// An instance of Record.
+ record,
+
+ /// An instance of RecordType
+ recordType,
+
+ /// An instance of Finalizer
+ finalizer,
+
+ /// An instance of WeakReference
+ weakReference,
}
bool isTypedData(InstanceKind? kind) {
@@ -463,7 +475,8 @@
abstract class BoundField {
FieldRef? get decl;
- Guarded<InstanceRef>? get value;
+ Guarded<dynamic>? get value;
+ dynamic get name;
}
abstract class NativeField {
diff --git a/runtime/observatory/lib/src/service/object.dart b/runtime/observatory/lib/src/service/object.dart
index c4af948..1a256a6 100644
--- a/runtime/observatory/lib/src/service/object.dart
+++ b/runtime/observatory/lib/src/service/object.dart
@@ -2108,7 +2108,7 @@
_map.clear();
map.forEach((k, v) => _map[k] = v);
- name = _map['name'];
+ name = _map['name']?.toString();
vmName = (_map.containsKey('_vmName') ? _map['_vmName'] : name);
}
@@ -2791,25 +2791,33 @@
return M.InstanceKind.typeRef;
case 'ReceivePort':
return M.InstanceKind.receivePort;
+ case '_RecordType':
+ return M.InstanceKind.recordType;
+ case '_Record':
+ return M.InstanceKind.record;
+ case 'Finalizer':
+ return M.InstanceKind.finalizer;
+ case 'WeakReference':
+ return M.InstanceKind.weakReference;
}
var message = 'Unrecognized instance kind: $s';
Logger.root.severe(message);
throw new ArgumentError(message);
}
-class Guarded<T extends ServiceObject> implements M.Guarded<T> {
+class Guarded<T> implements M.Guarded<T> {
bool get isValue => asValue != null;
bool get isSentinel => asSentinel != null;
final Sentinel? asSentinel;
final T? asValue;
- factory Guarded(ServiceObject obj) {
+ factory Guarded(dynamic obj) {
if (obj is Sentinel) {
return new Guarded.fromSentinel(obj);
} else if (obj is T) {
return new Guarded.fromValue(obj);
}
- throw new Exception('${obj.type} is neither Sentinel or $T');
+ throw new Exception('${obj.runtimeType} is neither Sentinel or $T');
}
Guarded.fromSentinel(this.asSentinel) : asValue = null;
@@ -2817,9 +2825,11 @@
}
class BoundField implements M.BoundField {
- final Field decl;
- final Guarded<Instance> value;
- BoundField(this.decl, value) : value = new Guarded(value);
+ final Field? decl;
+ // String|int
+ final dynamic name;
+ final Guarded<dynamic> value;
+ BoundField(this.decl, this.name, value) : value = new Guarded(value);
}
class NativeField implements M.NativeField {
@@ -2916,7 +2926,7 @@
Instance._empty(ServiceObjectOwner? owner) : super._empty(owner);
void _update(Map map, bool mapIsRef) {
- // Extract full properties.1
+ // Extract full properties.
_upgradeCollection(map, isolate);
super._update(map, mapIsRef);
@@ -2925,7 +2935,7 @@
// Coerce absence to false.
valueAsStringIsTruncated = map['valueAsStringIsTruncated'] == true;
closureFunction = map['closureFunction'];
- name = map['name'];
+ name = map['name']?.toString();
length = map['length'];
pattern = map['pattern'];
typeClass = map['typeClass'];
@@ -2958,7 +2968,7 @@
if (map['fields'] != null) {
var fields = <BoundField>[];
for (var f in map['fields']) {
- fields.add(new BoundField(f['decl'], f['value']));
+ fields.add(new BoundField(f['decl'], f['name'], f['value']));
}
this.fields = fields;
} else {
diff --git a/runtime/observatory/tests/ui/inspector.dart b/runtime/observatory/tests/ui/inspector.dart
index e8fc90a..eeb461f 100644
--- a/runtime/observatory/tests/ui/inspector.dart
+++ b/runtime/observatory/tests/ui/inspector.dart
@@ -2,6 +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.
+// @dart = 2.19
+
// See inspector.txt for expected behavior.
library manual_inspector_test;
@@ -43,10 +45,13 @@
var blockCopying;
var blockFull;
var blockFullWithChain;
+ var blockType;
var boundedType;
var capability;
var counter;
var expando;
+ var finalizer;
+ var finalizerEntry;
var float32x4;
var float64;
var float64x2;
@@ -62,7 +67,10 @@
var mirrorReference;
var portReceive;
var portSend;
+ var record;
+ var recordType;
var regex;
+ late var sentinel; // Not initialized
var smi;
var stacktrace;
var string;
@@ -76,9 +84,12 @@
var theTrue;
var type;
var typeParameter;
- var typedData;
+ var typedDataArray;
+ var typedDataView;
+ var typedDataUnmodifiableView;
var userTag;
var weakProperty;
+ var weakReference;
genStackTrace() {
try {
@@ -141,16 +152,19 @@
array[0] = 1;
array[1] = 2;
array[2] = 3;
- bigint = 1 << 65;
+ bigint = BigInt.one << 65;
blockClean = genCleanBlock();
blockCopying = genCopyingBlock();
blockFull = genFullBlock();
blockFullWithChain = genFullBlockWithChain();
+ blockType = blockClean.runtimeType;
boundedType = extractPrivateField(
reflect(new B<int>()).type.typeVariables.single, '_reflectee');
counter = new Counter("CounterName", "Counter description");
expando = new Expando("expando-name");
expando[array] = 'The weakly associated value';
+ finalizer = Finalizer<dynamic>((_){});
+ finalizer.attach(this, this);
float32x4 = new Float32x4(0.0, -1.0, 3.14, 2e28);
float64 = 3.14;
float64x2 = new Float64x2(0.0, 3.14);
@@ -170,6 +184,8 @@
mirrorReference = extractPrivateField(mirrorClass, '_reflectee');
portReceive = new RawReceivePort();
portSend = portReceive.sendPort;
+ record = (1, 2, three: 3, four: 4);
+ recordType = record.runtimeType;
regex = new RegExp("a*b+c");
smi = 7;
stacktrace = genStackTrace();
@@ -185,10 +201,13 @@
type = String;
typeParameter =
extractPrivateField(reflectClass(A).typeVariables.single, '_reflectee');
- typedData = extractPrivateField(new ByteData(64), '_typedData');
+ typedDataArray = Uint8List(32);
+ typedDataView = Uint8List.view(typedDataArray.buffer, 16);
+ typedDataUnmodifiableView = UnmodifiableUint8ListView(typedDataArray);
userTag = new UserTag("Example tag name");
weakProperty =
extractPrivateField(expando, '_data').firstWhere((e) => e != null);
+ weakReference = WeakReference(this);
Isolate.spawn(secondMain, "Hello2").then((otherIsolate) {
isolate = otherIsolate;
diff --git a/runtime/observatory/tests/ui/inspector_part.dart b/runtime/observatory/tests/ui/inspector_part.dart
index 06589e3..6222354 100644
--- a/runtime/observatory/tests/ui/inspector_part.dart
+++ b/runtime/observatory/tests/ui/inspector_part.dart
@@ -2,6 +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.
+// @dart = 2.19
+
part of manual_inspector_test;
functionInPart() {}
diff --git a/runtime/observatory_2/lib/src/elements/helpers/any_ref.dart b/runtime/observatory_2/lib/src/elements/helpers/any_ref.dart
index 0ae2930..a1f2d96 100644
--- a/runtime/observatory_2/lib/src/elements/helpers/any_ref.dart
+++ b/runtime/observatory_2/lib/src/elements/helpers/any_ref.dart
@@ -93,6 +93,8 @@
}
} else if (ref is M.Sentinel) {
return new SentinelValueElement(ref, queue: queue).element;
+ } else if (ref is num || ref is String) {
+ return new SpanElement()..text = ref.toString();
}
throw new Exception('Unknown ref type (${ref.runtimeType})');
}
diff --git a/runtime/observatory_2/lib/src/elements/instance_ref.dart b/runtime/observatory_2/lib/src/elements/instance_ref.dart
index a4b3f9e..e98148e 100644
--- a/runtime/observatory_2/lib/src/elements/instance_ref.dart
+++ b/runtime/observatory_2/lib/src/elements/instance_ref.dart
@@ -127,6 +127,7 @@
case M.InstanceKind.functionType:
case M.InstanceKind.typeRef:
case M.InstanceKind.typeParameter:
+ case M.InstanceKind.recordType:
return [
new AnchorElement(href: Uris.inspect(_isolate, object: _instance))
..text = _instance.name
@@ -192,12 +193,10 @@
]
];
case M.InstanceKind.mirrorReference:
- return [
- new AnchorElement(href: Uris.inspect(_isolate, object: _instance))
- ..classes = ['emphasize']
- ..text = _instance.clazz.name
- ];
case M.InstanceKind.weakProperty:
+ case M.InstanceKind.finalizer:
+ case M.InstanceKind.weakReference:
+ case M.InstanceKind.record:
return [
new AnchorElement(href: Uris.inspect(_isolate, object: _instance))
..classes = ['emphasize']
@@ -214,6 +213,7 @@
case M.InstanceKind.mirrorReference:
case M.InstanceKind.stackTrace:
case M.InstanceKind.weakProperty:
+ case M.InstanceKind.recordType:
return true;
case M.InstanceKind.list:
case M.InstanceKind.map:
@@ -337,6 +337,17 @@
queue: _r.queue)
.element,
];
+ case M.InstanceKind.recordType:
+ final fields = _loadedInstance.fields.toList();
+ return [
+ for (int i = 0; i < fields.length; ++i) ...[
+ new SpanElement()..text = '${fields[i].name} = ',
+ new InstanceRefElement(_isolate, fields[i].value.asValue, _objects,
+ queue: _r.queue)
+ .element,
+ if (i + 1 != fields.length) new BRElement(),
+ ]
+ ];
default:
return [];
}
diff --git a/runtime/observatory_2/lib/src/elements/instance_view.dart b/runtime/observatory_2/lib/src/elements/instance_view.dart
index fcc1812..592a978 100644
--- a/runtime/observatory_2/lib/src/elements/instance_view.dart
+++ b/runtime/observatory_2/lib/src/elements/instance_view.dart
@@ -334,9 +334,12 @@
..content = <Element>[
new DivElement()
..classes = ['memberList']
- ..children = fields
- .map<Element>((f) => member(f.decl, f.value))
- .toList()
+ ..children = fields.map<Element>((f) {
+ final name = _instance.kind == M.InstanceKind.record
+ ? f.name
+ : f.decl;
+ return member(name, f.value);
+ }).toList()
])
.element
]
diff --git a/runtime/observatory_2/lib/src/models/objects/instance.dart b/runtime/observatory_2/lib/src/models/objects/instance.dart
index b21c572..ff914bc 100644
--- a/runtime/observatory_2/lib/src/models/objects/instance.dart
+++ b/runtime/observatory_2/lib/src/models/objects/instance.dart
@@ -126,6 +126,18 @@
/// An instance of the Dart class RawReceivePort
receivePort,
+
+ /// An instance of Record.
+ record,
+
+ /// An instance of RecordType
+ recordType,
+
+ /// An instance of Finalizer
+ finalizer,
+
+ /// An instance of WeakReference
+ weakReference,
}
bool isTypedData(InstanceKind kind) {
@@ -454,7 +466,8 @@
abstract class BoundField {
FieldRef get decl;
- Guarded<InstanceRef> get value;
+ dynamic get name;
+ Guarded<dynamic> get value;
}
abstract class NativeField {
diff --git a/runtime/observatory_2/lib/src/service/object.dart b/runtime/observatory_2/lib/src/service/object.dart
index 5c9f6df..bfe5b05 100644
--- a/runtime/observatory_2/lib/src/service/object.dart
+++ b/runtime/observatory_2/lib/src/service/object.dart
@@ -2118,7 +2118,7 @@
_map.clear();
_map.addAll(map);
- name = _map['name'];
+ name = _map['name']?.toString();
vmName = (_map.containsKey('_vmName') ? _map['_vmName'] : name);
}
@@ -2800,25 +2800,33 @@
return M.InstanceKind.typeRef;
case 'ReceivePort':
return M.InstanceKind.receivePort;
+ case '_Record':
+ return M.InstanceKind.record;
+ case '_RecordType':
+ return M.InstanceKind.recordType;
+ case 'Finalizer':
+ return M.InstanceKind.finalizer;
+ case 'WeakReference':
+ return M.InstanceKind.weakReference;
}
var message = 'Unrecognized instance kind: $s';
Logger.root.severe(message);
throw new ArgumentError(message);
}
-class Guarded<T extends ServiceObject> implements M.Guarded<T> {
+class Guarded<T> implements M.Guarded<T> {
bool get isValue => asValue != null;
bool get isSentinel => asSentinel != null;
final Sentinel asSentinel;
final T asValue;
- factory Guarded(ServiceObject obj) {
+ factory Guarded(dynamic obj) {
if (obj is Sentinel) {
return new Guarded.fromSentinel(obj);
} else if (obj is T) {
return new Guarded.fromValue(obj);
}
- throw new Exception('${obj.type} is neither Sentinel or $T');
+ throw new Exception('${obj.runtimeType} is neither Sentinel or $T');
}
Guarded.fromSentinel(this.asSentinel) : asValue = null;
@@ -2827,8 +2835,10 @@
class BoundField implements M.BoundField {
final Field decl;
- final Guarded<Instance> value;
- BoundField(this.decl, value) : value = new Guarded(value);
+ // String|int
+ final dynamic name;
+ final Guarded<dynamic> value;
+ BoundField(this.decl, this.name, value) : value = new Guarded(value);
}
class NativeField implements M.NativeField {
@@ -2929,7 +2939,7 @@
Instance._empty(ServiceObjectOwner owner) : super._empty(owner);
void _update(Map map, bool mapIsRef) {
- // Extract full properties.1
+ // Extract full properties.
_upgradeCollection(map, isolate);
super._update(map, mapIsRef);
@@ -2938,7 +2948,7 @@
// Coerce absence to false.
valueAsStringIsTruncated = map['valueAsStringIsTruncated'] == true;
closureFunction = map['closureFunction'];
- name = map['name'];
+ name = map['name']?.toString();
length = map['length'];
pattern = map['pattern'];
typeClass = map['typeClass'];
@@ -2971,7 +2981,7 @@
if (map['fields'] != null) {
var fields = <BoundField>[];
for (var f in map['fields']) {
- fields.add(new BoundField(f['decl'], f['value']));
+ fields.add(new BoundField(f['decl'], f['name'], f['value']));
}
this.fields = fields;
} else {
diff --git a/runtime/vm/object_service.cc b/runtime/vm/object_service.cc
index 053c1c7..b33d279 100644
--- a/runtime/vm/object_service.cc
+++ b/runtime/vm/object_service.cc
@@ -1128,7 +1128,7 @@
Array& field_array = Array::Handle();
Field& field = Field::Handle();
- Instance& field_value = Instance::Handle();
+ Object& field_value = Object::Handle();
{
JSONArray jsarr(jsobj, "fields");
for (intptr_t i = classes.length() - 1; i >= 0; i--) {
@@ -1137,7 +1137,7 @@
for (intptr_t j = 0; j < field_array.Length(); j++) {
field ^= field_array.At(j);
if (!field.is_static()) {
- field_value ^= GetField(field);
+ field_value = GetField(field);
JSONObject jsfield(&jsarr);
jsfield.AddProperty("type", "BoundField");
jsfield.AddProperty("decl", field);
diff --git a/runtime/vm/service/service.md b/runtime/vm/service/service.md
index cc58737..8525ff0 100644
--- a/runtime/vm/service/service.md
+++ b/runtime/vm/service/service.md
@@ -3742,13 +3742,16 @@
// An object that is part of a retaining path.
@Object value;
- // The offset of the retaining object in a containing list.
+ // If `value` is a List, `parentListIndex` is the index where the previous
+ // object on the retaining path is located.
int parentListIndex [optional];
- // The key mapping to the retaining object in a containing map.
+ // If `value` is a Map, `parentMapKey` is the key mapping to the previous
+ // object on the retaining path.
@Object parentMapKey [optional];
- // The name of the field containing the retaining object within an object.
+ // If `value` is a non-List, non-Map object, `parentField` is the name of the
+ // field containing the previous object on the retaining path.
string parentField [optional];
}
```
diff --git a/sdk/lib/ffi/dynamic_library.dart b/sdk/lib/ffi/dynamic_library.dart
index 5009ab6..e31b2d7 100644
--- a/sdk/lib/ffi/dynamic_library.dart
+++ b/sdk/lib/ffi/dynamic_library.dart
@@ -68,6 +68,11 @@
/// Looks up a native function and returns it as a Dart function.
///
/// [T] is the C function signature, and [F] is the Dart function signature.
+ ///
+ /// [isLeaf] specifies whether the function is a leaf function.
+ /// A leaf function must not run Dart code or call back into the Dart VM.
+ /// Leaf calls are faster than non-leaf calls.
+ ///
/// For example:
///
/// ```c
diff --git a/sdk/lib/ffi/ffi.dart b/sdk/lib/ffi/ffi.dart
index 2668e71..98a7909 100644
--- a/sdk/lib/ffi/ffi.dart
+++ b/sdk/lib/ffi/ffi.dart
@@ -155,6 +155,10 @@
on Pointer<NativeFunction<NF>> {
/// Convert to Dart function, automatically marshalling the arguments
/// and return value.
+ ///
+ /// [isLeaf] specifies whether the function is a leaf function.
+ /// A leaf function must not run Dart code or call back into the Dart VM.
+ /// Leaf calls are faster than non-leaf calls.
external DF asFunction<@DartRepresentationOf('NF') DF extends Function>(
{bool isLeaf = false});
}
@@ -876,7 +880,13 @@
@Since('2.14')
class FfiNative<T> {
final String nativeName;
+
+ /// Specifies whether the function is a leaf function.
+ ///
+ /// A leaf function must not run Dart code or call back into the Dart VM.
+ /// Leaf calls are faster than non-leaf calls.
final bool isLeaf;
+
const FfiNative(this.nativeName, {this.isLeaf = false});
}
diff --git a/tests/ffi/regress_47594_test.dart b/tests/ffi/regress_47594_test.dart
index abd6628..4af4974 100644
--- a/tests/ffi/regress_47594_test.dart
+++ b/tests/ffi/regress_47594_test.dart
@@ -6,7 +6,7 @@
// FFI leaf calls did not mark the thread for the transition and would cause
// the stack walker to segfault when it was unable to interpret the frame.
//
-// VMOptions=--deterministic --enable-vm-service --profiler
+// VMOptions=--deterministic --enable-vm-service=0 --profiler
import 'dart:ffi';
diff --git a/tests/ffi_2/regress_47594_test.dart b/tests/ffi_2/regress_47594_test.dart
index abd6628..4af4974 100644
--- a/tests/ffi_2/regress_47594_test.dart
+++ b/tests/ffi_2/regress_47594_test.dart
@@ -6,7 +6,7 @@
// FFI leaf calls did not mark the thread for the transition and would cause
// the stack walker to segfault when it was unable to interpret the frame.
//
-// VMOptions=--deterministic --enable-vm-service --profiler
+// VMOptions=--deterministic --enable-vm-service=0 --profiler
import 'dart:ffi';
diff --git a/tests/standalone/out_of_memory_slow_growth_test.dart b/tests/standalone/out_of_memory_slow_growth_test.dart
index e534dc6..b284dee 100644
--- a/tests/standalone/out_of_memory_slow_growth_test.dart
+++ b/tests/standalone/out_of_memory_slow_growth_test.dart
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
// VMOptions=--old_gen_heap_size=20
-// VMOptions=--old_gen_heap_size=20 --enable_vm_service --pause_isolates_on_unhandled_exceptions
+// VMOptions=--old_gen_heap_size=20 --enable_vm_service=0 --pause_isolates_on_unhandled_exceptions
import "package:expect/expect.dart";
diff --git a/tests/standalone_2/out_of_memory_slow_growth_test.dart b/tests/standalone_2/out_of_memory_slow_growth_test.dart
index f624f10..ad9106c 100644
--- a/tests/standalone_2/out_of_memory_slow_growth_test.dart
+++ b/tests/standalone_2/out_of_memory_slow_growth_test.dart
@@ -5,7 +5,7 @@
// @dart = 2.9
// VMOptions=--old_gen_heap_size=20
-// VMOptions=--old_gen_heap_size=20 --enable_vm_service --pause_isolates_on_unhandled_exceptions
+// VMOptions=--old_gen_heap_size=20 --enable_vm_service=0 --pause_isolates_on_unhandled_exceptions
import "package:expect/expect.dart";
diff --git a/tools/VERSION b/tools/VERSION
index 2382c3e..e36783b 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 19
PATCH 0
-PRERELEASE 388
+PRERELEASE 389
PRERELEASE_PATCH 0