Version 2.18.0-162.0.dev
Merge commit 'f6e4242a5de8b944a740d8de988b249b2d841b66' into 'dev'
diff --git a/DEPS b/DEPS
index cc10c05..797f60b 100644
--- a/DEPS
+++ b/DEPS
@@ -120,7 +120,7 @@
"icu_rev": "81d656878ec611cb0b42d52c82e9dae93920d9ba",
"intl_rev": "9145f308f1458f37630a1ffce3b7d3b471ebbc56",
"jinja2_rev": "2222b31554f03e62600cd7e383376a7c187967a1",
- "json_rpc_2_rev": "7e00f893440a72de0637970325e4ea44bd1e8c8e",
+ "json_rpc_2_rev": "2de9a1f9821807fa2c85fd48e2f70b9cbcddcb67",
"linter_rev": "a8529c6692922b45bc287543b355c90d7b1286d3", # 1.24.0
"lints_rev": "8294e5648ab49474541527e2911e72e4c5aefe55",
"logging_rev": "f6979e3bc3b6e1847a08335b7eb6304e18986195",
@@ -155,7 +155,7 @@
"test_rev": "d54846bc2b5cfa4e1445fda85c5e48a00940aa68",
"typed_data_rev": "8b19e29bcf4077147de4d67adeabeb48270c65eb",
"usage_rev": "e85d575d6decb921c57a43b9844bba3607479f56",
- "vector_math_rev": "0cbed0914d49a6a44555e6d5444c438a4a4c3fc1",
+ "vector_math_rev": "1c72944e8c2f02340a1d90b32aab2e3836cef8cc",
"watcher_rev": "f76997ab0c857dc5537ac0975a9ada92b54ef949",
"web_components_rev": "8f57dac273412a7172c8ade6f361b407e2e4ed02",
"web_socket_channel_rev": "99dbdc5769e19b9eeaf69449a59079153c6a8b1f",
diff --git a/benchmarks/IsolateSendExit/dart/IsolateSendExitLatency.dart b/benchmarks/IsolateSendExit/dart/IsolateSendExitLatency.dart
new file mode 100644
index 0000000..8d00b01
--- /dev/null
+++ b/benchmarks/IsolateSendExit/dart/IsolateSendExitLatency.dart
@@ -0,0 +1,45 @@
+// 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.
+//
+// This test ensures that there are no long pauses when sending large objects
+// via exit/send.
+
+import 'dart:async';
+import 'dart:typed_data';
+import 'dart:math' as math;
+import 'dart:isolate';
+
+import 'latency.dart';
+
+main() async {
+ final statsFuture =
+ measureEventLoopLatency(const Duration(milliseconds: 1), 4000, work: () {
+ // Every 1 ms we allocate some objects which may trigger GC some time.
+ for (int i = 0; i < 32; i++) {
+ List.filled(32 * 1024 ~/ 8, null);
+ }
+ });
+
+ final result = await compute(() {
+ final l = <dynamic>[];
+ for (int i = 0; i < 10 * 1000 * 1000; ++i) {
+ l.add(Object());
+ }
+ return l;
+ });
+ if (result.length != 10 * 1000 * 1000) throw 'failed';
+
+ final stats = await statsFuture;
+ stats.report('IsolateSendExitLatency');
+}
+
+Future<T> compute<T>(T Function() fun) {
+ final rp = ReceivePort();
+ final sp = rp.sendPort;
+ Isolate.spawn((_) {
+ final value = fun();
+ Isolate.exit(sp, value);
+ }, null);
+ return rp.first.then((t) => t as T);
+}
diff --git a/benchmarks/IsolateSendExit/dart/latency.dart b/benchmarks/IsolateSendExit/dart/latency.dart
new file mode 100644
index 0000000..0a5054f
--- /dev/null
+++ b/benchmarks/IsolateSendExit/dart/latency.dart
@@ -0,0 +1,134 @@
+// Copyright (c) 2020, 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 'dart:async';
+import 'dart:io';
+import 'dart:math' as math;
+import 'dart:typed_data';
+
+/// Measures event loop responsiveness.
+///
+/// Schedules new timer events, [tickDuration] in the future, and measures how
+/// long it takes for these events to actually arrive.
+///
+/// Runs [numberOfTicks] times before completing with [EventLoopLatencyStats].
+Future<EventLoopLatencyStats> measureEventLoopLatency(
+ Duration tickDuration, int numberOfTicks,
+ {void Function()? work}) {
+ final completer = Completer<EventLoopLatencyStats>();
+
+ final tickDurationInUs = tickDuration.inMicroseconds;
+ final buffer = _TickLatencies(numberOfTicks);
+ final sw = Stopwatch()..start();
+ int lastTimestamp = 0;
+
+ void trigger() {
+ final int currentTimestamp = sw.elapsedMicroseconds;
+
+ // Every tick we missed to schedule we'll add with difference to when we
+ // would've scheduled it and when we became responsive again.
+ bool done = false;
+ while (!done && lastTimestamp < (currentTimestamp - tickDurationInUs)) {
+ done = !buffer.add(currentTimestamp - lastTimestamp - tickDurationInUs);
+ lastTimestamp += tickDurationInUs;
+ }
+
+ if (work != null) {
+ work();
+ }
+
+ if (!done) {
+ lastTimestamp = currentTimestamp;
+ Timer(tickDuration, trigger);
+ } else {
+ completer.complete(buffer.makeStats());
+ }
+ }
+
+ Timer(tickDuration, trigger);
+
+ return completer.future;
+}
+
+/// Result of the event loop latency measurement.
+class EventLoopLatencyStats {
+ /// Minimum latency between scheduling a tick and it's arrival (in ms).
+ final double minLatency;
+
+ /// Average latency between scheduling a tick and it's arrival (in ms).
+ final double avgLatency;
+
+ /// Maximum latency between scheduling a tick and it's arrival (in ms).
+ final double maxLatency;
+
+ /// The 50th percentile (median) (in ms).
+ final double percentile50th;
+
+ /// The 90th percentile (in ms).
+ final double percentile90th;
+
+ /// The 95th percentile (in ms).
+ final double percentile95th;
+
+ /// The 99th percentile (in ms).
+ final double percentile99th;
+
+ EventLoopLatencyStats(
+ this.minLatency,
+ this.avgLatency,
+ this.maxLatency,
+ this.percentile50th,
+ this.percentile90th,
+ this.percentile95th,
+ this.percentile99th);
+
+ void report(String name) {
+ print('$name.Min(RunTimeRaw): $minLatency ms.');
+ print('$name.Avg(RunTimeRaw): $avgLatency ms.');
+ print('$name.Percentile50(RunTimeRaw): $percentile50th ms.');
+ print('$name.Percentile90(RunTimeRaw): $percentile90th ms.');
+ print('$name.Percentile95(RunTimeRaw): $percentile95th ms.');
+ print('$name.Percentile99(RunTimeRaw): $percentile99th ms.');
+ print('$name.Max(RunTimeRaw): $maxLatency ms.');
+ }
+}
+
+/// Accumulates tick latencies and makes statistics for it.
+class _TickLatencies {
+ final Uint64List _timestamps;
+ int _index = 0;
+
+ _TickLatencies(int numberOfTicks) : _timestamps = Uint64List(numberOfTicks);
+
+ /// Returns `true` while the buffer has not been filled yet.
+ bool add(int latencyInUs) {
+ _timestamps[_index++] = latencyInUs;
+ return _index < _timestamps.length;
+ }
+
+ EventLoopLatencyStats makeStats() {
+ if (_index != _timestamps.length) {
+ throw 'Buffer has not been fully filled yet.';
+ }
+
+ _timestamps.sort();
+ final length = _timestamps.length;
+ final double avg = _timestamps.fold(0, (int a, int b) => a + b) / length;
+ final int min = _timestamps.fold(0x7fffffffffffffff, math.min);
+ final int max = _timestamps.fold(0, math.max);
+ final percentile50th = _timestamps[50 * length ~/ 100];
+ final percentile90th = _timestamps[90 * length ~/ 100];
+ final percentile95th = _timestamps[95 * length ~/ 100];
+ final percentile99th = _timestamps[99 * length ~/ 100];
+
+ return EventLoopLatencyStats(
+ min / 1000,
+ avg / 1000,
+ max / 1000,
+ percentile50th / 1000,
+ percentile90th / 1000,
+ percentile95th / 1000,
+ percentile99th / 1000);
+ }
+}
diff --git a/benchmarks/IsolateSendExit/dart2/IsolateSendExitLatency.dart b/benchmarks/IsolateSendExit/dart2/IsolateSendExitLatency.dart
new file mode 100644
index 0000000..c165c73
--- /dev/null
+++ b/benchmarks/IsolateSendExit/dart2/IsolateSendExitLatency.dart
@@ -0,0 +1,47 @@
+// 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.
+//
+// This test ensures that there are no long pauses when sending large objects
+// via exit/send.
+
+// @dart=2.9
+
+import 'dart:async';
+import 'dart:typed_data';
+import 'dart:math' as math;
+import 'dart:isolate';
+
+import 'latency.dart';
+
+main() async {
+ final statsFuture =
+ measureEventLoopLatency(const Duration(milliseconds: 1), 4000, work: () {
+ // Every 1 ms we allocate some objects which may trigger GC some time.
+ for (int i = 0; i < 32; i++) {
+ List.filled(32 * 1024 ~/ 8, null);
+ }
+ });
+
+ final result = await compute(() {
+ final l = <dynamic>[];
+ for (int i = 0; i < 10 * 1000 * 1000; ++i) {
+ l.add(Object());
+ }
+ return l;
+ });
+ if (result.length != 10 * 1000 * 1000) throw 'failed';
+
+ final stats = await statsFuture;
+ stats.report('IsolateSendExitLatency');
+}
+
+Future<T> compute<T>(T Function() fun) {
+ final rp = ReceivePort();
+ final sp = rp.sendPort;
+ Isolate.spawn((_) {
+ final value = fun();
+ Isolate.exit(sp, value);
+ }, null);
+ return rp.first.then((t) => t as T);
+}
diff --git a/benchmarks/IsolateSendExit/dart2/latency.dart b/benchmarks/IsolateSendExit/dart2/latency.dart
new file mode 100644
index 0000000..8b3352c
--- /dev/null
+++ b/benchmarks/IsolateSendExit/dart2/latency.dart
@@ -0,0 +1,136 @@
+// Copyright (c) 2020, 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.
+
+// @dart=2.9
+
+import 'dart:async';
+import 'dart:io';
+import 'dart:math' as math;
+import 'dart:typed_data';
+
+/// Measures event loop responsiveness.
+///
+/// Schedules new timer events, [tickDuration] in the future, and measures how
+/// long it takes for these events to actually arrive.
+///
+/// Runs [numberOfTicks] times before completing with [EventLoopLatencyStats].
+Future<EventLoopLatencyStats> measureEventLoopLatency(
+ Duration tickDuration, int numberOfTicks,
+ {void Function() work}) {
+ final completer = Completer<EventLoopLatencyStats>();
+
+ final tickDurationInUs = tickDuration.inMicroseconds;
+ final buffer = _TickLatencies(numberOfTicks);
+ final sw = Stopwatch()..start();
+ int lastTimestamp = 0;
+
+ void trigger() {
+ final int currentTimestamp = sw.elapsedMicroseconds;
+
+ // Every tick we missed to schedule we'll add with difference to when we
+ // would've scheduled it and when we became responsive again.
+ bool done = false;
+ while (!done && lastTimestamp < (currentTimestamp - tickDurationInUs)) {
+ done = !buffer.add(currentTimestamp - lastTimestamp - tickDurationInUs);
+ lastTimestamp += tickDurationInUs;
+ }
+
+ if (work != null) {
+ work();
+ }
+
+ if (!done) {
+ lastTimestamp = currentTimestamp;
+ Timer(tickDuration, trigger);
+ } else {
+ completer.complete(buffer.makeStats());
+ }
+ }
+
+ Timer(tickDuration, trigger);
+
+ return completer.future;
+}
+
+/// Result of the event loop latency measurement.
+class EventLoopLatencyStats {
+ /// Minimum latency between scheduling a tick and it's arrival (in ms).
+ final double minLatency;
+
+ /// Average latency between scheduling a tick and it's arrival (in ms).
+ final double avgLatency;
+
+ /// Maximum latency between scheduling a tick and it's arrival (in ms).
+ final double maxLatency;
+
+ /// The 50th percentile (median) (in ms).
+ final double percentile50th;
+
+ /// The 90th percentile (in ms).
+ final double percentile90th;
+
+ /// The 95th percentile (in ms).
+ final double percentile95th;
+
+ /// The 99th percentile (in ms).
+ final double percentile99th;
+
+ EventLoopLatencyStats(
+ this.minLatency,
+ this.avgLatency,
+ this.maxLatency,
+ this.percentile50th,
+ this.percentile90th,
+ this.percentile95th,
+ this.percentile99th);
+
+ void report(String name) {
+ print('$name.Min(RunTimeRaw): $minLatency ms.');
+ print('$name.Avg(RunTimeRaw): $avgLatency ms.');
+ print('$name.Percentile50(RunTimeRaw): $percentile50th ms.');
+ print('$name.Percentile90(RunTimeRaw): $percentile90th ms.');
+ print('$name.Percentile95(RunTimeRaw): $percentile95th ms.');
+ print('$name.Percentile99(RunTimeRaw): $percentile99th ms.');
+ print('$name.Max(RunTimeRaw): $maxLatency ms.');
+ }
+}
+
+/// Accumulates tick latencies and makes statistics for it.
+class _TickLatencies {
+ final Uint64List _timestamps;
+ int _index = 0;
+
+ _TickLatencies(int numberOfTicks) : _timestamps = Uint64List(numberOfTicks);
+
+ /// Returns `true` while the buffer has not been filled yet.
+ bool add(int latencyInUs) {
+ _timestamps[_index++] = latencyInUs;
+ return _index < _timestamps.length;
+ }
+
+ EventLoopLatencyStats makeStats() {
+ if (_index != _timestamps.length) {
+ throw 'Buffer has not been fully filled yet.';
+ }
+
+ _timestamps.sort();
+ final length = _timestamps.length;
+ final double avg = _timestamps.fold(0, (int a, int b) => a + b) / length;
+ final int min = _timestamps.fold(0x7fffffffffffffff, math.min);
+ final int max = _timestamps.fold(0, math.max);
+ final percentile50th = _timestamps[50 * length ~/ 100];
+ final percentile90th = _timestamps[90 * length ~/ 100];
+ final percentile95th = _timestamps[95 * length ~/ 100];
+ final percentile99th = _timestamps[99 * length ~/ 100];
+
+ return EventLoopLatencyStats(
+ min / 1000,
+ avg / 1000,
+ max / 1000,
+ percentile50th / 1000,
+ percentile90th / 1000,
+ percentile95th / 1000,
+ percentile99th / 1000);
+ }
+}
diff --git a/pkg/analysis_server/lib/src/lsp/json_parsing.dart b/pkg/analysis_server/lib/src/lsp/json_parsing.dart
index f396dde..5b33fea 100644
--- a/pkg/analysis_server/lib/src/lsp/json_parsing.dart
+++ b/pkg/analysis_server/lib/src/lsp/json_parsing.dart
@@ -5,6 +5,7 @@
import 'dart:collection';
final nullLspJsonReporter = _NullLspJsonReporter();
+final throwingLspJsonReporter = _ThrowingLspJsonReporter();
/// Tracks a path through a JSON object during validation to allow reporting
/// validation errors with user-friendly paths to the invalid fields.
@@ -51,3 +52,11 @@
@override
void reportError(String message) {}
}
+
+class _ThrowingLspJsonReporter extends LspJsonReporter {
+ @override
+ void reportError(String message) {
+ super.reportError(message);
+ throw errors.join('\n');
+ }
+}
diff --git a/pkg/analysis_server/test/src/cider/cider_service.dart b/pkg/analysis_server/test/src/cider/cider_service.dart
index fbc50e9..35e01af 100644
--- a/pkg/analysis_server/test/src/cider/cider_service.dart
+++ b/pkg/analysis_server/test/src/cider/cider_service.dart
@@ -5,6 +5,7 @@
import 'dart:convert';
import 'package:analyzer/src/dart/analysis/performance_logger.dart';
+import 'package:analyzer/src/dart/micro/cider_byte_store.dart';
import 'package:analyzer/src/dart/micro/resolve_file.dart';
import 'package:analyzer/src/dart/sdk/sdk.dart';
import 'package:analyzer/src/test_utilities/mock_sdk.dart';
@@ -36,12 +37,13 @@
)!;
fileResolver = FileResolver(
- logger,
- resourceProvider,
- workspace.createSourceFactory(sdk, null),
- (String path) => _getDigest(path),
- null,
+ logger: logger,
+ resourceProvider: resourceProvider,
+ sourceFactory: workspace.createSourceFactory(sdk, null),
+ getFileDigest: (String path) => _getDigest(path),
+ prefetchFiles: null,
workspace: workspace,
+ byteStore: CiderCachedByteStore(20 * 1024 * 1024 /* 20 MB */),
);
fileResolver.testView = FileResolverTestView();
}
diff --git a/pkg/analysis_server/test/tool/lsp_spec/json_test.dart b/pkg/analysis_server/test/tool/lsp_spec/json_test.dart
index b411ff6..9275e6d 100644
--- a/pkg/analysis_server/test/tool/lsp_spec/json_test.dart
+++ b/pkg/analysis_server/test/tool/lsp_spec/json_test.dart
@@ -130,7 +130,7 @@
test('canParse returns true for in-spec (restricted) enum values', () {
expect(
- MarkupKind.canParse('plaintext', nullLspJsonReporter),
+ MarkupKind.canParse('plaintext', throwingLspJsonReporter),
isTrue,
);
});
@@ -138,7 +138,7 @@
test('canParse returns true for out-of-spec (unrestricted) enum values',
() {
expect(
- SymbolKind.canParse(-1, nullLspJsonReporter),
+ SymbolKind.canParse(-1, throwingLspJsonReporter),
isTrue,
);
});
@@ -150,7 +150,7 @@
'processId': null,
'rootUri': null,
'capabilities': <String, Object>{}
- }, nullLspJsonReporter);
+ }, throwingLspJsonReporter);
expect(canParse, isTrue);
});
@@ -160,7 +160,7 @@
final canParse = CreateFile.canParse({
'kind': 'create',
'uri': 'file:///temp/foo',
- }, nullLspJsonReporter);
+ }, throwingLspJsonReporter);
expect(canParse, isTrue);
});
@@ -185,14 +185,17 @@
null: true,
'invalid': false,
};
- for (final testValue in testTraceValues.keys) {
- final expected = testTraceValues[testValue];
+ for (final entry in testTraceValues.entries) {
+ final testValue = entry.key;
+ final expected = entry.value;
+ final reporter =
+ expected ? throwingLspJsonReporter : nullLspJsonReporter;
final canParse = InitializeParams.canParse({
'processId': null,
'rootUri': null,
'capabilities': <String, Object>{},
'trace': testValue,
- }, nullLspJsonReporter);
+ }, reporter);
expect(canParse, expected,
reason: 'InitializeParams.canParse returned $canParse with a '
'"trace" value of "$testValue" but expected $expected');
@@ -201,11 +204,12 @@
test('canParse validates optional fields', () {
expect(
- RenameFileOptions.canParse(<String, Object>{}, nullLspJsonReporter),
+ RenameFileOptions.canParse(<String, Object>{}, throwingLspJsonReporter),
isTrue,
);
expect(
- RenameFileOptions.canParse({'overwrite': true}, nullLspJsonReporter),
+ RenameFileOptions.canParse(
+ {'overwrite': true}, throwingLspJsonReporter),
isTrue,
);
expect(
@@ -217,7 +221,7 @@
test('canParse ignores fields not in the spec', () {
expect(
RenameFileOptions.canParse(
- {'overwrite': true, 'invalidField': true}, nullLspJsonReporter),
+ {'overwrite': true, 'invalidField': true}, throwingLspJsonReporter),
isTrue,
);
expect(
@@ -398,6 +402,15 @@
expect(message.params, isNull);
expect(message.jsonrpc, equals('test'));
});
+
+ test('parses JSON with integers in double fields', () {
+ final input = '{"alpha":1.0,"blue":0,"green":1,"red":1.5}';
+ final message = Color.fromJson(jsonDecode(input) as Map<String, Object?>);
+ expect(message.alpha, 1.0);
+ expect(message.blue, 0.0);
+ expect(message.green, 1.0);
+ expect(message.red, 1.5);
+ });
});
test('objects with lists can round-trip through to json and back', () {
diff --git a/pkg/analyzer/lib/dart/analysis/results.dart b/pkg/analyzer/lib/dart/analysis/results.dart
index 97198d9..604cf8f 100644
--- a/pkg/analyzer/lib/dart/analysis/results.dart
+++ b/pkg/analyzer/lib/dart/analysis/results.dart
@@ -120,6 +120,17 @@
SomeResolvedLibraryResult {}
/// The type of [InvalidResult] returned when the given file is not a library,
+/// but an augmentation of a library.
+///
+/// Clients may not extend, implement or mix-in this class.
+class NotLibraryButAugmentationResult
+ implements
+ InvalidResult,
+ SomeLibraryElementResult,
+ SomeParsedLibraryResult,
+ SomeResolvedLibraryResult {}
+
+/// The type of [InvalidResult] returned when the given file is not a library,
/// but a part of a library.
///
/// Clients may not extend, implement or mix-in this class.
@@ -311,6 +322,8 @@
/// The result of building the element model for a single file.
///
/// Clients may not extend, implement or mix-in this class.
+///
+/// TODO(scheglov) Stop implementing [FileResult].
abstract class UnitElementResult implements SomeUnitElementResult, FileResult {
/// The element of the file.
CompilationUnitElement get element;
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 3aa510f..8b58a3c 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -741,8 +741,14 @@
return CannotResolveUriResult();
}
- if (file.isPart) {
+ final kind = file.kind;
+ if (kind is LibraryFileStateKind) {
+ } else if (kind is AugmentationFileStateKind) {
+ return NotLibraryButAugmentationResult();
+ } else if (kind is PartFileStateKind) {
return NotLibraryButPartResult();
+ } else {
+ throw UnimplementedError('(${kind.runtimeType}) $kind');
}
var unitResult = await getUnitElement(file.path);
@@ -789,10 +795,15 @@
return NotPathOfUriResult();
}
- FileState file = _fsState.getFileForPath(path);
-
- if (file.isPart) {
+ final file = _fsState.getFileForPath(path);
+ final kind = file.kind;
+ if (kind is LibraryFileStateKind) {
+ } else if (kind is AugmentationFileStateKind) {
+ return NotLibraryButAugmentationResult();
+ } else if (kind is PartFileStateKind) {
return NotLibraryButPartResult();
+ } else {
+ throw UnimplementedError('(${kind.runtimeType}) $kind');
}
var units = <ParsedUnitResult>[];
@@ -816,9 +827,6 @@
if (file == null) {
return CannotResolveUriResult();
}
- if (file.isPart) {
- return NotLibraryButPartResult();
- }
return getParsedLibrary(file.path);
},
(externalLibrary) {
@@ -841,25 +849,24 @@
/// state (including new states of the files previously reported using
/// [changeFile]), prior to the next time the analysis state transitions
/// to "idle".
- Future<SomeResolvedLibraryResult> getResolvedLibrary(String path) {
+ Future<SomeResolvedLibraryResult> getResolvedLibrary(String path) async {
if (!_isAbsolutePath(path)) {
- return Future.value(
- InvalidPathResult(),
- );
+ return InvalidPathResult();
}
if (!_fsState.hasUri(path)) {
- return Future.value(
- NotPathOfUriResult(),
- );
+ return NotPathOfUriResult();
}
- FileState file = _fsState.getFileForPath(path);
-
- if (file.isPart) {
- return Future.value(
- NotLibraryButPartResult(),
- );
+ final file = _fsState.getFileForPath(path);
+ final kind = file.kind;
+ if (kind is LibraryFileStateKind) {
+ } else if (kind is AugmentationFileStateKind) {
+ return NotLibraryButAugmentationResult();
+ } else if (kind is PartFileStateKind) {
+ return NotLibraryButPartResult();
+ } else {
+ throw UnimplementedError('(${kind.runtimeType}) $kind');
}
// Schedule analysis.
@@ -888,9 +895,6 @@
if (file == null) {
return CannotResolveUriResult();
}
- if (file.isPart) {
- return NotLibraryButPartResult();
- }
return getResolvedLibrary(file.path);
},
(externalLibrary) async {
@@ -996,23 +1000,6 @@
return completer.future;
}
- /// Return `true` is the file with the given absolute [uri] is a library,
- /// or `false` if it is a part. More specifically, return `true` if the file
- /// is not known to be a part.
- ///
- /// Correspondingly, return `true` if the [uri] does not correspond to a file,
- /// for any reason, e.g. the file does not exist, or the [uri] cannot be
- /// resolved to a file path, or the [uri] is invalid, e.g. a `package:` URI
- /// without a package name. In these cases we cannot prove that the file is
- /// not a part, so it must be a library.
- bool isLibraryByUri(Uri uri) {
- var fileOr = _fsState.getFileForUri(uri);
- return fileOr.map(
- (file) => file == null || !file.isPart,
- (uri) => false,
- );
- }
-
/// Return a [Future] that completes with a [ParsedUnitResult] for the file
/// with the given [path].
///
diff --git a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
index 198bb5b..3b94d9b 100644
--- a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
+++ b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
@@ -42,9 +42,6 @@
import 'package:meta/meta.dart';
import 'package:yaml/yaml.dart';
-const M = 1024 * 1024 /*1 MiB*/;
-const memoryCacheSize = 200 * M;
-
class CiderSearchInfo {
final CharacterLocation startPosition;
final int length;
@@ -129,24 +126,18 @@
@visibleForTesting
final Map<String, ResolvedLibraryResult> cachedResults = {};
- FileResolver(
- PerformanceLog logger,
- ResourceProvider resourceProvider,
- SourceFactory sourceFactory,
- String Function(String path) getFileDigest,
- void Function(List<String> paths)? prefetchFiles, {
- required Workspace workspace,
- bool Function(String path)? isGenerated,
- }) : this.from(
- logger: logger,
- resourceProvider: resourceProvider,
- sourceFactory: sourceFactory,
- getFileDigest: getFileDigest,
- prefetchFiles: prefetchFiles,
- workspace: workspace,
- isGenerated: isGenerated,
- );
+ FileResolver({
+ required this.logger,
+ required this.resourceProvider,
+ required this.sourceFactory,
+ required this.getFileDigest,
+ required this.prefetchFiles,
+ required this.workspace,
+ this.isGenerated,
+ required this.byteStore,
+ });
+ @Deprecated('Use the unnamed constructor instead')
FileResolver.from({
required this.logger,
required this.resourceProvider,
@@ -155,8 +146,8 @@
required this.prefetchFiles,
required this.workspace,
this.isGenerated,
- CiderByteStore? byteStore,
- }) : byteStore = byteStore ?? CiderCachedByteStore(memoryCacheSize);
+ required this.byteStore,
+ });
/// Update the resolver to reflect the fact that the file with the given
/// [path] was changed. We need to make sure that when this file, of any file
diff --git a/pkg/analyzer/test/src/dart/analysis/driver_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
index 7cc3008..ca5fff1 100644
--- a/pkg/analyzer/test/src/dart/analysis/driver_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
@@ -35,6 +35,7 @@
defineReflectiveSuite(() {
defineReflectiveTests(AnalysisDriverSchedulerTest);
defineReflectiveTests(AnalysisDriverTest);
+ defineReflectiveTests(AnalysisDriver_PubPackageTest);
defineReflectiveTests(AnalysisDriver_BazelWorkspaceTest);
});
}
@@ -102,6 +103,135 @@
}
@reflectiveTest
+class AnalysisDriver_PubPackageTest extends PubPackageResolutionTest {
+ test_getLibraryByUri_cannotResolveUri() async {
+ final driver = driverFor(testFile.path);
+ expect(
+ await driver.getLibraryByUri('foo:bar'),
+ isA<CannotResolveUriResult>(),
+ );
+ }
+
+ test_getLibraryByUri_notLibrary_augmentation() async {
+ final a = newFile('$testPackageLibPath/a.dart', r'''
+library augment 'b.dart';
+''');
+
+ final driver = driverFor(a.path);
+ expect(
+ await driver.getLibraryByUri('package:test/a.dart'),
+ isA<NotLibraryButAugmentationResult>(),
+ );
+ }
+
+ test_getLibraryByUri_notLibrary_part() async {
+ final a = newFile('$testPackageLibPath/a.dart', r'''
+part of 'b.dart';
+''');
+
+ final driver = driverFor(a.path);
+ expect(
+ await driver.getLibraryByUri('package:test/a.dart'),
+ isA<NotLibraryButPartResult>(),
+ );
+ }
+
+ test_getParsedLibraryByUri_cannotResolveUri() async {
+ final driver = driverFor(testFile.path);
+ final uri = Uri.parse('foo:bar');
+ expect(
+ driver.getParsedLibraryByUri(uri),
+ isA<CannotResolveUriResult>(),
+ );
+ }
+
+ test_getParsedLibraryByUri_notLibrary_augmentation() async {
+ final a = newFile('$testPackageLibPath/a.dart', r'''
+library augment 'b.dart';
+''');
+
+ final driver = driverFor(a.path);
+ final uri = Uri.parse('package:test/a.dart');
+ expect(
+ driver.getParsedLibraryByUri(uri),
+ isA<NotLibraryButAugmentationResult>(),
+ );
+ }
+
+ test_getParsedLibraryByUri_notLibrary_part() async {
+ final a = newFile('$testPackageLibPath/a.dart', r'''
+part of 'b.dart';
+''');
+
+ final driver = driverFor(a.path);
+ final uri = Uri.parse('package:test/a.dart');
+ expect(
+ driver.getParsedLibraryByUri(uri),
+ isA<NotLibraryButPartResult>(),
+ );
+ }
+
+ test_getResolvedLibrary_notLibrary_augmentation() async {
+ final a = newFile('$testPackageLibPath/a.dart', r'''
+library augment 'b.dart';
+''');
+
+ final driver = driverFor(a.path);
+ expect(
+ await driver.getResolvedLibrary(a.path),
+ isA<NotLibraryButAugmentationResult>(),
+ );
+ }
+
+ test_getResolvedLibrary_notLibrary_part() async {
+ final a = newFile('$testPackageLibPath/a.dart', r'''
+part of 'b.dart';
+''');
+
+ final driver = driverFor(a.path);
+ expect(
+ await driver.getResolvedLibrary(a.path),
+ isA<NotLibraryButPartResult>(),
+ );
+ }
+
+ test_getResolvedLibraryByUri_cannotResolveUri() async {
+ final driver = driverFor(testFile.path);
+ final uri = Uri.parse('foo:bar');
+ expect(
+ await driver.getResolvedLibraryByUri(uri),
+ isA<CannotResolveUriResult>(),
+ );
+ }
+
+ test_getResolvedLibraryByUri_notLibrary_augmentation() async {
+ final a = newFile('$testPackageLibPath/a.dart', r'''
+library augment 'b.dart';
+''');
+
+ final driver = driverFor(a.path);
+ final uri = Uri.parse('package:test/a.dart');
+ expect(
+ await driver.getResolvedLibraryByUri(uri),
+ isA<NotLibraryButAugmentationResult>(),
+ );
+ }
+
+ test_getResolvedLibraryByUri_notLibrary_part() async {
+ final a = newFile('$testPackageLibPath/a.dart', r'''
+part of 'b.dart';
+''');
+
+ final driver = driverFor(a.path);
+ final uri = Uri.parse('package:test/a.dart');
+ expect(
+ await driver.getResolvedLibraryByUri(uri),
+ isA<NotLibraryButPartResult>(),
+ );
+ }
+}
+
+@reflectiveTest
class AnalysisDriverSchedulerTest with ResourceProviderMixin {
final ByteStore byteStore = MemoryByteStore();
@@ -2329,16 +2459,6 @@
await waitForIdleWithoutExceptions();
}
- test_isLibraryByUri_doesNotExist() async {
- var uri = Uri.parse('file:///test.dart');
- expect(driver.isLibraryByUri(uri), isTrue);
- }
-
- test_isLibraryByUri_invalidUri() async {
- var uri = Uri.parse('package:aaa');
- expect(driver.isLibraryByUri(uri), isTrue);
- }
-
test_issue34619() async {
var a = convertPath('/test/lib/a.dart');
newFile(a, r'''
diff --git a/pkg/analyzer/test/src/dart/analysis/session_test.dart b/pkg/analyzer/test/src/dart/analysis/session_test.dart
index ee74d7f..3a7a851 100644
--- a/pkg/analyzer/test/src/dart/analysis/session_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/session_test.dart
@@ -268,6 +268,26 @@
);
}
+ test_getLibraryByUri_notLibrary_augmentation() async {
+ newFile(testFilePath, r'''
+library augment 'a.dart';
+''');
+
+ final session = contextFor(testFilePath).currentSession;
+ final result = await session.getLibraryByUri('package:test/test.dart');
+ expect(result, isA<NotLibraryButAugmentationResult>());
+ }
+
+ test_getLibraryByUri_notLibrary_part() async {
+ newFile(testFilePath, r'''
+part of 'a.dart';
+''');
+
+ final session = contextFor(testFilePath).currentSession;
+ final result = await session.getLibraryByUri('package:test/test.dart');
+ expect(result, isA<NotLibraryButPartResult>());
+ }
+
test_getLibraryByUri_unresolvedUri() async {
var session = contextFor(testFilePath).currentSession;
var result = await session.getLibraryByUri('package:foo/foo.dart');
@@ -402,6 +422,16 @@
expect(session.getParsedLibrary(test.path), isA<NotLibraryButPartResult>());
}
+ test_getParsedLibrary_notLibrary_augmentation() async {
+ newFile(testFilePath, r'''
+library augment 'a.dart';
+''');
+
+ final session = contextFor(testFilePath).currentSession;
+ final result = session.getParsedLibrary(testFilePath);
+ expect(result, isA<NotLibraryButAugmentationResult>());
+ }
+
test_getParsedLibrary_parts() async {
var aContent = r'''
part 'b.dart';
@@ -661,7 +691,17 @@
expect(result, isA<InvalidPathResult>());
}
- test_getResolvedLibrary_notLibrary() async {
+ test_getResolvedLibrary_notLibrary_augmentation() async {
+ newFile(testFilePath, r'''
+library augment of 'a.dart';
+''');
+
+ final session = contextFor(testFilePath).currentSession;
+ final result = await session.getResolvedLibrary(testFilePath);
+ expect(result, isA<NotLibraryButAugmentationResult>());
+ }
+
+ test_getResolvedLibrary_notLibrary_part() async {
var test = newFile(testFilePath, 'part of "a.dart";');
var session = contextFor(testFilePath).currentSession;
diff --git a/pkg/analyzer/test/src/dart/micro/file_resolution.dart b/pkg/analyzer/test/src/dart/micro/file_resolution.dart
index 51c7429..d9c67b6 100644
--- a/pkg/analyzer/test/src/dart/micro/file_resolution.dart
+++ b/pkg/analyzer/test/src/dart/micro/file_resolution.dart
@@ -50,7 +50,7 @@
)!;
byteStore.testView = CiderByteStoreTestView();
- fileResolver = FileResolver.from(
+ fileResolver = FileResolver(
logger: logger,
resourceProvider: resourceProvider,
byteStore: byteStore,
diff --git a/pkg/compiler/test/analyses/dart2js_allowed.json b/pkg/compiler/test/analyses/dart2js_allowed.json
index 2d86d0b..4ae46c3 100644
--- a/pkg/compiler/test/analyses/dart2js_allowed.json
+++ b/pkg/compiler/test/analyses/dart2js_allowed.json
@@ -104,11 +104,10 @@
"Dynamic access of 'index'.": 1
},
"pkg/dart2js_info/lib/src/util.dart": {
- "Dynamic access of 'name'.": 1,
"Dynamic invocation of '-'.": 1
},
"pkg/js_ast/lib/src/template.dart": {
"Dynamic invocation of '[]'.": 9,
"Dynamic invocation of 'toStatement'.": 1
}
-}
\ No newline at end of file
+}
diff --git a/pkg/dart2js_info/lib/src/util.dart b/pkg/dart2js_info/lib/src/util.dart
index f509ea6..48bb5d1 100644
--- a/pkg/dart2js_info/lib/src/util.dart
+++ b/pkg/dart2js_info/lib/src/util.dart
@@ -74,7 +74,7 @@
final infoPath = <Info>[];
Info? localInfo = info;
while (localInfo != null) {
- infoPath.add(info);
+ infoPath.add(localInfo);
localInfo = localInfo.parent;
}
var sb = StringBuffer();
diff --git a/runtime/vm/message_snapshot.cc b/runtime/vm/message_snapshot.cc
index 48f6aec..a24ad62 100644
--- a/runtime/vm/message_snapshot.cc
+++ b/runtime/vm/message_snapshot.cc
@@ -2848,7 +2848,7 @@
const intptr_t count = d->ReadUnsigned();
for (intptr_t i = 0; i < count; i++) {
intptr_t length = d->ReadUnsigned();
- d->AssignRef(Array::New(cid_, length));
+ d->AssignRef(Array::NewUninitialized(cid_, length));
}
}
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 46d3e1b..52de6b1 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -2638,7 +2638,21 @@
initial_value |= initial_value << 32;
}
#endif
- needs_init = true;
+ if (class_id == kArrayCid) {
+ // If the size is greater than both kNewAllocatableSize and
+ // kAllocatablePageSize, the object must have been allocated to a new
+ // large page, which must already have been zero initialized by the OS.
+ // Zero is a GC-safe value. The caller will initialize the fields to
+ // null with safepoint checks to avoid blocking for the full duration of
+ // initializing this array.
+ needs_init = Heap::IsAllocatableInNewSpace(size) ||
+ Heap::IsAllocatableViaFreeLists(size);
+ if (!needs_init) {
+ initial_value = 0; // For ASSERT below.
+ }
+ } else {
+ needs_init = true;
+ }
}
if (needs_init) {
while (cur < end) {
@@ -24321,17 +24335,6 @@
return hash;
}
-ArrayPtr Array::New(intptr_t len, Heap::Space space) {
- ASSERT(IsolateGroup::Current()->object_store()->array_class() !=
- Class::null());
- ArrayPtr result = New(kClassId, len, space);
- if (UseCardMarkingForAllocation(len)) {
- ASSERT(result->IsOldObject());
- result->untag()->SetCardRememberedBitUnsynchronized();
- }
- return result;
-}
-
ArrayPtr Array::New(intptr_t len,
const AbstractType& element_type,
Heap::Space space) {
@@ -24345,7 +24348,9 @@
return result.ptr();
}
-ArrayPtr Array::New(intptr_t class_id, intptr_t len, Heap::Space space) {
+ArrayPtr Array::NewUninitialized(intptr_t class_id,
+ intptr_t len,
+ Heap::Space space) {
if (!IsValidLength(len)) {
// This should be caught before we reach here.
FATAL1("Fatal error in Array::New: invalid len %" Pd "\n", len);
@@ -24356,23 +24361,56 @@
Array::ContainsCompressedPointers()));
NoSafepointScope no_safepoint;
raw->untag()->set_length(Smi::New(len));
+ if (UseCardMarkingForAllocation(len)) {
+ ASSERT(raw->IsOldObject());
+ raw->untag()->SetCardRememberedBitUnsynchronized();
+ }
return raw;
}
}
+ArrayPtr Array::New(intptr_t class_id, intptr_t len, Heap::Space space) {
+ if (!UseCardMarkingForAllocation(len)) {
+ return NewUninitialized(class_id, len, space);
+ }
+
+ Thread* thread = Thread::Current();
+ Array& result =
+ Array::Handle(thread->zone(), NewUninitialized(class_id, len, space));
+ result.SetTypeArguments(Object::null_type_arguments());
+ for (intptr_t i = 0; i < len; i++) {
+ result.SetAt(i, Object::null_object(), thread);
+ if (((i + 1) % KB) == 0) {
+ thread->CheckForSafepoint();
+ }
+ }
+ return result.ptr();
+}
+
ArrayPtr Array::Slice(intptr_t start,
intptr_t count,
bool with_type_argument) const {
- // TODO(vegorov) introduce an array allocation method that fills newly
- // allocated array with values from the given source array instead of
- // null-initializing all elements.
- Array& dest = Array::Handle(Array::New(count));
- dest.StoreArrayPointers(dest.ObjectAddr(0), ObjectAddr(start), count);
-
+ Thread* thread = Thread::Current();
+ Zone* zone = thread->zone();
+ const Array& dest = Array::Handle(zone, Array::NewUninitialized(count));
if (with_type_argument) {
- dest.SetTypeArguments(TypeArguments::Handle(GetTypeArguments()));
+ dest.SetTypeArguments(TypeArguments::Handle(zone, GetTypeArguments()));
+ } else {
+ dest.SetTypeArguments(Object::null_type_arguments());
}
-
+ if (!UseCardMarkingForAllocation(count)) {
+ NoSafepointScope no_safepoint(thread);
+ for (int i = 0; i < count; i++) {
+ dest.untag()->set_element(i, untag()->element(i + start), thread);
+ }
+ } else {
+ for (int i = 0; i < count; i++) {
+ dest.untag()->set_element(i, untag()->element(i + start), thread);
+ if (((i + 1) % KB) == 0) {
+ thread->CheckForSafepoint();
+ }
+ }
+ }
return dest.ptr();
}
@@ -24397,19 +24435,30 @@
Heap::Space space) {
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
- const Array& result = Array::Handle(zone, Array::New(new_length, space));
+ const Array& result =
+ Array::Handle(zone, Array::NewUninitialized(new_length, space));
intptr_t len = 0;
if (!source.IsNull()) {
len = source.Length();
result.SetTypeArguments(
TypeArguments::Handle(zone, source.GetTypeArguments()));
+ } else {
+ result.SetTypeArguments(Object::null_type_arguments());
}
ASSERT(new_length >= len); // Cannot copy 'source' into new array.
ASSERT(new_length != len); // Unnecessary copying of array.
- PassiveObject& obj = PassiveObject::Handle(zone);
- for (int i = 0; i < len; i++) {
- obj = source.At(i);
- result.SetAt(i, obj, thread);
+ if (!UseCardMarkingForAllocation(len)) {
+ NoSafepointScope no_safepoint(thread);
+ for (int i = 0; i < len; i++) {
+ result.untag()->set_element(i, source.untag()->element(i), thread);
+ }
+ } else {
+ for (int i = 0; i < len; i++) {
+ result.untag()->set_element(i, source.untag()->element(i), thread);
+ if (((i + 1) % KB) == 0) {
+ thread->CheckForSafepoint();
+ }
+ }
}
return result.ptr();
}
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 6f226c3..17080ec 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -10419,7 +10419,15 @@
// to ImmutableArray.
void MakeImmutable() const;
- static ArrayPtr New(intptr_t len, Heap::Space space = Heap::kNew);
+ static ArrayPtr New(intptr_t len, Heap::Space space = Heap::kNew) {
+ return New(kArrayCid, len, space);
+ }
+ // The result's type arguments and elements are GC-safe but not initialized to
+ // null.
+ static ArrayPtr NewUninitialized(intptr_t len,
+ Heap::Space space = Heap::kNew) {
+ return NewUninitialized(kArrayCid, len, space);
+ }
static ArrayPtr New(intptr_t len,
const AbstractType& element_type,
Heap::Space space = Heap::kNew);
@@ -10457,6 +10465,9 @@
static ArrayPtr New(intptr_t class_id,
intptr_t len,
Heap::Space space = Heap::kNew);
+ static ArrayPtr NewUninitialized(intptr_t class_id,
+ intptr_t len,
+ Heap::Space space = Heap::kNew);
private:
CompressedObjectPtr const* ObjectAddr(intptr_t index) const {
@@ -10477,25 +10488,6 @@
ptr()->untag()->StoreArrayPointer<type, order, value_type>(addr, value);
}
- // Store a range of pointers [from, from + count) into [to, to + count).
- // TODO(koda): Use this to fix Object::Clone's broken store buffer logic.
- void StoreArrayPointers(CompressedObjectPtr const* to,
- CompressedObjectPtr const* from,
- intptr_t count) {
- ASSERT(Contains(reinterpret_cast<uword>(to)));
- if (ptr()->IsNewObject()) {
- memmove(const_cast<CompressedObjectPtr*>(to), from,
- count * kBytesPerElement);
- } else {
- Thread* thread = Thread::Current();
- const uword heap_base = ptr()->heap_base();
- for (intptr_t i = 0; i < count; ++i) {
- untag()->StoreArrayPointer(&to[i], from[i].Decompress(heap_base),
- thread);
- }
- }
- }
-
FINAL_HEAP_OBJECT_IMPLEMENTATION(Array, Instance);
friend class Class;
friend class ImmutableArray;
diff --git a/runtime/vm/object_graph_copy.cc b/runtime/vm/object_graph_copy.cc
index 43c35b3..560a7a7 100644
--- a/runtime/vm/object_graph_copy.cc
+++ b/runtime/vm/object_graph_copy.cc
@@ -471,27 +471,30 @@
public:
explicit SlowForwardMap(Thread* thread)
: ForwardMapBase(thread),
- from_to_(thread->zone(), 20),
+ from_to_transition_(thread->zone(), 2),
+ from_to_(GrowableObjectArray::Handle(thread->zone(),
+ GrowableObjectArray::New(2))),
transferables_from_to_(thread->zone(), 0) {
- from_to_.Resize(2);
- from_to_[0] = &Object::null_object();
- from_to_[1] = &Object::null_object();
+ from_to_transition_.Resize(2);
+ from_to_transition_[0] = &PassiveObject::Handle();
+ from_to_transition_[1] = &PassiveObject::Handle();
+ from_to_.Add(Object::null_object());
+ from_to_.Add(Object::null_object());
fill_cursor_ = 2;
}
ObjectPtr ForwardedObject(ObjectPtr object) {
const intptr_t id = GetObjectId(object);
if (id == 0) return Marker();
- return from_to_[id + 1]->ptr();
+ return from_to_.At(id + 1);
}
- void Insert(ObjectPtr from, ObjectPtr to, intptr_t size) {
- ASSERT(ForwardedObject(from) == Marker());
- const auto id = from_to_.length();
- SetObjectId(from, id);
- from_to_.Resize(id + 2);
- from_to_[id] = &Object::Handle(Z, from);
- from_to_[id + 1] = &Object::Handle(Z, to);
+ void Insert(const Object& from, const Object& to, intptr_t size) {
+ ASSERT(ForwardedObject(from.ptr()) == Marker());
+ const auto id = from_to_.Length();
+ SetObjectId(from.ptr(), id);
+ from_to_.Add(from);
+ from_to_.Add(to);
allocated_bytes += size;
}
@@ -537,7 +540,8 @@
friend class SlowObjectCopy;
friend class ObjectGraphCopier;
- GrowableArray<const Object*> from_to_;
+ GrowableArray<const PassiveObject*> from_to_transition_;
+ GrowableObjectArray& from_to_;
GrowableArray<const TransferableTypedData*> transferables_from_to_;
GrowableArray<const ExternalTypedData*> external_typed_data_;
GrowableArray<const Object*> objects_to_rehash_;
@@ -560,6 +564,7 @@
class_table_(thread->isolate_group()->class_table()),
new_space_(heap_->new_space()),
tmp_(Object::Handle(thread->zone())),
+ to_(Object::Handle(thread->zone())),
expando_cid_(Class::GetClassId(
thread->isolate_group()->object_store()->expando_class())) {}
~ObjectCopyBase() {}
@@ -681,6 +686,7 @@
ClassTable* class_table_;
Scavenger* new_space_;
Object& tmp_;
+ Object& to_;
intptr_t expando_cid_;
const char* exception_msg_ = nullptr;
@@ -990,9 +996,10 @@
if (size == 0) {
size = from.ptr().untag()->HeapSize();
}
- ObjectPtr to = AllocateObject(cid, size);
- slow_forward_map_.Insert(from.ptr(), to, size);
- UpdateLengthField(cid, from.ptr(), to);
+ to_ = AllocateObject(cid, size);
+ UpdateLengthField(cid, from.ptr(), to_.ptr());
+ slow_forward_map_.Insert(from, to_, size); // SAFEPOINT
+ ObjectPtr to = to_.ptr();
if (cid == kArrayCid && !Heap::IsAllocatableInNewSpace(size)) {
to.untag()->SetCardRememberedBitUnsynchronized();
}
@@ -1678,16 +1685,16 @@
Object& to = Object::Handle(Z);
while (true) {
if (slow_forward_map_.fill_cursor_ ==
- slow_forward_map_.from_to_.length()) {
+ 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()) {
+ 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();
+ from = slow_forward_map_.from_to_.At(index);
+ to = slow_forward_map_.from_to_.At(index + 1);
CopyObject(from, to);
slow_forward_map_.fill_cursor_ += 2;
if (exception_msg_ != nullptr) {
@@ -1915,6 +1922,8 @@
/*compact=*/true);
}
+ ObjectifyFromToObjects();
+
// Fast copy failed due to
// - either failure to allocate into new space
// - or failure to copy object which we cannot copy
@@ -2021,20 +2030,28 @@
void HandlifyFromToObjects() {
auto& fast_forward_map = fast_object_copy_.fast_forward_map_;
auto& slow_forward_map = slow_object_copy_.slow_forward_map_;
-
- const intptr_t cursor = fast_forward_map.fill_cursor_;
const intptr_t length = fast_forward_map.raw_from_to_.length();
-
- slow_forward_map.from_to_.Resize(length);
- for (intptr_t i = 2; i < length; i += 2) {
- slow_forward_map.from_to_[i] =
- i < cursor ? nullptr
- : &Object::Handle(Z, fast_forward_map.raw_from_to_[i]);
- slow_forward_map.from_to_[i + 1] =
- &Object::Handle(Z, fast_forward_map.raw_from_to_[i + 1]);
+ slow_forward_map.from_to_transition_.Resize(length);
+ for (intptr_t i = 0; i < length; i++) {
+ slow_forward_map.from_to_transition_[i] =
+ &PassiveObject::Handle(Z, fast_forward_map.raw_from_to_[i]);
}
+ ASSERT(slow_forward_map.from_to_transition_.length() == length);
fast_forward_map.raw_from_to_.Clear();
}
+ void ObjectifyFromToObjects() {
+ auto& from_to_transition =
+ slow_object_copy_.slow_forward_map_.from_to_transition_;
+ auto& from_to = slow_object_copy_.slow_forward_map_.from_to_;
+ intptr_t length = from_to_transition.length();
+ from_to = GrowableObjectArray::New(length, Heap::kOld);
+ for (intptr_t i = 0; i < length; i++) {
+ from_to.Add(*from_to_transition[i]);
+ }
+ ASSERT(from_to.Length() == length);
+ from_to_transition.Clear();
+ }
+
void ThrowException(const char* exception_msg) {
const auto& msg_obj = String::Handle(Z, String::New(exception_msg));
const auto& args = Array::Handle(Z, Array::New(1));
diff --git a/tools/VERSION b/tools/VERSION
index ca2aee4..02213d8 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 18
PATCH 0
-PRERELEASE 161
+PRERELEASE 162
PRERELEASE_PATCH 0
\ No newline at end of file