Version 2.19.0-276.0.dev
Merge 76b615b80d24fcceb87e602626a163e829cb2995 into dev
diff --git a/DEPS b/DEPS
index d86fdec..cc97cb7 100644
--- a/DEPS
+++ b/DEPS
@@ -111,7 +111,7 @@
# For more details, see https://github.com/dart-lang/sdk/issues/30164.
"dart_style_rev": "49bc3ff32b5578b6e19f8fd376d668130941ee29", # manually rev'd
- "dartdoc_rev": "71545cdf2e5cc4ac3c8e81cbff3e1c91c6516f38",
+ "dartdoc_rev": "9908cf86dd66bce43cd4953cd1010766b01aae2b",
"devtools_rev": "b21cd59f1f6bb60cacd59ba39e376d2a50d82f74",
"ffi_rev": "fb5f2667826c0900e551d19101052f84e35f41bf",
"file_rev": "b2e31cb6ef40b223701dbfa0b907fe58468484d7",
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/add_key_to_constructors.dart b/pkg/analysis_server/lib/src/services/correction/dart/add_key_to_constructors.dart
index b74156e..3e8a7fd 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/add_key_to_constructors.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/add_key_to_constructors.dart
@@ -25,94 +25,11 @@
var node = this.node;
var parent = node.parent;
if (node is ClassDeclaration) {
- // The lint is on the name of the class when there are no constructors.
- var targetLocation =
- utils.prepareNewConstructorLocation(resolvedResult.session, node);
- if (targetLocation == null) {
- return;
- }
- var keyType = await _getKeyType();
- if (keyType == null) {
- return;
- }
- var className = node.name.lexeme;
- var constructors = node.declaredElement?.supertype?.constructors;
- if (constructors == null) {
- return;
- }
-
- var canBeConst = _canBeConst(node, constructors);
- await builder.addDartFileEdit(file, (builder) {
- builder.addInsertion(targetLocation.offset, (builder) {
- builder.write(targetLocation.prefix);
- if (canBeConst) {
- builder.write('const ');
- }
- builder.write(className);
- builder.write('({');
- if (libraryElement.featureSet.isEnabled(Feature.super_parameters)) {
- builder.write('super.key});');
- } else {
- builder.writeType(keyType);
- builder.write(' key}) : super(key: key);');
- }
- builder.write(targetLocation.suffix);
- });
- });
+ await _computeClassDeclaration(builder, node);
+ } else if (node is ConstructorDeclaration) {
+ await _computeConstructorDeclaration(builder, node);
} else if (parent is ConstructorDeclaration) {
- // The lint is on a constructor when that constructor doesn't have a `key`
- // parameter.
- var keyType = await _getKeyType();
- if (keyType == null) {
- return;
- }
- var superParameters =
- libraryElement.featureSet.isEnabled(Feature.super_parameters);
-
- void writeKey(DartEditBuilder builder) {
- if (superParameters) {
- builder.write('super.key');
- } else {
- builder.writeType(keyType);
- builder.write(' key');
- }
- }
-
- var parameterList = parent.parameters;
- var parameters = parameterList.parameters;
- if (parameters.isEmpty) {
- // There are no parameters, so add the first parameter.
- await builder.addDartFileEdit(file, (builder) {
- builder.addInsertion(parameterList.leftParenthesis.end, (builder) {
- builder.write('{');
- writeKey(builder);
- builder.write('}');
- });
- _updateSuper(builder, parent, superParameters);
- });
- return;
- }
- var leftDelimiter = parameterList.leftDelimiter;
- if (leftDelimiter == null) {
- // There are no named parameters, so add the delimiters.
- await builder.addDartFileEdit(file, (builder) {
- builder.addInsertion(parameters.last.end, (builder) {
- builder.write(', {');
- writeKey(builder);
- builder.write('}');
- });
- _updateSuper(builder, parent, superParameters);
- });
- } else if (leftDelimiter.type == TokenType.OPEN_CURLY_BRACKET) {
- // There are other named parameters, so add the new named parameter.
- await builder.addDartFileEdit(file, (builder) {
- builder.addInsertion(leftDelimiter.end, (builder) {
- writeKey(builder);
- builder.write(', ');
- });
- _updateSuper(builder, parent, superParameters);
- });
- }
+ await _computeConstructorDeclaration(builder, parent);
}
}
@@ -142,6 +59,101 @@
return true;
}
+ /// The lint is on the name of the class when there are no constructors.
+ Future<void> _computeClassDeclaration(
+ ChangeBuilder builder, ClassDeclaration node) async {
+ var targetLocation =
+ utils.prepareNewConstructorLocation(resolvedResult.session, node);
+ if (targetLocation == null) {
+ return;
+ }
+ var keyType = await _getKeyType();
+ if (keyType == null) {
+ return;
+ }
+ var className = node.name.lexeme;
+ var constructors = node.declaredElement?.supertype?.constructors;
+ if (constructors == null) {
+ return;
+ }
+
+ var canBeConst = _canBeConst(node, constructors);
+ await builder.addDartFileEdit(file, (builder) {
+ builder.addInsertion(targetLocation.offset, (builder) {
+ builder.write(targetLocation.prefix);
+ if (canBeConst) {
+ builder.write('const ');
+ }
+ builder.write(className);
+ builder.write('({');
+ if (libraryElement.featureSet.isEnabled(Feature.super_parameters)) {
+ builder.write('super.key});');
+ } else {
+ builder.writeType(keyType);
+ builder.write(' key}) : super(key: key);');
+ }
+ builder.write(targetLocation.suffix);
+ });
+ });
+ }
+
+ /// The lint is on a constructor when that constructor doesn't have a `key`
+ /// parameter.
+ Future<void> _computeConstructorDeclaration(
+ ChangeBuilder builder, ConstructorDeclaration node) async {
+ var keyType = await _getKeyType();
+ if (keyType == null) {
+ return;
+ }
+ var superParameters =
+ libraryElement.featureSet.isEnabled(Feature.super_parameters);
+
+ void writeKey(DartEditBuilder builder) {
+ if (superParameters) {
+ builder.write('super.key');
+ } else {
+ builder.writeType(keyType);
+ builder.write(' key');
+ }
+ }
+
+ var parameterList = node.parameters;
+ var parameters = parameterList.parameters;
+ if (parameters.isEmpty) {
+ // There are no parameters, so add the first parameter.
+ await builder.addDartFileEdit(file, (builder) {
+ builder.addInsertion(parameterList.leftParenthesis.end, (builder) {
+ builder.write('{');
+ writeKey(builder);
+ builder.write('}');
+ });
+ _updateSuper(builder, node, superParameters);
+ });
+ return;
+ }
+ var leftDelimiter = parameterList.leftDelimiter;
+ if (leftDelimiter == null) {
+ // There are no named parameters, so add the delimiters.
+ await builder.addDartFileEdit(file, (builder) {
+ builder.addInsertion(parameters.last.end, (builder) {
+ builder.write(', {');
+ writeKey(builder);
+ builder.write('}');
+ });
+ _updateSuper(builder, node, superParameters);
+ });
+ } else if (leftDelimiter.type == TokenType.OPEN_CURLY_BRACKET) {
+ // There are other named parameters, so add the new named parameter.
+ await builder.addDartFileEdit(file, (builder) {
+ builder.addInsertion(leftDelimiter.end, (builder) {
+ writeKey(builder);
+ builder.write(', ');
+ });
+ _updateSuper(builder, node, superParameters);
+ });
+ }
+ }
+
/// Return the type for the class `Key`.
Future<DartType?> _getKeyType() async {
var keyClass = await sessionHelper.getClass(flutter.widgetsUri, 'Key');
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_key_to_constructors_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_key_to_constructors_test.dart
index 6eabd61..becec30 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/add_key_to_constructors_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_key_to_constructors_test.dart
@@ -368,6 +368,40 @@
''');
}
+ Future<void> test_namedConstructor_namedParameters_withSuper_assert() async {
+ await resolveTestCode('''
+import 'package:flutter/material.dart';
+
+class MyWidget extends StatelessWidget {
+ MyWidget.named({required String s}) : assert(s.isNotEmpty), super();
+}
+''');
+ await assertHasFix('''
+import 'package:flutter/material.dart';
+
+class MyWidget extends StatelessWidget {
+ MyWidget.named({super.key, required String s}) : assert(s.isNotEmpty);
+}
+''');
+ }
+
+ Future<void> test_namedConstructor_noParameters_withoutSuper() async {
+ await resolveTestCode('''
+import 'package:flutter/material.dart';
+
+class MyWidget extends StatelessWidget {
+ MyWidget.named();
+}
+''');
+ await assertHasFix('''
+import 'package:flutter/material.dart';
+
+class MyWidget extends StatelessWidget {
+ MyWidget.named({super.key});
+}
+''');
+ }
+
Future<void> test_super_not_constant() async {
await resolveTestCode('''
import 'package:flutter/material.dart';
diff --git a/pkg/analyzer/lib/src/dart/ast/ast.dart b/pkg/analyzer/lib/src/dart/ast/ast.dart
index ae640e2..710e64c 100644
--- a/pkg/analyzer/lib/src/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/src/dart/ast/ast.dart
@@ -10347,7 +10347,7 @@
Token leftParenthesis;
/// The syntactic elements used to compute the fields of the record.
- final NodeListImpl<Expression> _fields = NodeListImpl._();
+ final NodeListImpl<ExpressionImpl> _fields = NodeListImpl._();
@override
Token rightParenthesis;
@@ -10356,7 +10356,7 @@
RecordLiteralImpl(
{required this.constKeyword,
required this.leftParenthesis,
- required List<Expression> fields,
+ required List<ExpressionImpl> fields,
required this.rightParenthesis}) {
_fields._initialize(this, fields);
}
@@ -10368,7 +10368,7 @@
Token get endToken => rightParenthesis;
@override
- NodeList<Expression> get fields => _fields;
+ NodeList<ExpressionImpl> get fields => _fields;
@override
bool get isConst => constKeyword != null || inConstantContext;
diff --git a/pkg/analyzer/lib/src/dart/element/scope.dart b/pkg/analyzer/lib/src/dart/element/scope.dart
index be08afa..ce6320a 100644
--- a/pkg/analyzer/lib/src/dart/element/scope.dart
+++ b/pkg/analyzer/lib/src/dart/element/scope.dart
@@ -165,8 +165,12 @@
final reference = exportedReference.reference;
if (combinators.allows(reference.name)) {
final element = elementFactory.elementOfReference(reference)!;
- _add(element,
- _isFromDeprecatedExport(importedLibrary, exportedReference));
+ if (_shouldAdd(importedLibrary, element)) {
+ _add(
+ element,
+ _isFromDeprecatedExport(importedLibrary, exportedReference),
+ );
+ }
}
}
if (import.prefix is DeferredImportElementPrefix) {
@@ -192,14 +196,6 @@
}
void _add(Element element, bool isFromDeprecatedExport) {
- // TODO(scheglov) Remove when `records` feature is enabled by default.
- if (element is ClassElementImpl &&
- element.isDartCoreRecord &&
- !_container.featureSet.isEnabled(Feature.records) &&
- Feature.records.status != FeatureStatus.current) {
- return;
- }
-
if (element is PropertyAccessorElement && element.isSetter) {
_addTo(element, isFromDeprecatedExport, isSetter: true);
} else {
@@ -267,6 +263,22 @@
);
}
+ bool _shouldAdd(LibraryElementImpl importedLibrary, Element element) {
+ // It is an error for the identifier `Record`, denoting the `Record` class
+ // from `dart:core`, where that import scope name is only imported from
+ // platform libraries, to appear in a library whose language version is
+ // less than `v`; assuming that `v` is the language version in which
+ // records are released.
+ if (!_container.featureSet.isEnabled(Feature.records)) {
+ if (importedLibrary.isInSdk &&
+ element is ClassElementImpl &&
+ element.isDartCoreRecord) {
+ return false;
+ }
+ }
+ return true;
+ }
+
static void _addElement(
Set<Element> conflictingElements,
Element element,
diff --git a/pkg/analyzer/lib/src/dart/resolver/record_literal_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/record_literal_resolver.dart
index f22a407..9612b79 100644
--- a/pkg/analyzer/lib/src/dart/resolver/record_literal_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/record_literal_resolver.dart
@@ -29,7 +29,7 @@
void reportDuplicateFieldDefinitions(RecordLiteralImpl node) {
var usedNames = <String, NamedExpression>{};
for (var field in node.fields) {
- if (field is NamedExpression) {
+ if (field is NamedExpressionImpl) {
var name = field.name.label.name;
var previousField = usedNames[name];
if (previousField != null) {
@@ -53,7 +53,7 @@
}
}
for (var field in fields) {
- if (field is NamedExpression) {
+ if (field is NamedExpressionImpl) {
var nameNode = field.name.label;
var name = nameNode.name;
if (name.startsWith('_')) {
@@ -96,7 +96,7 @@
final namedFields = <RecordTypeNamedFieldImpl>[];
for (final field in node.fields) {
final fieldType = field.typeOrThrow;
- if (field is NamedExpression) {
+ if (field is NamedExpressionImpl) {
namedFields.add(
RecordTypeNamedFieldImpl(
name: field.name.label.name,
@@ -123,9 +123,17 @@
);
}
- void _resolveField(Expression field, DartType? contextType) {
+ void _resolveField(ExpressionImpl field, DartType? contextType) {
_resolver.analyzeExpression(field, contextType);
- _resolver.popRewrite();
+ field = _resolver.popRewrite()!;
+
+ // Implicit cast from `dynamic`.
+ if (contextType != null && field.typeOrThrow.isDynamic) {
+ field.staticType = contextType;
+ if (field is NamedExpressionImpl) {
+ field.expression.staticType = contextType;
+ }
+ }
}
void _resolveFields(RecordLiteralImpl node, DartType? contextType) {
@@ -133,7 +141,7 @@
var index = 0;
for (final field in node.fields) {
DartType? fieldContextType;
- if (field is NamedExpression) {
+ if (field is NamedExpressionImpl) {
final name = field.name.label.name;
fieldContextType = contextType.namedField(name)?.type;
} else {
diff --git a/pkg/analyzer/lib/src/fasta/ast_builder.dart b/pkg/analyzer/lib/src/fasta/ast_builder.dart
index d31654b..46bf913 100644
--- a/pkg/analyzer/lib/src/fasta/ast_builder.dart
+++ b/pkg/analyzer/lib/src/fasta/ast_builder.dart
@@ -2556,16 +2556,12 @@
);
}
- var elements = popTypedList<Expression>(count) ?? const [];
- List<Expression> expressions = <Expression>[];
- for (var elem in elements) {
- expressions.add(elem);
- }
+ var fields = popTypedList<ExpressionImpl>(count) ?? const [];
push(RecordLiteralImpl(
constKeyword: constKeyword,
leftParenthesis: token,
- fields: expressions,
+ fields: fields,
rightParenthesis: token.endGroup!,
));
}
diff --git a/pkg/analyzer/lib/src/summary2/ast_binary_reader.dart b/pkg/analyzer/lib/src/summary2/ast_binary_reader.dart
index 3103fcf..aee911d 100644
--- a/pkg/analyzer/lib/src/summary2/ast_binary_reader.dart
+++ b/pkg/analyzer/lib/src/summary2/ast_binary_reader.dart
@@ -1011,7 +1011,7 @@
RecordLiteralImpl _readRecordLiteral() {
var flags = _readByte();
- var fields = _readNodeList<Expression>();
+ var fields = _readNodeList<ExpressionImpl>();
var node = RecordLiteralImpl(
constKeyword: AstBinaryFlags.isConst(flags) ? Tokens.const_() : null,
leftParenthesis: Tokens.openParenthesis(),
diff --git a/pkg/analyzer/test/src/dart/resolution/property_access_test.dart b/pkg/analyzer/test/src/dart/resolution/property_access_test.dart
index 3cd60c2..8254049 100644
--- a/pkg/analyzer/test/src/dart/resolution/property_access_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/property_access_test.dart
@@ -376,6 +376,35 @@
''');
}
+ test_ofRecordType_namedField_language218() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+final r = (foo: 42);
+''');
+
+ await assertNoErrorsInCode('''
+// @dart = 2.18
+import 'a.dart';
+void f() {
+ r.foo;
+}
+''');
+
+ final node = findNode.propertyAccess('foo;');
+ assertResolvedNodeText(node, r'''
+PropertyAccess
+ target: SimpleIdentifier
+ token: r
+ staticElement: package:test/a.dart::@getter::r
+ staticType: ({int foo})
+ operator: .
+ propertyName: SimpleIdentifier
+ token: foo
+ staticElement: <null>
+ staticType: int
+ staticType: int
+''');
+ }
+
test_ofRecordType_namedField_nullAware() async {
await assertNoErrorsInCode('''
void f(({int foo})? r) {
@@ -620,6 +649,35 @@
''');
}
+ test_ofRecordType_positionalField_language218() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+final r = (0, 'bar');
+''');
+
+ await assertNoErrorsInCode(r'''
+// @dart = 2.18
+import 'a.dart';
+void f() {
+ r.$0;
+}
+''');
+
+ final node = findNode.propertyAccess(r'$0;');
+ assertResolvedNodeText(node, r'''
+PropertyAccess
+ target: SimpleIdentifier
+ token: r
+ staticElement: package:test/a.dart::@getter::r
+ staticType: (int, String)
+ operator: .
+ propertyName: SimpleIdentifier
+ token: $0
+ staticElement: <null>
+ staticType: int
+ staticType: int
+''');
+ }
+
test_ofRecordType_positionalField_letterDollarZero() async {
await assertErrorsInCode(r'''
void f((int, String) r) {
diff --git a/pkg/analyzer/test/src/dart/resolution/record_literal_test.dart b/pkg/analyzer/test/src/dart/resolution/record_literal_test.dart
index e3917f2..0a62154 100644
--- a/pkg/analyzer/test/src/dart/resolution/record_literal_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/record_literal_test.dart
@@ -14,6 +14,177 @@
@reflectiveTest
class RecordLiteralTest extends PubPackageResolutionTest {
+ test_field_rewrite_named() async {
+ await assertNoErrorsInCode(r'''
+void f((int, String) r) {
+ (f1: r.$0, );
+}
+''');
+
+ final node = findNode.recordLiteral('(f1');
+ assertResolvedNodeText(node, r'''
+RecordLiteral
+ leftParenthesis: (
+ fields
+ NamedExpression
+ name: Label
+ label: SimpleIdentifier
+ token: f1
+ staticElement: <null>
+ staticType: null
+ colon: :
+ expression: PropertyAccess
+ target: SimpleIdentifier
+ token: r
+ staticElement: self::@function::f::@parameter::r
+ staticType: (int, String)
+ operator: .
+ propertyName: SimpleIdentifier
+ token: $0
+ staticElement: <null>
+ staticType: int
+ staticType: int
+ rightParenthesis: )
+ staticType: ({int f1})
+''');
+ }
+
+ test_field_rewrite_positional() async {
+ await assertNoErrorsInCode(r'''
+void f((int, String) r) {
+ (r.$0, );
+}
+''');
+
+ final node = findNode.recordLiteral('(r');
+ assertResolvedNodeText(node, r'''
+RecordLiteral
+ leftParenthesis: (
+ fields
+ PropertyAccess
+ target: SimpleIdentifier
+ token: r
+ staticElement: self::@function::f::@parameter::r
+ staticType: (int, String)
+ operator: .
+ propertyName: SimpleIdentifier
+ token: $0
+ staticElement: <null>
+ staticType: int
+ staticType: int
+ rightParenthesis: )
+ staticType: (int)
+''');
+ }
+
+ test_hasContext_implicitCallReference_named() async {
+ await assertNoErrorsInCode(r'''
+class A {
+ void call() {}
+}
+
+final a = A();
+final ({void Function() f1}) x = (f1: a);
+''');
+
+ final node = findNode.recordLiteral('(f1');
+ assertResolvedNodeText(node, r'''
+RecordLiteral
+ leftParenthesis: (
+ fields
+ NamedExpression
+ name: Label
+ label: SimpleIdentifier
+ token: f1
+ staticElement: <null>
+ staticType: null
+ colon: :
+ expression: ImplicitCallReference
+ expression: SimpleIdentifier
+ token: a
+ staticElement: self::@getter::a
+ staticType: A
+ staticElement: self::@class::A::@method::call
+ staticType: void Function()
+ rightParenthesis: )
+ staticType: ({void Function() f1})
+''');
+ }
+
+ test_hasContext_implicitCallReference_positional() async {
+ await assertNoErrorsInCode(r'''
+class A {
+ void call() {}
+}
+
+final a = A();
+final (void Function(), ) x = (a, );
+''');
+
+ final node = findNode.recordLiteral('(a');
+ assertResolvedNodeText(node, r'''
+RecordLiteral
+ leftParenthesis: (
+ fields
+ ImplicitCallReference
+ expression: SimpleIdentifier
+ token: a
+ staticElement: self::@getter::a
+ staticType: A
+ staticElement: self::@class::A::@method::call
+ staticType: void Function()
+ rightParenthesis: )
+ staticType: (void Function())
+''');
+ }
+
+ test_hasContext_implicitCast_fromDynamic_named() async {
+ await assertNoErrorsInCode(r'''
+final dynamic a = 0;
+final ({int f1}) x = (f1: a);
+''');
+
+ final node = findNode.recordLiteral('(f1');
+ assertResolvedNodeText(node, r'''
+RecordLiteral
+ leftParenthesis: (
+ fields
+ NamedExpression
+ name: Label
+ label: SimpleIdentifier
+ token: f1
+ staticElement: <null>
+ staticType: null
+ colon: :
+ expression: SimpleIdentifier
+ token: a
+ staticElement: self::@getter::a
+ staticType: int
+ rightParenthesis: )
+ staticType: ({int f1})
+''');
+ }
+
+ test_hasContext_implicitCast_fromDynamic_positional() async {
+ await assertNoErrorsInCode(r'''
+final dynamic a = 0;
+final (int, ) x = (a, );
+''');
+
+ final node = findNode.recordLiteral('(a');
+ assertResolvedNodeText(node, r'''
+RecordLiteral
+ leftParenthesis: (
+ fields
+ SimpleIdentifier
+ token: a
+ staticElement: self::@getter::a
+ staticType: int
+ rightParenthesis: )
+ staticType: (int)
+''');
+ }
+
test_hasContext_mixed() async {
await assertNoErrorsInCode(r'''
class A1 {}
diff --git a/pkg/analyzer/test/src/diagnostics/undefined_class_test.dart b/pkg/analyzer/test/src/diagnostics/undefined_class_test.dart
index ee4beab..2749200 100644
--- a/pkg/analyzer/test/src/diagnostics/undefined_class_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/undefined_class_test.dart
@@ -215,7 +215,6 @@
}
test_Record_language218() async {
- // TODO(scheglov) Update when `records` feature is enabled by default.
await assertErrorsInCode('''
// @dart = 2.18
void f(Record r) {}
@@ -224,6 +223,18 @@
]);
}
+ test_Record_language218_exported() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+export 'dart:core' show Record;
+''');
+
+ await assertNoErrorsInCode('''
+// @dart = 2.18
+import 'a.dart';
+void f(Record r) {}
+''');
+ }
+
test_variableDeclaration() async {
await assertErrorsInCode('''
f() { C c; }
diff --git a/pkg/analyzer/test/src/summary/resolved_ast_printer.dart b/pkg/analyzer/test/src/summary/resolved_ast_printer.dart
index 0bb1ab0..ae02e70 100644
--- a/pkg/analyzer/test/src/summary/resolved_ast_printer.dart
+++ b/pkg/analyzer/test/src/summary/resolved_ast_printer.dart
@@ -924,6 +924,16 @@
_withIndent(() {
_writeNamedChildEntities(node);
_writeParameterElement(node);
+ // Types of the node and its expression must be the same.
+ if (node.expression.staticType != node.staticType) {
+ final nodeType = node.staticType;
+ final expressionType = node.expression.staticType;
+ fail(
+ 'Must be the same:\n'
+ 'nodeType: $nodeType\n'
+ 'expressionType: $expressionType',
+ );
+ }
});
}
diff --git a/pkg/compiler/lib/src/js_backend/field_naming_mixin.dart b/pkg/compiler/lib/src/js_backend/field_naming_mixin.dart
index e710d15..b49b75a 100644
--- a/pkg/compiler/lib/src/js_backend/field_naming_mixin.dart
+++ b/pkg/compiler/lib/src/js_backend/field_naming_mixin.dart
@@ -21,7 +21,7 @@
}
_FieldNamingScope names;
- if (element is JRecordField) {
+ if (element is JContextField) {
names = _FieldNamingScope.forBox(element.box, fieldRegistry);
} else {
ClassEntity cls = element.enclosingClass;
diff --git a/pkg/compiler/lib/src/js_model/closure.dart b/pkg/compiler/lib/src/js_model/closure.dart
index dc2d401..deb2c1f 100644
--- a/pkg/compiler/lib/src/js_model/closure.dart
+++ b/pkg/compiler/lib/src/js_model/closure.dart
@@ -20,7 +20,7 @@
import 'js_world_builder_interfaces.dart' show JsClosedWorldBuilder;
export 'closure_migrated.dart'
- show AnonymousClosureLocal, JClosureField, JRecordField;
+ show AnonymousClosureLocal, JClosureField, JContextField;
class ClosureDataImpl implements ClosureData {
/// Tag used for identifying serialized [ClosureData] objects in a
@@ -339,15 +339,15 @@
ClosureRtiNeed rtiNeed,
List<FunctionEntity> callMethods) {
void processModel(MemberEntity member, ClosureScopeModel model) {
- Map<ir.VariableDeclaration, migrated.JRecordField> allBoxedVariables =
- _elementMap.makeRecordContainer(model.scopeInfo!, member);
+ Map<ir.VariableDeclaration, migrated.JContextField> allBoxedVariables =
+ _elementMap.makeContextContainer(model.scopeInfo!, member);
_scopeMap[member] = JsScopeInfo.from(
allBoxedVariables, model.scopeInfo!, member.enclosingClass);
model.capturedScopesMap
.forEach((ir.Node node, KernelCapturedScope scope) {
- Map<ir.VariableDeclaration, migrated.JRecordField> boxedVariables =
- _elementMap.makeRecordContainer(scope, member);
+ Map<ir.VariableDeclaration, migrated.JContextField> boxedVariables =
+ _elementMap.makeContextContainer(scope, member);
_updateScopeBasedOnRtiNeed(scope, rtiNeed, member);
if (scope is KernelCapturedLoopScope) {
@@ -424,7 +424,7 @@
MemberEntity member,
ir.FunctionNode node,
KernelScopeInfo info,
- Map<ir.VariableDeclaration, migrated.JRecordField> boxedVariables,
+ Map<ir.VariableDeclaration, migrated.JContextField> boxedVariables,
ClosureRtiNeed rtiNeed,
{required bool createSignatureMethod}) {
_updateScopeBasedOnRtiNeed(info, rtiNeed, member);
@@ -461,9 +461,9 @@
@override
final Local? thisLocal;
- final Map<ir.VariableDeclaration, migrated.JRecordField> _boxedVariables;
+ final Map<ir.VariableDeclaration, migrated.JContextField> _boxedVariables;
- Map<Local, migrated.JRecordField>? _boxedVariablesCache;
+ Map<Local, migrated.JContextField>? _boxedVariablesCache;
JsScopeInfo.internal(
this._localsUsedInTryOrSync, this.thisLocal, this._boxedVariables);
@@ -478,9 +478,9 @@
if (_boxedVariables.isEmpty) {
_boxedVariablesCache = const {};
} else {
- final cache = <Local, migrated.JRecordField>{};
+ final cache = <Local, migrated.JContextField>{};
_boxedVariables.forEach(
- (ir.VariableDeclaration node, migrated.JRecordField field) {
+ (ir.VariableDeclaration node, migrated.JContextField field) {
cache[localsMap.getLocalVariable(node)] = field;
});
_boxedVariablesCache = cache;
@@ -529,9 +529,9 @@
Iterable<ir.VariableDeclaration> localsUsedInTryOrSync =
source.readTreeNodes<ir.VariableDeclaration>();
Local? thisLocal = source.readLocalOrNull();
- Map<ir.VariableDeclaration, migrated.JRecordField> boxedVariables =
- source.readTreeNodeMap<ir.VariableDeclaration, migrated.JRecordField>(
- () => source.readMember() as migrated.JRecordField);
+ Map<ir.VariableDeclaration, migrated.JContextField> boxedVariables =
+ source.readTreeNodeMap<ir.VariableDeclaration, migrated.JContextField>(
+ () => source.readMember() as migrated.JContextField);
source.end(tag);
if (boxedVariables.isEmpty) boxedVariables = const {};
return JsScopeInfo.internal(
@@ -575,9 +575,9 @@
Iterable<ir.VariableDeclaration> localsUsedInTryOrSync =
source.readTreeNodes<ir.VariableDeclaration>();
Local? thisLocal = source.readLocalOrNull();
- Map<ir.VariableDeclaration, migrated.JRecordField> boxedVariables =
- source.readTreeNodeMap<ir.VariableDeclaration, migrated.JRecordField>(
- () => source.readMember() as migrated.JRecordField);
+ Map<ir.VariableDeclaration, migrated.JContextField> boxedVariables =
+ source.readTreeNodeMap<ir.VariableDeclaration, migrated.JContextField>(
+ () => source.readMember() as migrated.JContextField);
Local? context = source.readLocalOrNull();
source.end(tag);
return JsCapturedScope.internal(
@@ -620,9 +620,9 @@
Iterable<ir.VariableDeclaration> localsUsedInTryOrSync =
source.readTreeNodes<ir.VariableDeclaration>();
Local? thisLocal = source.readLocalOrNull();
- Map<ir.VariableDeclaration, migrated.JRecordField> boxedVariables =
- source.readTreeNodeMap<ir.VariableDeclaration, migrated.JRecordField>(
- () => source.readMember() as migrated.JRecordField);
+ Map<ir.VariableDeclaration, migrated.JContextField> boxedVariables =
+ source.readTreeNodeMap<ir.VariableDeclaration, migrated.JContextField>(
+ () => source.readMember() as migrated.JContextField);
Local? context = source.readLocalOrNull();
List<ir.VariableDeclaration> boxedLoopVariables =
source.readTreeNodes<ir.VariableDeclaration>();
@@ -688,7 +688,7 @@
JsClosureClassInfo.internal(
Iterable<ir.VariableDeclaration> localsUsedInTryOrSync,
this.thisLocal,
- Map<ir.VariableDeclaration, migrated.JRecordField> boxedVariables,
+ Map<ir.VariableDeclaration, migrated.JContextField> boxedVariables,
this.callMethod,
this.signatureMethod,
this._closureEntity,
@@ -702,7 +702,7 @@
JsClosureClassInfo.fromScopeInfo(
this.closureClassEntity,
ir.FunctionNode closureSourceNode,
- Map<ir.VariableDeclaration, migrated.JRecordField> boxedVariables,
+ Map<ir.VariableDeclaration, migrated.JContextField> boxedVariables,
KernelScopeInfo info,
ClassEntity? enclosingClass,
this._closureEntity,
@@ -718,9 +718,9 @@
Iterable<ir.VariableDeclaration> localsUsedInTryOrSync =
source.readTreeNodes<ir.VariableDeclaration>();
Local? thisLocal = source.readLocalOrNull();
- Map<ir.VariableDeclaration, migrated.JRecordField> boxedVariables =
- source.readTreeNodeMap<ir.VariableDeclaration, migrated.JRecordField>(
- () => source.readMember() as migrated.JRecordField);
+ Map<ir.VariableDeclaration, migrated.JContextField> boxedVariables =
+ source.readTreeNodeMap<ir.VariableDeclaration, migrated.JContextField>(
+ () => source.readMember() as migrated.JContextField);
JFunction callMethod = source.readMember() as JFunction;
JSignatureMethod? signatureMethod =
source.readMemberOrNull() as JSignatureMethod?;
@@ -800,7 +800,7 @@
void registerFieldForBoxedVariable(
ir.VariableDeclaration node, JField field) {
assert(_boxedVariablesCache == null);
- _boxedVariables[node] = field as migrated.JRecordField;
+ _boxedVariables[node] = field as migrated.JContextField;
}
void _ensureFieldToLocalsMap(KernelToLocalsMap localsMap) {
diff --git a/pkg/compiler/lib/src/js_model/closure_migrated.dart b/pkg/compiler/lib/src/js_model/closure_migrated.dart
index 26a8c2f..2959ff3 100644
--- a/pkg/compiler/lib/src/js_model/closure_migrated.dart
+++ b/pkg/compiler/lib/src/js_model/closure_migrated.dart
@@ -19,31 +19,29 @@
import 'element_map_migrated.dart';
import 'elements.dart';
import 'env.dart';
-import 'jrecord_field_interface.dart';
/// A container for variables declared in a particular scope that are accessed
/// elsewhere.
-// TODO(johnniwinther): Don't implement JClass. This isn't actually a
-// class.
-class JRecord extends JClass {
- /// Tag used for identifying serialized [JRecord] objects in a
- /// debugging data stream.
- static const String tag = 'record';
+// TODO(johnniwinther): Don't implement JClass. This isn't actually a class.
+class JContext extends JClass {
+ /// Tag used for identifying serialized [JContext] objects in a debugging data
+ /// stream.
+ static const String tag = 'context';
- JRecord(LibraryEntity library, String name)
+ JContext(LibraryEntity library, String name)
: super(library as JLibrary, name, isAbstract: false);
- factory JRecord.readFromDataSource(DataSourceReader source) {
+ factory JContext.readFromDataSource(DataSourceReader source) {
source.begin(tag);
JLibrary library = source.readLibrary() as JLibrary;
String name = source.readString();
source.end(tag);
- return JRecord(library, name);
+ return JContext(library, name);
}
@override
void writeToDataSink(DataSinkWriter sink) {
- sink.writeEnum(JClassKind.record);
+ sink.writeEnum(JClassKind.context);
sink.begin(tag);
sink.writeLibrary(library);
sink.writeString(name);
@@ -54,36 +52,36 @@
bool get isClosure => false;
@override
- String toString() => '${jsElementPrefix}record_container($name)';
+ String toString() => '${jsElementPrefix}context($name)';
}
-/// A variable that has been "boxed" to prevent name shadowing with the
-/// original variable and ensure that this variable is updated/read with the
-/// most recent value.
-class JRecordField extends JField implements JRecordFieldInterface {
- /// Tag used for identifying serialized [JRecordField] objects in a
- /// debugging data stream.
- static const String tag = 'record-field';
+/// A variable that has been "boxed" to prevent name shadowing with the original
+/// variable and ensure that this variable is updated/read with the most recent
+/// value.
+class JContextField extends JField {
+ /// Tag used for identifying serialized [JContextField] objects in a debugging
+ /// data stream.
+ static const String tag = 'context-field';
final BoxLocal box;
- JRecordField(String name, this.box, {required bool isConst})
+ JContextField(String name, this.box, {required bool isConst})
: super(box.container.library as JLibrary, box.container as JClass,
Name(name, box.container.library.canonicalUri),
isStatic: false, isAssignable: true, isConst: isConst);
- factory JRecordField.readFromDataSource(DataSourceReader source) {
+ factory JContextField.readFromDataSource(DataSourceReader source) {
source.begin(tag);
String name = source.readString();
final enclosingClass = source.readClass() as JClass;
bool isConst = source.readBool();
source.end(tag);
- return JRecordField(name, BoxLocal(enclosingClass), isConst: isConst);
+ return JContextField(name, BoxLocal(enclosingClass), isConst: isConst);
}
@override
void writeToDataSink(DataSinkWriter sink) {
- sink.writeEnum(JMemberKind.recordField);
+ sink.writeEnum(JMemberKind.contextField);
sink.begin(tag);
sink.writeString(name);
sink.writeClass(enclosingClass!);
@@ -227,10 +225,10 @@
void registerFieldForBoxedVariable(ir.VariableDeclaration node, JField field);
}
-class RecordClassData implements JClassData {
- /// Tag used for identifying serialized [RecordClassData] objects in a
+class ContextClassData implements JClassData {
+ /// Tag used for identifying serialized [ContextClassData] objects in a
/// debugging data stream.
- static const String tag = 'record-class-data';
+ static const String tag = 'context-class-data';
@override
final ClassDefinition definition;
@@ -244,22 +242,22 @@
@override
final InterfaceType? supertype;
- RecordClassData(
+ ContextClassData(
this.definition, this.thisType, this.supertype, this.orderedTypeSet);
- factory RecordClassData.readFromDataSource(DataSourceReader source) {
+ factory ContextClassData.readFromDataSource(DataSourceReader source) {
source.begin(tag);
ClassDefinition definition = ClassDefinition.readFromDataSource(source);
InterfaceType thisType = source.readDartType() as InterfaceType;
InterfaceType supertype = source.readDartType() as InterfaceType;
OrderedTypeSet orderedTypeSet = OrderedTypeSet.readFromDataSource(source);
source.end(tag);
- return RecordClassData(definition, thisType, supertype, orderedTypeSet);
+ return ContextClassData(definition, thisType, supertype, orderedTypeSet);
}
@override
void writeToDataSink(DataSinkWriter sink) {
- sink.writeEnum(JClassDataKind.record);
+ sink.writeEnum(JClassDataKind.context);
sink.begin(tag);
definition.writeToDataSink(sink);
sink.writeDartType(thisType!);
@@ -296,7 +294,7 @@
List<Variance> getVariances() => [];
}
-class ClosureClassData extends RecordClassData {
+class ClosureClassData extends ContextClassData {
/// Tag used for identifying serialized [ClosureClassData] objects in a
/// debugging data stream.
static const String tag = 'closure-class-data';
diff --git a/pkg/compiler/lib/src/js_model/element_map.dart b/pkg/compiler/lib/src/js_model/element_map.dart
index 99d2abd..7651af7 100644
--- a/pkg/compiler/lib/src/js_model/element_map.dart
+++ b/pkg/compiler/lib/src/js_model/element_map.dart
@@ -161,10 +161,10 @@
/// Returns the constructor body entity corresponding to [function].
JGeneratorBody getGeneratorBody(FunctionEntity function);
- /// Make a record to ensure variables that are are declared in one scope and
- /// modified in another get their values updated correctly.
+ /// Make a mapping from closed-over variables to the context fields where they
+ /// are stored.
@override
- Map<ir.VariableDeclaration, JRecordField> makeRecordContainer(
+ Map<ir.VariableDeclaration, JContextField> makeContextContainer(
KernelScopeInfo info, MemberEntity member);
/// Returns a provider for static types for [member].
diff --git a/pkg/compiler/lib/src/js_model/element_map_impl.dart b/pkg/compiler/lib/src/js_model/element_map_impl.dart
index fe5706c..aaad54a 100644
--- a/pkg/compiler/lib/src/js_model/element_map_impl.dart
+++ b/pkg/compiler/lib/src/js_model/element_map_impl.dart
@@ -366,7 +366,7 @@
if (env.cls != null) {
classMap[env.cls!] = cls;
}
- if (cls is! closureMigrated.JRecord &&
+ if (cls is! closureMigrated.JContext &&
cls is! closureMigrated.JClosureClass) {
// Synthesized classes are not part of the library environment.
libraries.getEnv(cls.library).registerClass(cls.name, env);
@@ -1730,13 +1730,13 @@
_injectedClassMembers[cls]?.forEach(f);
}
- JRecordField _constructRecordFieldEntry(
+ JContextField _constructContextFieldEntry(
InterfaceType? memberThisType,
ir.VariableDeclaration variable,
BoxLocal boxLocal,
Map<Name, MemberEntity> memberMap) {
- JRecordField boxedField =
- JRecordField(variable.name!, boxLocal, isConst: variable.isConst);
+ JContextField boxedField =
+ JContextField(variable.name!, boxLocal, isConst: variable.isConst);
members.register(
boxedField,
closureMigrated.ClosureFieldData(
@@ -1748,36 +1748,36 @@
return boxedField;
}
- /// Make a container controlling access to records, that is, variables that
+ /// Make a container controlling access to contexts, that is, variables that
/// are accessed in different scopes. This function creates the container
/// and returns a map of locals to the corresponding records created.
@override
- Map<ir.VariableDeclaration, JRecordField> makeRecordContainer(
+ Map<ir.VariableDeclaration, JContextField> makeContextContainer(
KernelScopeInfo info, MemberEntity member) {
- Map<ir.VariableDeclaration, JRecordField> boxedFields = {};
+ Map<ir.VariableDeclaration, JContextField> boxedFields = {};
if (info.boxedVariables.isNotEmpty) {
NodeBox box = info.capturedVariablesAccessor!;
Map<Name, IndexedMember> memberMap = {};
- closureMigrated.JRecord container =
- closureMigrated.JRecord(member.library, box.name);
+ closureMigrated.JContext container =
+ closureMigrated.JContext(member.library, box.name);
BoxLocal boxLocal = BoxLocal(container);
InterfaceType thisType =
types.interfaceType(container, const <DartType>[]);
InterfaceType supertype = commonElements.objectType;
- JClassData containerData = closureMigrated.RecordClassData(
- RecordContainerDefinition(getMemberDefinition(member).location),
+ JClassData containerData = closureMigrated.ContextClassData(
+ ContextContainerDefinition(getMemberDefinition(member).location),
thisType,
supertype,
getOrderedTypeSet(supertype.element as IndexedClass)
.extendClass(types, thisType));
- classes.register(container, containerData, RecordEnv(memberMap));
+ classes.register(container, containerData, ContextEnv(memberMap));
InterfaceType? memberThisType = member.enclosingClass != null
? elementEnvironment.getThisType(member.enclosingClass!)
: null;
for (ir.VariableDeclaration variable in info.boxedVariables) {
- boxedFields[variable] = _constructRecordFieldEntry(
+ boxedFields[variable] = _constructContextFieldEntry(
memberThisType, variable, boxLocal, memberMap);
}
}
@@ -1810,7 +1810,7 @@
MemberEntity member,
ir.FunctionNode node,
JLibrary enclosingLibrary,
- Map<ir.VariableDeclaration, JRecordField> recordFieldsVisibleInScope,
+ Map<ir.VariableDeclaration, JContextField> contextFieldsVisibleInScope,
KernelScopeInfo info,
InterfaceType supertype,
{required bool createSignatureMethod}) {
@@ -1873,14 +1873,14 @@
JsClosureClassInfo closureClassInfo = JsClosureClassInfo.fromScopeInfo(
classEntity,
node,
- <ir.VariableDeclaration, JRecordField>{},
+ <ir.VariableDeclaration, JContextField>{},
info,
member.enclosingClass,
closureEntity,
closureEntityNode,
info.hasThisLocal ? ThisLocal(member.enclosingClass!) : null);
_buildClosureClassFields(closureClassInfo, member, memberThisType, info,
- recordFieldsVisibleInScope, memberMap);
+ contextFieldsVisibleInScope, memberMap);
if (createSignatureMethod) {
_constructSignatureMethod(closureClassInfo, memberMap, node,
@@ -1908,7 +1908,7 @@
MemberEntity member,
InterfaceType? memberThisType,
KernelScopeInfo info,
- Map<ir.VariableDeclaration, JRecordField> recordFieldsVisibleInScope,
+ Map<ir.VariableDeclaration, JContextField> contextFieldsVisibleInScope,
Map<Name, MemberEntity> memberMap) {
// TODO(efortuna): Limit field number usage to when we need to distinguish
// between two variables with the same name from different scopes.
@@ -1922,14 +1922,14 @@
for (ir.Node variable in info.freeVariables) {
if (variable is ir.VariableDeclaration) {
- if (recordFieldsVisibleInScope.containsKey(variable)) {
+ if (contextFieldsVisibleInScope.containsKey(variable)) {
bool constructedField = _constructClosureFieldForRecord(
variable,
closureClassInfo,
memberThisType,
memberMap,
variable,
- recordFieldsVisibleInScope,
+ contextFieldsVisibleInScope,
fieldNumber);
if (constructedField) fieldNumber++;
}
@@ -1956,7 +1956,7 @@
// Make a corresponding field entity in this closure class for the
// free variables in the KernelScopeInfo.freeVariable.
if (variable is ir.VariableDeclaration) {
- if (!recordFieldsVisibleInScope.containsKey(variable)) {
+ if (!contextFieldsVisibleInScope.containsKey(variable)) {
closureClassInfo.registerFieldForVariable(
variable,
_constructClosureField(
@@ -1998,11 +1998,11 @@
}
}
- /// Records point to one or more local variables declared in another scope
+ /// Contexts point to one or more local variables declared in another scope
/// that are captured in a scope. Access to those variables goes entirely
- /// through the record container, so we only create a field for the *record*
+ /// through the context container, so we only create a field for the *context*
/// holding [capturedLocal] and not the individual local variables accessed
- /// through the record. Records, by definition, are not mutable (though the
+ /// through the context. Contexts, by definition, are not mutable (though the
/// locals they contain may be). Returns `true` if we constructed a new field
/// in the closure class.
bool _constructClosureFieldForRecord(
@@ -2011,20 +2011,20 @@
InterfaceType? memberThisType,
Map<Name, MemberEntity> memberMap,
ir.TreeNode sourceNode,
- Map<ir.VariableDeclaration, JRecordField> recordFieldsVisibleInScope,
+ Map<ir.VariableDeclaration, JContextField> contextFieldsVisibleInScope,
int fieldNumber) {
- JRecordField recordField = recordFieldsVisibleInScope[capturedLocal]!;
+ JContextField contextField = contextFieldsVisibleInScope[capturedLocal]!;
// Don't construct a new field if the box that holds this local already has
// a field in the closure class.
- if (closureClassInfo.hasFieldForLocal(recordField.box)) {
+ if (closureClassInfo.hasFieldForLocal(contextField.box)) {
closureClassInfo.registerFieldForBoxedVariable(
- capturedLocal, recordField);
+ capturedLocal, contextField);
return false;
}
final closureField = JClosureField(
- '_box_$fieldNumber', closureClassInfo, recordField.box.name,
+ '_box_$fieldNumber', closureClassInfo, contextField.box.name,
isConst: true, isAssignable: false);
members.register<IndexedField, JFieldData>(
@@ -2034,8 +2034,8 @@
MemberKind.closureField, sourceNode),
memberThisType));
memberMap[closureField.memberName] = closureField;
- closureClassInfo.registerFieldForLocal(recordField.box, closureField);
- closureClassInfo.registerFieldForBoxedVariable(capturedLocal, recordField);
+ closureClassInfo.registerFieldForLocal(contextField.box, closureField);
+ closureClassInfo.registerFieldForBoxedVariable(capturedLocal, contextField);
return true;
}
diff --git a/pkg/compiler/lib/src/js_model/element_map_interfaces.dart b/pkg/compiler/lib/src/js_model/element_map_interfaces.dart
index f7367f9..a6261bc 100644
--- a/pkg/compiler/lib/src/js_model/element_map_interfaces.dart
+++ b/pkg/compiler/lib/src/js_model/element_map_interfaces.dart
@@ -32,7 +32,7 @@
MemberDefinition getMemberDefinition(MemberEntity member);
ConstantValue getRequiredSentinelConstantValue();
- Map<ir.VariableDeclaration, JRecordField> makeRecordContainer(
+ Map<ir.VariableDeclaration, JContextField> makeContextContainer(
KernelScopeInfo info, MemberEntity member);
NativeBehavior getNativeBehaviorForJsCall(ir.StaticInvocation node);
}
diff --git a/pkg/compiler/lib/src/js_model/element_map_migrated.dart b/pkg/compiler/lib/src/js_model/element_map_migrated.dart
index 1794774..0f76e82 100644
--- a/pkg/compiler/lib/src/js_model/element_map_migrated.dart
+++ b/pkg/compiler/lib/src/js_model/element_map_migrated.dart
@@ -344,9 +344,9 @@
enum ClassKind {
regular,
closure,
- // TODO(efortuna, johnniwinther): Record is not a class, but is
+ // TODO(efortuna, johnniwinther): Context is not a class, but is
// masquerading as one currently for consistency with the old element model.
- record,
+ context,
}
/// Definition information for a [ClassEntity].
@@ -369,8 +369,8 @@
return RegularClassDefinition.readFromDataSource(source);
case ClassKind.closure:
return ClosureClassDefinition.readFromDataSource(source);
- case ClassKind.record:
- return RecordContainerDefinition.readFromDataSource(source);
+ case ClassKind.context:
+ return ContextContainerDefinition.readFromDataSource(source);
}
}
@@ -451,40 +451,40 @@
String toString() => 'ClosureClassDefinition(kind:$kind,location:$location)';
}
-class RecordContainerDefinition implements ClassDefinition {
- /// Tag used for identifying serialized [RecordContainerDefinition] objects in
+class ContextContainerDefinition implements ClassDefinition {
+ /// Tag used for identifying serialized [ContextContainerDefinition] objects in
/// a debugging data stream.
- static const String tag = 'record-definition';
+ static const String tag = 'context-definition';
@override
final SourceSpan location;
- RecordContainerDefinition(this.location);
+ ContextContainerDefinition(this.location);
- factory RecordContainerDefinition.readFromDataSource(
+ factory ContextContainerDefinition.readFromDataSource(
DataSourceReader source) {
source.begin(tag);
SourceSpan location = source.readSourceSpan();
source.end(tag);
- return RecordContainerDefinition(location);
+ return ContextContainerDefinition(location);
}
@override
void writeToDataSink(DataSinkWriter sink) {
- sink.writeEnum(ClassKind.record);
+ sink.writeEnum(ClassKind.context);
sink.begin(tag);
sink.writeSourceSpan(location);
sink.end(tag);
}
@override
- ClassKind get kind => ClassKind.record;
+ ClassKind get kind => ClassKind.context;
@override
ir.Node get node =>
- throw UnsupportedError('RecordContainerDefinition.node for $location');
+ throw UnsupportedError('ContextContainerDefinition.node for $location');
@override
String toString() =>
- 'RecordContainerDefinition(kind:$kind,location:$location)';
+ 'ContextContainerDefinition(kind:$kind,location:$location)';
}
diff --git a/pkg/compiler/lib/src/js_model/elements.dart b/pkg/compiler/lib/src/js_model/elements.dart
index 44ec959..e60daa6 100644
--- a/pkg/compiler/lib/src/js_model/elements.dart
+++ b/pkg/compiler/lib/src/js_model/elements.dart
@@ -53,7 +53,7 @@
}
/// Enum used for identifying [JClass] subclasses in serialization.
-enum JClassKind { node, closure, record }
+enum JClassKind { node, closure, context }
class JClass extends IndexedClass with ClassHierarchyNodesMapKey {
/// Tag used for identifying serialized [JClass] objects in a
@@ -83,8 +83,8 @@
return JClass(library, name, isAbstract: isAbstract);
case JClassKind.closure:
return JClosureClass.readFromDataSource(source);
- case JClassKind.record:
- return JRecord.readFromDataSource(source);
+ case JClassKind.context:
+ return JContext.readFromDataSource(source);
}
}
@@ -118,7 +118,7 @@
closureCallMethod,
generatorBody,
signatureMethod,
- recordField,
+ contextField,
}
abstract class JMember extends IndexedMember {
@@ -159,8 +159,8 @@
return JGeneratorBody.readFromDataSource(source);
case JMemberKind.signatureMethod:
return JSignatureMethod.readFromDataSource(source);
- case JMemberKind.recordField:
- return JRecordField.readFromDataSource(source);
+ case JMemberKind.contextField:
+ return JContextField.readFromDataSource(source);
}
}
diff --git a/pkg/compiler/lib/src/js_model/env.dart b/pkg/compiler/lib/src/js_model/env.dart
index 5e712fa..78281c4 100644
--- a/pkg/compiler/lib/src/js_model/env.dart
+++ b/pkg/compiler/lib/src/js_model/env.dart
@@ -21,7 +21,7 @@
import 'closure_migrated.dart'
show
ClosureClassData,
- RecordClassData,
+ ContextClassData,
ClosureFunctionData,
ClosureFieldData;
import 'element_map_interfaces.dart' show JsToElementMap, JsKernelToElementMap;
@@ -178,7 +178,7 @@
}
/// Enum used for identifying [JClassEnv] subclasses in serialization.
-enum JClassEnvKind { node, closure, record }
+enum JClassEnvKind { node, closure, context }
/// Member data for a class.
abstract class JClassEnv {
@@ -190,8 +190,8 @@
return JClassEnvImpl.readFromDataSource(source);
case JClassEnvKind.closure:
return ClosureClassEnv.readFromDataSource(source);
- case JClassEnvKind.record:
- return RecordEnv.readFromDataSource(source);
+ case JClassEnvKind.context:
+ return ContextEnv.readFromDataSource(source);
}
}
@@ -315,26 +315,26 @@
}
}
-class RecordEnv implements JClassEnv {
- /// Tag used for identifying serialized [RecordEnv] objects in a
- /// debugging data stream.
- static const String tag = 'record-env';
+class ContextEnv implements JClassEnv {
+ /// Tag used for identifying serialized [ContextEnv] objects in a debugging
+ /// data stream.
+ static const String tag = 'context-env';
final Map<Name, IndexedMember> _memberMap;
- RecordEnv(this._memberMap);
+ ContextEnv(this._memberMap);
- factory RecordEnv.readFromDataSource(DataSourceReader source) {
+ factory ContextEnv.readFromDataSource(DataSourceReader source) {
source.begin(tag);
Map<Name, IndexedMember> _memberMap =
source.readNameMap(() => source.readMember() as IndexedMember)!;
source.end(tag);
- return RecordEnv(_memberMap);
+ return ContextEnv(_memberMap);
}
@override
void writeToDataSink(DataSinkWriter sink) {
- sink.writeEnum(JClassEnvKind.record);
+ sink.writeEnum(JClassEnvKind.context);
sink.begin(tag);
sink.writeNameMap(
_memberMap, (IndexedMember member) => sink.writeMember(member));
@@ -378,7 +378,7 @@
ir.Class? get cls => null;
}
-class ClosureClassEnv extends RecordEnv {
+class ClosureClassEnv extends ContextEnv {
/// Tag used for identifying serialized [ClosureClassEnv] objects in a
/// debugging data stream.
static const String tag = 'closure-class-env';
@@ -404,7 +404,7 @@
}
/// Enum used for identifying [JClassData] subclasses in serialization.
-enum JClassDataKind { node, closure, record }
+enum JClassDataKind { node, closure, context }
abstract class JClassData {
/// Deserializes a [JClassData] object from [source].
@@ -415,8 +415,8 @@
return JClassDataImpl.readFromDataSource(source);
case JClassDataKind.closure:
return ClosureClassData.readFromDataSource(source);
- case JClassDataKind.record:
- return RecordClassData.readFromDataSource(source);
+ case JClassDataKind.context:
+ return ContextClassData.readFromDataSource(source);
}
}
diff --git a/pkg/compiler/lib/src/js_model/jrecord_field_interface.dart b/pkg/compiler/lib/src/js_model/jrecord_field_interface.dart
deleted file mode 100644
index 17f3bd0..0000000
--- a/pkg/compiler/lib/src/js_model/jrecord_field_interface.dart
+++ /dev/null
@@ -1,6 +0,0 @@
-// 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.
-
-// TODO(48820): delete this class once migration is complete.
-abstract class JRecordFieldInterface {}
diff --git a/pkg/compiler/lib/src/js_model/js_world_builder.dart b/pkg/compiler/lib/src/js_model/js_world_builder.dart
index 612444e..c22ccfc 100644
--- a/pkg/compiler/lib/src/js_model/js_world_builder.dart
+++ b/pkg/compiler/lib/src/js_model/js_world_builder.dart
@@ -321,7 +321,7 @@
MemberEntity member,
ir.FunctionNode originalClosureFunctionNode,
JLibrary enclosingLibrary,
- Map<ir.VariableDeclaration, JRecordField> boxedVariables,
+ Map<ir.VariableDeclaration, JContextField> boxedVariables,
KernelScopeInfo info,
{bool createSignatureMethod}) {
ClassEntity superclass =
diff --git a/pkg/compiler/lib/src/js_model/js_world_builder_interfaces.dart b/pkg/compiler/lib/src/js_model/js_world_builder_interfaces.dart
index 52b8965..b20aa18 100644
--- a/pkg/compiler/lib/src/js_model/js_world_builder_interfaces.dart
+++ b/pkg/compiler/lib/src/js_model/js_world_builder_interfaces.dart
@@ -14,7 +14,7 @@
MemberEntity member,
ir.FunctionNode originalClosureFunctionNode,
JLibrary enclosingLibrary,
- Map<ir.VariableDeclaration, JRecordField> boxedVariables,
+ Map<ir.VariableDeclaration, JContextField> boxedVariables,
KernelScopeInfo info,
{required bool createSignatureMethod});
}
diff --git a/pkg/compiler/lib/src/ssa/locals_handler.dart b/pkg/compiler/lib/src/ssa/locals_handler.dart
index ca2fe43..7017849 100644
--- a/pkg/compiler/lib/src/ssa/locals_handler.dart
+++ b/pkg/compiler/lib/src/ssa/locals_handler.dart
@@ -15,7 +15,7 @@
import '../io/source_information.dart';
import '../js_backend/native_data.dart';
import '../js_backend/interceptor_data.dart';
-import '../js_model/closure.dart' show JRecordField, JClosureField;
+import '../js_model/closure.dart' show JContextField, JClosureField;
import '../js_model/locals.dart' show GlobalLocalsMap, JLocal;
import '../world_interfaces.dart' show JClosedWorld;
@@ -384,7 +384,7 @@
// accessed through a closure-field.
// Calling [readLocal] makes sure we generate the correct code to get
// the box.
- if (redirect is JRecordField) {
+ if (redirect is JContextField) {
localBox = redirect.box;
}
assert(localBox != null);
@@ -452,7 +452,7 @@
FieldEntity redirect = redirectionMapping[local];
assert(redirect != null);
BoxLocal localBox;
- if (redirect is JRecordField) {
+ if (redirect is JContextField) {
localBox = redirect.box;
}
assert(localBox != null);
diff --git a/pkg/compiler/lib/src/universe/member_usage.dart b/pkg/compiler/lib/src/universe/member_usage.dart
index d9321e7..48b022d 100644
--- a/pkg/compiler/lib/src/universe/member_usage.dart
+++ b/pkg/compiler/lib/src/universe/member_usage.dart
@@ -7,7 +7,7 @@
import '../common.dart';
import '../constants/values.dart';
import '../elements/entities.dart';
-import '../js_model/jrecord_field_interface.dart' show JRecordFieldInterface;
+import '../js_model/closure_migrated.dart' show JContextField;
import '../serialization/serialization.dart';
import '../util/enumset.dart';
import 'call_structure.dart';
@@ -64,7 +64,7 @@
} else if (member.isInstanceMember) {
return EnumSet.fromValues(Access.values);
} else {
- assert(member is JRecordFieldInterface, "Unexpected member: $member");
+ assert(member is JContextField, "Unexpected member: $member");
return EnumSet();
}
}
diff --git a/pkg/compiler/lib/src/universe/use.dart b/pkg/compiler/lib/src/universe/use.dart
index 0bda473..3d62eaf 100644
--- a/pkg/compiler/lib/src/universe/use.dart
+++ b/pkg/compiler/lib/src/universe/use.dart
@@ -22,7 +22,7 @@
import '../elements/entities.dart';
import '../inferrer/abstract_value_domain.dart';
import '../serialization/serialization.dart';
-import '../js_model/jrecord_field_interface.dart' show JRecordFieldInterface;
+import '../js_model/closure_migrated.dart' show JContextField;
import '../util/util.dart' show equalElements, Hashing;
import 'call_structure.dart' show CallStructure;
import 'selector.dart' show Selector;
@@ -622,7 +622,7 @@
/// Read access of an instance field or boxed field [element].
factory StaticUse.fieldGet(FieldEntity element) {
assert(
- element.isInstanceMember || element is JRecordFieldInterface,
+ element.isInstanceMember || element is JContextField,
failedAt(element,
"Field init element $element must be an instance or boxed field."));
return StaticUse.internal(element, StaticUseKind.INSTANCE_FIELD_GET);
@@ -631,7 +631,7 @@
/// Write access of an instance field or boxed field [element].
factory StaticUse.fieldSet(FieldEntity element) {
assert(
- element.isInstanceMember || element is JRecordFieldInterface,
+ element.isInstanceMember || element is JContextField,
failedAt(element,
"Field init element $element must be an instance or boxed field."));
return StaticUse.internal(element, StaticUseKind.INSTANCE_FIELD_SET);
diff --git a/pkg/dart2wasm/lib/constants.dart b/pkg/dart2wasm/lib/constants.dart
index a2f327e..4ba4963 100644
--- a/pkg/dart2wasm/lib/constants.dart
+++ b/pkg/dart2wasm/lib/constants.dart
@@ -780,7 +780,7 @@
@override
ConstantInfo? visitTypeLiteralConstant(TypeLiteralConstant constant) {
- DartType type = constant.type;
+ DartType type = types.normalize(constant.type);
ClassInfo info = translator.classInfo[types.classForType(type)]!;
translator.functions.allocateClass(info.classId);
diff --git a/pkg/dart2wasm/lib/types.dart b/pkg/dart2wasm/lib/types.dart
index 20da36b..3eb2fed 100644
--- a/pkg/dart2wasm/lib/types.dart
+++ b/pkg/dart2wasm/lib/types.dart
@@ -114,7 +114,9 @@
for (InterfaceType subtype in subtypes) {
interfaceTypeEnvironment._add(subtype);
List<DartType>? typeArguments = translator.hierarchy
- .getTypeArgumentsAsInstanceOf(subtype, superclass);
+ .getTypeArgumentsAsInstanceOf(subtype, superclass)
+ ?.map(normalize)
+ .toList();
ClassInfo subclassInfo = translator.classInfo[subtype.classNode]!;
Map<int, List<DartType>> substitutionMap =
subtypeMap[subclassInfo.classId] ??= {};
@@ -311,6 +313,57 @@
_makeTypeList(codeGen, type.typeArguments);
}
+ DartType normalizeFutureOrType(FutureOrType type) {
+ final s = normalize(type.typeArgument);
+
+ // `coreTypes.isTope` and `coreTypes.isObject` take into account the
+ // normalization rules of `futureOr`.
+ if (coreTypes.isTop(type) || coreTypes.isObject(type)) {
+ return s;
+ } else if (s is NeverType) {
+ return InterfaceType(coreTypes.futureClass, Nullability.nonNullable,
+ const [const NeverType.nonNullable()]);
+ } else if (s is NullType) {
+ return InterfaceType(coreTypes.futureClass, Nullability.nullable,
+ const [const NullType()]);
+ }
+
+ // The type is normalized, and remains a `FutureOr` so now we normalize its
+ // nullability.
+ final declaredNullability = s.nullability == Nullability.nullable
+ ? Nullability.nonNullable
+ : type.declaredNullability;
+ return FutureOrType(s, declaredNullability);
+ }
+
+ /// Normalizes a Dart type. Many rules are already applied for us, but some we
+ /// have to apply manually, particularly to [FutureOr].
+ DartType normalize(DartType type) {
+ if (type is InterfaceType) {
+ return InterfaceType(type.classNode, type.nullability,
+ type.typeArguments.map(normalize).toList());
+ } else if (type is FunctionType) {
+ return FunctionType(type.positionalParameters.map(normalize).toList(),
+ normalize(type.returnType), type.nullability,
+ namedParameters: type.namedParameters
+ .map((namedType) => NamedType(
+ namedType.name, normalize(namedType.type),
+ isRequired: namedType.isRequired))
+ .toList(),
+ typeParameters: type.typeParameters
+ .map((typeParameter) => TypeParameter(
+ typeParameter.name,
+ normalize(typeParameter.bound),
+ normalize(typeParameter.defaultType)))
+ .toList(),
+ requiredParameterCount: type.requiredParameterCount);
+ } else if (type is FutureOrType) {
+ return normalizeFutureOrType(type);
+ } else {
+ return type;
+ }
+ }
+
void _makeFutureOrType(CodeGenerator codeGen, FutureOrType type) {
w.Instructions b = codeGen.b;
w.DefinedFunction function = codeGen.function;
@@ -382,6 +435,8 @@
/// TODO(joshualitt): Refactor this logic to remove the dependency on
/// CodeGenerator.
w.ValueType makeType(CodeGenerator codeGen, DartType type) {
+ // Always ensure type is normalized before making a type.
+ type = normalize(type);
w.Instructions b = codeGen.b;
if (_isTypeConstant(type)) {
translator.constants.instantiateConstant(
diff --git a/runtime/vm/app_snapshot.cc b/runtime/vm/app_snapshot.cc
index 9a80a2a..7b3726e 100644
--- a/runtime/vm/app_snapshot.cc
+++ b/runtime/vm/app_snapshot.cc
@@ -4124,7 +4124,7 @@
}
#endif
WriteFromTo(type);
- s->WriteUnsigned(type->untag()->flags_);
+ s->WriteUnsigned(type->untag()->flags());
}
};
#endif // !DART_PRECOMPILED_RUNTIME
@@ -4153,7 +4153,7 @@
Deserializer::InitializeHeader(type, kTypeCid, Type::InstanceSize(),
mark_canonical);
d.ReadFromTo(type);
- type->untag()->flags_ = d.ReadUnsigned();
+ type->untag()->set_flags(d.ReadUnsigned());
}
}
@@ -4235,8 +4235,8 @@
void WriteFunctionType(Serializer* s, FunctionTypePtr type) {
AutoTraceObject(type);
WriteFromTo(type);
- ASSERT(Utils::IsUint(8, type->untag()->flags_));
- s->Write<uint8_t>(type->untag()->flags_);
+ ASSERT(Utils::IsUint(8, type->untag()->flags()));
+ s->Write<uint8_t>(type->untag()->flags());
s->Write<uint32_t>(type->untag()->packed_parameter_counts_);
s->Write<uint16_t>(type->untag()->packed_type_parameter_counts_);
}
@@ -4267,7 +4267,7 @@
Deserializer::InitializeHeader(
type, kFunctionTypeCid, FunctionType::InstanceSize(), mark_canonical);
d.ReadFromTo(type);
- type->untag()->flags_ = d.Read<uint8_t>();
+ type->untag()->set_flags(d.Read<uint8_t>());
type->untag()->packed_parameter_counts_ = d.Read<uint32_t>();
type->untag()->packed_type_parameter_counts_ = d.Read<uint16_t>();
}
@@ -4351,8 +4351,8 @@
void WriteRecordType(Serializer* s, RecordTypePtr type) {
AutoTraceObject(type);
WriteFromTo(type);
- ASSERT(Utils::IsUint(8, type->untag()->flags_));
- s->Write<uint8_t>(type->untag()->flags_);
+ ASSERT(Utils::IsUint(8, type->untag()->flags()));
+ s->Write<uint8_t>(type->untag()->flags());
}
};
#endif // !DART_PRECOMPILED_RUNTIME
@@ -4380,7 +4380,7 @@
Deserializer::InitializeHeader(
type, kRecordTypeCid, RecordType::InstanceSize(), mark_canonical);
d.ReadFromTo(type);
- type->untag()->flags_ = d.Read<uint8_t>();
+ type->untag()->set_flags(d.Read<uint8_t>());
}
}
@@ -4556,8 +4556,8 @@
s->Write<int32_t>(type->untag()->parameterized_class_id_);
s->Write<uint8_t>(type->untag()->base_);
s->Write<uint8_t>(type->untag()->index_);
- ASSERT(Utils::IsUint(8, type->untag()->flags_));
- s->Write<uint8_t>(type->untag()->flags_);
+ ASSERT(Utils::IsUint(8, type->untag()->flags()));
+ s->Write<uint8_t>(type->untag()->flags());
}
};
#endif // !DART_PRECOMPILED_RUNTIME
@@ -4590,7 +4590,7 @@
type->untag()->parameterized_class_id_ = d.Read<int32_t>();
type->untag()->base_ = d.Read<uint8_t>();
type->untag()->index_ = d.Read<uint8_t>();
- type->untag()->flags_ = d.Read<uint8_t>();
+ type->untag()->set_flags(d.Read<uint8_t>());
}
}
diff --git a/runtime/vm/compiler/assembler/assembler_arm.cc b/runtime/vm/compiler/assembler/assembler_arm.cc
index db372c1..7587b27 100644
--- a/runtime/vm/compiler/assembler/assembler_arm.cc
+++ b/runtime/vm/compiler/assembler/assembler_arm.cc
@@ -1704,38 +1704,6 @@
}
}
-// Preserves object and value registers.
-void Assembler::StoreIntoObjectFilter(Register object,
- Register value,
- Label* label,
- CanBeSmi value_can_be_smi,
- BarrierFilterMode how_to_jump) {
- COMPILE_ASSERT((target::ObjectAlignment::kNewObjectAlignmentOffset ==
- target::kWordSize) &&
- (target::ObjectAlignment::kOldObjectAlignmentOffset == 0));
- // For the value we are only interested in the new/old bit and the tag bit.
- // And the new bit with the tag bit. The resulting bit will be 0 for a Smi.
- if (value_can_be_smi == kValueCanBeSmi) {
- and_(
- IP, value,
- Operand(value, LSL, target::ObjectAlignment::kObjectAlignmentLog2 - 1));
- // And the result with the negated space bit of the object.
- bic(IP, IP, Operand(object));
- } else {
-#if defined(DEBUG)
- Label okay;
- BranchIfNotSmi(value, &okay);
- Stop("Unexpected Smi!");
- Bind(&okay);
-#endif
- bic(IP, value, Operand(object));
- }
- tst(IP, Operand(target::ObjectAlignment::kNewObjectAlignmentOffset));
- if (how_to_jump != kNoJump) {
- b(label, how_to_jump == kJumpToNoUpdate ? EQ : NE);
- }
-}
-
Register UseRegister(Register reg, RegList* used) {
ASSERT(reg != THR);
ASSERT(reg != SP);
@@ -1782,7 +1750,7 @@
// Compare UntaggedObject::StorePointer.
Label done;
if (can_be_smi == kValueCanBeSmi) {
- BranchIfSmi(value, &done);
+ BranchIfSmi(value, &done, kNearJump);
}
const bool preserve_lr = lr_state().LRContainsReturnAddress();
if (preserve_lr) {
@@ -1853,7 +1821,7 @@
// Compare UntaggedObject::StorePointer.
Label done;
if (can_be_smi == kValueCanBeSmi) {
- BranchIfSmi(value, &done);
+ BranchIfSmi(value, &done, kNearJump);
}
const bool preserve_lr = lr_state().LRContainsReturnAddress();
if (preserve_lr) {
@@ -1915,13 +1883,14 @@
// reachable via a constant pool, so it doesn't matter if it is not traced via
// 'object'.
Label done;
- StoreIntoObjectFilter(object, value, &done, kValueCanBeSmi, kJumpToNoUpdate);
-
+ BranchIfSmi(value, &done, kNearJump);
+ ldrb(TMP, FieldAddress(value, target::Object::tags_offset()));
+ tst(TMP, Operand(1 << target::UntaggedObject::kNewBit));
+ b(&done, ZERO);
ldrb(TMP, FieldAddress(object, target::Object::tags_offset()));
tst(TMP, Operand(1 << target::UntaggedObject::kOldAndNotRememberedBit));
b(&done, ZERO);
-
- Stop("Store buffer update is required");
+ Stop("Write barrier is required");
Bind(&done);
#endif // defined(DEBUG)
// No store buffer update.
@@ -1998,16 +1967,6 @@
strd(value_even, value_odd, begin, -2 * target::kWordSize, LS);
b(&init_loop, CC);
str(value_even, Address(begin, -2 * target::kWordSize), HI);
-#if defined(DEBUG)
- Label done;
- StoreIntoObjectFilter(object, value_even, &done, kValueCanBeSmi,
- kJumpToNoUpdate);
- StoreIntoObjectFilter(object, value_odd, &done, kValueCanBeSmi,
- kJumpToNoUpdate);
- Stop("Store buffer update is required");
- Bind(&done);
-#endif // defined(DEBUG)
- // No store buffer update.
}
void Assembler::InitializeFieldsNoBarrierUnrolled(Register object,
@@ -2026,16 +1985,6 @@
str(value_even, Address(base, current_offset));
current_offset += target::kWordSize;
}
-#if defined(DEBUG)
- Label done;
- StoreIntoObjectFilter(object, value_even, &done, kValueCanBeSmi,
- kJumpToNoUpdate);
- StoreIntoObjectFilter(object, value_odd, &done, kValueCanBeSmi,
- kJumpToNoUpdate);
- Stop("Store buffer update is required");
- Bind(&done);
-#endif // defined(DEBUG)
- // No store buffer update.
}
void Assembler::StoreIntoSmiField(const Address& dest, Register value) {
diff --git a/runtime/vm/compiler/assembler/assembler_arm.h b/runtime/vm/compiler/assembler/assembler_arm.h
index a8d45d3..25d2c63 100644
--- a/runtime/vm/compiler/assembler/assembler_arm.h
+++ b/runtime/vm/compiler/assembler/assembler_arm.h
@@ -1645,27 +1645,6 @@
int32_t EncodeTstOffset(int32_t offset, int32_t inst);
int32_t DecodeTstOffset(int32_t inst);
- enum BarrierFilterMode {
- // Filter falls through into the barrier update code. Target label
- // is a "after-store" label.
- kJumpToNoUpdate,
-
- // Filter falls through to the "after-store" code. Target label
- // is barrier update code label.
- kJumpToBarrier,
-
- // Filter falls through into the conditional barrier update code and does
- // not jump. Target label is unused. The barrier should run if the NE
- // condition is set.
- kNoJump
- };
-
- void StoreIntoObjectFilter(Register object,
- Register value,
- Label* label,
- CanBeSmi can_be_smi,
- BarrierFilterMode barrier_filter_mode);
-
friend class dart::FlowGraphCompiler;
std::function<void(Condition, Register)>
generate_invoke_write_barrier_wrapper_;
diff --git a/runtime/vm/compiler/assembler/assembler_arm64.cc b/runtime/vm/compiler/assembler/assembler_arm64.cc
index 61117e1..e7956e4 100644
--- a/runtime/vm/compiler/assembler/assembler_arm64.cc
+++ b/runtime/vm/compiler/assembler/assembler_arm64.cc
@@ -994,43 +994,6 @@
#endif
}
-// Preserves object and value registers.
-void Assembler::StoreIntoObjectFilter(Register object,
- Register value,
- Label* label,
- CanBeSmi value_can_be_smi,
- BarrierFilterMode how_to_jump) {
- COMPILE_ASSERT((target::ObjectAlignment::kNewObjectAlignmentOffset ==
- target::kWordSize) &&
- (target::ObjectAlignment::kOldObjectAlignmentOffset == 0));
-
- // Write-barrier triggers if the value is in the new space (has bit set) and
- // the object is in the old space (has bit cleared).
- if (value_can_be_smi == kValueIsNotSmi) {
-#if defined(DEBUG)
- Label okay;
- BranchIfNotSmi(value, &okay);
- Stop("Unexpected Smi!");
- Bind(&okay);
-#endif
- // To check that, we compute value & ~object and skip the write barrier
- // if the bit is not set. We can't destroy the object.
- bic(TMP, value, Operand(object));
- } else {
- // For the value we are only interested in the new/old bit and the tag bit.
- // And the new bit with the tag bit. The resulting bit will be 0 for a Smi.
- and_(TMP, value,
- Operand(value, LSL, target::ObjectAlignment::kNewObjectBitPosition));
- // And the result with the negated space bit of the object.
- bic(TMP, TMP, Operand(object));
- }
- if (how_to_jump == kJumpToNoUpdate) {
- tbz(label, TMP, target::ObjectAlignment::kNewObjectBitPosition);
- } else {
- tbnz(label, TMP, target::ObjectAlignment::kNewObjectBitPosition);
- }
-}
-
void Assembler::StoreIntoObjectOffset(Register object,
int32_t offset,
Register value,
@@ -1220,13 +1183,12 @@
// reachable via a constant pool, so it doesn't matter if it is not traced via
// 'object'.
Label done;
- StoreIntoObjectFilter(object, value, &done, kValueCanBeSmi, kJumpToNoUpdate);
-
+ BranchIfSmi(value, &done, kNearJump);
+ ldr(TMP, FieldAddress(value, target::Object::tags_offset()), kUnsignedByte);
+ tbz(&done, TMP, target::UntaggedObject::kNewBit);
ldr(TMP, FieldAddress(object, target::Object::tags_offset()), kUnsignedByte);
- tsti(TMP, Immediate(1 << target::UntaggedObject::kOldAndNotRememberedBit));
- b(&done, ZERO);
-
- Stop("Store buffer update is required");
+ tbz(&done, TMP, target::UntaggedObject::kOldAndNotRememberedBit);
+ Stop("Write barrier is required");
Bind(&done);
#endif // defined(DEBUG)
// No store buffer update.
@@ -1246,13 +1208,12 @@
// reachable via a constant pool, so it doesn't matter if it is not traced via
// 'object'.
Label done;
- StoreIntoObjectFilter(object, value, &done, kValueCanBeSmi, kJumpToNoUpdate);
-
+ BranchIfSmi(value, &done, kNearJump);
+ ldr(TMP, FieldAddress(value, target::Object::tags_offset()), kUnsignedByte);
+ tbz(&done, TMP, target::UntaggedObject::kNewBit);
ldr(TMP, FieldAddress(object, target::Object::tags_offset()), kUnsignedByte);
- tsti(TMP, Immediate(1 << target::UntaggedObject::kOldAndNotRememberedBit));
- b(&done, ZERO);
-
- Stop("Store buffer update is required");
+ tbz(&done, TMP, target::UntaggedObject::kOldAndNotRememberedBit);
+ Stop("Write barrier is required");
Bind(&done);
#endif // defined(DEBUG)
// No store buffer update.
diff --git a/runtime/vm/compiler/assembler/assembler_arm64.h b/runtime/vm/compiler/assembler/assembler_arm64.h
index 58d8a84..a06a0a9 100644
--- a/runtime/vm/compiler/assembler/assembler_arm64.h
+++ b/runtime/vm/compiler/assembler/assembler_arm64.h
@@ -2982,22 +2982,6 @@
Emit(encoding);
}
- enum BarrierFilterMode {
- // Filter falls through into the barrier update code. Target label
- // is a "after-store" label.
- kJumpToNoUpdate,
-
- // Filter falls through to the "after-store" code. Target label
- // is barrier update code label.
- kJumpToBarrier,
- };
-
- void StoreIntoObjectFilter(Register object,
- Register value,
- Label* label,
- CanBeSmi can_be_smi,
- BarrierFilterMode barrier_filter_mode);
-
friend class dart::FlowGraphCompiler;
std::function<void(Register reg)> generate_invoke_write_barrier_wrapper_;
std::function<void()> generate_invoke_array_write_barrier_;
diff --git a/runtime/vm/compiler/assembler/assembler_ia32.cc b/runtime/vm/compiler/assembler/assembler_ia32.cc
index 6ef1b78..d25fd16 100644
--- a/runtime/vm/compiler/assembler/assembler_ia32.cc
+++ b/runtime/vm/compiler/assembler/assembler_ia32.cc
@@ -2086,16 +2086,15 @@
// reachable via a constant pool, so it doesn't matter if it is not traced via
// 'object'.
Label done;
- pushl(value);
- StoreIntoObjectFilter(object, value, &done, kValueCanBeSmi, kJumpToNoUpdate);
-
+ BranchIfSmi(value, &done, kNearJump);
+ testb(FieldAddress(value, target::Object::tags_offset()),
+ Immediate(1 << target::UntaggedObject::kNewBit));
+ j(ZERO, &done, Assembler::kNearJump);
testb(FieldAddress(object, target::Object::tags_offset()),
Immediate(1 << target::UntaggedObject::kOldAndNotRememberedBit));
j(ZERO, &done, Assembler::kNearJump);
-
- Stop("Store buffer update is required");
+ Stop("Write barrier is required");
Bind(&done);
- popl(value);
#endif // defined(DEBUG)
// No store buffer update.
}
diff --git a/runtime/vm/compiler/assembler/assembler_riscv.cc b/runtime/vm/compiler/assembler/assembler_riscv.cc
index ea90057..f7d20b5 100644
--- a/runtime/vm/compiler/assembler/assembler_riscv.cc
+++ b/runtime/vm/compiler/assembler/assembler_riscv.cc
@@ -3144,15 +3144,14 @@
// reachable via a constant pool, so it doesn't matter if it is not traced via
// 'object'.
Label done;
- beq(object, value, &done, kNearJump);
BranchIfSmi(value, &done, kNearJump);
- lbu(TMP, FieldAddress(object, target::Object::tags_offset()));
lbu(TMP2, FieldAddress(value, target::Object::tags_offset()));
- srli(TMP, TMP, target::UntaggedObject::kBarrierOverlapShift);
- and_(TMP, TMP, TMP2);
- andi(TMP, TMP, target::UntaggedObject::kGenerationalBarrierMask);
- beqz(TMP, &done, kNearJump);
- Stop("Store buffer update is required");
+ andi(TMP2, TMP2, 1 << target::UntaggedObject::kNewBit);
+ beqz(TMP2, &done, kNearJump);
+ lbu(TMP2, FieldAddress(object, target::Object::tags_offset()));
+ andi(TMP2, TMP2, 1 << target::UntaggedObject::kOldAndNotRememberedBit);
+ beqz(TMP2, &done, kNearJump);
+ Stop("Write barrier is required");
Bind(&done);
#endif
}
@@ -3178,15 +3177,14 @@
// reachable via a constant pool, so it doesn't matter if it is not traced via
// 'object'.
Label done;
- beq(object, value, &done, kNearJump);
BranchIfSmi(value, &done, kNearJump);
- lbu(TMP, FieldAddress(object, target::Object::tags_offset()));
lbu(TMP2, FieldAddress(value, target::Object::tags_offset()));
- srli(TMP, TMP, target::UntaggedObject::kBarrierOverlapShift);
- and_(TMP, TMP, TMP2);
- andi(TMP, TMP, target::UntaggedObject::kGenerationalBarrierMask);
- beqz(TMP, &done, kNearJump);
- Stop("Store buffer update is required");
+ andi(TMP2, TMP2, 1 << target::UntaggedObject::kNewBit);
+ beqz(TMP2, &done, kNearJump);
+ lbu(TMP2, FieldAddress(object, target::Object::tags_offset()));
+ andi(TMP2, TMP2, 1 << target::UntaggedObject::kOldAndNotRememberedBit);
+ beqz(TMP2, &done, kNearJump);
+ Stop("Write barrier is required");
Bind(&done);
#endif
}
diff --git a/runtime/vm/compiler/assembler/assembler_riscv.h b/runtime/vm/compiler/assembler/assembler_riscv.h
index 598fe65..64a11f4 100644
--- a/runtime/vm/compiler/assembler/assembler_riscv.h
+++ b/runtime/vm/compiler/assembler/assembler_riscv.h
@@ -1478,22 +1478,6 @@
// Note: the function never clobbers TMP, TMP2 scratch registers.
void LoadObjectHelper(Register dst, const Object& obj, bool is_unique);
- enum BarrierFilterMode {
- // Filter falls through into the barrier update code. Target label
- // is a "after-store" label.
- kJumpToNoUpdate,
-
- // Filter falls through to the "after-store" code. Target label
- // is barrier update code label.
- kJumpToBarrier,
- };
-
- void StoreIntoObjectFilter(Register object,
- Register value,
- Label* label,
- CanBeSmi can_be_smi,
- BarrierFilterMode barrier_filter_mode);
-
friend class dart::FlowGraphCompiler;
std::function<void(Register reg)> generate_invoke_write_barrier_wrapper_;
std::function<void()> generate_invoke_array_write_barrier_;
diff --git a/runtime/vm/compiler/assembler/assembler_x64.cc b/runtime/vm/compiler/assembler/assembler_x64.cc
index 199b126..f98805b 100644
--- a/runtime/vm/compiler/assembler/assembler_x64.cc
+++ b/runtime/vm/compiler/assembler/assembler_x64.cc
@@ -1434,44 +1434,6 @@
#endif
}
-// Destroys the value register.
-void Assembler::StoreIntoObjectFilter(Register object,
- Register value,
- Label* label,
- CanBeSmi can_be_smi,
- BarrierFilterMode how_to_jump) {
- COMPILE_ASSERT((target::ObjectAlignment::kNewObjectAlignmentOffset ==
- target::kWordSize) &&
- (target::ObjectAlignment::kOldObjectAlignmentOffset == 0));
-
- if (can_be_smi == kValueIsNotSmi) {
-#if defined(DEBUG)
- Label okay;
- BranchIfNotSmi(value, &okay);
- Stop("Unexpected Smi!");
- Bind(&okay);
-#endif
- // Write-barrier triggers if the value is in the new space (has bit set) and
- // the object is in the old space (has bit cleared).
- // To check that we could compute value & ~object and skip the write barrier
- // if the bit is not set. However we can't destroy the object.
- // However to preserve the object we compute negated expression
- // ~value | object instead and skip the write barrier if the bit is set.
- notl(value);
- orl(value, object);
- testl(value, Immediate(target::ObjectAlignment::kNewObjectAlignmentOffset));
- } else {
- ASSERT(kHeapObjectTag == 1);
- // Detect value being ...1001 and object being ...0001.
- andl(value, Immediate(0xf));
- leal(value, Address(value, object, TIMES_2, 0x15));
- testl(value, Immediate(0x1f));
- }
- Condition condition = how_to_jump == kJumpToNoUpdate ? NOT_ZERO : ZERO;
- JumpDistance distance = how_to_jump == kJumpToNoUpdate ? kNearJump : kFarJump;
- j(condition, label, distance);
-}
-
void Assembler::StoreIntoObject(Register object,
const Address& dest,
Register value,
@@ -1515,8 +1477,7 @@
// Compare UntaggedObject::StorePointer.
Label done;
if (can_be_smi == kValueCanBeSmi) {
- testq(value, Immediate(kSmiTagMask));
- j(ZERO, &done, kNearJump);
+ BranchIfSmi(value, &done, kNearJump);
}
movb(ByteRegisterOf(TMP),
FieldAddress(object, target::Object::tags_offset()));
@@ -1582,8 +1543,7 @@
// Compare UntaggedObject::StorePointer.
Label done;
if (can_be_smi == kValueCanBeSmi) {
- testq(value, Immediate(kSmiTagMask));
- j(ZERO, &done, kNearJump);
+ BranchIfSmi(value, &done, kNearJump);
}
movb(ByteRegisterOf(TMP),
FieldAddress(object, target::Object::tags_offset()));
@@ -1621,16 +1581,15 @@
// reachable via a constant pool, so it doesn't matter if it is not traced via
// 'object'.
Label done;
- pushq(value);
- StoreIntoObjectFilter(object, value, &done, kValueCanBeSmi, kJumpToNoUpdate);
-
+ BranchIfSmi(value, &done, kNearJump);
+ testb(FieldAddress(value, target::Object::tags_offset()),
+ Immediate(1 << target::UntaggedObject::kNewBit));
+ j(ZERO, &done, Assembler::kNearJump);
testb(FieldAddress(object, target::Object::tags_offset()),
Immediate(1 << target::UntaggedObject::kOldAndNotRememberedBit));
j(ZERO, &done, Assembler::kNearJump);
-
- Stop("Store buffer update is required");
+ Stop("Write barrier is required");
Bind(&done);
- popq(value);
#endif // defined(DEBUG)
// No store buffer update.
}
@@ -1651,16 +1610,15 @@
// reachable via a constant pool, so it doesn't matter if it is not traced via
// 'object'.
Label done;
- pushq(value);
- StoreIntoObjectFilter(object, value, &done, kValueCanBeSmi, kJumpToNoUpdate);
-
+ BranchIfSmi(value, &done, kNearJump);
+ testb(FieldAddress(value, target::Object::tags_offset()),
+ Immediate(1 << target::UntaggedObject::kNewBit));
+ j(ZERO, &done, Assembler::kNearJump);
testb(FieldAddress(object, target::Object::tags_offset()),
Immediate(1 << target::UntaggedObject::kOldAndNotRememberedBit));
j(ZERO, &done, Assembler::kNearJump);
-
- Stop("Store buffer update is required");
+ Stop("Write barrier is required");
Bind(&done);
- popq(value);
#endif // defined(DEBUG)
// No store buffer update.
}
diff --git a/runtime/vm/compiler/assembler/assembler_x64.h b/runtime/vm/compiler/assembler/assembler_x64.h
index a9c45e6..3b4eb27 100644
--- a/runtime/vm/compiler/assembler/assembler_x64.h
+++ b/runtime/vm/compiler/assembler/assembler_x64.h
@@ -1491,11 +1491,6 @@
kJumpToBarrier,
};
- void StoreIntoObjectFilter(Register object,
- Register value,
- Label* label,
- CanBeSmi can_be_smi,
- BarrierFilterMode barrier_filter_mode);
void StoreIntoArrayBarrier(Register object,
Register slot,
Register value,
diff --git a/runtime/vm/compiler/runtime_api.cc b/runtime/vm/compiler/runtime_api.cc
index 0ae58f0..10ecb9e 100644
--- a/runtime/vm/compiler/runtime_api.cc
+++ b/runtime/vm/compiler/runtime_api.cc
@@ -360,6 +360,8 @@
const word UntaggedObject::kCanonicalBit = dart::UntaggedObject::kCanonicalBit;
+const word UntaggedObject::kNewBit = dart::UntaggedObject::kNewBit;
+
const word UntaggedObject::kOldAndNotRememberedBit =
dart::UntaggedObject::kOldAndNotRememberedBit;
diff --git a/runtime/vm/compiler/runtime_api.h b/runtime/vm/compiler/runtime_api.h
index 7afa8c7..e9324ae 100644
--- a/runtime/vm/compiler/runtime_api.h
+++ b/runtime/vm/compiler/runtime_api.h
@@ -411,6 +411,7 @@
public:
static const word kCardRememberedBit;
static const word kCanonicalBit;
+ static const word kNewBit;
static const word kOldAndNotRememberedBit;
static const word kOldAndNotMarkedBit;
static const word kImmutableBit;
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index e7070ee..a86369e 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -17414,10 +17414,8 @@
Code::ContainsCompressedPointers());
NoSafepointScope no_safepoint;
result ^= raw;
+ result.set_state_bits(0);
result.set_pointer_offsets_length(pointer_offsets_length);
- result.set_is_optimized(false);
- result.set_is_force_optimized(false);
- result.set_is_alive(false);
#if defined(INCLUDE_IL_PRINTER)
result.set_comments(Comments::New(0));
#endif
@@ -20336,19 +20334,19 @@
}
void AbstractType::set_flags(uint32_t value) const {
- StoreNonPointer(&untag()->flags_, value);
+ untag()->set_flags(value);
}
void AbstractType::set_type_state(UntaggedAbstractType::TypeState value) const {
ASSERT(!IsCanonical());
set_flags(
- UntaggedAbstractType::TypeStateBits::update(value, untag()->flags_));
+ UntaggedAbstractType::TypeStateBits::update(value, untag()->flags()));
}
void AbstractType::set_nullability(Nullability value) const {
ASSERT(!IsCanonical());
set_flags(UntaggedAbstractType::NullabilityBits::update(
- static_cast<uint8_t>(value), untag()->flags_));
+ static_cast<uint8_t>(value), untag()->flags()));
}
bool AbstractType::IsEquivalent(const Instance& other,
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index fec8db2..7d45ed5 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -8197,7 +8197,7 @@
Nullability nullability() const {
return static_cast<Nullability>(
- UntaggedAbstractType::NullabilityBits::decode(untag()->flags_));
+ UntaggedAbstractType::NullabilityBits::decode(untag()->flags()));
}
// Returns true if type has '?' nullability suffix, or it is a
// built-in type which is always nullable (Null, dynamic or void).
@@ -8493,7 +8493,7 @@
UntaggedAbstractType::TypeState type_state() const {
return static_cast<UntaggedAbstractType::TypeState>(
- UntaggedAbstractType::TypeStateBits::decode(untag()->flags_));
+ UntaggedAbstractType::TypeStateBits::decode(untag()->flags()));
}
void set_flags(uint32_t value) const;
void set_type_state(UntaggedAbstractType::TypeState value) const;
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 60b40e0..15975c6 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -2613,10 +2613,15 @@
// Accessed from generated code.
std::atomic<uword> type_test_stub_entry_point_;
// Accessed from generated code.
- uint32_t flags_;
+ std::atomic<uint32_t> flags_;
COMPRESSED_POINTER_FIELD(CodePtr, type_test_stub)
VISIT_FROM(type_test_stub)
+ uint32_t flags() const { return flags_.load(std::memory_order_relaxed); }
+ void set_flags(uint32_t value) {
+ flags_.store(value, std::memory_order_relaxed);
+ }
+
public:
enum TypeState {
kAllocated, // Initial state.
@@ -2625,13 +2630,13 @@
kFinalizedUninstantiated, // Uninstantiated type ready for use.
};
- using NullabilityBits = BitField<decltype(flags_), uint8_t, 0, 2>;
+ using NullabilityBits = BitField<uint32_t, uint8_t, 0, 2>;
static constexpr intptr_t kNullabilityMask = NullabilityBits::mask();
static constexpr intptr_t kTypeStateShift = NullabilityBits::kNextBit;
static constexpr intptr_t kTypeStateBits = 2;
using TypeStateBits =
- BitField<decltype(flags_), uint8_t, kTypeStateShift, kTypeStateBits>;
+ BitField<uint32_t, uint8_t, kTypeStateShift, kTypeStateBits>;
private:
RAW_HEAP_OBJECT_IMPLEMENTATION(AbstractType);
@@ -2643,7 +2648,7 @@
class UntaggedType : public UntaggedAbstractType {
public:
static constexpr intptr_t kTypeClassIdShift = TypeStateBits::kNextBit;
- using TypeClassIdBits = BitField<decltype(flags_),
+ using TypeClassIdBits = BitField<uint32_t,
ClassIdTagType,
kTypeClassIdShift,
sizeof(ClassIdTagType) * kBitsPerByte>;
@@ -2658,10 +2663,10 @@
CompressedObjectPtr* to_snapshot(Snapshot::Kind kind) { return to(); }
ClassIdTagType type_class_id() const {
- return TypeClassIdBits::decode(flags_);
+ return TypeClassIdBits::decode(flags());
}
void set_type_class_id(ClassIdTagType value) {
- flags_ = TypeClassIdBits::update(value, flags_);
+ set_flags(TypeClassIdBits::update(value, flags()));
}
friend class compiler::target::UntaggedType;
diff --git a/tests/modular/issue38703/main.dart b/tests/modular/issue38703/main.dart
index 076b9f1..e14eaf1 100644
--- a/tests/modular/issue38703/main.dart
+++ b/tests/modular/issue38703/main.dart
@@ -4,5 +4,5 @@
const Map<Key, String> m = {someKey: "PASSED"};
main() {
- Expect.equals(m[someKey], "PASSED");
+ Expect.equals("PASSED", m[someKey]);
}
diff --git a/tools/VERSION b/tools/VERSION
index 3799845..08d79d8 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 19
PATCH 0
-PRERELEASE 275
+PRERELEASE 276
PRERELEASE_PATCH 0