Version 2.14.0-392.0.dev
Merge commit 'd7c0271ac0d37f4c6391e77af3fcf987aaf47cfb' into 'dev'
diff --git a/DEPS b/DEPS
index e33bf57..d3facc6 100644
--- a/DEPS
+++ b/DEPS
@@ -133,7 +133,7 @@
"mime_rev": "c931f4bed87221beaece356494b43731445ce7b8",
"mockito_rev": "d39ac507483b9891165e422ec98d9fb480037c8b",
"oauth2_rev": "7cd3284049fe5badbec9f2bea2afc41d14c01057",
- "package_config_rev": "a84c0d45401f215fbe9384df923a38f4022a3c45",
+ "package_config_rev": "fb736aa12316dd2d882b202a438a6946a4b4bea0",
"path_rev": "c20d73c3516d3a0061c90f14b761ff532b9bf707",
"pedantic_rev": "66f2f6c27581c7936482e83be80b27be2719901c",
"platform_rev": "c20e6fa315e9f8820e51c0ae721f63aff33b8e17",
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/remove_comparison.dart b/pkg/analysis_server/lib/src/services/correction/dart/remove_comparison.dart
index 77e8d88..d0d1f5f 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/remove_comparison.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/remove_comparison.dart
@@ -4,6 +4,7 @@
import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analysis_server/src/services/linter/lint_names.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/error/error.dart';
@@ -15,17 +16,30 @@
class RemoveComparison extends CorrectionProducer {
@override
+ bool canBeAppliedInBulk;
+
+ @override
+ bool canBeAppliedToFile;
+
+ RemoveComparison(this.canBeAppliedInBulk, this.canBeAppliedToFile);
+
+ @override
FixKind get fixKind => DartFixKind.REMOVE_COMPARISON;
+ @override
+ FixKind get multiFixKind => DartFixKind.REMOVE_COMPARISON_MULTI;
+
/// Return `true` if the null comparison will always return `false`.
bool get _conditionIsFalse =>
(diagnostic as AnalysisError).errorCode ==
HintCode.UNNECESSARY_NULL_COMPARISON_FALSE;
/// Return `true` if the null comparison will always return `true`.
- bool get _conditionIsTrue =>
- (diagnostic as AnalysisError).errorCode ==
- HintCode.UNNECESSARY_NULL_COMPARISON_TRUE;
+ bool get _conditionIsTrue {
+ var errorCode = (diagnostic as AnalysisError).errorCode;
+ return errorCode == HintCode.UNNECESSARY_NULL_COMPARISON_TRUE ||
+ errorCode.name == LintNames.avoid_null_checks_in_equality_operators;
+ }
@override
Future<void> compute(ChangeBuilder builder) async {
@@ -108,5 +122,10 @@
}
/// Return an instance of this class. Used as a tear-off in `FixProcessor`.
- static RemoveComparison newInstance() => RemoveComparison();
+ static RemoveComparison newInstance() => RemoveComparison(false, false);
+
+ /// Return an instance of this class that can apply bulk and in-file fixes.
+ /// Used as a tear-off in `FixProcessor`.
+ static RemoveComparison newInstanceBulkFixable() =>
+ RemoveComparison(true, true);
}
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index ad30563..73c3779 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -126,8 +126,11 @@
'dart.fix.add.neNull', DartFixKindPriority.DEFAULT, 'Add != null');
static const ADD_NE_NULL_MULTI = FixKind('dart.fix.add.neNull.multi',
DartFixKindPriority.IN_FILE, 'Add != null everywhere in file');
- static const ADD_NULL_CHECK = FixKind('dart.fix.add.nullCheck',
- DartFixKindPriority.DEFAULT - 1, 'Add a null check (!)',);
+ static const ADD_NULL_CHECK = FixKind(
+ 'dart.fix.add.nullCheck',
+ DartFixKindPriority.DEFAULT - 1,
+ 'Add a null check (!)',
+ );
static const ADD_OVERRIDE = FixKind('dart.fix.add.override',
DartFixKindPriority.DEFAULT, "Add '@override' annotation");
static const ADD_OVERRIDE_MULTI = FixKind(
@@ -454,6 +457,10 @@
DartFixKindPriority.IN_FILE, 'Remove awaits in file');
static const REMOVE_COMPARISON = FixKind('dart.fix.remove.comparison',
DartFixKindPriority.DEFAULT, 'Remove comparison');
+ static const REMOVE_COMPARISON_MULTI = FixKind(
+ 'dart.fix.remove.comparison.multi',
+ DartFixKindPriority.IN_FILE,
+ 'Remove comparisons in file');
static const REMOVE_CONST = FixKind(
'dart.fix.remove.const', DartFixKindPriority.DEFAULT, 'Remove const');
static const REMOVE_DEAD_CODE = FixKind('dart.fix.remove.deadCode',
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index e4e975c..d5fd17e 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -344,6 +344,9 @@
LintNames.avoid_init_to_null: [
RemoveInitializer.newInstance,
],
+ LintNames.avoid_null_checks_in_equality_operators: [
+ RemoveComparison.newInstanceBulkFixable,
+ ],
LintNames.avoid_private_typedef_functions: [
InlineTypedef.newInstance,
],
diff --git a/pkg/analysis_server/lib/src/services/linter/lint_names.dart b/pkg/analysis_server/lib/src/services/linter/lint_names.dart
index 2d61eacc..0f5d7d0 100644
--- a/pkg/analysis_server/lib/src/services/linter/lint_names.dart
+++ b/pkg/analysis_server/lib/src/services/linter/lint_names.dart
@@ -16,6 +16,8 @@
static const String avoid_function_literals_in_foreach_calls =
'avoid_function_literals_in_foreach_calls';
static const String avoid_init_to_null = 'avoid_init_to_null';
+ static const String avoid_null_checks_in_equality_operators =
+ 'avoid_null_checks_in_equality_operators';
static const String avoid_private_typedef_functions =
'avoid_private_typedef_functions';
static const String avoid_redundant_argument_values =
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_comparison_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_comparison_test.dart
index 5494406..2de13b2 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_comparison_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_comparison_test.dart
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analysis_server/src/services/linter/lint_names.dart';
+import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -12,6 +14,8 @@
void main() {
defineReflectiveSuite(() {
defineReflectiveTests(RemoveComparisonTest);
+ defineReflectiveTests(RemoveNullCheckComparisonTest);
+ defineReflectiveTests(RemoveNullCheckComparisonBulkTest);
});
}
@@ -188,3 +192,219 @@
''');
}
}
+
+@reflectiveTest
+class RemoveNullCheckComparisonBulkTest extends BulkFixProcessorTest {
+ @override
+ String get lintCode => LintNames.avoid_null_checks_in_equality_operators;
+
+ Future<void> test_singleFile() async {
+ await resolveTestCode('''
+class Person {
+ final String name = '';
+
+ @override
+ operator ==(other) =>
+ other != null &&
+ other is Person &&
+ name == other.name;
+}
+
+class Person2 {
+ final String name = '';
+
+ @override
+ operator ==(other) =>
+ other != null &&
+ other is Person &&
+ name == other.name;
+}
+''');
+ await assertHasFix('''
+class Person {
+ final String name = '';
+
+ @override
+ operator ==(other) =>
+ other is Person &&
+ name == other.name;
+}
+
+class Person2 {
+ final String name = '';
+
+ @override
+ operator ==(other) =>
+ other is Person &&
+ name == other.name;
+}
+''');
+ }
+
+ @FailingTest(reason: 'Only the first comparison is removed')
+ Future<void> test_singleFile_overlapping() async {
+ await resolveTestCode('''
+class Person {
+ final String name = '';
+
+ @override
+ operator ==(other) =>
+ other != null &&
+ other != null &&
+ other is Person &&
+ name == other.name;
+}
+''');
+ await assertHasFix('''
+class Person {
+ final String name = '';
+
+ @override
+ operator ==(other) =>
+ other is Person &&
+ name == other.name;
+}
+''');
+ }
+}
+
+@reflectiveTest
+class RemoveNullCheckComparisonTest extends FixProcessorLintTest {
+ @override
+ FixKind get kind => DartFixKind.REMOVE_COMPARISON;
+
+ @override
+ String get lintCode => LintNames.avoid_null_checks_in_equality_operators;
+
+ Future<void> test_expressionBody() async {
+ await resolveTestCode('''
+class Person {
+ final String name = '';
+
+ @override
+ operator ==(other) =>
+ other != null &&
+ other is Person &&
+ name == other.name;
+}
+''');
+ await assertHasFix('''
+class Person {
+ final String name = '';
+
+ @override
+ operator ==(other) =>
+ other is Person &&
+ name == other.name;
+}
+''',
+ errorFilter: (error) =>
+ error.errorCode == HintCode.UNNECESSARY_NULL_COMPARISON_TRUE);
+ }
+
+ Future<void> test_functionBody() async {
+ await resolveTestCode('''
+class Person {
+ final String name = '';
+
+ @override
+ operator ==(other) {
+ return other != null &&
+ other is Person &&
+ name == other.name;
+ }
+}
+''');
+ await assertHasFix('''
+class Person {
+ final String name = '';
+
+ @override
+ operator ==(other) {
+ return other is Person &&
+ name == other.name;
+ }
+}
+''',
+ errorFilter: (error) =>
+ error.errorCode == HintCode.UNNECESSARY_NULL_COMPARISON_TRUE);
+ }
+
+ Future<void> test_ifNullAssignmentStatement() async {
+ await resolveTestCode('''
+class Person {
+ final String name = '';
+
+ @override
+ operator ==(other) {
+ if (other is! Person) return false;
+ other ??= Person();
+ return other.name == name;
+ }
+}
+''');
+ await assertNoFix();
+ }
+
+ /// todo(pq): consider implementing
+ @FailingTest(reason: 'Fix unimplemented')
+ Future<void> test_ifNullStatement() async {
+ await resolveTestCode('''
+class Person {
+ final String name = '';
+
+ @override
+ operator ==(other) {
+ if (other is! Person) return false;
+ final toCompare = other ?? Person();
+ return toCompare.name == name;
+ }
+}
+''');
+
+ await assertHasFix('''
+class Person {
+ final String name = '';
+
+ @override
+ operator ==(other) {
+ if (other is! Person) return false;
+ final toCompare = other;
+ return toCompare.name == name;
+ }
+}
+''',
+ errorFilter: (error) =>
+ error.errorCode == StaticWarningCode.DEAD_NULL_AWARE_EXPRESSION);
+ }
+
+ /// todo(pq): implement or remove the lint (see: https://github.com/dart-lang/linter/issues/2864)
+ @FailingTest(reason: 'Fix unimplemented')
+ Future<void> test_ifStatement() async {
+ await resolveTestCode('''
+class Person {
+ final String name = '';
+
+ @override
+ operator ==(other) {
+ if (other == null) return false;
+ return other is Person &&
+ name == other.name;
+ }
+}
+''');
+ await assertHasFix('''
+class Person {
+ final String name = '';
+
+ @override
+ operator ==(other) {
+ return other is Person &&
+ name == other.name;
+ }
+}
+''',
+ errorFilter: (error) =>
+ error.errorCode == HintCode.UNNECESSARY_NULL_COMPARISON_FALSE);
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/const_eval_throws_exception_test.dart b/pkg/analyzer/test/src/diagnostics/const_eval_throws_exception_test.dart
index 077c88d..6c17ac2 100644
--- a/pkg/analyzer/test/src/diagnostics/const_eval_throws_exception_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/const_eval_throws_exception_test.dart
@@ -11,65 +11,47 @@
main() {
defineReflectiveSuite(() {
defineReflectiveTests(ConstEvalThrowsExceptionTest);
- defineReflectiveTests(ConstEvalThrowsExceptionWithNullSafetyTest);
+ defineReflectiveTests(ConstEvalThrowsExceptionWithoutNullSafetyTest);
});
}
@reflectiveTest
class ConstEvalThrowsExceptionTest extends PubPackageResolutionTest
- with WithoutNullSafetyMixin, ConstEvalThrowsExceptionTestCases {
- test_binaryMinus_null() async {
+ with ConstEvalThrowsExceptionTestCases {
+ test_asExpression_typeParameter() async {
await assertErrorsInCode('''
-const dynamic D = null;
-const C = D - 5;
-''', [
- error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 34, 5),
- ]);
-
- await assertErrorsInCode('''
-const dynamic D = null;
-const C = 5 - D;
-''', [
- error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 34, 5),
- ]);
- }
-
- test_binaryPlus_null() async {
- await assertErrorsInCode('''
-const dynamic D = null;
-const C = D + 5;
-''', [
- error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 34, 5),
- ]);
-
- await assertErrorsInCode('''
-const dynamic D = null;
-const C = 5 + D;
-''', [
- error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 34, 5),
- ]);
- }
-
- test_eqEq_nonPrimitiveRightOperand() async {
- await assertNoErrorsInCode('''
-const c = const T.eq(1, const Object());
-class T {
- final Object value;
- const T.eq(Object o1, Object o2) : value = o1 == o2;
+class C<T> {
+ final t;
+ const C(dynamic x) : t = x as T;
}
-''');
- }
-
- test_fromEnvironment_ifElement() async {
- await assertNoErrorsInCode('''
-const b = bool.fromEnvironment('foo');
main() {
- const l1 = [1, 2, 3];
- const l2 = [if (b) ...l1];
- print(l2);
+ const C<int>(0);
+ const C<int>('foo');
+ const C<int>(null);
}
-''');
+''', [
+ error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 92, 19),
+ error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 115, 18),
+ ]);
+ }
+
+ test_asExpression_typeParameter_nested() async {
+ await assertErrorsInCode('''
+class C<T> {
+ final t;
+ const C(dynamic x) : t = x as List<T>;
+}
+
+main() {
+ const C<int>(<int>[]);
+ const C<int>(<num>[]);
+ const C<int>(null);
+}
+''', [
+ error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 104, 21),
+ error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 129, 18),
+ ]);
}
}
@@ -393,41 +375,60 @@
}
@reflectiveTest
-class ConstEvalThrowsExceptionWithNullSafetyTest
- extends ConstEvalThrowsExceptionTest with WithNullSafetyMixin {
- test_asExpression_typeParameter() async {
+class ConstEvalThrowsExceptionWithoutNullSafetyTest
+ extends PubPackageResolutionTest
+ with WithoutNullSafetyMixin, ConstEvalThrowsExceptionTestCases {
+ test_binaryMinus_null() async {
await assertErrorsInCode('''
-class C<T> {
- final t;
- const C(dynamic x) : t = x as T;
-}
-
-main() {
- const C<int>(0);
- const C<int>('foo');
- const C<int>(null);
-}
+const dynamic D = null;
+const C = D - 5;
''', [
- error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 92, 19),
- error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 115, 18),
+ error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 34, 5),
+ ]);
+
+ await assertErrorsInCode('''
+const dynamic D = null;
+const C = 5 - D;
+''', [
+ error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 34, 5),
]);
}
- test_asExpression_typeParameter_nested() async {
+ test_binaryPlus_null() async {
await assertErrorsInCode('''
-class C<T> {
- final t;
- const C(dynamic x) : t = x as List<T>;
+const dynamic D = null;
+const C = D + 5;
+''', [
+ error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 34, 5),
+ ]);
+
+ await assertErrorsInCode('''
+const dynamic D = null;
+const C = 5 + D;
+''', [
+ error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 34, 5),
+ ]);
+ }
+
+ test_eqEq_nonPrimitiveRightOperand() async {
+ await assertNoErrorsInCode('''
+const c = const T.eq(1, const Object());
+class T {
+ final Object value;
+ const T.eq(Object o1, Object o2) : value = o1 == o2;
}
+''');
+ }
+
+ test_fromEnvironment_ifElement() async {
+ await assertNoErrorsInCode('''
+const b = bool.fromEnvironment('foo');
main() {
- const C<int>(<int>[]);
- const C<int>(<num>[]);
- const C<int>(null);
+ const l1 = [1, 2, 3];
+ const l2 = [if (b) ...l1];
+ print(l2);
}
-''', [
- error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 104, 21),
- error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 129, 18),
- ]);
+''');
}
}
diff --git a/pkg/analyzer/test/src/diagnostics/const_not_initialized_test.dart b/pkg/analyzer/test/src/diagnostics/const_not_initialized_test.dart
index f96bfde..85b1174 100644
--- a/pkg/analyzer/test/src/diagnostics/const_not_initialized_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/const_not_initialized_test.dart
@@ -10,13 +10,15 @@
main() {
defineReflectiveSuite(() {
defineReflectiveTests(ConstNotInitializedTest);
- defineReflectiveTests(ConstNotInitializedWithNullSafetyTest);
+ defineReflectiveTests(ConstNotInitializedWithoutNullSafetyTest);
});
}
@reflectiveTest
class ConstNotInitializedTest extends PubPackageResolutionTest
- with WithoutNullSafetyMixin {
+ with ConstNotInitializedTestCases {}
+
+mixin ConstNotInitializedTestCases on PubPackageResolutionTest {
test_extension_static() async {
await assertErrorsInCode('''
extension E on String {
@@ -57,5 +59,5 @@
}
@reflectiveTest
-class ConstNotInitializedWithNullSafetyTest extends ConstNotInitializedTest
- with WithNullSafetyMixin {}
+class ConstNotInitializedWithoutNullSafetyTest extends PubPackageResolutionTest
+ with ConstNotInitializedTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/analyzer/test/src/diagnostics/implements_non_class_test.dart b/pkg/analyzer/test/src/diagnostics/implements_non_class_test.dart
index 7172e98..01890bb 100644
--- a/pkg/analyzer/test/src/diagnostics/implements_non_class_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/implements_non_class_test.dart
@@ -10,13 +10,23 @@
main() {
defineReflectiveSuite(() {
defineReflectiveTests(ImplementsNonClassTest);
- defineReflectiveTests(ImplementsNonClassWithNullSafetyTest);
+ defineReflectiveTests(ImplementsNonClassWithoutNullSafetyTest);
});
}
@reflectiveTest
class ImplementsNonClassTest extends PubPackageResolutionTest
- with WithoutNullSafetyMixin {
+ with ImplementsNonClassTestCases {
+ test_Never() async {
+ await assertErrorsInCode('''
+class A implements Never {}
+''', [
+ error(CompileTimeErrorCode.IMPLEMENTS_NON_CLASS, 19, 5),
+ ]);
+ }
+}
+
+mixin ImplementsNonClassTestCases on PubPackageResolutionTest {
test_class() async {
await assertErrorsInCode(r'''
int A = 7;
@@ -56,13 +66,5 @@
}
@reflectiveTest
-class ImplementsNonClassWithNullSafetyTest extends ImplementsNonClassTest
- with WithNullSafetyMixin {
- test_Never() async {
- await assertErrorsInCode('''
-class A implements Never {}
-''', [
- error(CompileTimeErrorCode.IMPLEMENTS_NON_CLASS, 19, 5),
- ]);
- }
-}
+class ImplementsNonClassWithoutNullSafetyTest extends PubPackageResolutionTest
+ with ImplementsNonClassTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/analyzer/test/src/diagnostics/implicit_this_reference_in_initializer_test.dart b/pkg/analyzer/test/src/diagnostics/implicit_this_reference_in_initializer_test.dart
index 52e2b7c..3818269 100644
--- a/pkg/analyzer/test/src/diagnostics/implicit_this_reference_in_initializer_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/implicit_this_reference_in_initializer_test.dart
@@ -10,13 +10,61 @@
main() {
defineReflectiveSuite(() {
defineReflectiveTests(ImplicitThisReferenceInInitializerTest);
- defineReflectiveTests(ImplicitThisReferenceInInitializerWithNullSafetyTest);
+ defineReflectiveTests(
+ ImplicitThisReferenceInInitializerWithoutNullSafetyTest);
});
}
@reflectiveTest
class ImplicitThisReferenceInInitializerTest extends PubPackageResolutionTest
- with WithoutNullSafetyMixin {
+ with ImplicitThisReferenceInInitializerTestCases {
+ test_class_field_late_invokeInstanceMethod() async {
+ await assertNoErrorsInCode(r'''
+class A {
+ late int x = foo();
+ int foo() => 0;
+}
+''');
+ }
+
+ test_class_field_late_invokeStaticMethod() async {
+ await assertNoErrorsInCode(r'''
+class A {
+ late int x = foo();
+ static int foo() => 0;
+}
+''');
+ }
+
+ test_class_field_late_readInstanceField() async {
+ await assertNoErrorsInCode(r'''
+class A {
+ int a = 0;
+ late int x = a;
+}
+''');
+ }
+
+ test_class_field_late_readStaticField() async {
+ await assertNoErrorsInCode(r'''
+class A {
+ static int a = 0;
+ late int x = a;
+}
+''');
+ }
+
+ test_mixin_field_late_readInstanceField() async {
+ await assertNoErrorsInCode(r'''
+mixin M {
+ int a = 0;
+ late int x = a;
+}
+''');
+ }
+}
+
+mixin ImplicitThisReferenceInInitializerTestCases on PubPackageResolutionTest {
test_class_field_commentReference_prefixedIdentifier() async {
await assertNoErrorsInCode(r'''
class A {
@@ -272,50 +320,6 @@
}
@reflectiveTest
-class ImplicitThisReferenceInInitializerWithNullSafetyTest
- extends ImplicitThisReferenceInInitializerTest with WithNullSafetyMixin {
- test_class_field_late_invokeInstanceMethod() async {
- await assertNoErrorsInCode(r'''
-class A {
- late int x = foo();
- int foo() => 0;
-}
-''');
- }
-
- test_class_field_late_invokeStaticMethod() async {
- await assertNoErrorsInCode(r'''
-class A {
- late int x = foo();
- static int foo() => 0;
-}
-''');
- }
-
- test_class_field_late_readInstanceField() async {
- await assertNoErrorsInCode(r'''
-class A {
- int a = 0;
- late int x = a;
-}
-''');
- }
-
- test_class_field_late_readStaticField() async {
- await assertNoErrorsInCode(r'''
-class A {
- static int a = 0;
- late int x = a;
-}
-''');
- }
-
- test_mixin_field_late_readInstanceField() async {
- await assertNoErrorsInCode(r'''
-mixin M {
- int a = 0;
- late int x = a;
-}
-''');
- }
-}
+class ImplicitThisReferenceInInitializerWithoutNullSafetyTest
+ extends PubPackageResolutionTest
+ with ImplicitThisReferenceInInitializerTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_constant_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_constant_test.dart
index 1eaf99d..78185db 100644
--- a/pkg/analyzer/test/src/diagnostics/invalid_constant_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/invalid_constant_test.dart
@@ -10,13 +10,24 @@
main() {
defineReflectiveSuite(() {
defineReflectiveTests(InvalidConstantTest);
- defineReflectiveTests(InvalidConstantWithNullSafetyTest);
+ defineReflectiveTests(InvalidConstantWithoutNullSafetyTest);
});
}
@reflectiveTest
class InvalidConstantTest extends PubPackageResolutionTest
- with WithoutNullSafetyMixin {
+ with InvalidConstantTestCases {
+ test_in_initializer_field_as() async {
+ await assertNoErrorsInCode('''
+class C<T> {
+ final l;
+ const C.test(dynamic x) : l = x as List<T>;
+}
+''');
+ }
+}
+
+mixin InvalidConstantTestCases on PubPackageResolutionTest {
test_in_initializer_assert_condition() async {
await assertErrorsInCode('''
class A {
@@ -164,14 +175,5 @@
}
@reflectiveTest
-class InvalidConstantWithNullSafetyTest extends InvalidConstantTest
- with WithNullSafetyMixin {
- test_in_initializer_field_as() async {
- await assertNoErrorsInCode('''
-class C<T> {
- final l;
- const C.test(dynamic x) : l = x as List<T>;
-}
-''');
- }
-}
+class InvalidConstantWithoutNullSafetyTest extends PubPackageResolutionTest
+ with InvalidConstantTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_implementation_override_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_implementation_override_test.dart
index 7e0de60..a9578f5 100644
--- a/pkg/analyzer/test/src/diagnostics/invalid_implementation_override_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/invalid_implementation_override_test.dart
@@ -10,13 +10,15 @@
main() {
defineReflectiveSuite(() {
defineReflectiveTests(InvalidImplementationOverrideTest);
- defineReflectiveTests(InvalidImplementationOverrideWithNullSafetyTest);
+ defineReflectiveTests(InvalidImplementationOverrideWithoutNullSafetyTest);
});
}
@reflectiveTest
class InvalidImplementationOverrideTest extends PubPackageResolutionTest
- with WithoutNullSafetyMixin {
+ with InvalidImplementationOverrideTestCases {}
+
+mixin InvalidImplementationOverrideTestCases on PubPackageResolutionTest {
test_getter_abstractOverridesConcrete() async {
await assertErrorsInCode('''
class A {
@@ -112,5 +114,6 @@
}
@reflectiveTest
-class InvalidImplementationOverrideWithNullSafetyTest
- extends PubPackageResolutionTest with WithNullSafetyMixin {}
+class InvalidImplementationOverrideWithoutNullSafetyTest
+ extends PubPackageResolutionTest
+ with InvalidImplementationOverrideTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_null_aware_operator_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_null_aware_operator_test.dart
index 7c37ddd..a411785 100644
--- a/pkg/analyzer/test/src/diagnostics/invalid_null_aware_operator_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/invalid_null_aware_operator_test.dart
@@ -17,7 +17,7 @@
@reflectiveTest
class InvalidNullAwareOperatorAfterShortCircuitTest
- extends PubPackageResolutionTest with WithNullSafetyMixin {
+ extends PubPackageResolutionTest {
Future<void> test_getter_previousTarget() async {
await assertErrorsInCode('''
void f(String? s) {
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_override_different_default_values_named_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_override_different_default_values_named_test.dart
index 2c6d4f1..016d36b 100644
--- a/pkg/analyzer/test/src/diagnostics/invalid_override_different_default_values_named_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/invalid_override_different_default_values_named_test.dart
@@ -307,8 +307,7 @@
@reflectiveTest
class InvalidOverrideDifferentDefaultValuesNamedWithNullSafetyTest
- extends InvalidOverrideDifferentDefaultValuesNamedTest
- with WithNullSafetyMixin {
+ extends InvalidOverrideDifferentDefaultValuesNamedTest {
test_concrete_equal_optIn_extends_optOut() async {
newFile('$testPackageLibPath/a.dart', content: r'''
// @dart = 2.7
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_override_different_default_values_positional_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_override_different_default_values_positional_test.dart
index c25b8e0..fb62381 100644
--- a/pkg/analyzer/test/src/diagnostics/invalid_override_different_default_values_positional_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/invalid_override_different_default_values_positional_test.dart
@@ -322,8 +322,7 @@
@reflectiveTest
class InvalidOverrideDifferentDefaultValuesPositionalWithNullSafetyTest
- extends InvalidOverrideDifferentDefaultValuesPositionalTest
- with WithNullSafetyMixin {
+ extends InvalidOverrideDifferentDefaultValuesPositionalTest {
test_concrete_equal_optIn_extends_optOut() async {
newFile('$testPackageLibPath/a.dart', content: r'''
// @dart = 2.7
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_reference_to_this_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_reference_to_this_test.dart
index f58cb1a..b643469 100644
--- a/pkg/analyzer/test/src/diagnostics/invalid_reference_to_this_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/invalid_reference_to_this_test.dart
@@ -10,13 +10,39 @@
main() {
defineReflectiveSuite(() {
defineReflectiveTests(InvalidReferenceToThisTest);
- defineReflectiveTests(InvalidReferenceToThisWithNullSafetyTest);
+ defineReflectiveTests(InvalidReferenceToThisWithoutNullSafetyTest);
});
}
@reflectiveTest
class InvalidReferenceToThisTest extends PubPackageResolutionTest
- with WithoutNullSafetyMixin {
+ with InvalidReferenceToThisTestCases {
+ test_instanceVariableInitializer_inDeclaration_late() async {
+ await assertNoErrorsInCode(r'''
+class A {
+ late var f = this;
+}
+''');
+ }
+
+ test_mixinVariableInitializer_inDeclaration_late() async {
+ await assertNoErrorsInCode(r'''
+mixin A {
+ late var f = this;
+}
+''');
+ }
+
+ test_variableInitializer_late() async {
+ await assertErrorsInCode('''
+late var x = this;
+''', [
+ error(CompileTimeErrorCode.INVALID_REFERENCE_TO_THIS, 13, 4),
+ ]);
+ }
+}
+
+mixin InvalidReferenceToThisTestCases on PubPackageResolutionTest {
test_class_constructor() async {
await assertErrorsInCode(r'''
class A {
@@ -121,29 +147,6 @@
}
@reflectiveTest
-class InvalidReferenceToThisWithNullSafetyTest
- extends InvalidReferenceToThisTest with WithNullSafetyMixin {
- test_instanceVariableInitializer_inDeclaration_late() async {
- await assertNoErrorsInCode(r'''
-class A {
- late var f = this;
-}
-''');
- }
-
- test_mixinVariableInitializer_inDeclaration_late() async {
- await assertNoErrorsInCode(r'''
-mixin A {
- late var f = this;
-}
-''');
- }
-
- test_variableInitializer_late() async {
- await assertErrorsInCode('''
-late var x = this;
-''', [
- error(CompileTimeErrorCode.INVALID_REFERENCE_TO_THIS, 13, 4),
- ]);
- }
-}
+class InvalidReferenceToThisWithoutNullSafetyTest
+ extends PubPackageResolutionTest
+ with InvalidReferenceToThisTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/analyzer/test/src/diagnostics/label_undefined_test.dart b/pkg/analyzer/test/src/diagnostics/label_undefined_test.dart
index abe2d6a..3231737 100644
--- a/pkg/analyzer/test/src/diagnostics/label_undefined_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/label_undefined_test.dart
@@ -10,13 +10,15 @@
main() {
defineReflectiveSuite(() {
defineReflectiveTests(LabelUndefinedTest);
- defineReflectiveTests(LabelUndefinedWithNullSafetyTest);
+ defineReflectiveTests(LabelUndefinedWithoutNullSafetyTest);
});
}
@reflectiveTest
class LabelUndefinedTest extends PubPackageResolutionTest
- with WithoutNullSafetyMixin {
+ with LabelUndefinedTestCases {}
+
+mixin LabelUndefinedTestCases on PubPackageResolutionTest {
test_break() async {
await assertErrorsInCode(r'''
f() {
@@ -69,5 +71,5 @@
}
@reflectiveTest
-class LabelUndefinedWithNullSafetyTest extends LabelUndefinedTest
- with WithNullSafetyMixin {}
+class LabelUndefinedWithoutNullSafetyTest extends PubPackageResolutionTest
+ with LabelUndefinedTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/dds/test/dap/integration/debug_exceptions_test.dart b/pkg/dds/test/dap/integration/debug_exceptions_test.dart
new file mode 100644
index 0000000..752ce5f
--- /dev/null
+++ b/pkg/dds/test/dap/integration/debug_exceptions_test.dart
@@ -0,0 +1,75 @@
+// Copyright (c) 2021, 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:test/test.dart';
+
+import 'test_client.dart';
+import 'test_scripts.dart';
+import 'test_support.dart';
+
+main() {
+ group('debug mode', () {
+ late DapTestSession dap;
+ setUp(() async {
+ dap = await DapTestSession.setUp();
+ });
+ tearDown(() => dap.tearDown());
+
+ test('does not pause on exceptions if mode not set', () async {
+ final client = dap.client;
+ final testFile = dap.createTestFile(simpleThrowingProgram);
+
+ // Run the app and expect it to complete (it should not pause).
+ final outputEvents = await client.collectOutput(file: testFile);
+
+ // Expect error info printed to stderr.
+ final output = outputEvents
+ .where((e) => e.category == 'stderr')
+ .map((e) => e.output)
+ .join();
+ expectLinesStartWith(output, [
+ 'Unhandled exception:',
+ 'error',
+ ]);
+ });
+
+ test('pauses on uncaught exceptions when mode=Unhandled', () async {
+ final client = dap.client;
+ final testFile = dap.createTestFile(simpleThrowingProgram);
+
+ // Run and expect to pause on an exception.
+ await client.pauseOnException(
+ testFile,
+ exceptionPauseMode: 'Unhandled',
+ );
+ });
+
+ test('does not pauses on caught exceptions when mode=Unhandled', () async {
+ final client = dap.client;
+ final testFile = dap.createTestFile(simpleCaughtErrorProgram);
+
+ // Run the app and expect it to complete (it should not pause).
+ final outputEvents = await client.collectOutput(file: testFile);
+
+ // Expect error info printed to stderr.
+ final output = outputEvents
+ .where((e) => e.category == 'stdout')
+ .map((e) => e.output)
+ .join();
+ expectLines(output, ['Caught!']);
+ });
+
+ test('pauses on caught exceptions when mode=All', () async {
+ final client = dap.client;
+ final testFile = dap.createTestFile(simpleCaughtErrorProgram);
+
+ // Run and expect to pause on an exception.
+ await client.pauseOnException(
+ testFile,
+ exceptionPauseMode: 'All',
+ );
+ });
+ // These tests can be slow due to starting up the external server process.
+ }, timeout: Timeout.none);
+}
diff --git a/pkg/dds/test/dap/integration/test_client.dart b/pkg/dds/test/dap/integration/test_client.dart
index ffd3150..b7c9ecd 100644
--- a/pkg/dds/test/dap/integration/test_client.dart
+++ b/pkg/dds/test/dap/integration/test_client.dart
@@ -409,6 +409,31 @@
return stop;
}
+ /// Sets the exception pause mode to [pauseMode] and expects to pause after
+ /// running the script.
+ ///
+ /// Launch options can be customised by passing a custom [launch] function that
+ /// will be used instead of calling `launch(file.path)`.
+ Future<StoppedEventBody> pauseOnException(
+ File file, {
+ String? exceptionPauseMode, // All, Unhandled, None
+ Future<Response> Function()? launch,
+ }) async {
+ final stop = expectStop('exception', file: file);
+
+ await Future.wait([
+ initialize(),
+ sendRequest(
+ SetExceptionBreakpointsArguments(
+ filters: [if (exceptionPauseMode != null) exceptionPauseMode],
+ ),
+ ),
+ launch?.call() ?? this.launch(file.path),
+ ], eagerError: true);
+
+ return stop;
+ }
+
/// Returns whether DDS is available for the VM Service the debug adapter
/// is connected to.
Future<bool> get ddsAvailable async {
diff --git a/pkg/dds/test/dap/integration/test_scripts.dart b/pkg/dds/test/dap/integration/test_scripts.dart
index 6b6a05b..0a5a492 100644
--- a/pkg/dds/test/dap/integration/test_scripts.dart
+++ b/pkg/dds/test/dap/integration/test_scripts.dart
@@ -15,3 +15,21 @@
print('Hello!'); // BREAKPOINT
}
''';
+
+/// A simple Dart script that throws an error and catches it in user code.
+const simpleCaughtErrorProgram = r'''
+ void main(List<String> args) async {
+ try {
+ throw 'error';
+ } catch (e) {
+ print('Caught!');
+ }
+ }
+''';
+
+/// A simple Dart script that throws in user code.
+const simpleThrowingProgram = r'''
+ void main(List<String> args) async {
+ throw 'error';
+ }
+''';
diff --git a/pkg/dds/test/dap/integration/test_support.dart b/pkg/dds/test/dap/integration/test_support.dart
index dc88974..d5d8372 100644
--- a/pkg/dds/test/dap/integration/test_support.dart
+++ b/pkg/dds/test/dap/integration/test_support.dart
@@ -31,9 +31,21 @@
final vmServiceUriPattern = RegExp(r'Connecting to VM Service at ([^\s]+)\s');
/// Expects [actual] to equal the lines [expected], ignoring differences in line
-/// endings.
+/// endings and trailing whitespace.
void expectLines(String actual, List<String> expected) {
- expect(actual.replaceAll('\r\n', '\n'), equals(expected.join('\n')));
+ expect(
+ actual.replaceAll('\r\n', '\n').trim(),
+ equals(expected.join('\n').trim()),
+ );
+}
+
+/// Expects [actual] starts with [expected], ignoring differences in line
+/// endings and trailing whitespace.
+void expectLinesStartWith(String actual, List<String> expected) {
+ expect(
+ actual.replaceAll('\r\n', '\n').trim(),
+ startsWith(expected.join('\n').trim()),
+ );
}
/// Returns the 1-base line in [file] that contains [searchText].
diff --git a/runtime/lib/isolate.cc b/runtime/lib/isolate.cc
index 0b72f7f..48cb890 100644
--- a/runtime/lib/isolate.cc
+++ b/runtime/lib/isolate.cc
@@ -299,8 +299,12 @@
Object& validated_result = Object::Handle(zone);
const Object& msg_obj = Object::Handle(zone, obj.ptr());
validated_result = ValidateMessageObject(zone, isolate, msg_obj);
- // msg_array = [<message>, <object-in-message-to-rehash>]
- const Array& msg_array = Array::Handle(zone, Array::New(2));
+ // msg_array = [
+ // <message>,
+ // <collection-lib-objects-to-rehash>,
+ // <core-lib-objects-to-rehash>,
+ // ]
+ const Array& msg_array = Array::Handle(zone, Array::New(3));
msg_array.SetAt(0, msg_obj);
if (validated_result.IsUnhandledException()) {
Exceptions::PropagateError(Error::Cast(validated_result));
diff --git a/runtime/tests/vm/dart/isolates/fast_object_copy_test.dart b/runtime/tests/vm/dart/isolates/fast_object_copy_test.dart
index dda2b0f..4f71fae 100644
--- a/runtime/tests/vm/dart/isolates/fast_object_copy_test.dart
+++ b/runtime/tests/vm/dart/isolates/fast_object_copy_test.dart
@@ -2,7 +2,7 @@
// 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.
-// VMOptions=
+// VMOptions=--no-enable-isolate-groups
// VMOptions=--enable-isolate-groups --no-enable-fast-object-copy
// VMOptions=--enable-isolate-groups --enable-fast-object-copy
// VMOptions=--enable-isolate-groups --no-enable-fast-object-copy --gc-on-foc-slow-path --force-evacuation --verify-store-buffer
@@ -219,6 +219,8 @@
await testFastOnly();
await testSlowOnly();
+
+ await testWeakProperty();
}
Future testTransferrable() async {
@@ -579,6 +581,69 @@
await sendReceive([notAllocatableInTLAB, smallContainer]));
}
}
+
+ Future testWeakProperty() async {
+ final key = Object();
+ final expando1 = Expando();
+ final expando2 = Expando();
+ final expando3 = Expando();
+ final expando4 = Expando();
+ expando1[key] = expando2;
+ expando2[expando2] = expando3;
+ expando3[expando3] = expando4;
+ expando4[expando4] = {'foo': 'bar'};
+
+ {
+ final result = await sendReceive([
+ key,
+ expando1,
+ ]);
+ final keyCopy = result[0];
+ final expando1Copy = result[1] as Expando;
+ final expando2Copy = expando1Copy[keyCopy] as Expando;
+ final expando3Copy = expando2Copy[expando2Copy] as Expando;
+ final expando4Copy = expando3Copy[expando3Copy] as Expando;
+ Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
+ }
+ {
+ final result = await sendReceive([
+ expando1,
+ key,
+ ]);
+ final expando1Copy = result[0] as Expando;
+ final keyCopy = result[1];
+ final expando2Copy = expando1Copy[keyCopy] as Expando;
+ final expando3Copy = expando2Copy[expando2Copy] as Expando;
+ final expando4Copy = expando3Copy[expando3Copy] as Expando;
+ Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
+ }
+ {
+ final result = await sendReceive([
+ expando1,
+ notAllocatableInTLAB,
+ key,
+ ]);
+ final expando1Copy = result[0] as Expando;
+ final keyCopy = result[2];
+ final expando2Copy = expando1Copy[keyCopy] as Expando;
+ final expando3Copy = expando2Copy[expando2Copy] as Expando;
+ final expando4Copy = expando3Copy[expando3Copy] as Expando;
+ Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
+ }
+ {
+ final result = await sendReceive([
+ key,
+ notAllocatableInTLAB,
+ expando1,
+ ]);
+ final keyCopy = result[0];
+ final expando1Copy = result[2] as Expando;
+ final expando2Copy = expando1Copy[keyCopy] as Expando;
+ final expando3Copy = expando2Copy[expando2Copy] as Expando;
+ final expando4Copy = expando3Copy[expando3Copy] as Expando;
+ Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
+ }
+ }
}
main() async {
diff --git a/runtime/tests/vm/dart_2/isolates/fast_object_copy_test.dart b/runtime/tests/vm/dart_2/isolates/fast_object_copy_test.dart
index 933283f..73b82f4 100644
--- a/runtime/tests/vm/dart_2/isolates/fast_object_copy_test.dart
+++ b/runtime/tests/vm/dart_2/isolates/fast_object_copy_test.dart
@@ -4,7 +4,7 @@
// @dart = 2.9
-// VMOptions=
+// VMOptions=--no-enable-isolate-groups
// VMOptions=--enable-isolate-groups --no-enable-fast-object-copy
// VMOptions=--enable-isolate-groups --enable-fast-object-copy
// VMOptions=--enable-isolate-groups --no-enable-fast-object-copy --gc-on-foc-slow-path --force-evacuation --verify-store-buffer
@@ -221,6 +221,8 @@
await testFastOnly();
await testSlowOnly();
+
+ await testWeakProperty();
}
Future testTransferrable() async {
@@ -581,6 +583,69 @@
await sendReceive([notAllocatableInTLAB, smallContainer]));
}
}
+
+ Future testWeakProperty() async {
+ final key = Object();
+ final expando1 = Expando();
+ final expando2 = Expando();
+ final expando3 = Expando();
+ final expando4 = Expando();
+ expando1[key] = expando2;
+ expando2[expando2] = expando3;
+ expando3[expando3] = expando4;
+ expando4[expando4] = {'foo': 'bar'};
+
+ {
+ final result = await sendReceive([
+ key,
+ expando1,
+ ]);
+ final keyCopy = result[0];
+ final expando1Copy = result[1] as Expando;
+ final expando2Copy = expando1Copy[keyCopy] as Expando;
+ final expando3Copy = expando2Copy[expando2Copy] as Expando;
+ final expando4Copy = expando3Copy[expando3Copy] as Expando;
+ Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
+ }
+ {
+ final result = await sendReceive([
+ expando1,
+ key,
+ ]);
+ final expando1Copy = result[0] as Expando;
+ final keyCopy = result[1];
+ final expando2Copy = expando1Copy[keyCopy] as Expando;
+ final expando3Copy = expando2Copy[expando2Copy] as Expando;
+ final expando4Copy = expando3Copy[expando3Copy] as Expando;
+ Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
+ }
+ {
+ final result = await sendReceive([
+ expando1,
+ notAllocatableInTLAB,
+ key,
+ ]);
+ final expando1Copy = result[0] as Expando;
+ final keyCopy = result[2];
+ final expando2Copy = expando1Copy[keyCopy] as Expando;
+ final expando3Copy = expando2Copy[expando2Copy] as Expando;
+ final expando4Copy = expando3Copy[expando3Copy] as Expando;
+ Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
+ }
+ {
+ final result = await sendReceive([
+ key,
+ notAllocatableInTLAB,
+ expando1,
+ ]);
+ final keyCopy = result[0];
+ final expando1Copy = result[2] as Expando;
+ final expando2Copy = expando1Copy[keyCopy] as Expando;
+ final expando3Copy = expando2Copy[expando2Copy] as Expando;
+ final expando4Copy = expando3Copy[expando3Copy] as Expando;
+ Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
+ }
+ }
}
main() async {
diff --git a/runtime/vm/dart_entry.cc b/runtime/vm/dart_entry.cc
index f062909..8b2adff9 100644
--- a/runtime/vm/dart_entry.cc
+++ b/runtime/vm/dart_entry.cc
@@ -786,24 +786,36 @@
return result.ptr();
}
-ObjectPtr DartLibraryCalls::RehashObjects(
- Thread* thread,
- const Object& array_or_growable_array) {
+static ObjectPtr RehashObjects(Zone* zone,
+ const Library& library,
+ const Object& array_or_growable_array) {
ASSERT(array_or_growable_array.IsArray() ||
array_or_growable_array.IsGrowableObjectArray());
-
- auto zone = thread->zone();
- const Library& collections_lib =
- Library::Handle(zone, Library::CollectionLibrary());
- const Function& rehashing_function = Function::Handle(
- zone,
- collections_lib.LookupFunctionAllowPrivate(Symbols::_rehashObjects()));
+ const auto& rehashing_function = Function::Handle(
+ zone, library.LookupFunctionAllowPrivate(Symbols::_rehashObjects()));
ASSERT(!rehashing_function.IsNull());
- const Array& arguments = Array::Handle(zone, Array::New(1));
+ const auto& arguments = Array::Handle(zone, Array::New(1));
arguments.SetAt(0, array_or_growable_array);
return DartEntry::InvokeFunction(rehashing_function, arguments);
}
+ObjectPtr DartLibraryCalls::RehashObjectsInDartCollection(
+ Thread* thread,
+ const Object& array_or_growable_array) {
+ auto zone = thread->zone();
+ const auto& collections_lib =
+ Library::Handle(zone, Library::CollectionLibrary());
+ return RehashObjects(zone, collections_lib, array_or_growable_array);
+}
+
+ObjectPtr DartLibraryCalls::RehashObjectsInDartCore(
+ Thread* thread,
+ const Object& array_or_growable_array) {
+ auto zone = thread->zone();
+ const auto& core_lib = Library::Handle(zone, Library::CoreLibrary());
+ return RehashObjects(zone, core_lib, array_or_growable_array);
+}
+
} // namespace dart
diff --git a/runtime/vm/dart_entry.h b/runtime/vm/dart_entry.h
index 5740337..c69d5c8 100644
--- a/runtime/vm/dart_entry.h
+++ b/runtime/vm/dart_entry.h
@@ -306,9 +306,15 @@
// Returns null on success, an ErrorPtr on failure.
static ObjectPtr EnsureScheduleImmediate();
- // Runs the `_rehashObjects()` function.
- static ObjectPtr RehashObjects(Thread* thread,
- const Object& array_or_growable_array);
+ // Runs the `_rehashObjects()` function in `dart:collection`.
+ static ObjectPtr RehashObjectsInDartCollection(
+ Thread* thread,
+ const Object& array_or_growable_array);
+
+ // Runs the `_rehashObjects()` function in `dart:core`.
+ static ObjectPtr RehashObjectsInDartCore(
+ Thread* thread,
+ const Object& array_or_growable_array);
};
} // namespace dart
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index b1a7315..458cc40 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -1329,14 +1329,29 @@
// Parse the message.
Object& msg_obj = Object::Handle(zone);
if (message->IsPersistentHandle()) {
- // msg_array = [<message>, <object-in-message-to-rehash>]
+ // msg_array = [
+ // <message>,
+ // <collection-lib-objects-to-rehash>,
+ // <core-lib-objects-to-rehash>,
+ // ]
const auto& msg_array = Array::Handle(
zone, Array::RawCast(message->persistent_handle()->ptr()));
+ ASSERT(msg_array.Length() == 3);
msg_obj = msg_array.At(0);
if (msg_array.At(1) != Object::null()) {
const auto& objects_to_rehash = Object::Handle(zone, msg_array.At(1));
- const auto& result = Object::Handle(
- zone, DartLibraryCalls::RehashObjects(thread, objects_to_rehash));
+ auto& result = Object::Handle(zone);
+ result = DartLibraryCalls::RehashObjectsInDartCollection(
+ thread, objects_to_rehash);
+ if (result.ptr() != Object::null()) {
+ msg_obj = result.ptr();
+ }
+ }
+ if (msg_array.At(2) != Object::null()) {
+ const auto& objects_to_rehash = Object::Handle(zone, msg_array.At(2));
+ auto& result = Object::Handle(zone);
+ result =
+ DartLibraryCalls::RehashObjectsInDartCore(thread, objects_to_rehash);
if (result.ptr() != Object::null()) {
msg_obj = result.ptr();
}
diff --git a/runtime/vm/message_snapshot.cc b/runtime/vm/message_snapshot.cc
index b9400ee..c3ab4ec 100644
--- a/runtime/vm/message_snapshot.cc
+++ b/runtime/vm/message_snapshot.cc
@@ -483,7 +483,7 @@
Array& maps = Array::Handle(d->zone(), d->refs());
maps = maps.Slice(start_index_, stop_index_ - start_index_,
/*with_type_argument=*/false);
- return DartLibraryCalls::RehashObjects(d->thread(), maps);
+ return DartLibraryCalls::RehashObjectsInDartCollection(d->thread(), maps);
}
class ClassMessageSerializationCluster : public MessageSerializationCluster {
@@ -916,18 +916,14 @@
}
if (cls_.ptr() == d->isolate_group()->object_store()->expando_class()) {
- Instance& instance = Instance::Handle(d->zone());
- const String& selector = Library::PrivateCoreLibName(Symbols::_rehash());
- Array& args = Array::Handle(d->zone(), Array::New(1));
- for (intptr_t i = start_index_; i < stop_index_; i++) {
+ const auto& expandos =
+ Array::Handle(d->zone(), Array::New(stop_index_ - start_index_));
+ auto& instance = Instance::Handle(d->zone());
+ for (intptr_t i = start_index_, j = 0; i < stop_index_; i++, j++) {
instance ^= d->Ref(i);
- args.SetAt(0, instance);
- ObjectPtr error = instance.Invoke(selector, args, Object::empty_array(),
- false, false);
- if (error != Object::null()) {
- return error;
- }
+ expandos.SetAt(j, instance);
}
+ return DartLibraryCalls::RehashObjectsInDartCore(d->thread(), expandos);
}
return nullptr;
}
diff --git a/runtime/vm/object_graph_copy.cc b/runtime/vm/object_graph_copy.cc
index 7b9189f..ea1edf5 100644
--- a/runtime/vm/object_graph_copy.cc
+++ b/runtime/vm/object_graph_copy.cc
@@ -3,11 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
#include "vm/object_graph_copy.h"
+
#include "vm/dart_api_state.h"
#include "vm/flags.h"
#include "vm/heap/weak_table.h"
#include "vm/longjump.h"
#include "vm/object.h"
+#include "vm/object_store.h"
#include "vm/snapshot.h"
#include "vm/symbols.h"
@@ -79,7 +81,6 @@
V(UnlinkedCall) \
V(UnwindError) \
V(UserTag) \
- V(WeakProperty) \
V(WeakSerializationReference)
namespace dart {
@@ -353,7 +354,8 @@
: ForwardMapBase(thread),
raw_from_to_(thread->zone(), 20),
raw_transferables_from_to_(thread->zone(), 0),
- raw_objects_to_rehash_(thread->zone(), 0) {
+ raw_objects_to_rehash_(thread->zone(), 0),
+ raw_expandos_to_rehash_(thread->zone(), 0) {
raw_from_to_.Resize(2);
raw_from_to_[0] = Object::null();
raw_from_to_[1] = Object::null();
@@ -381,11 +383,13 @@
raw_transferables_from_to_.Add(from);
raw_transferables_from_to_.Add(to);
}
+ void AddWeakProperty(WeakPropertyPtr from) { raw_weak_properties_.Add(from); }
void AddExternalTypedData(ExternalTypedDataPtr to) {
raw_external_typed_data_to_.Add(to);
}
void AddObjectToRehash(ObjectPtr to) { raw_objects_to_rehash_.Add(to); }
+ void AddExpandoToRehash(ObjectPtr to) { raw_expandos_to_rehash_.Add(to); }
private:
friend class FastObjectCopy;
@@ -395,6 +399,8 @@
GrowableArray<TransferableTypedDataPtr> raw_transferables_from_to_;
GrowableArray<ExternalTypedDataPtr> raw_external_typed_data_to_;
GrowableArray<ObjectPtr> raw_objects_to_rehash_;
+ GrowableArray<ObjectPtr> raw_expandos_to_rehash_;
+ GrowableArray<WeakPropertyPtr> raw_weak_properties_;
intptr_t fill_cursor_ = 0;
DISALLOW_COPY_AND_ASSIGN(FastForwardMap);
@@ -432,14 +438,18 @@
transferables_from_to_.Add(&TransferableTypedData::Handle(from.ptr()));
transferables_from_to_.Add(&TransferableTypedData::Handle(to.ptr()));
}
-
+ void AddWeakProperty(const WeakProperty& from) {
+ weak_properties_.Add(&WeakProperty::Handle(from.ptr()));
+ }
void AddExternalTypedData(ExternalTypedDataPtr to) {
external_typed_data_.Add(&ExternalTypedData::Handle(to));
}
-
void AddObjectToRehash(const Object& to) {
objects_to_rehash_.Add(&Object::Handle(to.ptr()));
}
+ void AddExpandoToRehash(const Object& to) {
+ expandos_to_rehash_.Add(&Object::Handle(to.ptr()));
+ }
void FinalizeTransferables() {
for (intptr_t i = 0; i < transferables_from_to_.length(); i += 2) {
@@ -464,6 +474,8 @@
GrowableArray<const TransferableTypedData*> transferables_from_to_;
GrowableArray<const ExternalTypedData*> external_typed_data_;
GrowableArray<const Object*> objects_to_rehash_;
+ GrowableArray<const Object*> expandos_to_rehash_;
+ GrowableArray<const WeakProperty*> weak_properties_;
intptr_t fill_cursor_ = 0;
DISALLOW_COPY_AND_ASSIGN(SlowForwardMap);
@@ -478,7 +490,9 @@
heap_(thread->isolate_group()->heap()),
class_table_(thread->isolate_group()->class_table()),
new_space_(heap_->new_space()),
- tmp_(Object::Handle(thread->zone())) {}
+ tmp_(Object::Handle(thread->zone())),
+ expando_cid_(Class::GetClassId(
+ thread->isolate_group()->object_store()->expando_class())) {}
~ObjectCopyBase() {}
protected:
@@ -590,6 +604,7 @@
ClassTable* class_table_;
Scavenger* new_space_;
Object& tmp_;
+ intptr_t expando_cid_;
const char* exception_msg_ = nullptr;
};
@@ -670,7 +685,7 @@
return;
}
- auto to = Forward(tags, value.Decompress(heap_base_));
+ auto to = Forward(tags, value_decompressed);
StoreCompressedPointerNoBarrier(dst, offset, to);
}
@@ -708,9 +723,15 @@
TransferableTypedDataPtr to) {
fast_forward_map_.AddTransferable(from, to);
}
+ void EnqueueWeakProperty(WeakPropertyPtr from) {
+ fast_forward_map_.AddWeakProperty(from);
+ }
void EnqueueObjectToRehash(ObjectPtr to) {
fast_forward_map_.AddObjectToRehash(to);
}
+ void EnqueueExpandoToRehash(ObjectPtr to) {
+ fast_forward_map_.AddExpandoToRehash(to);
+ }
static void StoreCompressedArrayPointers(intptr_t array_length,
ObjectPtr src,
@@ -888,9 +909,15 @@
const TransferableTypedData& to) {
slow_forward_map_.AddTransferable(from, to);
}
+ void EnqueueWeakProperty(const WeakProperty& from) {
+ slow_forward_map_.AddWeakProperty(from);
+ }
void EnqueueObjectToRehash(const Object& to) {
slow_forward_map_.AddObjectToRehash(to);
}
+ void EnqueueExpandoToRehash(const Object& to) {
+ slow_forward_map_.AddExpandoToRehash(to);
+ }
void StoreCompressedArrayPointers(intptr_t array_length,
const Object& src,
@@ -1136,6 +1163,7 @@
CopyLinkedHashBase<2, typename Types::LinkedHashMap>(
from, to, UntagLinkedHashMap(from), UntagLinkedHashMap(to));
}
+
void CopyLinkedHashSet(typename Types::LinkedHashSet from,
typename Types::LinkedHashSet to) {
CopyLinkedHashBase<1, typename Types::LinkedHashSet>(
@@ -1205,7 +1233,17 @@
raw_to->offset_in_bytes_ = raw_from->offset_in_bytes_;
raw_to->data_ = nullptr;
- if (raw_to->typed_data_.Decompress(Base::heap_base_) == Object::null()) {
+ auto forwarded_backing_store =
+ raw_to->typed_data_.Decompress(Base::heap_base_);
+ if (forwarded_backing_store == Marker() ||
+ forwarded_backing_store == Object::null()) {
+ // Ensure the backing store is never "sentinel" - the scavenger doesn't
+ // like it.
+ Base::StoreCompressedPointerNoBarrier(
+ Types::GetTypedDataViewPtr(to),
+ OFFSET_OF(UntaggedTypedDataView, typed_data_), Object::null());
+ raw_to->length_ = 0;
+ raw_to->offset_in_bytes_ = 0;
ASSERT(Base::exception_msg_ != nullptr);
return;
}
@@ -1257,6 +1295,20 @@
Base::EnqueueTransferable(from, to);
}
+ void CopyWeakProperty(typename Types::WeakProperty from,
+ typename Types::WeakProperty to) {
+ // We store `null`s as keys/values and let the main algorithm know that
+ // we should check reachability of the key again after the fixpoint (if it
+ // became reachable, forward the key/value).
+ Base::StoreCompressedPointerNoBarrier(Types::GetWeakPropertyPtr(to),
+ OFFSET_OF(UntaggedWeakProperty, key_),
+ Object::null());
+ Base::StoreCompressedPointerNoBarrier(
+ Types::GetWeakPropertyPtr(to), OFFSET_OF(UntaggedWeakProperty, value_),
+ Object::null());
+ Base::EnqueueWeakProperty(from);
+ }
+
#define DEFINE_UNSUPPORTED(clazz) \
void Copy##clazz(typename Types::clazz from, typename Types::clazz to) { \
FATAL("Objects of type " #clazz " should not occur in object graphs"); \
@@ -1291,33 +1343,85 @@
if (root_copy == Marker()) {
return root_copy;
}
- while (fast_forward_map_.fill_cursor_ <
- fast_forward_map_.raw_from_to_.length()) {
- const intptr_t index = fast_forward_map_.fill_cursor_;
- ObjectPtr from = fast_forward_map_.raw_from_to_[index];
- ObjectPtr to = fast_forward_map_.raw_from_to_[index + 1];
- FastCopyObject(from, to);
- if (exception_msg_ != nullptr) {
- return root_copy;
+ auto& from_weak_property = WeakProperty::Handle(zone_);
+ auto& to_weak_property = WeakProperty::Handle(zone_);
+ auto& weak_property_key = Object::Handle(zone_);
+ while (true) {
+ if (fast_forward_map_.fill_cursor_ ==
+ fast_forward_map_.raw_from_to_.length()) {
+ break;
}
- fast_forward_map_.fill_cursor_ += 2;
+
+ // Run fixpoint to copy all objects.
+ while (fast_forward_map_.fill_cursor_ <
+ fast_forward_map_.raw_from_to_.length()) {
+ const intptr_t index = fast_forward_map_.fill_cursor_;
+ ObjectPtr from = fast_forward_map_.raw_from_to_[index];
+ ObjectPtr to = fast_forward_map_.raw_from_to_[index + 1];
+ FastCopyObject(from, to);
+ if (exception_msg_ != nullptr) {
+ return root_copy;
+ }
+ fast_forward_map_.fill_cursor_ += 2;
+ }
+
+ // Possibly forward values of [WeakProperty]s if keys became reachable.
+ intptr_t i = 0;
+ auto& weak_properties = fast_forward_map_.raw_weak_properties_;
+ while (i < weak_properties.length()) {
+ from_weak_property = weak_properties[i];
+ weak_property_key =
+ fast_forward_map_.ForwardedObject(from_weak_property.key());
+ if (weak_property_key.ptr() != Marker()) {
+ to_weak_property ^=
+ fast_forward_map_.ForwardedObject(from_weak_property.ptr());
+
+ // The key became reachable so we'll change the forwarded
+ // [WeakProperty]'s key to the new key (it is `null` at this point).
+ to_weak_property.set_key(weak_property_key);
+
+ // Since the key has become strongly reachable in the copied graph,
+ // we'll also need to forward the value.
+ ForwardCompressedPointer(from_weak_property.ptr(),
+ to_weak_property.ptr(),
+ OFFSET_OF(UntaggedWeakProperty, value_));
+
+ // We don't need to process this [WeakProperty] again.
+ const intptr_t last = weak_properties.length() - 1;
+ if (i < last) {
+ weak_properties[i] = weak_properties[last];
+ weak_properties.SetLength(last);
+ continue;
+ }
+ }
+ i++;
+ }
}
if (root_copy != Marker()) {
- TryBuildArrayOfObjectsToRehash();
+ ObjectPtr array;
+ array = TryBuildArrayOfObjectsToRehash(
+ fast_forward_map_.raw_objects_to_rehash_);
+ if (array == Marker()) return root_copy;
+ raw_objects_to_rehash_ = Array::RawCast(array);
+
+ array = TryBuildArrayOfObjectsToRehash(
+ fast_forward_map_.raw_expandos_to_rehash_);
+ if (array == Marker()) return root_copy;
+ raw_expandos_to_rehash_ = Array::RawCast(array);
}
return root_copy;
}
- void TryBuildArrayOfObjectsToRehash() {
- const auto& objects_to_rehash = fast_forward_map_.raw_objects_to_rehash_;
+ ObjectPtr TryBuildArrayOfObjectsToRehash(
+ const GrowableArray<ObjectPtr>& objects_to_rehash) {
const intptr_t length = objects_to_rehash.length();
- if (length == 0) return;
+ if (length == 0) return Object::null();
const intptr_t size = Array::InstanceSize(length);
const uword array_addr = new_space_->TryAllocate(thread_, size);
if (array_addr == 0) {
exception_msg_ = kFastAllocationFailed;
- return;
+ return Marker();
}
const uword header_size =
@@ -1333,7 +1437,7 @@
for (intptr_t i = 0; i < length; ++i) {
array_data[i] = objects_to_rehash[i];
}
- raw_objects_to_rehash_ = array;
+ return array;
}
private:
@@ -1365,15 +1469,21 @@
#else
CopyUserdefinedInstance(Instance::RawCast(from), Instance::RawCast(to));
#endif
+ if (cid == expando_cid_) {
+ EnqueueExpandoToRehash(to);
+ }
}
ArrayPtr raw_objects_to_rehash_ = Array::null();
+ ArrayPtr raw_expandos_to_rehash_ = Array::null();
};
class SlowObjectCopy : public ObjectCopy<SlowObjectCopyBase> {
public:
explicit SlowObjectCopy(Thread* thread)
- : ObjectCopy(thread), objects_to_rehash_(Array::Handle(thread->zone())) {}
+ : ObjectCopy(thread),
+ objects_to_rehash_(Array::Handle(thread->zone())),
+ expandos_to_rehash_(Array::Handle(thread->zone())) {}
~SlowObjectCopy() {}
ObjectPtr ContinueCopyGraphSlow(const Object& root,
@@ -1383,32 +1493,76 @@
root_copy = Forward(TagsFromUntaggedObject(root.ptr().untag()), root);
}
+ WeakProperty& weak_property = WeakProperty::Handle(Z);
Object& from = Object::Handle(Z);
Object& to = Object::Handle(Z);
- while (slow_forward_map_.fill_cursor_ <
- slow_forward_map_.from_to_.length()) {
- const intptr_t index = slow_forward_map_.fill_cursor_;
- from = slow_forward_map_.from_to_[index]->ptr();
- to = slow_forward_map_.from_to_[index + 1]->ptr();
- CopyObject(from, to);
- slow_forward_map_.fill_cursor_ += 2;
- if (exception_msg_ != nullptr) {
- return Marker();
+ while (true) {
+ if (slow_forward_map_.fill_cursor_ ==
+ slow_forward_map_.from_to_.length()) {
+ break;
+ }
+
+ // Run fixpoint to copy all objects.
+ while (slow_forward_map_.fill_cursor_ <
+ slow_forward_map_.from_to_.length()) {
+ const intptr_t index = slow_forward_map_.fill_cursor_;
+ from = slow_forward_map_.from_to_[index]->ptr();
+ to = slow_forward_map_.from_to_[index + 1]->ptr();
+ CopyObject(from, to);
+ slow_forward_map_.fill_cursor_ += 2;
+ if (exception_msg_ != nullptr) {
+ return Marker();
+ }
+ }
+
+ // Possibly forward values of [WeakProperty]s if keys became reachable.
+ intptr_t i = 0;
+ auto& weak_properties = slow_forward_map_.weak_properties_;
+ while (i < weak_properties.length()) {
+ const auto& from_weak_property = *weak_properties[i];
+ to = slow_forward_map_.ForwardedObject(from_weak_property.key());
+ if (to.ptr() != Marker()) {
+ weak_property ^=
+ slow_forward_map_.ForwardedObject(from_weak_property.ptr());
+
+ // The key became reachable so we'll change the forwarded
+ // [WeakProperty]'s key to the new key (it is `null` at this point).
+ weak_property.set_key(to);
+
+ // Since the key has become strongly reachable in the copied graph,
+ // we'll also need to forward the value.
+ ForwardCompressedPointer(from_weak_property, weak_property,
+ OFFSET_OF(UntaggedWeakProperty, value_));
+
+ // We don't need to process this [WeakProperty] again.
+ const intptr_t last = weak_properties.length() - 1;
+ if (i < last) {
+ weak_properties[i] = weak_properties[last];
+ weak_properties.SetLength(last);
+ continue;
+ }
+ }
+ i++;
}
}
- BuildArrayOfObjectsToRehash();
+
+ objects_to_rehash_ =
+ BuildArrayOfObjectsToRehash(slow_forward_map_.objects_to_rehash_);
+ expandos_to_rehash_ =
+ BuildArrayOfObjectsToRehash(slow_forward_map_.expandos_to_rehash_);
return root_copy.ptr();
}
- void BuildArrayOfObjectsToRehash() {
- const auto& objects_to_rehash = slow_forward_map_.objects_to_rehash_;
+ ArrayPtr BuildArrayOfObjectsToRehash(
+ const GrowableArray<const Object*>& objects_to_rehash) {
const intptr_t length = objects_to_rehash.length();
- if (length == 0) return;
+ if (length == 0) return Array::null();
- objects_to_rehash_ = Array::New(length);
+ const auto& array = Array::Handle(zone_, Array::New(length));
for (intptr_t i = 0; i < length; ++i) {
- objects_to_rehash_.SetAt(i, *objects_to_rehash[i]);
+ array.SetAt(i, *objects_to_rehash[i]);
}
+ return array.ptr();
}
private:
@@ -1429,9 +1583,13 @@
#else
CopyUserdefinedInstance(from, to);
#endif
+ if (cid == expando_cid_) {
+ EnqueueExpandoToRehash(to);
+ }
}
Array& objects_to_rehash_;
+ Array& expandos_to_rehash_;
};
class ObjectGraphCopier {
@@ -1492,7 +1650,7 @@
private:
ObjectPtr CopyObjectGraphInternal(const Object& root,
const char* volatile* exception_msg) {
- const auto& result_array = Array::Handle(zone_, Array::New(2));
+ const auto& result_array = Array::Handle(zone_, Array::New(3));
if (!root.ptr()->IsHeapObject()) {
result_array.SetAt(0, root);
return result_array.ptr();
@@ -1523,6 +1681,8 @@
result_array.SetAt(0, result);
fast_object_copy_.tmp_ = fast_object_copy_.raw_objects_to_rehash_;
result_array.SetAt(1, fast_object_copy_.tmp_);
+ fast_object_copy_.tmp_ = fast_object_copy_.raw_expandos_to_rehash_;
+ result_array.SetAt(2, fast_object_copy_.tmp_);
HandlifyExternalTypedData();
HandlifyTransferables();
return result_array.ptr();
@@ -1563,6 +1723,7 @@
result_array.SetAt(0, result);
result_array.SetAt(1, slow_object_copy_.objects_to_rehash_);
+ result_array.SetAt(2, slow_object_copy_.expandos_to_rehash_);
return result_array.ptr();
}
@@ -1572,8 +1733,10 @@
MakeUninitializedNewSpaceObjectsGCSafe();
HandlifyTransferables();
+ HandlifyWeakProperties();
HandlifyExternalTypedData();
HandlifyObjectsToReHash();
+ HandlifyExpandosToReHash();
HandlifyFromToObjects();
slow_forward_map.fill_cursor_ = fast_forward_map.fill_cursor_;
}
@@ -1598,46 +1761,35 @@
}
}
void HandlifyTransferables() {
- auto& raw_transferables =
- fast_object_copy_.fast_forward_map_.raw_transferables_from_to_;
- const auto length = raw_transferables.length();
- if (length > 0) {
- auto& transferables =
- slow_object_copy_.slow_forward_map_.transferables_from_to_;
- transferables.Resize(length);
- for (intptr_t i = 0; i < length; i++) {
- transferables[i] =
- &TransferableTypedData::Handle(Z, raw_transferables[i]);
- }
- raw_transferables.Clear();
- }
+ Handlify(&fast_object_copy_.fast_forward_map_.raw_transferables_from_to_,
+ &slow_object_copy_.slow_forward_map_.transferables_from_to_);
+ }
+ void HandlifyWeakProperties() {
+ Handlify(&fast_object_copy_.fast_forward_map_.raw_weak_properties_,
+ &slow_object_copy_.slow_forward_map_.weak_properties_);
}
void HandlifyExternalTypedData() {
- auto& raw_external_typed_data =
- fast_object_copy_.fast_forward_map_.raw_external_typed_data_to_;
- const auto length = raw_external_typed_data.length();
- if (length > 0) {
- auto& external_typed_data =
- slow_object_copy_.slow_forward_map_.external_typed_data_;
- external_typed_data.Resize(length);
- for (intptr_t i = 0; i < length; i++) {
- external_typed_data[i] =
- &ExternalTypedData::Handle(Z, raw_external_typed_data[i]);
- }
- raw_external_typed_data.Clear();
- }
+ Handlify(&fast_object_copy_.fast_forward_map_.raw_external_typed_data_to_,
+ &slow_object_copy_.slow_forward_map_.external_typed_data_);
}
void HandlifyObjectsToReHash() {
- auto& fast_forward_map = fast_object_copy_.fast_forward_map_;
- auto& slow_forward_map = slow_object_copy_.slow_forward_map_;
- const auto length = fast_forward_map.raw_transferables_from_to_.length();
+ Handlify(&fast_object_copy_.fast_forward_map_.raw_objects_to_rehash_,
+ &slow_object_copy_.slow_forward_map_.objects_to_rehash_);
+ }
+ void HandlifyExpandosToReHash() {
+ Handlify(&fast_object_copy_.fast_forward_map_.raw_expandos_to_rehash_,
+ &slow_object_copy_.slow_forward_map_.expandos_to_rehash_);
+ }
+ template <typename RawType, typename HandleType>
+ void Handlify(GrowableArray<RawType>* from,
+ GrowableArray<const HandleType*>* to) {
+ const auto length = from->length();
if (length > 0) {
- slow_forward_map.objects_to_rehash_.Resize(length);
+ to->Resize(length);
for (intptr_t i = 0; i < length; i++) {
- slow_forward_map.objects_to_rehash_[i] =
- &Object::Handle(Z, fast_forward_map.raw_objects_to_rehash_[i]);
+ (*to)[i] = &HandleType::Handle(Z, (*from)[i]);
}
- fast_forward_map.raw_objects_to_rehash_.Clear();
+ from->Clear();
}
}
void HandlifyFromToObjects() {
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index e50a979..f99afbe 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -3206,6 +3206,8 @@
friend class Scavenger;
template <bool>
friend class ScavengerVisitorBase;
+ friend class FastObjectCopy; // For OFFSET_OF
+ friend class SlowObjectCopy; // For OFFSET_OF
};
// MirrorReferences are used by mirrors to hold reflectees that are VM
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index 76d0070..03dd529 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -415,7 +415,6 @@
V(_objectNoSuchMethod, "_objectNoSuchMethod") \
V(_objectToString, "_objectToString") \
V(_onData, "_onData") \
- V(_rehash, "_rehash") \
V(_rehashObjects, "_rehashObjects") \
V(_resultOrListeners, "_resultOrListeners") \
V(_runExtension, "_runExtension") \
diff --git a/runtime/vm/virtual_memory.h b/runtime/vm/virtual_memory.h
index 54ffc7b..c674d1c 100644
--- a/runtime/vm/virtual_memory.h
+++ b/runtime/vm/virtual_memory.h
@@ -90,6 +90,10 @@
// can give back the virtual memory to the system. Returns true on success.
static bool FreeSubSegment(void* address, intptr_t size);
+ static VirtualMemory* Reserve(intptr_t size, intptr_t alignment);
+ static void Commit(void* address, intptr_t size);
+ static void Decommit(void* address, intptr_t size);
+
// These constructors are only used internally when reserving new virtual
// spaces. They do not reserve any virtual address space on their own.
VirtualMemory(const MemoryRegion& region,
@@ -112,6 +116,7 @@
MemoryRegion reserved_;
static uword page_size_;
+ static VirtualMemory* compressed_heap_;
DISALLOW_IMPLICIT_CONSTRUCTORS(VirtualMemory);
};
diff --git a/runtime/vm/virtual_memory_posix.cc b/runtime/vm/virtual_memory_posix.cc
index 2d8757da..14ff79d 100644
--- a/runtime/vm/virtual_memory_posix.cc
+++ b/runtime/vm/virtual_memory_posix.cc
@@ -44,6 +44,7 @@
#endif
uword VirtualMemory::page_size_ = 0;
+VirtualMemory* VirtualMemory::compressed_heap_ = nullptr;
static void unmap(uword start, uword end);
@@ -76,30 +77,22 @@
}
void VirtualMemory::Init() {
+ page_size_ = CalculatePageSize();
+
#if defined(DART_COMPRESSED_POINTERS)
- if (VirtualMemoryCompressedHeap::GetRegion() == nullptr) {
- void* address = GenericMapAligned(
- nullptr, PROT_NONE, kCompressedHeapSize, kCompressedHeapAlignment,
- kCompressedHeapSize + kCompressedHeapAlignment,
- MAP_PRIVATE | MAP_ANONYMOUS);
- if (address == nullptr) {
+ if (compressed_heap_ == nullptr) {
+ compressed_heap_ = Reserve(kCompressedHeapSize, kCompressedHeapAlignment);
+ if (compressed_heap_ == nullptr) {
int error = errno;
const int kBufferSize = 1024;
char error_buf[kBufferSize];
- FATAL2("Failed to reserve region for compressed heap: %d (%s)", error,
- Utils::StrError(error, error_buf, kBufferSize));
+ FATAL("Failed to reserve region for compressed heap: %d (%s)", error,
+ Utils::StrError(error, error_buf, kBufferSize));
}
- VirtualMemoryCompressedHeap::Init(address);
+ VirtualMemoryCompressedHeap::Init(compressed_heap_->address());
}
#endif // defined(DART_COMPRESSED_POINTERS)
- if (page_size_ != 0) {
- // Already initialized.
- return;
- }
-
- page_size_ = CalculatePageSize();
-
#if defined(DUAL_MAPPING_SUPPORTED)
// Perf is Linux-specific and the flags aren't defined in Product.
#if defined(DART_TARGET_OS_LINUX) && !defined(PRODUCT)
@@ -159,9 +152,8 @@
void VirtualMemory::Cleanup() {
#if defined(DART_COMPRESSED_POINTERS)
- uword heap_base =
- reinterpret_cast<uword>(VirtualMemoryCompressedHeap::GetRegion());
- unmap(heap_base, heap_base + kCompressedHeapSize);
+ delete compressed_heap_;
+ compressed_heap_ = nullptr;
VirtualMemoryCompressedHeap::Cleanup();
#endif // defined(DART_COMPRESSED_POINTERS)
}
@@ -259,7 +251,7 @@
if (region.pointer() == nullptr) {
return nullptr;
}
- mprotect(region.pointer(), region.size(), PROT_READ | PROT_WRITE);
+ Commit(region.pointer(), region.size());
return new VirtualMemory(region, region);
}
#endif // defined(DART_COMPRESSED_POINTERS)
@@ -359,10 +351,48 @@
return new VirtualMemory(region, region);
}
+VirtualMemory* VirtualMemory::Reserve(intptr_t size, intptr_t alignment) {
+ ASSERT(Utils::IsAligned(size, PageSize()));
+ ASSERT(Utils::IsPowerOfTwo(alignment));
+ ASSERT(Utils::IsAligned(alignment, PageSize()));
+ intptr_t allocated_size = size + alignment - PageSize();
+ void* address =
+ GenericMapAligned(nullptr, PROT_NONE, size, alignment, allocated_size,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE);
+ if (address == nullptr) {
+ return nullptr;
+ }
+ MemoryRegion region(address, size);
+ return new VirtualMemory(region, region);
+}
+
+void VirtualMemory::Commit(void* address, intptr_t size) {
+ ASSERT(Utils::IsAligned(address, PageSize()));
+ ASSERT(Utils::IsAligned(size, PageSize()));
+ void* result = mmap(address, size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
+ if (result == MAP_FAILED) {
+ int error = errno;
+ FATAL("Failed to commit: %d\n", error);
+ }
+}
+
+void VirtualMemory::Decommit(void* address, intptr_t size) {
+ ASSERT(Utils::IsAligned(address, PageSize()));
+ ASSERT(Utils::IsAligned(size, PageSize()));
+ void* result =
+ mmap(address, size, PROT_NONE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_FIXED, -1, 0);
+ if (result == MAP_FAILED) {
+ int error = errno;
+ FATAL("Failed to decommit: %d\n", error);
+ }
+}
+
VirtualMemory::~VirtualMemory() {
#if defined(DART_COMPRESSED_POINTERS)
if (VirtualMemoryCompressedHeap::Contains(reserved_.pointer())) {
- madvise(reserved_.pointer(), reserved_.size(), MADV_DONTNEED);
+ Decommit(reserved_.pointer(), reserved_.size());
VirtualMemoryCompressedHeap::Free(reserved_.pointer(), reserved_.size());
return;
}
diff --git a/runtime/vm/virtual_memory_win.cc b/runtime/vm/virtual_memory_win.cc
index e352adc..2e380c0 100644
--- a/runtime/vm/virtual_memory_win.cc
+++ b/runtime/vm/virtual_memory_win.cc
@@ -17,6 +17,7 @@
DECLARE_FLAG(bool, write_protect_code);
uword VirtualMemory::page_size_ = 0;
+VirtualMemory* VirtualMemory::compressed_heap_ = nullptr;
intptr_t VirtualMemory::CalculatePageSize() {
SYSTEM_INFO info;
@@ -55,24 +56,21 @@
page_size_ = CalculatePageSize();
#if defined(DART_COMPRESSED_POINTERS)
- if (VirtualMemoryCompressedHeap::GetRegion() == nullptr) {
- void* address =
- AllocateAlignedImpl(kCompressedHeapSize, kCompressedHeapAlignment,
- kCompressedHeapSize + kCompressedHeapAlignment,
- PAGE_READWRITE, nullptr);
- if (address == nullptr) {
+ if (compressed_heap_ == nullptr) {
+ compressed_heap_ = Reserve(kCompressedHeapSize, kCompressedHeapAlignment);
+ if (compressed_heap_ == nullptr) {
int error = GetLastError();
FATAL("Failed to reserve region for compressed heap: %d", error);
}
- VirtualMemoryCompressedHeap::Init(address);
+ VirtualMemoryCompressedHeap::Init(compressed_heap_->address());
}
#endif // defined(DART_COMPRESSED_POINTERS)
}
void VirtualMemory::Cleanup() {
#if defined(DART_COMPRESSED_POINTERS)
- void* heap_base = VirtualMemoryCompressedHeap::GetRegion();
- VirtualFree(heap_base, kCompressedHeapSize, MEM_RELEASE);
+ delete compressed_heap_;
+ compressed_heap_ = nullptr;
VirtualMemoryCompressedHeap::Cleanup();
#endif // defined(DART_COMPRESSED_POINTERS)
}
@@ -99,6 +97,7 @@
if (region.pointer() == nullptr) {
return nullptr;
}
+ Commit(region.pointer(), region.size());
return new VirtualMemory(region, region);
}
#endif // defined(DART_COMPRESSED_POINTERS)
@@ -120,6 +119,44 @@
return new VirtualMemory(region, reserved);
}
+VirtualMemory* VirtualMemory::Reserve(intptr_t size, intptr_t alignment) {
+ ASSERT(Utils::IsAligned(size, PageSize()));
+ ASSERT(Utils::IsPowerOfTwo(alignment));
+ ASSERT(Utils::IsAligned(alignment, PageSize()));
+ intptr_t reserved_size = size + alignment - PageSize();
+ void* reserved_address =
+ VirtualAlloc(nullptr, reserved_size, MEM_RESERVE, PAGE_NOACCESS);
+ if (reserved_address == nullptr) {
+ return nullptr;
+ }
+
+ void* aligned_address = reinterpret_cast<void*>(
+ Utils::RoundUp(reinterpret_cast<uword>(reserved_address), alignment));
+ MemoryRegion region(aligned_address, size);
+ MemoryRegion reserved(reserved_address, reserved_size);
+ return new VirtualMemory(region, reserved);
+}
+
+void VirtualMemory::Commit(void* address, intptr_t size) {
+ ASSERT(Utils::IsAligned(address, PageSize()));
+ ASSERT(Utils::IsAligned(size, PageSize()));
+ void* result = VirtualAlloc(address, size, MEM_COMMIT, PAGE_READWRITE);
+ if (result == nullptr) {
+ int error = GetLastError();
+ FATAL("Failed to commit: %d\n", error);
+ }
+}
+
+void VirtualMemory::Decommit(void* address, intptr_t size) {
+ ASSERT(Utils::IsAligned(address, PageSize()));
+ ASSERT(Utils::IsAligned(size, PageSize()));
+ bool result = VirtualFree(address, size, MEM_DECOMMIT);
+ if (!result) {
+ int error = GetLastError();
+ FATAL("Failed to decommit: %d\n", error);
+ }
+}
+
VirtualMemory::~VirtualMemory() {
// Note that the size of the reserved region might be set to 0 by
// Truncate(0, true) but that does not actually release the mapping
@@ -127,6 +164,7 @@
// with original base pointer and MEM_RELEASE.
#if defined(DART_COMPRESSED_POINTERS)
if (VirtualMemoryCompressedHeap::Contains(reserved_.pointer())) {
+ Decommit(reserved_.pointer(), reserved_.size());
VirtualMemoryCompressedHeap::Free(reserved_.pointer(), reserved_.size());
return;
}
diff --git a/sdk/lib/_internal/vm/lib/expando_patch.dart b/sdk/lib/_internal/vm/lib/expando_patch.dart
index 36c65df..f707102 100644
--- a/sdk/lib/_internal/vm/lib/expando_patch.dart
+++ b/sdk/lib/_internal/vm/lib/expando_patch.dart
@@ -4,6 +4,16 @@
// part of "core_patch.dart";
+// This function takes care of rehashing of the expandos in [objects]. We
+// do this eagerly after snapshot deserialization.
+@pragma("vm:entry-point", "call")
+void _rehashObjects(List objects) {
+ final int length = objects.length;
+ for (int i = 0; i < length; ++i) {
+ objects[i]._rehash();
+ }
+}
+
@patch
@pragma("vm:entry-point")
class Expando<T> {
@@ -96,7 +106,6 @@
this[object] = value; // Recursively add the value.
}
- @pragma("vm:entry-point", "call")
_rehash() {
// Determine the population count of the map to allocate an appropriately
// sized map below.
diff --git a/tests/lib/isolate/weak_property_message_1_test.dart b/tests/lib/isolate/weak_property_message_1_test.dart
index 2f4d344..4946222 100644
--- a/tests/lib/isolate/weak_property_message_1_test.dart
+++ b/tests/lib/isolate/weak_property_message_1_test.dart
@@ -4,7 +4,9 @@
// See https://github.com/dart-lang/sdk/issues/25559
-import "dart:async";
+// VMOptions=--no-enable-isolate-groups
+// VMOptions=--enable-isolate-groups
+
import "dart:isolate";
import "package:async_helper/async_helper.dart";
@@ -30,7 +32,6 @@
asyncEnd();
});
-
var key1 = new Object();
var key2 = new Object();
var key3 = new Object();
diff --git a/tests/lib/isolate/weak_property_message_2_test.dart b/tests/lib/isolate/weak_property_message_2_test.dart
index 236bcfe3..fd0dbc6 100644
--- a/tests/lib/isolate/weak_property_message_2_test.dart
+++ b/tests/lib/isolate/weak_property_message_2_test.dart
@@ -4,12 +4,13 @@
// See https://github.com/dart-lang/sdk/issues/25559
-import "dart:async";
+// VMOptions=--no-enable-isolate-groups
+// VMOptions=--enable-isolate-groups
+
import "dart:developer";
import "dart:isolate";
import "package:async_helper/async_helper.dart";
-import "package:expect/expect.dart";
main() {
asyncStart();
@@ -23,12 +24,11 @@
asyncEnd();
});
-
var unwrittenKey = new Object();
var expando = new Expando();
expando[unwrittenKey] = new UserTag("cant send this");
port.sendPort.send(expando);
- print(unwrittenKey); // Ensure [unwrittenKey] is live during [send].
+ print(unwrittenKey); // Ensure [unwrittenKey] is live during [send].
}
diff --git a/tests/lib_2/isolate/weak_property_message_1_test.dart b/tests/lib_2/isolate/weak_property_message_1_test.dart
index fa8a6d1..b58e261 100644
--- a/tests/lib_2/isolate/weak_property_message_1_test.dart
+++ b/tests/lib_2/isolate/weak_property_message_1_test.dart
@@ -6,7 +6,9 @@
// @dart = 2.9
-import "dart:async";
+// VMOptions=--no-enable-isolate-groups
+// VMOptions=--enable-isolate-groups
+
import "dart:isolate";
import "package:async_helper/async_helper.dart";
@@ -32,7 +34,6 @@
asyncEnd();
});
-
var key1 = new Object();
var key2 = new Object();
var key3 = new Object();
diff --git a/tests/lib_2/isolate/weak_property_message_2_test.dart b/tests/lib_2/isolate/weak_property_message_2_test.dart
index 7b5ad95..353a8f5 100644
--- a/tests/lib_2/isolate/weak_property_message_2_test.dart
+++ b/tests/lib_2/isolate/weak_property_message_2_test.dart
@@ -6,12 +6,13 @@
// @dart = 2.9
-import "dart:async";
+// VMOptions=--no-enable-isolate-groups
+// VMOptions=--enable-isolate-groups
+
import "dart:developer";
import "dart:isolate";
import "package:async_helper/async_helper.dart";
-import "package:expect/expect.dart";
main() {
asyncStart();
@@ -25,12 +26,11 @@
asyncEnd();
});
-
var unwrittenKey = new Object();
var expando = new Expando();
expando[unwrittenKey] = new UserTag("cant send this");
port.sendPort.send(expando);
- print(unwrittenKey); // Ensure [unwrittenKey] is live during [send].
+ print(unwrittenKey); // Ensure [unwrittenKey] is live during [send].
}
diff --git a/tools/VERSION b/tools/VERSION
index d820e4a..766dd7a 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 14
PATCH 0
-PRERELEASE 391
+PRERELEASE 392
PRERELEASE_PATCH 0
\ No newline at end of file