Version 2.15.0-24.0.dev
Merge commit '5b3cadc7e6d7be94ef959e3733357980ff69c684' into 'dev'
diff --git a/benchmarks/SDKArtifactSizes/dart/SDKArtifactSizes.dart b/benchmarks/SDKArtifactSizes/dart/SDKArtifactSizes.dart
index ece1bcc..5987a50 100644
--- a/benchmarks/SDKArtifactSizes/dart/SDKArtifactSizes.dart
+++ b/benchmarks/SDKArtifactSizes/dart/SDKArtifactSizes.dart
@@ -63,4 +63,24 @@
'$rootDir/dart-sdk/bin/snapshots/$snapshot.dart.snapshot';
await reportArtifactSize(snapshotPath, snapshot);
}
+
+ // Measure the (compressed) sdk size.
+ final tempDir = Directory.systemTemp.createTempSync('dartdev');
+ final sdkArchive =
+ compress(File(Platform.resolvedExecutable).parent.parent, tempDir);
+ await reportArtifactSize(sdkArchive?.path, 'sdk');
+ tempDir.deleteSync(recursive: true);
+}
+
+File compress(Directory sourceDir, Directory targetDir) {
+ final outFile = File('${targetDir.path}/sdk.zip');
+
+ if (Platform.isMacOS || Platform.isLinux) {
+ Process.runSync(
+ 'zip', ['-r', outFile.absolute.path, sourceDir.absolute.path]);
+ } else {
+ return null;
+ }
+
+ return outFile;
}
diff --git a/benchmarks/SDKArtifactSizes/dart2/SDKArtifactSizes.dart b/benchmarks/SDKArtifactSizes/dart2/SDKArtifactSizes.dart
index ece1bcc..0cefa4d 100644
--- a/benchmarks/SDKArtifactSizes/dart2/SDKArtifactSizes.dart
+++ b/benchmarks/SDKArtifactSizes/dart2/SDKArtifactSizes.dart
@@ -63,4 +63,24 @@
'$rootDir/dart-sdk/bin/snapshots/$snapshot.dart.snapshot';
await reportArtifactSize(snapshotPath, snapshot);
}
+
+ // Measure the (compressed) sdk size.
+ final tempDir = Directory.systemTemp.createTempSync('dartdev');
+ final sdkArchive =
+ compress(File(Platform.resolvedExecutable).parent.parent, tempDir);
+ await reportArtifactSize(sdkArchive?.path ?? '', 'sdk');
+ tempDir.deleteSync(recursive: true);
+}
+
+File? compress(Directory sourceDir, Directory targetDir) {
+ final outFile = File('${targetDir.path}/sdk.zip');
+
+ if (Platform.isMacOS || Platform.isLinux) {
+ Process.runSync(
+ 'zip', ['-r', outFile.absolute.path, sourceDir.absolute.path]);
+ } else {
+ return null;
+ }
+
+ return outFile;
}
diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart
index 3896905..38529f2 100644
--- a/pkg/analysis_server/lib/src/analysis_server.dart
+++ b/pkg/analysis_server/lib/src/analysis_server.dart
@@ -24,6 +24,7 @@
import 'package:analysis_server/src/domain_execution.dart';
import 'package:analysis_server/src/domain_kythe.dart';
import 'package:analysis_server/src/domain_server.dart';
+import 'package:analysis_server/src/domains/analysis/macro_files.dart';
import 'package:analysis_server/src/domains/analysis/occurrences.dart';
import 'package:analysis_server/src/domains/analysis/occurrences_dart.dart';
import 'package:analysis_server/src/edit/edit_domain.dart';
@@ -54,6 +55,7 @@
import 'package:analyzer/src/util/file_paths.dart' as file_paths;
import 'package:analyzer_plugin/protocol/protocol_common.dart' hide Element;
import 'package:analyzer_plugin/src/utilities/navigation/navigation.dart';
+import 'package:analyzer_plugin/utilities/analyzer_converter.dart';
import 'package:analyzer_plugin/utilities/navigation/navigation_dart.dart';
import 'package:http/http.dart' as http;
import 'package:telemetry/crash_reporting.dart';
@@ -718,7 +720,10 @@
server.AnalysisNavigationParams _computeNavigationParams(
String path, CompilationUnit unit) {
var collector = NavigationCollectorImpl();
- computeDartNavigation(resourceProvider, collector, unit, null, null);
+ computeDartNavigation(resourceProvider, collector, unit, null, null,
+ analyzerConverter: AnalyzerConverter(
+ locationProvider:
+ MacroElementLocationProvider(MacroFiles(resourceProvider))));
collector.createRegions();
return server.AnalysisNavigationParams(
path, collector.regions, collector.targets, collector.files);
diff --git a/pkg/analysis_server/lib/src/domains/analysis/macro_files.dart b/pkg/analysis_server/lib/src/domains/analysis/macro_files.dart
new file mode 100644
index 0000000..be391be
--- /dev/null
+++ b/pkg/analysis_server/lib/src/domains/analysis/macro_files.dart
@@ -0,0 +1,121 @@
+// 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:analyzer/dart/element/element.dart';
+import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/source/line_info.dart';
+import 'package:analyzer/src/dart/element/element.dart';
+import 'package:analyzer/src/util/file_paths.dart' as file_paths;
+import 'package:analyzer_plugin/protocol/protocol_common.dart' as protocol;
+import 'package:analyzer_plugin/utilities/analyzer_converter.dart';
+
+class MacroElementLocationProvider implements ElementLocationProvider {
+ final MacroFiles _macroFiles;
+
+ MacroElementLocationProvider(this._macroFiles);
+
+ @override
+ protocol.Location? forElement(Element element) {
+ if (element is HasMacroGenerationData) {
+ var macro = (element as HasMacroGenerationData).macro;
+ if (macro != null) {
+ return _forElement(element, macro);
+ }
+ }
+ }
+
+ protocol.Location? _forElement(Element element, MacroGenerationData macro) {
+ var unitElement = element.thisOrAncestorOfType<CompilationUnitElement>();
+ if (unitElement is! CompilationUnitElementImpl) {
+ return null;
+ }
+
+ var generatedFile = _macroFiles.generatedFile(unitElement);
+ if (generatedFile == null) {
+ return null;
+ }
+
+ var nameOffset = element.nameOffset;
+ var nameLength = element.nameLength;
+
+ var lineInfo = generatedFile.lineInfo;
+ var offsetLocation = lineInfo.getLocation(nameOffset);
+ var endLocation = lineInfo.getLocation(nameOffset + nameLength);
+
+ return protocol.Location(generatedFile.path, nameOffset, nameLength,
+ offsetLocation.lineNumber, offsetLocation.columnNumber,
+ endLine: endLocation.lineNumber, endColumn: endLocation.columnNumber);
+ }
+}
+
+/// Note, this class changes the file system.
+class MacroFiles {
+ final ResourceProvider _resourceProvider;
+
+ /// Keys are source paths.
+ final Map<String, _MacroGeneratedFile> _files = {};
+
+ MacroFiles(this._resourceProvider);
+
+ /// If [unitElement] has macro-generated elements, write the combined
+ /// content into a new file in `.dart_tool`, and return the description of
+ /// this file.
+ _MacroGeneratedFile? generatedFile(CompilationUnitElementImpl unitElement) {
+ var sourcePath = unitElement.source.fullName;
+
+ var result = _files[sourcePath];
+ if (result != null) {
+ return result;
+ }
+
+ var sourceFile = _resourceProvider.getFile(sourcePath);
+
+ // TODO(scheglov) Use workspace?
+ Folder? packageRoot;
+ for (var parent in sourceFile.parent2.withAncestors) {
+ if (parent.getChildAssumingFile(file_paths.pubspecYaml).exists) {
+ packageRoot = parent;
+ break;
+ }
+ }
+ if (packageRoot == null) {
+ return null;
+ }
+
+ var pathContext = _resourceProvider.pathContext;
+ var relativePath = pathContext.relative(
+ sourcePath,
+ from: packageRoot.path,
+ );
+ var generatedPath = pathContext.join(
+ packageRoot.path, '.dart_tool', 'analyzer', 'macro', relativePath);
+
+ var generatedContent = unitElement.macroGeneratedContent;
+ if (generatedContent == null) {
+ return null;
+ }
+
+ try {
+ _resourceProvider.getFile(generatedPath)
+ ..parent2.create()
+ ..writeAsStringSync(generatedContent);
+ } on FileSystemException {
+ return null;
+ }
+
+ return _files[sourcePath] = _MacroGeneratedFile(
+ generatedPath,
+ generatedContent,
+ );
+ }
+}
+
+class _MacroGeneratedFile {
+ final String path;
+ final String content;
+ final LineInfo lineInfo;
+
+ _MacroGeneratedFile(this.path, this.content)
+ : lineInfo = LineInfo.fromContent(content);
+}
diff --git a/pkg/analysis_server/test/analysis/notification_navigation_test.dart b/pkg/analysis_server/test/analysis/notification_navigation_test.dart
index bdd6ad3..8a8f918 100644
--- a/pkg/analysis_server/test/analysis/notification_navigation_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_navigation_test.dart
@@ -775,6 +775,64 @@
assertHasTargetString('my.lib');
}
+ Future<void> test_macro_simpleIdentifier_getter() async {
+ // TODO(scheglov) Use PubPackageResolutionTest?
+ newFile('$projectPath/pubspec.yaml', content: '');
+
+ newFile('$projectPath/bin/macro_annotations.dart', content: r'''
+library analyzer.macro.annotations;
+const observable = 0;
+''');
+
+ addTestFile(r'''
+import 'macro_annotations.dart';
+
+class A {
+ @observable
+ int _foo = 0;
+}
+
+void f(A a) {
+ a.foo;
+}
+''');
+
+ await prepareNavigation();
+ assertHasRegionString('foo;', 3);
+
+ var generatedFile = getFile(
+ '/project/.dart_tool/analyzer/macro/bin/test.dart',
+ );
+
+ var generatedContent = generatedFile.readAsStringSync();
+ // TODO(scheglov) Improve macro to be more formatted.
+ expect(generatedContent, r'''
+import 'macro_annotations.dart';
+
+class A {
+ @observable
+ int _foo = 0;
+
+int get foo => _foo;
+
+set foo(int val) {
+ print('Setting foo to ${val}');
+ _foo = val;
+}
+}
+
+void f(A a) {
+ a.foo;
+}
+''');
+
+ assertHasFileTarget(
+ generatedFile.path,
+ generatedContent.indexOf('foo =>'),
+ 'foo'.length,
+ );
+ }
+
Future<void> test_multiplyDefinedElement() async {
newFile('$projectPath/bin/libA.dart', content: 'library A; int TEST = 1;');
newFile('$projectPath/bin/libB.dart', content: 'library B; int TEST = 2;');
diff --git a/pkg/analyzer_plugin/lib/utilities/analyzer_converter.dart b/pkg/analyzer_plugin/lib/utilities/analyzer_converter.dart
index 575cd5b..10640fc 100644
--- a/pkg/analyzer_plugin/lib/utilities/analyzer_converter.dart
+++ b/pkg/analyzer_plugin/lib/utilities/analyzer_converter.dart
@@ -17,6 +17,13 @@
///
/// Clients may not extend, implement or mix-in this class.
class AnalyzerConverter {
+ /// Optional provider for [analyzer.Element] location, used for example to
+ /// override the location of macro-generated elements.
+ final ElementLocationProvider? _locationProvider;
+
+ AnalyzerConverter({ElementLocationProvider? locationProvider})
+ : _locationProvider = locationProvider;
+
/// Convert the analysis [error] from the 'analyzer' package to an analysis
/// error defined by the plugin API. If a [lineInfo] is provided then the
/// error's location will have a start line and start column. If a [severity]
@@ -199,6 +206,12 @@
if (element == null || element.source == null) {
return null;
}
+
+ var result = _locationProvider?.forElement(element);
+ if (result != null) {
+ return result;
+ }
+
offset ??= element.nameOffset;
length ??= element.nameLength;
if (element is analyzer.CompilationUnitElement ||
@@ -412,3 +425,9 @@
endLine: endLine, endColumn: endColumn);
}
}
+
+abstract class ElementLocationProvider {
+ /// Return the location of [element] that this provider wants to override,
+ /// or `null` if the default location should be used.
+ plugin.Location? forElement(analyzer.Element element);
+}
diff --git a/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart b/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart
index e0a5372..557e7c1 100644
--- a/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart
+++ b/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart
@@ -16,12 +16,15 @@
import 'package:analyzer_plugin/utilities/navigation/navigation.dart';
NavigationCollector computeDartNavigation(
- ResourceProvider resourceProvider,
- NavigationCollector collector,
- CompilationUnit unit,
- int? offset,
- int? length) {
- var dartCollector = _DartNavigationCollector(collector, offset, length);
+ ResourceProvider resourceProvider,
+ NavigationCollector collector,
+ CompilationUnit unit,
+ int? offset,
+ int? length, {
+ AnalyzerConverter? analyzerConverter,
+}) {
+ var dartCollector = _DartNavigationCollector(
+ collector, offset, length, analyzerConverter ?? AnalyzerConverter());
var visitor = _DartNavigationComputerVisitor(resourceProvider, dartCollector);
if (offset == null || length == null) {
unit.accept(visitor);
@@ -66,9 +69,14 @@
final NavigationCollector collector;
final int? requestedOffset;
final int? requestedLength;
+ final AnalyzerConverter _analyzerConverter;
_DartNavigationCollector(
- this.collector, this.requestedOffset, this.requestedLength);
+ this.collector,
+ this.requestedOffset,
+ this.requestedLength,
+ this._analyzerConverter,
+ );
void _addRegion(int offset, int length, Element? element) {
element = element?.nonSynthetic;
@@ -85,15 +93,14 @@
if (!_isWithinRequestedRange(offset, length)) {
return;
}
- var converter = AnalyzerConverter();
- var kind = converter.convertElementKind(element.kind);
- var location = converter.locationFromElement(element);
+ var kind = _analyzerConverter.convertElementKind(element.kind);
+ var location = _analyzerConverter.locationFromElement(element);
if (location == null) {
return;
}
var codeLocation = collector.collectCodeLocations
- ? _getCodeLocation(element, location, converter)
+ ? _getCodeLocation(element, location)
: null;
collector.addRegion(offset, length, kind, location,
@@ -122,8 +129,8 @@
}
/// Get the location of the code (excluding leading doc comments) for this element.
- protocol.Location? _getCodeLocation(Element element,
- protocol.Location location, AnalyzerConverter converter) {
+ protocol.Location? _getCodeLocation(
+ Element element, protocol.Location location) {
var codeElement = element;
// For synthetic getters created for fields, we need to access the associated
// variable to get the codeOffset/codeLength.
@@ -162,7 +169,7 @@
codeOffset = offsetAfterDocs;
}
- return converter.locationFromElement(element,
+ return _analyzerConverter.locationFromElement(element,
offset: codeOffset, length: codeLength);
}
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index 57a1c38a..4f3d3c5 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -1127,13 +1127,29 @@
var mixinName =
getLocalClassName(superclass) + '_' + getLocalClassName(mixinClass);
var mixinId = _emitTemporaryId(mixinName + '\$');
- // Collect all forwarding stubs from anonymous mixins classes. These will
- // contain covariant parameter checks that need to be applied.
- var forwardingMethodStubs = [
+ // Collect all forwarding stub setters from anonymous mixins classes.
+ // These will contain covariant parameter checks that need to be applied.
+ var savedClassProperties = _classProperties;
+ _classProperties =
+ ClassPropertyModel.build(_types, _extensionTypes, _virtualFields, m);
+
+ var forwardingSetters = {
for (var procedure in m.procedures)
if (procedure.isForwardingStub && !procedure.isAbstract)
- _emitMethodDeclaration(procedure)
- ];
+ procedure.name.text: procedure
+ };
+
+ var forwardingMethodStubs = <js_ast.Method>[];
+ for (var s in forwardingSetters.values) {
+ forwardingMethodStubs.add(_emitMethodDeclaration(s));
+ // If there are getters matching the setters somewhere above in the
+ // class hierarchy we must also generate a forwarding getter due to the
+ // representation used in the compiled JavaScript.
+ var getterWrapper = _emitSuperAccessorWrapper(s, {}, forwardingSetters);
+ if (getterWrapper != null) forwardingMethodStubs.add(getterWrapper);
+ }
+
+ _classProperties = savedClassProperties;
// Bind the mixin class to a name to workaround a V8 bug with es6 classes
// and anonymous function names.
diff --git a/runtime/observatory/lib/src/elements/heap_snapshot.dart b/runtime/observatory/lib/src/elements/heap_snapshot.dart
index 1ddb59e..e83ec9e 100644
--- a/runtime/observatory/lib/src/elements/heap_snapshot.dart
+++ b/runtime/observatory/lib/src/elements/heap_snapshot.dart
@@ -1197,7 +1197,7 @@
static Iterable _getChildrenSuccessor(nodeDynamic) {
SnapshotObject node = nodeDynamic;
- return node.successors.take(kMaxChildren).toList();
+ return node.successors.toList();
}
static Iterable _getChildrenPredecessor(nodeDynamic) {
diff --git a/runtime/observatory/lib/src/service/object.dart b/runtime/observatory/lib/src/service/object.dart
index 7d83c71..df4a4cd 100644
--- a/runtime/observatory/lib/src/service/object.dart
+++ b/runtime/observatory/lib/src/service/object.dart
@@ -676,8 +676,6 @@
String targetCPU = 'unknown';
String embedder = 'unknown';
int architectureBits = 0;
- bool assertsEnabled = false;
- bool typeChecksEnabled = false;
int nativeZoneMemoryUsage = 0;
int pid = 0;
int mallocUsed = 0;
@@ -1050,8 +1048,6 @@
maxRSS = map['_maxRSS'];
currentRSS = map['_currentRSS'];
profileVM = map['_profilerMode'] == 'VM';
- assertsEnabled = map['_assertsEnabled'];
- typeChecksEnabled = map['_typeChecksEnabled'];
_removeDeadIsolates([
...map['isolates'],
...map['systemIsolates'],
@@ -1550,7 +1546,7 @@
// There are sometimes isolate refs in ServiceEvents.
return vm.getFromMap(map);
}
- String mapId = map['id'];
+ String? mapId = map['id'];
var obj = (mapId != null) ? _cache[mapId] : null;
if (obj != null) {
obj.updateFromServiceMap(map);
@@ -1559,7 +1555,7 @@
// Build the object from the map directly.
obj = ServiceObject._fromMap(this, map);
if ((obj != null) && obj.canCache) {
- _cache[mapId] = obj;
+ _cache[mapId!] = obj;
}
return obj;
}
diff --git a/runtime/observatory/tests/service/object_graph_isolate_group_test.dart b/runtime/observatory/tests/service/object_graph_isolate_group_test.dart
new file mode 100644
index 0000000..d325f75
--- /dev/null
+++ b/runtime/observatory/tests/service/object_graph_isolate_group_test.dart
@@ -0,0 +1,70 @@
+// 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.
+
+// VMOptions=--enable_isolate_groups
+
+import 'dart:isolate' as isolate;
+import 'package:observatory/object_graph.dart';
+import 'package:observatory/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+// Make sure these fields are not removed by the tree shaker.
+@pragma("vm:entry-point")
+dynamic bigGlobal;
+
+child(message) {
+ var bigString = message[0] as String;
+ var replyPort = message[1] as isolate.SendPort;
+ bigGlobal = bigString;
+ replyPort.send(null);
+ new isolate.RawReceivePort(); // Keep child alive.
+}
+
+void script() {
+ var bigString = "x" * (1 << 20);
+ var port;
+ for (var i = 0; i < 2; i++) {
+ port = new isolate.RawReceivePort((_) => port.close());
+ isolate.Isolate.spawn(child, [bigString, port.sendPort]);
+ }
+ bigGlobal = bigString;
+ print("Ready");
+}
+
+var tests = <IsolateTest>[
+ (Isolate isolate) async {
+ var graph = await isolate.fetchHeapSnapshot().done;
+
+ // We are assuming the big string is the largest in the heap, and that it
+ // was shared/pass-by-pointer.
+ List<SnapshotObject> strings = graph.objects
+ .where((SnapshotObject obj) => obj.klass.name == "_OneByteString")
+ .toList();
+ strings.sort((u, v) => v.shallowSize - u.shallowSize);
+ SnapshotObject bigString = strings[0];
+ print("bigString: $bigString");
+ expect(bigString.shallowSize, greaterThanOrEqualTo(1 << 20));
+
+ int matchingPredecessors = 0;
+ for (SnapshotObject predecessor in bigString.predecessors) {
+ print("predecessor $predecessor ${predecessor.label}");
+ if (predecessor.label.contains("bigGlobal") &&
+ predecessor.klass.name.contains("Isolate")) {
+ matchingPredecessors++;
+ }
+ }
+
+ for (SnapshotObject object in graph.objects) {
+ if (object.klass.name.contains("Isolate")) {
+ print("$object / ${object.description}");
+ }
+ }
+
+ // Parent and two children. Seeing all 3 means we visited all the field tables.
+ expect(matchingPredecessors, equals(3));
+ }
+];
+
+main(args) => runIsolateTests(args, tests, testeeBefore: script);
diff --git a/runtime/observatory/tests/service/object_graph_vm_test.dart b/runtime/observatory/tests/service/object_graph_vm_test.dart
index a4bf3fe..8a8e7ec 100644
--- a/runtime/observatory/tests/service/object_graph_vm_test.dart
+++ b/runtime/observatory/tests/service/object_graph_vm_test.dart
@@ -90,8 +90,8 @@
int internalSum = 0;
int externalSum = 0;
for (SnapshotObject instance in klass.instances) {
- if (instance == graph.root) {
- // The root may have 0 self size.
+ if (instance == graph.root || instance.klass.name.contains("Isolate")) {
+ // The root and fake root subdivisions have 0 self size.
expect(instance.internalSize, greaterThanOrEqualTo(0));
expect(instance.externalSize, greaterThanOrEqualTo(0));
expect(instance.shallowSize, greaterThanOrEqualTo(0));
@@ -122,8 +122,8 @@
int internalSum = 0;
int externalSum = 0;
for (SnapshotObject instance in graph.objects) {
- if (instance == graph.root) {
- // The root may have 0 self size.
+ if (instance == graph.root || instance.klass.name.contains("Isolate")) {
+ // The root and fake root subdivisions have 0 self size.
expect(instance.internalSize, greaterThanOrEqualTo(0));
expect(instance.externalSize, greaterThanOrEqualTo(0));
expect(instance.shallowSize, greaterThanOrEqualTo(0));
diff --git a/runtime/observatory_2/lib/src/elements/heap_snapshot.dart b/runtime/observatory_2/lib/src/elements/heap_snapshot.dart
index bfe8412..4fde130 100644
--- a/runtime/observatory_2/lib/src/elements/heap_snapshot.dart
+++ b/runtime/observatory_2/lib/src/elements/heap_snapshot.dart
@@ -1196,7 +1196,7 @@
static Iterable _getChildrenSuccessor(nodeDynamic) {
SnapshotObject node = nodeDynamic;
- return node.successors.take(kMaxChildren).toList();
+ return node.successors.toList();
}
static Iterable _getChildrenPredecessor(nodeDynamic) {
diff --git a/runtime/observatory_2/lib/src/service/object.dart b/runtime/observatory_2/lib/src/service/object.dart
index 14d72c2..5e220dc 100644
--- a/runtime/observatory_2/lib/src/service/object.dart
+++ b/runtime/observatory_2/lib/src/service/object.dart
@@ -677,8 +677,6 @@
String targetCPU;
String embedder;
int architectureBits;
- bool assertsEnabled = false;
- bool typeChecksEnabled = false;
int nativeZoneMemoryUsage = 0;
int pid = 0;
int mallocUsed = 0;
@@ -1053,8 +1051,6 @@
maxRSS = map['_maxRSS'];
currentRSS = map['_currentRSS'];
profileVM = map['_profilerMode'] == 'VM';
- assertsEnabled = map['_assertsEnabled'];
- typeChecksEnabled = map['_typeChecksEnabled'];
_removeDeadIsolates([
...map['isolates'],
...map['systemIsolates'],
diff --git a/runtime/observatory_2/tests/service_2/object_graph_isolate_group_test.dart b/runtime/observatory_2/tests/service_2/object_graph_isolate_group_test.dart
new file mode 100644
index 0000000..b9ddf82
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/object_graph_isolate_group_test.dart
@@ -0,0 +1,72 @@
+// 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.
+
+// VMOptions=--enable_isolate_groups
+
+// @dart = 2.7
+
+import 'dart:isolate' as isolate;
+import 'package:observatory_2/object_graph.dart';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+// Make sure these fields are not removed by the tree shaker.
+@pragma("vm:entry-point")
+dynamic bigGlobal;
+
+child(message) {
+ var bigString = message[0] as String;
+ var replyPort = message[1] as isolate.SendPort;
+ bigGlobal = bigString;
+ replyPort.send(null);
+ new isolate.RawReceivePort(); // Keep child alive.
+}
+
+void script() {
+ var bigString = "x" * (1 << 20);
+ var port;
+ for (var i = 0; i < 2; i++) {
+ port = new isolate.RawReceivePort((_) => port.close());
+ isolate.Isolate.spawn(child, [bigString, port.sendPort]);
+ }
+ bigGlobal = bigString;
+ print("Ready");
+}
+
+var tests = <IsolateTest>[
+ (Isolate isolate) async {
+ var graph = await isolate.fetchHeapSnapshot().done;
+
+ // We are assuming the big string is the largest in the heap, and that it
+ // was shared/pass-by-pointer.
+ List<SnapshotObject> strings = graph.objects
+ .where((SnapshotObject obj) => obj.klass.name == "_OneByteString")
+ .toList();
+ strings.sort((u, v) => v.shallowSize - u.shallowSize);
+ SnapshotObject bigString = strings[0];
+ print("bigString: $bigString");
+ expect(bigString.shallowSize, greaterThanOrEqualTo(1 << 20));
+
+ int matchingPredecessors = 0;
+ for (SnapshotObject predecessor in bigString.predecessors) {
+ print("predecessor $predecessor ${predecessor.label}");
+ if (predecessor.label.contains("bigGlobal") &&
+ predecessor.klass.name.contains("Isolate")) {
+ matchingPredecessors++;
+ }
+ }
+
+ for (SnapshotObject object in graph.objects) {
+ if (object.klass.name.contains("Isolate")) {
+ print("$object / ${object.description}");
+ }
+ }
+
+ // Parent and two children. Seeing all 3 means we visited all the field tables.
+ expect(matchingPredecessors, equals(3));
+ }
+];
+
+main(args) => runIsolateTests(args, tests, testeeBefore: script);
diff --git a/runtime/observatory_2/tests/service_2/object_graph_vm_test.dart b/runtime/observatory_2/tests/service_2/object_graph_vm_test.dart
index d1cb655..81be62e 100644
--- a/runtime/observatory_2/tests/service_2/object_graph_vm_test.dart
+++ b/runtime/observatory_2/tests/service_2/object_graph_vm_test.dart
@@ -90,8 +90,8 @@
int internalSum = 0;
int externalSum = 0;
for (SnapshotObject instance in klass.instances) {
- if (instance == graph.root) {
- // The root may have 0 self size.
+ if (instance == graph.root || instance.klass.name.contains("Isolate")) {
+ // The root and fake root subdivisions have 0 self size.
expect(instance.internalSize, greaterThanOrEqualTo(0));
expect(instance.externalSize, greaterThanOrEqualTo(0));
expect(instance.shallowSize, greaterThanOrEqualTo(0));
@@ -122,8 +122,8 @@
int internalSum = 0;
int externalSum = 0;
for (SnapshotObject instance in graph.objects) {
- if (instance == graph.root) {
- // The root may have 0 self size.
+ if (instance == graph.root || instance.klass.name.contains("Isolate")) {
+ // The root and fake root subdivisions have 0 self size.
expect(instance.internalSize, greaterThanOrEqualTo(0));
expect(instance.externalSize, greaterThanOrEqualTo(0));
expect(instance.shallowSize, greaterThanOrEqualTo(0));
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 35236ef..b1967f0 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -2628,16 +2628,18 @@
ValidationPolicy validate_frames) {
ASSERT(visitor != nullptr);
+ // Visit objects in the field table.
+ // N.B.: The heap snapshot writer requires visiting the field table first, so
+ // that the pointer visitation order aligns with order of field name metadata.
+ if (!visitor->trace_values_through_fields()) {
+ field_table()->VisitObjectPointers(visitor);
+ }
+
// Visit objects in the isolate object store.
if (isolate_object_store() != nullptr) {
isolate_object_store()->VisitObjectPointers(visitor);
}
- // Visit objects in the field table.
- if (!visitor->trace_values_through_fields()) {
- field_table()->VisitObjectPointers(visitor);
- }
-
visitor->clear_gc_root_type();
// Visit the objects directly referenced from the isolate structure.
visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(¤t_tag_));
@@ -2810,14 +2812,19 @@
void IsolateGroup::VisitObjectPointers(ObjectPointerVisitor* visitor,
ValidationPolicy validate_frames) {
+ VisitSharedPointers(visitor);
+ for (Isolate* isolate : isolates_) {
+ isolate->VisitObjectPointers(visitor, validate_frames);
+ }
+ VisitStackPointers(visitor, validate_frames);
+}
+
+void IsolateGroup::VisitSharedPointers(ObjectPointerVisitor* visitor) {
// if class table is shared, it's stored on isolate group
if (class_table() != nullptr) {
// Visit objects in the class table.
class_table()->VisitObjectPointers(visitor);
}
- for (Isolate* isolate : isolates_) {
- isolate->VisitObjectPointers(visitor, validate_frames);
- }
api_state()->VisitObjectPointersUnlocked(visitor);
// Visit objects in the object store.
if (object_store() != nullptr) {
@@ -2825,7 +2832,6 @@
}
visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&saved_unlinked_calls_));
initial_field_table()->VisitObjectPointers(visitor);
- VisitStackPointers(visitor, validate_frames);
// Visit the boxed_field_list_.
// 'boxed_field_list_' access via mutator and background compilation threads
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index d7c8e5d..8b4f88b 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -713,6 +713,7 @@
// running, and the visitor must not allocate.
void VisitObjectPointers(ObjectPointerVisitor* visitor,
ValidationPolicy validate_frames);
+ void VisitSharedPointers(ObjectPointerVisitor* visitor);
void VisitStackPointers(ObjectPointerVisitor* visitor,
ValidationPolicy validate_frames);
void VisitObjectIdRingPointers(ObjectPointerVisitor* visitor);
diff --git a/runtime/vm/object_graph.cc b/runtime/vm/object_graph.cc
index 40e55a83..749c0f3 100644
--- a/runtime/vm/object_graph.cc
+++ b/runtime/vm/object_graph.cc
@@ -812,8 +812,6 @@
HandleVisitor(Thread::Current()),
writer_(writer) {}
- virtual bool trace_values_through_fields() const { return true; }
-
void VisitObject(ObjectPtr obj) {
if (obj->IsPseudoObject()) return;
@@ -865,6 +863,13 @@
static const intptr_t kMaxStringElements = 128;
+enum ExtraCids {
+ kRootExtraCid = 1, // 1-origin
+ kIsolateExtraCid = 2,
+
+ kNumExtraCids = 2,
+};
+
class Pass2Visitor : public ObjectVisitor,
public ObjectPointerVisitor,
public HandleVisitor {
@@ -876,13 +881,11 @@
isolate_group_(thread()->isolate_group()),
writer_(writer) {}
- virtual bool trace_values_through_fields() const { return true; }
-
void VisitObject(ObjectPtr obj) {
if (obj->IsPseudoObject()) return;
intptr_t cid = obj->GetClassId();
- writer_->WriteUnsigned(cid);
+ writer_->WriteUnsigned(cid + kNumExtraCids);
writer_->WriteUnsigned(discount_sizes_ ? 0 : obj->untag()->HeapSize());
if (cid == kNullCid) {
@@ -891,6 +894,16 @@
writer_->WriteUnsigned(kBoolData);
writer_->WriteUnsigned(
static_cast<uintptr_t>(static_cast<BoolPtr>(obj)->untag()->value_));
+ } else if (cid == kSentinelCid) {
+ if (obj == Object::sentinel().ptr()) {
+ writer_->WriteUnsigned(kNameData);
+ writer_->WriteUtf8("uninitialized");
+ } else if (obj == Object::transition_sentinel().ptr()) {
+ writer_->WriteUnsigned(kNameData);
+ writer_->WriteUtf8("initializing");
+ } else {
+ writer_->WriteUnsigned(kNoData);
+ }
} else if (cid == kSmiCid) {
UNREACHABLE();
} else if (cid == kMintCid) {
@@ -1073,6 +1086,16 @@
}
}
+ void CountExtraRefs(intptr_t count) {
+ ASSERT(!writing_);
+ counted_ += count;
+ }
+ void WriteExtraRef(intptr_t oid) {
+ ASSERT(writing_);
+ written_++;
+ writer_->WriteUnsigned(oid);
+ }
+
private:
IsolateGroup* isolate_group_;
HeapSnapshotWriter* const writer_;
@@ -1105,6 +1128,36 @@
DISALLOW_COPY_AND_ASSIGN(Pass3Visitor);
};
+class CollectStaticFieldNames : public ObjectVisitor {
+ public:
+ CollectStaticFieldNames(intptr_t field_table_size,
+ const char** field_table_names)
+ : ObjectVisitor(),
+ field_table_size_(field_table_size),
+ field_table_names_(field_table_names),
+ field_(Field::Handle()) {}
+
+ void VisitObject(ObjectPtr obj) {
+ if (obj->IsField()) {
+ field_ ^= obj;
+ if (field_.is_static()) {
+ intptr_t id = field_.field_id();
+ if (id > 0) {
+ ASSERT(id < field_table_size_);
+ field_table_names_[id] = field_.UserVisibleNameCString();
+ }
+ }
+ }
+ }
+
+ private:
+ intptr_t field_table_size_;
+ const char** field_table_names_;
+ Field& field_;
+
+ DISALLOW_COPY_AND_ASSIGN(CollectStaticFieldNames);
+};
+
void HeapSnapshotWriter::Write() {
HeapIterationScope iteration(thread());
@@ -1134,7 +1187,46 @@
Array& fields = Array::Handle();
Field& field = Field::Handle();
- WriteUnsigned(class_count_);
+ intptr_t field_table_size = isolate()->field_table()->NumFieldIds();
+ const char** field_table_names =
+ thread()->zone()->Alloc<const char*>(field_table_size);
+ for (intptr_t i = 0; i < field_table_size; i++) {
+ field_table_names[i] = nullptr;
+ }
+ {
+ CollectStaticFieldNames visitor(field_table_size, field_table_names);
+ iteration.IterateObjects(&visitor);
+ }
+
+ WriteUnsigned(class_count_ + kNumExtraCids);
+ {
+ ASSERT(kRootExtraCid == 1);
+ WriteUnsigned(0); // Flags
+ WriteUtf8("Root"); // Name
+ WriteUtf8(""); // Library name
+ WriteUtf8(""); // Library uri
+ WriteUtf8(""); // Reserved
+ WriteUnsigned(0); // Field count
+ }
+ {
+ ASSERT(kIsolateExtraCid == 2);
+ WriteUnsigned(0); // Flags
+ WriteUtf8("Isolate"); // Name
+ WriteUtf8(""); // Library name
+ WriteUtf8(""); // Library uri
+ WriteUtf8(""); // Reserved
+
+ WriteUnsigned(field_table_size); // Field count
+ for (intptr_t i = 0; i < field_table_size; i++) {
+ intptr_t flags = 1; // Strong.
+ WriteUnsigned(flags);
+ WriteUnsigned(i); // Index.
+ const char* name = field_table_names[i];
+ WriteUtf8(name == nullptr ? "" : name);
+ WriteUtf8(""); // Reserved
+ }
+ }
+ ASSERT(kNumExtraCids == 2);
for (intptr_t cid = 1; cid <= class_count_; cid++) {
if (!class_table->HasValidClassAt(cid)) {
WriteUnsigned(0); // Flags
@@ -1227,13 +1319,22 @@
SetupCountingPages();
+ intptr_t num_isolates = 0;
{
Pass1Visitor visitor(this);
- // Root "object".
+ // Root "objects".
++object_count_;
- isolate()->VisitObjectPointers(&visitor,
- ValidationPolicy::kDontValidateFrames);
+ isolate_group()->VisitSharedPointers(&visitor);
+ isolate_group()->ForEachIsolate(
+ [&](Isolate* isolate) {
+ ++object_count_;
+ isolate->VisitObjectPointers(&visitor,
+ ValidationPolicy::kDontValidateFrames);
+ ++num_isolates;
+ },
+ /*at_safepoint=*/true);
+ CountReferences(num_isolates);
// Heap objects.
iteration.IterateVMIsolateObjects(&visitor);
@@ -1249,16 +1350,35 @@
WriteUnsigned(reference_count_);
WriteUnsigned(object_count_);
- // Root "object".
- WriteUnsigned(0); // cid
- WriteUnsigned(0); // shallowSize
- WriteUnsigned(kNoData);
- visitor.DoCount();
- isolate()->VisitObjectPointers(&visitor,
- ValidationPolicy::kDontValidateFrames);
- visitor.DoWrite();
- isolate()->VisitObjectPointers(&visitor,
- ValidationPolicy::kDontValidateFrames);
+ // Root "objects".
+ {
+ WriteUnsigned(kRootExtraCid);
+ WriteUnsigned(0); // shallowSize
+ WriteUnsigned(kNoData);
+ visitor.DoCount();
+ isolate_group()->VisitSharedPointers(&visitor);
+ visitor.CountExtraRefs(num_isolates);
+ visitor.DoWrite();
+ isolate_group()->VisitSharedPointers(&visitor);
+ for (intptr_t i = 0; i < num_isolates; i++) {
+ visitor.WriteExtraRef(i + 2); // 0 = sentinel, 1 = root, 2+ = isolates
+ }
+ }
+ isolate_group()->ForEachIsolate(
+ [&](Isolate* isolate) {
+ WriteUnsigned(kIsolateExtraCid);
+ WriteUnsigned(0); // shallowSize
+ WriteUnsigned(kNameData);
+ WriteUtf8(
+ OS::SCreate(thread()->zone(), "%" Pd64, isolate->main_port()));
+ visitor.DoCount();
+ isolate->VisitObjectPointers(&visitor,
+ ValidationPolicy::kDontValidateFrames);
+ visitor.DoWrite();
+ isolate->VisitObjectPointers(&visitor,
+ ValidationPolicy::kDontValidateFrames);
+ },
+ /*at_safepoint=*/true);
// Heap objects.
visitor.set_discount_sizes(true);
@@ -1277,6 +1397,8 @@
// Handle root object.
WriteUnsigned(0);
+ isolate_group()->ForEachIsolate([&](Isolate* isolate) { WriteUnsigned(0); },
+ /*at_safepoint=*/true);
// Handle visit rest of the objects.
iteration.IterateVMIsolateObjects(&visitor);
diff --git a/runtime/vm/raw_object_fields.cc b/runtime/vm/raw_object_fields.cc
index 0c64af6..186f510 100644
--- a/runtime/vm/raw_object_fields.cc
+++ b/runtime/vm/raw_object_fields.cc
@@ -156,15 +156,23 @@
F(String, length_) \
F(Array, type_arguments_) \
F(Array, length_) \
+ F(ImmutableArray, type_arguments_) \
+ F(ImmutableArray, length_) \
F(GrowableObjectArray, type_arguments_) \
F(GrowableObjectArray, length_) \
F(GrowableObjectArray, data_) \
- F(LinkedHashBase, type_arguments_) \
- F(LinkedHashBase, index_) \
- F(LinkedHashBase, hash_mask_) \
- F(LinkedHashBase, data_) \
- F(LinkedHashBase, used_data_) \
- F(LinkedHashBase, deleted_keys_) \
+ F(LinkedHashMap, type_arguments_) \
+ F(LinkedHashMap, index_) \
+ F(LinkedHashMap, hash_mask_) \
+ F(LinkedHashMap, data_) \
+ F(LinkedHashMap, used_data_) \
+ F(LinkedHashSet, deleted_keys_) \
+ F(LinkedHashSet, type_arguments_) \
+ F(LinkedHashSet, index_) \
+ F(LinkedHashSet, hash_mask_) \
+ F(LinkedHashSet, data_) \
+ F(LinkedHashSet, used_data_) \
+ F(LinkedHashSet, deleted_keys_) \
F(TypedData, length_) \
F(ExternalTypedData, length_) \
F(ReceivePort, send_port_) \
diff --git a/tests/language/regress/regress46867_test.dart b/tests/language/regress/regress46867_test.dart
new file mode 100644
index 0000000..7999cc8
--- /dev/null
+++ b/tests/language/regress/regress46867_test.dart
@@ -0,0 +1,32 @@
+// 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.
+
+// Regression test for https://github.com/dart-lang/sdk/issues/46867
+
+import 'package:expect/expect.dart';
+
+class Interface {
+ covariant Object x = 'from Interface';
+}
+
+mixin Mixin implements Interface {}
+
+class BaseClass {
+ static var getterCallCount = 0;
+ static var setterCallCount = 0;
+ Object get x => getterCallCount++;
+ set x(Object value) => setterCallCount++;
+}
+
+class SubClass extends BaseClass with Mixin {}
+
+void main() {
+ Expect.equals(0, BaseClass.getterCallCount);
+ SubClass().x;
+ Expect.equals(1, BaseClass.getterCallCount);
+
+ Expect.equals(0, BaseClass.setterCallCount);
+ SubClass().x = 42;
+ Expect.equals(1, BaseClass.setterCallCount);
+}
diff --git a/tests/language_2/regress/regress46867_test.dart b/tests/language_2/regress/regress46867_test.dart
new file mode 100644
index 0000000..750b2234
--- /dev/null
+++ b/tests/language_2/regress/regress46867_test.dart
@@ -0,0 +1,34 @@
+// 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.
+
+// @dart = 2.9
+
+// Regression test for https://github.com/dart-lang/sdk/issues/46867
+
+import 'package:expect/expect.dart';
+
+class Interface {
+ covariant Object x = 'from Interface';
+}
+
+mixin Mixin implements Interface {}
+
+class BaseClass {
+ static var getterCallCount = 0;
+ static var setterCallCount = 0;
+ Object get x => getterCallCount++;
+ set x(Object value) => setterCallCount++;
+}
+
+class SubClass extends BaseClass with Mixin {}
+
+void main() {
+ Expect.equals(0, BaseClass.getterCallCount);
+ SubClass().x;
+ Expect.equals(1, BaseClass.getterCallCount);
+
+ Expect.equals(0, BaseClass.setterCallCount);
+ SubClass().x = 42;
+ Expect.equals(1, BaseClass.setterCallCount);
+}
diff --git a/tools/VERSION b/tools/VERSION
index f5bacd2..6459dd6 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 15
PATCH 0
-PRERELEASE 23
+PRERELEASE 24
PRERELEASE_PATCH 0
\ No newline at end of file