[CFE] Allow experimental invalidation to work on DillLibraryBuilders

This CL makes experimental invalidation work on DillLibraryBuilders,
and solves all found issues (e.g. old references (aka leaks)) with it.

Note: This CL introduces a few writes that seems weird (e.g. setting
variables that's about to be out-of-scope to null). This is done to
prevent "leaks", or probably more likely, prevent a "false positive"
leak detection and it currently gives a "clean bill of health" from
the leak detector.

Change-Id: I5b01df6e9ede710a5b624a8a4c21015214140318
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/134288
Commit-Queue: Jens Johansen <jensj@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
Jens Johansen
2020-02-06 12:19:51 +00:00
committed by commit-bot@chromium.org
parent be38459b98
commit ddd6e31dee
23 changed files with 1462 additions and 852 deletions
@@ -32,6 +32,7 @@ import '../builder/builder.dart';
import '../builder/class_builder.dart';
import '../builder/dynamic_type_builder.dart';
import '../builder/extension_builder.dart';
import '../builder/modifier_builder.dart';
import '../builder/never_type_builder.dart';
import '../builder/invalid_type_declaration_builder.dart';
import '../builder/library_builder.dart';
@@ -89,7 +90,7 @@ class DillLibraryBuilder extends LibraryBuilderImpl {
/// [../kernel/kernel_library_builder.dart].
Map<String, String> unserializableExports;
// TODO(jensj): These 4 booleans could potentially be merged into a single
// TODO(jensj): These 5 booleans could potentially be merged into a single
// state field.
bool isReadyToBuild = false;
bool isReadyToFinalizeExports = false;
@@ -294,53 +295,75 @@ class DillLibraryBuilder extends LibraryBuilderImpl {
exportScopeBuilder.addMember(name, declaration);
});
Map<Reference, Builder> sourceBuildersMap =
loader.currentSourceLoader?.buildersCreatedWithReferences;
for (Reference reference in library.additionalExports) {
NamedNode node = reference.node;
Uri libraryUri;
String name;
bool isSetter = false;
if (node is Class) {
libraryUri = node.enclosingLibrary.importUri;
name = node.name;
} else if (node is Procedure) {
libraryUri = node.enclosingLibrary.importUri;
name = node.name.name;
isSetter = node.isSetter;
} else if (node is Member) {
libraryUri = node.enclosingLibrary.importUri;
name = node.name.name;
} else if (node is Typedef) {
libraryUri = node.enclosingLibrary.importUri;
name = node.name;
} else if (node is Extension) {
libraryUri = node.enclosingLibrary.importUri;
name = node.name;
} else {
unhandled("${node.runtimeType}", "finalizeExports", -1, fileUri);
}
DillLibraryBuilder library = loader.builders[libraryUri];
if (library == null) {
internalProblem(
templateUnspecified.withArguments("No builder for '$libraryUri'."),
-1,
fileUri);
}
Builder declaration;
if (isSetter) {
declaration = library.exportScope.lookupLocalMember(name, setter: true);
exportScopeBuilder.addSetter(name, declaration);
String name;
if (sourceBuildersMap?.containsKey(reference) == true) {
declaration = sourceBuildersMap[reference];
assert(declaration != null);
if (declaration is ModifierBuilder) {
name = declaration.name;
} else {
throw new StateError(
"Unexpected: $declaration (${declaration.runtimeType}");
}
if (declaration.isSetter) {
exportScopeBuilder.addSetter(name, declaration);
} else {
exportScopeBuilder.addMember(name, declaration);
}
} else {
declaration =
library.exportScope.lookupLocalMember(name, setter: false);
exportScopeBuilder.addMember(name, declaration);
}
if (declaration == null) {
internalProblem(
templateUnspecified.withArguments(
"Exported element '$name' not found in '$libraryUri'."),
-1,
fileUri);
Uri libraryUri;
bool isSetter = false;
if (node is Class) {
libraryUri = node.enclosingLibrary.importUri;
name = node.name;
} else if (node is Procedure) {
libraryUri = node.enclosingLibrary.importUri;
name = node.name.name;
isSetter = node.isSetter;
} else if (node is Member) {
libraryUri = node.enclosingLibrary.importUri;
name = node.name.name;
} else if (node is Typedef) {
libraryUri = node.enclosingLibrary.importUri;
name = node.name;
} else if (node is Extension) {
libraryUri = node.enclosingLibrary.importUri;
name = node.name;
} else {
unhandled("${node.runtimeType}", "finalizeExports", -1, fileUri);
}
LibraryBuilder library = loader.builders[libraryUri];
if (library == null) {
internalProblem(
templateUnspecified
.withArguments("No builder for '$libraryUri'."),
-1,
fileUri);
}
if (isSetter) {
declaration =
library.exportScope.lookupLocalMember(name, setter: true);
exportScopeBuilder.addSetter(name, declaration);
} else {
declaration =
library.exportScope.lookupLocalMember(name, setter: false);
exportScopeBuilder.addMember(name, declaration);
}
if (declaration == null) {
internalProblem(
templateUnspecified.withArguments(
"Exported element '$name' not found in '$libraryUri'."),
-1,
fileUri);
}
}
assert(
(declaration is ClassBuilder && node == declaration.cls) ||
(declaration is TypeAliasBuilder &&
@@ -21,6 +21,8 @@ import '../loader.dart' show Loader;
import '../problems.dart' show unhandled;
import '../source/source_loader.dart' show SourceLoader;
import '../target_implementation.dart' show TargetImplementation;
import 'dill_library_builder.dart' show DillLibraryBuilder;
@@ -28,6 +30,8 @@ import 'dill_library_builder.dart' show DillLibraryBuilder;
import 'dill_target.dart' show DillTarget;
class DillLoader extends Loader {
SourceLoader currentSourceLoader;
DillLoader(TargetImplementation target) : super(target);
Template<SummaryTemplate> get outlineSummaryTemplate =>
@@ -129,7 +129,7 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
List<LibraryBuilder> platformBuilders;
Map<Uri, LibraryBuilder> userBuilders;
final Uri initializeFromDillUri;
final Component componentToInitializeFrom;
Component componentToInitializeFrom;
bool initializedFromDill = false;
bool initializedIncrementalSerializer = false;
Uri previousPackagesUri;
@@ -213,13 +213,14 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
KernelTarget userCodeOld = userCode;
setupNewUserCode(c, uriTranslator, hierarchy, reusedLibraries,
experimentalInvalidation, entryPoints.first);
Map<LibraryBuilder, List<SourceLibraryBuilder>> rebuildBodiesMap =
Map<LibraryBuilder, List<LibraryBuilder>> rebuildBodiesMap =
experimentalInvalidationCreateRebuildBodiesBuilders(
experimentalInvalidation, uriTranslator);
entryPoints = userCode.setEntryPoints(entryPoints);
await userCode.loader.buildOutlines();
experimentalInvalidationPatchUpScopes(
experimentalInvalidation, rebuildBodiesMap);
rebuildBodiesMap = null;
// Checkpoint: Build the actual outline.
// Note that the [Component] is not the "full" component.
@@ -277,10 +278,14 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
if (componentWithDill == null) {
userCode.loader.builders.clear();
userCode = userCodeOld;
dillLoadedData.loader.currentSourceLoader = userCode.loader;
} else {
previousSourceBuilders = await convertSourceLibraryBuildersToDill();
previousSourceBuilders =
await convertSourceLibraryBuildersToDill(experimentalInvalidation);
}
experimentalInvalidation = null;
// Output result.
Procedure mainMethod = componentWithDill == null
? data.userLoadedUriMain
@@ -295,11 +300,17 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
/// Convert every SourceLibraryBuilder to a DillLibraryBuilder.
/// As we always do this, this will only be the new ones.
///
/// If doing experimental invalidation that means that some of the old dill
/// library builders might have links (via export scopes) to the
/// source builders and they will thus be patched up here too.
///
/// Returns the set of Libraries that now has new (dill) builders.
Future<Set<Library>> convertSourceLibraryBuildersToDill() async {
Future<Set<Library>> convertSourceLibraryBuildersToDill(
ExperimentalInvalidation experimentalInvalidation) async {
bool changed = false;
Set<Library> newDillLibraryBuilders = new Set<Library>();
userBuilders ??= <Uri, LibraryBuilder>{};
Map<LibraryBuilder, List<LibraryBuilder>> convertedLibraries;
for (MapEntry<Uri, LibraryBuilder> entry
in userCode.loader.builders.entries) {
if (entry.value is SourceLibraryBuilder) {
@@ -309,14 +320,53 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
userCode.loader.builders[entry.key] = dillBuilder;
userBuilders[entry.key] = dillBuilder;
newDillLibraryBuilders.add(builder.library);
if (userCode.loader.first == builder) {
userCode.loader.first = dillBuilder;
}
changed = true;
if (experimentalInvalidation != null) {
convertedLibraries ??=
new Map<LibraryBuilder, List<LibraryBuilder>>();
convertedLibraries[builder] = [dillBuilder];
}
}
}
if (changed) {
// We suppress finalization errors because they have already been
// reported.
await dillLoadedData.buildOutlines(suppressFinalizationErrors: true);
if (experimentalInvalidation != null) {
/// If doing experimental invalidation that means that some of the old
/// dill library builders might have links (via export scopes) to the
/// source builders. Patch that up.
// Maps from old library builder to map of new content.
Map<LibraryBuilder, Map<String, Builder>> replacementMap = {};
// Maps from old library builder to map of new content.
Map<LibraryBuilder, Map<String, Builder>> replacementSettersMap = {};
experimentalInvalidationFillReplacementMaps(
convertedLibraries, replacementMap, replacementSettersMap);
for (LibraryBuilder builder
in experimentalInvalidation.originalNotReusedLibraries) {
DillLibraryBuilder dillBuilder = builder;
if (dillBuilder.isBuilt) {
dillBuilder.exportScope
.patchUpScope(replacementMap, replacementSettersMap);
}
}
replacementMap = null;
replacementSettersMap = null;
}
}
userCode.loader.buildersCreatedWithReferences.clear();
userCode.loader.builderHierarchy.nodes.clear();
userCode.loader.referenceFromIndex = null;
convertedLibraries = null;
experimentalInvalidation = null;
if (userBuilders.isEmpty) userBuilders = null;
return newDillLibraryBuilders;
}
@@ -415,17 +465,17 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
/// Fill in the replacement maps that describe the replacements that need to
/// happen because of experimental invalidation.
void experimentalInvalidationFillReplacementMaps(
Map<LibraryBuilder, List<SourceLibraryBuilder>> rebuildBodiesMap,
Map<LibraryBuilder, List<LibraryBuilder>> rebuildBodiesMap,
Map<LibraryBuilder, Map<String, Builder>> replacementMap,
Map<LibraryBuilder, Map<String, Builder>> replacementSettersMap) {
for (MapEntry<LibraryBuilder, List<SourceLibraryBuilder>> entry
for (MapEntry<LibraryBuilder, List<LibraryBuilder>> entry
in rebuildBodiesMap.entries) {
Map<String, Builder> childReplacementMap = {};
Map<String, Builder> childReplacementSettersMap = {};
List<SourceLibraryBuilder> builders = rebuildBodiesMap[entry.key];
List<LibraryBuilder> builders = rebuildBodiesMap[entry.key];
replacementMap[entry.key] = childReplacementMap;
replacementSettersMap[entry.key] = childReplacementSettersMap;
for (SourceLibraryBuilder builder in builders) {
for (LibraryBuilder builder in builders) {
NameIterator iterator = builder.nameIterator;
while (iterator.moveNext()) {
Builder childBuilder = iterator.current;
@@ -450,22 +500,22 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
/// When doing experimental invalidation, we have some builders that needs to
/// be rebuild special, namely they have to be [userCode.loader.read] with
/// references from the original [Library] for things to work.
Map<LibraryBuilder, List<SourceLibraryBuilder>>
Map<LibraryBuilder, List<LibraryBuilder>>
experimentalInvalidationCreateRebuildBodiesBuilders(
ExperimentalInvalidation experimentalInvalidation,
UriTranslator uriTranslator) {
// Any builder(s) in [rebuildBodies] should be semi-reused: Create source
// builders based on the underlying libraries.
// Maps from old library builder to list of new library builder(s).
Map<LibraryBuilder, List<SourceLibraryBuilder>> rebuildBodiesMap =
new Map<LibraryBuilder, List<SourceLibraryBuilder>>.identity();
Map<LibraryBuilder, List<LibraryBuilder>> rebuildBodiesMap =
new Map<LibraryBuilder, List<LibraryBuilder>>.identity();
if (experimentalInvalidation != null) {
for (LibraryBuilder library in experimentalInvalidation.rebuildBodies) {
LibraryBuilder newBuilder = userCode.loader.read(library.importUri, -1,
accessor: userCode.loader.first,
fileUri: library.fileUri,
referencesFrom: library.library);
List<SourceLibraryBuilder> builders = [newBuilder];
List<LibraryBuilder> builders = [newBuilder];
rebuildBodiesMap[library] = builders;
for (LibraryPart part in library.library.parts) {
// We need to pass the reference to make any class, procedure etc
@@ -492,7 +542,7 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
/// didn't do anything special.
void experimentalInvalidationPatchUpScopes(
ExperimentalInvalidation experimentalInvalidation,
Map<LibraryBuilder, List<SourceLibraryBuilder>> rebuildBodiesMap) {
Map<LibraryBuilder, List<LibraryBuilder>> rebuildBodiesMap) {
if (experimentalInvalidation != null) {
// Maps from old library builder to map of new content.
Map<LibraryBuilder, Map<String, Builder>> replacementMap = {};
@@ -557,8 +607,16 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
}
}
}
} else if (builder is DillLibraryBuilder) {
DillLibraryBuilder dillBuilder = builder;
// There's only something to patch up if it was build already.
if (dillBuilder.isBuilt) {
dillBuilder.exportScope
.patchUpScope(replacementMap, replacementSettersMap);
}
} else {
throw "Currently unsupported";
throw new StateError(
"Unexpected builder: $builder (${builder.runtimeType})");
}
}
}
@@ -581,6 +639,7 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
dillLoadedData,
uriTranslator);
userCode.loader.hierarchy = hierarchy;
dillLoadedData.loader.currentSourceLoader = userCode.loader;
// Re-use the libraries we've deemed re-usable.
for (LibraryBuilder library in reusedLibraries) {
@@ -754,6 +813,10 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
// rebuild the bodies.
for (int i = 0; i < reusedResult.directlyInvalidated.length; i++) {
LibraryBuilder builder = reusedResult.directlyInvalidated[i];
if (builder.library.problemsAsJson != null) {
assert(builder.library.problemsAsJson.isNotEmpty);
return null;
}
Iterator<Builder> iterator = builder.iterator;
while (iterator.moveNext()) {
Builder childBuilder = iterator.current;
@@ -866,6 +929,7 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
// If initializing from a component it has to include the sdk,
// so we explicitly don't load it here.
initializeFromComponent(uriTranslator, c, data);
componentToInitializeFrom = null;
} else {
List<int> summaryBytes = await c.options.loadSdkSummaryBytes();
bytesLength = prepareSummary(summaryBytes, uriTranslator, c, data);
@@ -39,13 +39,14 @@ import 'package:kernel/ast.dart'
Nullability,
Procedure,
ProcedureKind,
Reference,
SetLiteral,
StaticInvocation,
StringLiteral,
Supertype,
Typedef,
TypeParameter,
TypeParameterType,
Typedef,
VariableDeclaration,
VoidType;
@@ -774,13 +775,18 @@ class SourceLibraryBuilder extends LibraryBuilderImpl {
}
}
Builder addBuilder(String name, Builder declaration, int charOffset) {
@override
Builder addBuilder(String name, Builder declaration, int charOffset,
{Reference reference}) {
// TODO(ahe): Set the parent correctly here. Could then change the
// implementation of MemberBuilder.isTopLevel to test explicitly for a
// LibraryBuilder.
if (name == null) {
unhandled("null", "name", charOffset, fileUri);
}
if (reference != null) {
loader.buildersCreatedWithReferences[reference] = declaration;
}
if (currentTypeParameterScopeBuilder == libraryDeclaration) {
if (declaration is MemberBuilder) {
declaration.parent = this;
@@ -1134,9 +1140,12 @@ class SourceLibraryBuilder extends LibraryBuilderImpl {
// DietListener can find them.
for (int i = duplicated.length; i > 0; i--) {
Builder declaration = duplicated[i - 1];
// No reference: There should be no duplicates when using references.
addBuilder(name, declaration, declaration.charOffset);
}
} else {
// No reference: The part is in the same loader so the reference
// - if needed - was already added.
addBuilder(name, declaration, declaration.charOffset);
}
}
@@ -1553,7 +1562,8 @@ class SourceLibraryBuilder extends LibraryBuilderImpl {
members.forEach(setParentAndCheckConflicts);
constructors.forEach(setParentAndCheckConflicts);
setters.forEach(setParentAndCheckConflicts);
addBuilder(className, classBuilder, nameOffset);
addBuilder(className, classBuilder, nameOffset,
reference: referencesFromClass?.reference);
}
Map<String, TypeVariableBuilder> checkTypeVariables(
@@ -1668,7 +1678,8 @@ class SourceLibraryBuilder extends LibraryBuilderImpl {
members.forEach(setParentAndCheckConflicts);
constructors.forEach(setParentAndCheckConflicts);
setters.forEach(setParentAndCheckConflicts);
addBuilder(extensionName, extensionBuilder, nameOffset);
addBuilder(extensionName, extensionBuilder, nameOffset,
reference: referenceFrom?.reference);
}
TypeBuilder applyMixins(TypeBuilder type, int startCharOffset, int charOffset,
@@ -1906,7 +1917,8 @@ class SourceLibraryBuilder extends LibraryBuilderImpl {
// pkg/analyzer/test/src/summary/resynthesize_kernel_test.dart can't
// handle that :(
application.cls.isAnonymousMixin = !isNamedMixinApplication;
addBuilder(fullname, application, charOffset);
addBuilder(fullname, application, charOffset,
reference: referencesFromClass?.reference);
supertype = addNamedType(fullname, const NullabilityBuilder.omitted(),
applicationTypeArguments, charOffset);
}
@@ -2062,7 +2074,8 @@ class SourceLibraryBuilder extends LibraryBuilderImpl {
getterReferenceFrom,
setterReferenceFrom);
fieldBuilder.constInitializerToken = constInitializerToken;
addBuilder(name, fieldBuilder, charOffset);
addBuilder(name, fieldBuilder, charOffset,
reference: referenceFrom?.reference);
if (type == null && initializerToken != null && fieldBuilder.next == null) {
// Only the first one (the last one in the linked list of next pointers)
// are added to the tree, had parent pointers and can infer correctly.
@@ -2114,7 +2127,8 @@ class SourceLibraryBuilder extends LibraryBuilderImpl {
metadataCollector?.setConstructorNameOffset(
constructorBuilder.constructor, name);
checkTypeVariables(typeVariables, constructorBuilder);
addBuilder(constructorName, constructorBuilder, charOffset);
addBuilder(constructorName, constructorBuilder, charOffset,
reference: referenceFrom?.reference);
if (nativeMethodName != null) {
addNativeMethod(constructorBuilder);
}
@@ -2219,7 +2233,8 @@ class SourceLibraryBuilder extends LibraryBuilderImpl {
metadataCollector?.setDocumentationComment(
procedureBuilder.procedure, documentationComment);
checkTypeVariables(typeVariables, procedureBuilder);
addBuilder(name, procedureBuilder, charOffset);
addBuilder(name, procedureBuilder, charOffset,
reference: referenceFrom?.reference);
if (nativeMethodName != null) {
addNativeMethod(procedureBuilder);
}
@@ -2319,7 +2334,8 @@ class SourceLibraryBuilder extends LibraryBuilderImpl {
currentTypeParameterScopeBuilder = savedDeclaration;
factoryDeclaration.resolveTypes(procedureBuilder.typeVariables, this);
addBuilder(procedureName, procedureBuilder, charOffset);
addBuilder(procedureName, procedureBuilder, charOffset,
reference: referenceFrom?.reference);
if (nativeMethodName != null) {
addNativeMethod(procedureBuilder);
}
@@ -2352,7 +2368,8 @@ class SourceLibraryBuilder extends LibraryBuilderImpl {
charEndOffset,
referencesFromClass,
referencesFromIndexedClass);
addBuilder(name, builder, charOffset);
addBuilder(name, builder, charOffset,
reference: referencesFromClass?.reference);
metadataCollector?.setDocumentationComment(
builder.cls, documentationComment);
}
@@ -2379,7 +2396,8 @@ class SourceLibraryBuilder extends LibraryBuilderImpl {
// Nested declaration began in `OutlineBuilder.beginFunctionTypeAlias`.
endNestedDeclaration(TypeParameterScopeKind.typedef, "#typedef")
.resolveTypes(typeVariables, this);
addBuilder(name, typedefBuilder, charOffset);
addBuilder(name, typedefBuilder, charOffset,
reference: referenceFrom?.reference);
}
FunctionTypeBuilder addFunctionType(
@@ -40,6 +40,7 @@ import 'package:kernel/ast.dart'
LibraryDependency,
Nullability,
ProcedureKind,
Reference,
Supertype,
TreeNode;
@@ -152,6 +153,11 @@ class SourceLoader extends Loader {
ClassHierarchy hierarchy;
CoreTypes _coreTypes;
/// For builders created with a reference, this maps from that reference to
/// that builder. This is used for looking up source builders when finalizing
/// exports in dill builders.
Map<Reference, Builder> buildersCreatedWithReferences = {};
/// Used when checking whether a return type of an async function is valid.
///
/// The said return type is valid if it's a subtype of [futureOfBottom].
@@ -9,6 +9,9 @@ import 'dart:io' show File;
import 'package:_fe_analyzer_shared/src/parser/class_member_parser.dart'
show ClassMemberParser;
import 'package:_fe_analyzer_shared/src/parser/declaration_kind.dart'
show DeclarationKind;
import 'package:_fe_analyzer_shared/src/scanner/scanner.dart'
show ErrorToken, LanguageVersionToken, Scanner;
@@ -47,9 +50,12 @@ String textualOutline(List<int> rawBytes, {bool makeMoreReadable: false}) {
if (token is ErrorToken) {
return null;
}
if (printed && token.offset > endOfLast) {
if (addLinebreak) {
sb.write("\n");
} else if (printed && token.offset > endOfLast) {
sb.write(" ");
}
addLinebreak = false;
sb.write(token.lexeme);
printed = true;
@@ -58,19 +64,19 @@ String textualOutline(List<int> rawBytes, {bool makeMoreReadable: false}) {
if (token.lexeme == ";") {
addLinebreak = true;
} else if (token.endGroup != null &&
listener.endOffsets.contains(token.endGroup.offset)) {
(listener.nonClassEndOffsets.contains(token.endGroup.offset) ||
listener.classEndOffsets.contains(token.endGroup.offset))) {
addLinebreak = true;
} else if (listener.endOffsets.contains(token.offset)) {
} else if (listener.nonClassEndOffsets.contains(token.offset) ||
listener.classEndOffsets.contains(token.offset)) {
addLinebreak = true;
}
}
if (addLinebreak) sb.write("\n");
addLinebreak = false;
if (token.isEof) break;
if (token.endGroup != null &&
listener.endOffsets.contains(token.endGroup.offset)) {
listener.nonClassEndOffsets.contains(token.endGroup.offset)) {
token = token.endGroup;
} else {
token = token.next;
@@ -86,17 +92,30 @@ main(List<String> args) {
}
class EndOffsetListener extends DirectiveListener {
Set<int> endOffsets = new Set<int>();
Set<int> nonClassEndOffsets = new Set<int>();
Set<int> classEndOffsets = new Set<int>();
@override
void endClassMethod(Token getOrSet, Token beginToken, Token beginParam,
Token beginInitializers, Token endToken) {
endOffsets.add(endToken.offset);
nonClassEndOffsets.add(endToken.offset);
}
@override
void endTopLevelMethod(Token beginToken, Token getOrSet, Token endToken) {
endOffsets.add(endToken.offset);
nonClassEndOffsets.add(endToken.offset);
}
@override
void endClassFactoryMethod(
Token beginToken, Token factoryKeyword, Token endToken) {
nonClassEndOffsets.add(endToken.offset);
}
@override
void endClassOrMixinBody(
DeclarationKind kind, int memberCount, Token beginToken, Token endToken) {
classEndOffsets.add(endToken.offset);
}
@override
@@ -31,58 +31,50 @@ main(List<String> args) async {
}
}
Stopwatch stopwatch = new Stopwatch()..start();
Uri input = Platform.script.resolve("../../compiler/bin/dart2js.dart");
CompilerOptions options = helper.getOptions(targetName: "VM");
helper.TestIncrementalCompiler compiler =
new helper.TestIncrementalCompiler(options, input);
compiler.useExperimentalInvalidation = useExperimentalInvalidation;
Component c = await compiler.computeDelta();
print("Compiled dart2js to Component with ${c.libraries.length} libraries "
"in ${stopwatch.elapsedMilliseconds} ms.");
stopwatch.reset();
Dart2jsTester dart2jsTester =
new Dart2jsTester(useExperimentalInvalidation, fast, addDebugBreaks);
await dart2jsTester.test();
}
class Dart2jsTester {
final bool useExperimentalInvalidation;
final bool fast;
final bool addDebugBreaks;
Stopwatch stopwatch = new Stopwatch();
List<int> firstCompileData;
Map<Uri, List<int>> libToData;
if (fast) {
libToData = {};
c.libraries.sort((l1, l2) {
return "${l1.fileUri}".compareTo("${l2.fileUri}");
});
c.problemsAsJson?.sort();
c.computeCanonicalNames();
for (Library library in c.libraries) {
library.additionalExports.sort((Reference r1, Reference r2) {
return "${r1.canonicalName}".compareTo("${r2.canonicalName}");
});
library.problemsAsJson?.sort();
List<int> libSerialized =
serializeComponent(c, filter: (l) => l == library);
libToData[library.importUri] = libSerialized;
}
} else {
firstCompileData = util.postProcess(c);
}
print("Serialized in ${stopwatch.elapsedMilliseconds} ms");
stopwatch.reset();
List<Uri> uris = c.uriToSource.values
.map((s) => s != null ? s.importUri : null)
.where((u) => u != null && u.scheme != "dart")
.toSet()
.toList();
c = null;
List<Uri> uris;
List<Uri> diffs = new List<Uri>();
Set<Uri> componentUris = new Set<Uri>();
Stopwatch localStopwatch = new Stopwatch()..start();
for (int i = 0; i < uris.length; i++) {
Uri uri = uris[i];
Dart2jsTester(
this.useExperimentalInvalidation, this.fast, this.addDebugBreaks);
void test() async {
helper.TestIncrementalCompiler compiler = await setup();
diffs = new List<Uri>();
componentUris = new Set<Uri>();
Stopwatch localStopwatch = new Stopwatch()..start();
for (int i = 0; i < uris.length; i++) {
Uri uri = uris[i];
await step(uri, i, compiler, localStopwatch);
}
print("A total of ${diffs.length} diffs:");
for (Uri uri in diffs) {
print(" - $uri");
}
print("Done after ${uris.length} recompiles in "
"${stopwatch.elapsedMilliseconds} ms");
}
Future step(Uri uri, int i, helper.TestIncrementalCompiler compiler,
Stopwatch localStopwatch) async {
print("Invalidating $uri ($i)");
compiler.invalidate(uri);
localStopwatch.reset();
@@ -168,24 +160,65 @@ main(List<String> args) async {
print("-----");
}
print("A total of ${diffs.length} diffs:");
for (Uri uri in diffs) {
print(" - $uri");
Future<helper.TestIncrementalCompiler> setup() async {
stopwatch.reset();
stopwatch.start();
Uri input = Platform.script.resolve("../../compiler/bin/dart2js.dart");
CompilerOptions options = helper.getOptions(targetName: "VM");
helper.TestIncrementalCompiler compiler =
new helper.TestIncrementalCompiler(options, input);
compiler.useExperimentalInvalidation = useExperimentalInvalidation;
Component c = await compiler.computeDelta();
print("Compiled dart2js to Component with ${c.libraries.length} libraries "
"in ${stopwatch.elapsedMilliseconds} ms.");
stopwatch.reset();
if (fast) {
libToData = {};
c.libraries.sort((l1, l2) {
return "${l1.fileUri}".compareTo("${l2.fileUri}");
});
c.problemsAsJson?.sort();
c.computeCanonicalNames();
for (Library library in c.libraries) {
library.additionalExports.sort((Reference r1, Reference r2) {
return "${r1.canonicalName}".compareTo("${r2.canonicalName}");
});
library.problemsAsJson?.sort();
List<int> libSerialized =
serializeComponent(c, filter: (l) => l == library);
libToData[library.importUri] = libSerialized;
}
} else {
firstCompileData = util.postProcess(c);
}
print("Serialized in ${stopwatch.elapsedMilliseconds} ms");
stopwatch.reset();
uris = c.uriToSource.values
.map((s) => s != null ? s.importUri : null)
.where((u) => u != null && u.scheme != "dart")
.toSet()
.toList();
c = null;
return compiler;
}
print("Done after ${uris.length} recompiles in "
"${stopwatch.elapsedMilliseconds} ms");
}
bool isEqual(List<int> a, List<int> b) {
int length = a.length;
if (b.length != length) {
return false;
}
for (int i = 0; i < length; ++i) {
if (a[i] != b[i]) {
bool isEqual(List<int> a, List<int> b) {
int length = a.length;
if (b.length != length) {
return false;
}
for (int i = 0; i < length; ++i) {
if (a[i] != b[i]) {
return false;
}
}
return true;
}
return true;
}
File diff suppressed because it is too large Load Diff
@@ -122,6 +122,7 @@ deleting
depended
depfile
desc
detector
deviation
dfast
dictionaries
@@ -138,6 +139,7 @@ disallowed
disconnect
discovering
dispatcher
dispose
dist
doctype
doesnt
@@ -185,6 +187,7 @@ finder
fisk
five
floor
foos
forbidden
forces
foreign
@@ -381,6 +384,7 @@ splay
splitting
sqrt
sssp
stats
std
stdio
strip
@@ -26,11 +26,23 @@ main(List<String> args) async {
["_extension"]),
new helper.Interest(Uri.parse("package:kernel/ast.dart"), "Extension",
["name", "fileUri"]),
new helper.Interest(Uri.parse("package:kernel/ast.dart"), "Library",
["fileUri", "_libraryIdString"]),
],
true);
// heapHelper.start([
// "--enable-asserts",
// Platform.script.resolve("incremental_dart2js_tester.dart").toString(),
// "--addDebugBreaks",
// "--fast",
// "--experimental",
// ]);
heapHelper.start([
Platform.script.resolve("incremental_dart2js_tester.dart").toString(),
"--fast",
"--addDebugBreaks",
"--enable-asserts",
Platform.script.resolve("incremental_load_from_dill_suite.dart").toString(),
"-DaddDebugBreaks=true",
// "--",
// "incremental_load_from_dill/no_outline_change_...",
]);
}
@@ -0,0 +1,40 @@
import "dart:io";
import "vm_service_heap_helper.dart";
class Foo {
final String x;
final int y;
Foo(this.x, this.y);
}
main() async {
List<Foo> foos = [];
foos.add(new Foo("hello", 42));
foos.add(new Foo("world", 43));
foos.add(new Foo("!", 44));
String connectTo = ask("Connect to");
VMServiceHeapHelperBase vm = VMServiceHeapHelperBase();
await vm.connect(Uri.parse(connectTo));
String isolateId = await vm.getIsolateId();
String classToFind = ask("Find what class");
await vm.printAllocationProfile(isolateId, filter: classToFind);
String fieldToFilter = ask("Filter on what field");
Set<String> fieldValues = {};
while (true) {
String fieldValue = ask("Look for value in field (empty to stop)");
if (fieldValue == "") break;
fieldValues.add(fieldValue);
}
await vm.filterAndPrintInstances(
isolateId, classToFind, fieldToFilter, fieldValues);
await vm.disconnect();
print("Disconnect done!");
}
String ask(String question) {
stdout.write("$question: ");
return stdin.readLineSync();
}
+331 -216
View File
@@ -1,4 +1,5 @@
import "dart:convert";
import "dart:developer";
import "dart:io";
import "package:vm_service/vm_service.dart" as vmService;
@@ -6,230 +7,20 @@ import "package:vm_service/vm_service_io.dart" as vmService;
import "dijkstras_sssp_algorithm.dart";
class VMServiceHeapHelper {
Process _process;
class VMServiceHeapHelperBase {
vmService.VmService _serviceClient;
bool _started = false;
final Map<Uri, Map<String, List<String>>> _interests =
new Map<Uri, Map<String, List<String>>>();
final Map<Uri, Map<String, List<String>>> _prettyPrints =
new Map<Uri, Map<String, List<String>>>();
final bool throwOnPossibleLeak;
VMServiceHeapHelper(List<Interest> interests, List<Interest> prettyPrints,
this.throwOnPossibleLeak) {
if (interests.isEmpty) throw "Empty list of interests given";
for (Interest interest in interests) {
Map<String, List<String>> classToFields = _interests[interest.uri];
if (classToFields == null) {
classToFields = Map<String, List<String>>();
_interests[interest.uri] = classToFields;
}
List<String> fields = classToFields[interest.className];
if (fields == null) {
fields = new List<String>();
classToFields[interest.className] = fields;
}
fields.addAll(interest.fieldNames);
}
for (Interest interest in prettyPrints) {
Map<String, List<String>> classToFields = _prettyPrints[interest.uri];
if (classToFields == null) {
classToFields = Map<String, List<String>>();
_prettyPrints[interest.uri] = classToFields;
}
List<String> fields = classToFields[interest.className];
if (fields == null) {
fields = new List<String>();
classToFields[interest.className] = fields;
}
fields.addAll(interest.fieldNames);
}
}
VMServiceHeapHelperBase();
void start(List<String> scriptAndArgs) async {
if (_started) throw "Already started";
_started = true;
_process = await Process.start(
Platform.resolvedExecutable,
["--pause_isolates_on_start", "--enable-vm-service=0"]
..addAll(scriptAndArgs));
_process.stdout
.transform(utf8.decoder)
.transform(new LineSplitter())
.listen((line) {
const kObservatoryListening = 'Observatory listening on ';
if (line.startsWith(kObservatoryListening)) {
Uri observatoryUri =
Uri.parse(line.substring(kObservatoryListening.length));
_gotObservatoryUri(observatoryUri);
}
stdout.writeln("> $line");
});
_process.stderr
.transform(utf8.decoder)
.transform(new LineSplitter())
.listen((line) {
stderr.writeln("> $line");
});
}
void _gotObservatoryUri(Uri observatoryUri) async {
Future connect(Uri observatoryUri) async {
String wsUriString =
'ws://${observatoryUri.authority}${observatoryUri.path}ws';
_serviceClient = await vmService.vmServiceConnectUri(wsUriString,
log: const StdOutLog());
await _run();
}
void _run() async {
vmService.VM vm = await _serviceClient.getVM();
if (vm.isolates.length != 1) {
throw "Expected 1 isolate, got ${vm.isolates.length}";
}
vmService.IsolateRef isolateRef = vm.isolates.single;
await _forceGC(isolateRef.id);
assert(await _isPausedAtStart(isolateRef.id));
await _serviceClient.resume(isolateRef.id);
int iterationNumber = 1;
while (true) {
await _waitUntilPaused(isolateRef.id);
print("Iteration: #$iterationNumber");
iterationNumber++;
await _forceGC(isolateRef.id);
vmService.HeapSnapshotGraph heapSnapshotGraph =
await vmService.HeapSnapshotGraph.getSnapshot(
_serviceClient, isolateRef);
HeapGraph graph = convertHeapGraph(heapSnapshotGraph);
Set<String> seenPrints = {};
Set<String> duplicatePrints = {};
Map<String, List<HeapGraphElement>> groupedByToString = {};
for (HeapGraphClassActual c in graph.classes) {
Map<String, List<String>> interests = _interests[c.libraryUri];
if (interests != null && interests.isNotEmpty) {
List<String> fieldsToUse = interests[c.name];
if (fieldsToUse != null && fieldsToUse.isNotEmpty) {
for (HeapGraphElement instance in c.instances) {
StringBuffer sb = new StringBuffer();
sb.writeln("Instance: ${instance}");
if (instance is HeapGraphElementActual) {
for (String fieldName in fieldsToUse) {
String prettyPrinted = instance
.getField(fieldName)
.getPrettyPrint(_prettyPrints);
sb.writeln(" $fieldName: "
"${prettyPrinted}");
}
}
String sbToString = sb.toString();
if (!seenPrints.add(sbToString)) {
duplicatePrints.add(sbToString);
}
groupedByToString[sbToString] ??= [];
groupedByToString[sbToString].add(instance);
}
}
}
}
if (duplicatePrints.isNotEmpty) {
print("======================================");
print("WARNING: Duplicated pretty prints of objects.");
print("This might be a memory leak!");
print("");
for (String s in duplicatePrints) {
int count = groupedByToString[s].length;
print("$s ($count)");
print("");
}
print("======================================");
for (String duplicateString in duplicatePrints) {
print("$duplicateString:");
List<HeapGraphElement> Function(HeapGraphElement target)
dijkstraTarget = dijkstra(graph.elements.first, graph);
for (HeapGraphElement duplicate
in groupedByToString[duplicateString]) {
print("${duplicate} pointed to from:");
List<HeapGraphElement> shortestPath = dijkstraTarget(duplicate);
for (int i = 0; i < shortestPath.length - 1; i++) {
HeapGraphElement thisOne = shortestPath[i];
HeapGraphElement nextOne = shortestPath[i + 1];
String indexFieldName;
if (thisOne is HeapGraphElementActual) {
HeapGraphClass c = thisOne.class_;
if (c is HeapGraphClassActual) {
for (vmService.HeapSnapshotField field in c.origin.fields) {
if (thisOne.references[field.index] == nextOne) {
indexFieldName = field.name;
}
}
}
}
if (indexFieldName == null) {
indexFieldName = "no field found; index "
"${thisOne.references.indexOf(nextOne)}";
}
print(" $thisOne -> $nextOne ($indexFieldName)");
}
print("---------------------------");
}
}
if (throwOnPossibleLeak) throw "Possible leak detected.";
}
await _serviceClient.resume(isolateRef.id);
}
}
List<HeapGraphElement> Function(HeapGraphElement target) dijkstra(
HeapGraphElement source, HeapGraph heapGraph) {
Map<HeapGraphElement, int> elementNum = {};
Map<HeapGraphElement, GraphNode<HeapGraphElement>> elements = {};
elements[heapGraph.elementSentinel] =
new GraphNode<HeapGraphElement>(heapGraph.elementSentinel);
elementNum[heapGraph.elementSentinel] = elements.length;
for (HeapGraphElementActual element in heapGraph.elements) {
elements[element] = new GraphNode<HeapGraphElement>(element);
elementNum[element] = elements.length;
}
for (HeapGraphElementActual element in heapGraph.elements) {
GraphNode<HeapGraphElement> node = elements[element];
for (HeapGraphElement out in element.references) {
node.addOutgoing(elements[out]);
}
}
DijkstrasAlgorithm<HeapGraphElement> result =
new DijkstrasAlgorithm<HeapGraphElement>(
elements.values,
elements[source],
(HeapGraphElement a, HeapGraphElement b) {
if (identical(a, b)) {
throw "Comparing two identical ones was unexpected";
}
return elementNum[a] - elementNum[b];
},
(HeapGraphElement a, HeapGraphElement b) {
if (identical(a, b)) return 0;
// Prefer not to go via sentinel and via "Context".
if (b is HeapGraphElementSentinel) return 100;
HeapGraphElementActual bb = b;
if (bb.class_ is HeapGraphClassSentinel) return 100;
HeapGraphClassActual c = bb.class_;
if (c.name == "Context") {
if (c.libraryUri.toString().isEmpty) return 100;
}
return 1;
},
);
return (HeapGraphElement target) {
return result.getPathFromTarget(elements[source], elements[target]);
};
Future disconnect() async {
await _serviceClient.dispose();
}
Future<void> _waitUntilPaused(String isolateId) async {
@@ -310,6 +101,329 @@ class VMServiceHeapHelper {
}
}
}
Future<void> printAllocationProfile(String isolateId, {String filter}) async {
await _waitUntilIsolateIsRunnable(isolateId);
vmService.AllocationProfile allocationProfile =
await _serviceClient.getAllocationProfile(isolateId);
for (vmService.ClassHeapStats member in allocationProfile.members) {
if (filter != null) {
if (member.classRef.name != filter) continue;
} else {
if (member.classRef.name == "") continue;
if (member.instancesCurrent == 0) continue;
}
vmService.Class c =
await _serviceClient.getObject(isolateId, member.classRef.id);
if (c.location?.script?.uri == null) continue;
print("${member.classRef.name}: ${member.instancesCurrent}");
}
}
Future<void> filterAndPrintInstances(String isolateId, String filter,
String fieldName, Set<String> fieldValues) async {
await _waitUntilIsolateIsRunnable(isolateId);
vmService.AllocationProfile allocationProfile =
await _serviceClient.getAllocationProfile(isolateId);
for (vmService.ClassHeapStats member in allocationProfile.members) {
if (member.classRef.name != filter) continue;
vmService.Class c =
await _serviceClient.getObject(isolateId, member.classRef.id);
if (c.location?.script?.uri == null) continue;
print("${member.classRef.name}: ${member.instancesCurrent}");
print(c.location.script.uri);
vmService.InstanceSet instances = await _serviceClient.getInstances(
isolateId, member.classRef.id, 10000);
int instanceNum = 0;
for (vmService.ObjRef instance in instances.instances) {
instanceNum++;
var receivedObject =
await _serviceClient.getObject(isolateId, instance.id);
if (receivedObject is! vmService.Instance) continue;
vmService.Instance object = receivedObject;
for (vmService.BoundField field in object.fields) {
if (field.decl.name == fieldName) {
if (field.value is vmService.Sentinel) continue;
var receivedValue =
await _serviceClient.getObject(isolateId, field.value.id);
if (receivedValue is! vmService.Instance) continue;
String value = (receivedValue as vmService.Instance).valueAsString;
if (!fieldValues.contains(value)) continue;
print("${instanceNum}: ${field.decl.name}: "
"${value} --- ${instance.id}");
}
}
}
}
print("Done!");
}
Future<String> getIsolateId() async {
vmService.VM vm = await _serviceClient.getVM();
if (vm.isolates.length != 1) {
throw "Expected 1 isolate, got ${vm.isolates.length}";
}
vmService.IsolateRef isolateRef = vm.isolates.single;
return isolateRef.id;
}
}
class VMServiceHeapHelper extends VMServiceHeapHelperBase {
Process _process;
bool _started = false;
final Map<Uri, Map<String, List<String>>> _interests =
new Map<Uri, Map<String, List<String>>>();
final Map<Uri, Map<String, List<String>>> _prettyPrints =
new Map<Uri, Map<String, List<String>>>();
final bool throwOnPossibleLeak;
VMServiceHeapHelper(List<Interest> interests, List<Interest> prettyPrints,
this.throwOnPossibleLeak) {
if (interests.isEmpty) throw "Empty list of interests given";
for (Interest interest in interests) {
Map<String, List<String>> classToFields = _interests[interest.uri];
if (classToFields == null) {
classToFields = Map<String, List<String>>();
_interests[interest.uri] = classToFields;
}
List<String> fields = classToFields[interest.className];
if (fields == null) {
fields = new List<String>();
classToFields[interest.className] = fields;
}
fields.addAll(interest.fieldNames);
}
for (Interest interest in prettyPrints) {
Map<String, List<String>> classToFields = _prettyPrints[interest.uri];
if (classToFields == null) {
classToFields = Map<String, List<String>>();
_prettyPrints[interest.uri] = classToFields;
}
List<String> fields = classToFields[interest.className];
if (fields == null) {
fields = new List<String>();
classToFields[interest.className] = fields;
}
fields.addAll(interest.fieldNames);
}
}
void start(List<String> scriptAndArgs) async {
if (_started) throw "Already started";
_started = true;
_process = await Process.start(
Platform.resolvedExecutable,
["--pause_isolates_on_start", "--enable-vm-service=0"]
..addAll(scriptAndArgs));
_process.stdout
.transform(utf8.decoder)
.transform(new LineSplitter())
.listen((line) {
const kObservatoryListening = 'Observatory listening on ';
if (line.startsWith(kObservatoryListening)) {
Uri observatoryUri =
Uri.parse(line.substring(kObservatoryListening.length));
_setupAndRun(observatoryUri);
}
stdout.writeln("> $line");
});
_process.stderr
.transform(utf8.decoder)
.transform(new LineSplitter())
.listen((line) {
stderr.writeln("> $line");
});
}
void _setupAndRun(Uri observatoryUri) async {
await connect(observatoryUri);
await _run();
}
void _run() async {
vmService.VM vm = await _serviceClient.getVM();
if (vm.isolates.length != 1) {
throw "Expected 1 isolate, got ${vm.isolates.length}";
}
vmService.IsolateRef isolateRef = vm.isolates.single;
await _forceGC(isolateRef.id);
assert(await _isPausedAtStart(isolateRef.id));
await _serviceClient.resume(isolateRef.id);
int iterationNumber = 1;
while (true) {
await _waitUntilPaused(isolateRef.id);
print("Iteration: #$iterationNumber");
iterationNumber++;
await _forceGC(isolateRef.id);
vmService.HeapSnapshotGraph heapSnapshotGraph =
await vmService.HeapSnapshotGraph.getSnapshot(
_serviceClient, isolateRef);
HeapGraph graph = convertHeapGraph(heapSnapshotGraph);
Set<String> seenPrints = {};
Set<String> duplicatePrints = {};
Map<String, List<HeapGraphElement>> groupedByToString = {};
for (HeapGraphClassActual c in graph.classes) {
Map<String, List<String>> interests = _interests[c.libraryUri];
if (interests != null && interests.isNotEmpty) {
List<String> fieldsToUse = interests[c.name];
if (fieldsToUse != null && fieldsToUse.isNotEmpty) {
for (HeapGraphElement instance in c.instances) {
StringBuffer sb = new StringBuffer();
sb.writeln("Instance: ${instance}");
if (instance is HeapGraphElementActual) {
for (String fieldName in fieldsToUse) {
String prettyPrinted = instance
.getField(fieldName)
.getPrettyPrint(_prettyPrints);
sb.writeln(" $fieldName: "
"${prettyPrinted}");
}
}
String sbToString = sb.toString();
if (!seenPrints.add(sbToString)) {
duplicatePrints.add(sbToString);
}
groupedByToString[sbToString] ??= [];
groupedByToString[sbToString].add(instance);
}
}
}
}
if (duplicatePrints.isNotEmpty) {
print("======================================");
print("WARNING: Duplicated pretty prints of objects.");
print("This might be a memory leak!");
print("");
for (String s in duplicatePrints) {
int count = groupedByToString[s].length;
print("$s ($count)");
print("");
}
print("======================================");
for (String duplicateString in duplicatePrints) {
print("$duplicateString:");
List<HeapGraphElement> Function(HeapGraphElement target)
dijkstraTarget = dijkstra(graph.elements.first, graph);
for (HeapGraphElement duplicate
in groupedByToString[duplicateString]) {
print("${duplicate} pointed to from:");
print(duplicate.getPrettyPrint(_prettyPrints));
List<HeapGraphElement> shortestPath = dijkstraTarget(duplicate);
for (int i = 0; i < shortestPath.length - 1; i++) {
HeapGraphElement thisOne = shortestPath[i];
HeapGraphElement nextOne = shortestPath[i + 1];
String indexFieldName;
if (thisOne is HeapGraphElementActual) {
HeapGraphClass c = thisOne.class_;
if (c is HeapGraphClassActual) {
for (vmService.HeapSnapshotField field in c.origin.fields) {
if (thisOne.references[field.index] == nextOne) {
indexFieldName = field.name;
}
}
}
}
if (indexFieldName == null) {
indexFieldName = "no field found; index "
"${thisOne.references.indexOf(nextOne)}";
}
print(" $thisOne -> $nextOne ($indexFieldName)");
}
print("---------------------------");
}
}
if (throwOnPossibleLeak) {
debugger();
throw "Possible leak detected.";
}
}
await _serviceClient.resume(isolateRef.id);
}
}
List<HeapGraphElement> Function(HeapGraphElement target) dijkstra(
HeapGraphElement source, HeapGraph heapGraph) {
Map<HeapGraphElement, int> elementNum = {};
Map<HeapGraphElement, GraphNode<HeapGraphElement>> elements = {};
elements[heapGraph.elementSentinel] =
new GraphNode<HeapGraphElement>(heapGraph.elementSentinel);
elementNum[heapGraph.elementSentinel] = elements.length;
for (HeapGraphElementActual element in heapGraph.elements) {
elements[element] = new GraphNode<HeapGraphElement>(element);
elementNum[element] = elements.length;
}
for (HeapGraphElementActual element in heapGraph.elements) {
GraphNode<HeapGraphElement> node = elements[element];
for (HeapGraphElement out in element.references) {
node.addOutgoing(elements[out]);
}
}
DijkstrasAlgorithm<HeapGraphElement> result =
new DijkstrasAlgorithm<HeapGraphElement>(
elements.values,
elements[source],
(HeapGraphElement a, HeapGraphElement b) {
if (identical(a, b)) {
throw "Comparing two identical ones was unexpected";
}
return elementNum[a] - elementNum[b];
},
(HeapGraphElement a, HeapGraphElement b) {
if (identical(a, b)) return 0;
// Prefer going via actual field.
if (a is HeapGraphElementActual) {
HeapGraphClass c = a.class_;
if (c is HeapGraphClassActual) {
for (vmService.HeapSnapshotField field in c.origin.fields) {
if (a.references[field.index] == b) {
// Via actual field!
return 1;
}
}
}
}
// Prefer not to go directly from HeapGraphClassSentinel to Procedure.
if (a is HeapGraphElementActual && b is HeapGraphElementActual) {
HeapGraphElementActual aa = a;
HeapGraphElementActual bb = b;
if (aa.class_ is HeapGraphClassSentinel &&
bb.class_ is HeapGraphClassActual) {
HeapGraphClassActual c = bb.class_;
if (c.name == "Procedure") {
return 1000;
}
}
}
// Prefer not to go via sentinel and via "Context".
if (b is HeapGraphElementSentinel) return 100;
HeapGraphElementActual bb = b;
if (bb.class_ is HeapGraphClassSentinel) return 100;
HeapGraphClassActual c = bb.class_;
if (c.name == "Context") {
if (c.libraryUri.toString().isEmpty) return 100;
}
// Not via actual field.
return 10;
},
);
return (HeapGraphElement target) {
return result.getPathFromTarget(elements[source], elements[target]);
};
}
}
class Interest {
@@ -412,7 +526,8 @@ abstract class HeapGraphElement {
if (fields != null) {
return "${c.name}[" +
fields.map((field) {
return "$field: ${me.getField(field).getPrettyPrint(prettyPrints)}";
return "$field: "
"${me.getField(field).getPrettyPrint(prettyPrints)}";
}).join(", ") +
"]";
}
@@ -41,4 +41,4 @@ worlds:
print("exports!")
}
expectedLibraryCount: 3
expectsRebuildBodiesOnly: true
expectsRebuildBodiesOnly: false # For now, libraries with errors cannot have bodies rebuild.
@@ -48,4 +48,4 @@ worlds:
}
}
expectedLibraryCount: 1
expectsRebuildBodiesOnly: true
expectsRebuildBodiesOnly: false # For now, libraries with errors cannot have bodies rebuild.
@@ -40,4 +40,4 @@ worlds:
new A1.foo();
}
expectedLibraryCount: 1
expectsRebuildBodiesOnly: true
expectsRebuildBodiesOnly: false # For now, libraries with errors cannot have bodies rebuild.
@@ -95,7 +95,6 @@ worlds:
}
expectedLibraryCount: 3
expectsRebuildBodiesOnly: true
- entry: main.dart
useExperimentalInvalidation: true
worldType: updated
@@ -1,6 +1,8 @@
main = <No Member>;
library from "org-dartlang-test:///lib1.dart" as lib1 {
additionalExports = (main::main,
additionalExports = (main::Extension|get#method,
main::Extension|method,
main::main,
main::Class,
main::Extension)
@@ -0,0 +1,81 @@
# Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
# for details. All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE.md file.
# Compile an application, change a file, but don't change the outline.
# Test FFI compilation.
type: newworld
worlds:
- entry: main.dart
useExperimentalInvalidation: true
sources:
main.dart: |
import 'lib.dart';
main() {
Coordinate coordinate = new Coordinate.allocate(42.0, 42.0, null);
print(coordinate.x);
print(coordinate.y);
print(coordinate.next);
}
lib.dart: |
import 'dart:ffi';
class Coordinate extends Struct {
@Double()
double x;
@Double()
double y;
Pointer<Coordinate> next;
factory Coordinate.allocate(double x, double y, Pointer<Coordinate> next) {
return null;
}
}
expectedLibraryCount: 2
- entry: main.dart
useExperimentalInvalidation: true
worldType: updated
expectInitializeFromDill: false
invalidate:
- main.dart
sources:
main.dart: |
import 'lib.dart';
main() {
Coordinate coordinate = new Coordinate.allocate(42.0, 42.0, null);
print(coordinate.x);
print(coordinate.y);
print(coordinate.next);
print("Done!");
}
expectedLibraryCount: 2
expectsRebuildBodiesOnly: true
- entry: main.dart
useExperimentalInvalidation: true
worldType: updated
expectInitializeFromDill: false
invalidate:
- lib.dart
sources:
lib.dart: |
import 'dart:ffi';
class Coordinate extends Struct {
@Double()
double x;
@Double()
double y;
Pointer<Coordinate> next;
factory Coordinate.allocate(double x, double y, Pointer<Coordinate> next) {
print("hello");
return null;
}
}
expectedLibraryCount: 2
expectsRebuildBodiesOnly: true
@@ -0,0 +1,60 @@
main = <No Member>;
library from "org-dartlang-test:///lib.dart" as lib {
import "dart:ffi";
@#C3
class Coordinate extends dart.ffi::Struct {
@#C3
static final field dart.core::int* #sizeOf = (#C6).{dart.core::List::[]}(dart.ffi::_abi());
@#C3
constructor #fromPointer(dynamic #pointer) → dynamic
: super dart.ffi::Struct::_fromPointer(#pointer)
;
static factory allocate(dart.core::double* x, dart.core::double* y, dart.ffi::Pointer<lib::Coordinate*>* next) → lib::Coordinate* {
return null;
}
get #_ptr_x() → dart.ffi::Pointer<dart.ffi::Double*>*
return this.{dart.ffi::Struct::_addressOf}.{dart.ffi::Pointer::cast}<dart.ffi::Double*>();
get x() → dart.core::double*
return dart.ffi::_loadDouble(this.{lib::Coordinate::#_ptr_x}, #C7);
set x(dart.core::double* #v) → void
return dart.ffi::_storeDouble(this.{lib::Coordinate::#_ptr_x}, #C7, #v);
get #_ptr_y() → dart.ffi::Pointer<dart.ffi::Double*>*
return this.{dart.ffi::Struct::_addressOf}.{dart.ffi::Pointer::_offsetBy}((#C9).{dart.core::List::[]}(dart.ffi::_abi())).{dart.ffi::Pointer::cast}<dart.ffi::Double*>();
get y() → dart.core::double*
return dart.ffi::_loadDouble(this.{lib::Coordinate::#_ptr_y}, #C7);
set y(dart.core::double* #v) → void
return dart.ffi::_storeDouble(this.{lib::Coordinate::#_ptr_y}, #C7, #v);
get #_ptr_next() → dart.ffi::Pointer<dart.ffi::Pointer<lib::Coordinate*>*>*
return this.{dart.ffi::Struct::_addressOf}.{dart.ffi::Pointer::_offsetBy}((#C11).{dart.core::List::[]}(dart.ffi::_abi())).{dart.ffi::Pointer::cast}<dart.ffi::Pointer<lib::Coordinate*>*>();
get next() → dart.ffi::Pointer<lib::Coordinate*>*
return dart.ffi::_loadPointer<dart.ffi::Pointer<lib::Coordinate*>*>(this.{lib::Coordinate::#_ptr_next}, #C7);
set next(dart.ffi::Pointer<lib::Coordinate*>* #v) → void
return dart.ffi::_storePointer<dart.ffi::Pointer<lib::Coordinate*>*>(this.{lib::Coordinate::#_ptr_next}, #C7, #v);
}
}
library from "org-dartlang-test:///main.dart" as main {
import "org-dartlang-test:///lib.dart";
static method main() → dynamic {
lib::Coordinate* coordinate = lib::Coordinate::allocate(42.0, 42.0, null);
dart.core::print(coordinate.{lib::Coordinate::x});
dart.core::print(coordinate.{lib::Coordinate::y});
dart.core::print(coordinate.{lib::Coordinate::next});
}
}
constants {
#C1 = "vm:entry-point"
#C2 = null
#C3 = dart.core::pragma {name:#C1, options:#C2}
#C4 = 24
#C5 = 20
#C6 = <dart.core::int*>[#C4, #C5, #C4]
#C7 = 0
#C8 = 8
#C9 = <dart.core::int*>[#C8, #C8, #C8]
#C10 = 16
#C11 = <dart.core::int*>[#C10, #C10, #C10]
}
@@ -0,0 +1,61 @@
main = <No Member>;
library from "org-dartlang-test:///lib.dart" as lib {
import "dart:ffi";
@#C3
class Coordinate extends dart.ffi::Struct {
@#C3
static final field dart.core::int* #sizeOf = (#C6).{dart.core::List::[]}(dart.ffi::_abi());
@#C3
constructor #fromPointer(dynamic #pointer) → dynamic
: super dart.ffi::Struct::_fromPointer(#pointer)
;
static factory allocate(dart.core::double* x, dart.core::double* y, dart.ffi::Pointer<lib::Coordinate*>* next) → lib::Coordinate* {
return null;
}
get #_ptr_x() → dart.ffi::Pointer<dart.ffi::Double*>*
return this.{dart.ffi::Struct::_addressOf}.{dart.ffi::Pointer::cast}<dart.ffi::Double*>();
get x() → dart.core::double*
return dart.ffi::_loadDouble(this.{lib::Coordinate::#_ptr_x}, #C7);
set x(dart.core::double* #v) → void
return dart.ffi::_storeDouble(this.{lib::Coordinate::#_ptr_x}, #C7, #v);
get #_ptr_y() → dart.ffi::Pointer<dart.ffi::Double*>*
return this.{dart.ffi::Struct::_addressOf}.{dart.ffi::Pointer::_offsetBy}((#C9).{dart.core::List::[]}(dart.ffi::_abi())).{dart.ffi::Pointer::cast}<dart.ffi::Double*>();
get y() → dart.core::double*
return dart.ffi::_loadDouble(this.{lib::Coordinate::#_ptr_y}, #C7);
set y(dart.core::double* #v) → void
return dart.ffi::_storeDouble(this.{lib::Coordinate::#_ptr_y}, #C7, #v);
get #_ptr_next() → dart.ffi::Pointer<dart.ffi::Pointer<lib::Coordinate*>*>*
return this.{dart.ffi::Struct::_addressOf}.{dart.ffi::Pointer::_offsetBy}((#C11).{dart.core::List::[]}(dart.ffi::_abi())).{dart.ffi::Pointer::cast}<dart.ffi::Pointer<lib::Coordinate*>*>();
get next() → dart.ffi::Pointer<lib::Coordinate*>*
return dart.ffi::_loadPointer<dart.ffi::Pointer<lib::Coordinate*>*>(this.{lib::Coordinate::#_ptr_next}, #C7);
set next(dart.ffi::Pointer<lib::Coordinate*>* #v) → void
return dart.ffi::_storePointer<dart.ffi::Pointer<lib::Coordinate*>*>(this.{lib::Coordinate::#_ptr_next}, #C7, #v);
}
}
library from "org-dartlang-test:///main.dart" as main {
import "org-dartlang-test:///lib.dart";
static method main() → dynamic {
lib::Coordinate* coordinate = lib::Coordinate::allocate(42.0, 42.0, null);
dart.core::print(coordinate.{lib::Coordinate::x});
dart.core::print(coordinate.{lib::Coordinate::y});
dart.core::print(coordinate.{lib::Coordinate::next});
dart.core::print("Done!");
}
}
constants {
#C1 = "vm:entry-point"
#C2 = null
#C3 = dart.core::pragma {name:#C1, options:#C2}
#C4 = 24
#C5 = 20
#C6 = <dart.core::int*>[#C4, #C5, #C4]
#C7 = 0
#C8 = 8
#C9 = <dart.core::int*>[#C8, #C8, #C8]
#C10 = 16
#C11 = <dart.core::int*>[#C10, #C10, #C10]
}
@@ -0,0 +1,62 @@
main = <No Member>;
library from "org-dartlang-test:///lib.dart" as lib {
import "dart:ffi";
@#C3
class Coordinate extends dart.ffi::Struct {
@#C3
static final field dart.core::int* #sizeOf = (#C6).{dart.core::List::[]}(dart.ffi::_abi());
@#C3
constructor #fromPointer(dynamic #pointer) → dynamic
: super dart.ffi::Struct::_fromPointer(#pointer)
;
static factory allocate(dart.core::double* x, dart.core::double* y, dart.ffi::Pointer<lib::Coordinate*>* next) → lib::Coordinate* {
dart.core::print("hello");
return null;
}
get #_ptr_x() → dart.ffi::Pointer<dart.ffi::Double*>*
return this.{dart.ffi::Struct::_addressOf}.{dart.ffi::Pointer::cast}<dart.ffi::Double*>();
get x() → dart.core::double*
return dart.ffi::_loadDouble(this.{lib::Coordinate::#_ptr_x}, #C7);
set x(dart.core::double* #v) → void
return dart.ffi::_storeDouble(this.{lib::Coordinate::#_ptr_x}, #C7, #v);
get #_ptr_y() → dart.ffi::Pointer<dart.ffi::Double*>*
return this.{dart.ffi::Struct::_addressOf}.{dart.ffi::Pointer::_offsetBy}((#C9).{dart.core::List::[]}(dart.ffi::_abi())).{dart.ffi::Pointer::cast}<dart.ffi::Double*>();
get y() → dart.core::double*
return dart.ffi::_loadDouble(this.{lib::Coordinate::#_ptr_y}, #C7);
set y(dart.core::double* #v) → void
return dart.ffi::_storeDouble(this.{lib::Coordinate::#_ptr_y}, #C7, #v);
get #_ptr_next() → dart.ffi::Pointer<dart.ffi::Pointer<lib::Coordinate*>*>*
return this.{dart.ffi::Struct::_addressOf}.{dart.ffi::Pointer::_offsetBy}((#C11).{dart.core::List::[]}(dart.ffi::_abi())).{dart.ffi::Pointer::cast}<dart.ffi::Pointer<lib::Coordinate*>*>();
get next() → dart.ffi::Pointer<lib::Coordinate*>*
return dart.ffi::_loadPointer<dart.ffi::Pointer<lib::Coordinate*>*>(this.{lib::Coordinate::#_ptr_next}, #C7);
set next(dart.ffi::Pointer<lib::Coordinate*>* #v) → void
return dart.ffi::_storePointer<dart.ffi::Pointer<lib::Coordinate*>*>(this.{lib::Coordinate::#_ptr_next}, #C7, #v);
}
}
library from "org-dartlang-test:///main.dart" as main {
import "org-dartlang-test:///lib.dart";
static method main() → dynamic {
lib::Coordinate* coordinate = lib::Coordinate::allocate(42.0, 42.0, null);
dart.core::print(coordinate.{lib::Coordinate::x});
dart.core::print(coordinate.{lib::Coordinate::y});
dart.core::print(coordinate.{lib::Coordinate::next});
dart.core::print("Done!");
}
}
constants {
#C1 = "vm:entry-point"
#C2 = null
#C3 = dart.core::pragma {name:#C1, options:#C2}
#C4 = 24
#C5 = 20
#C6 = <dart.core::int*>[#C4, #C5, #C4]
#C7 = 0
#C8 = 8
#C9 = <dart.core::int*>[#C8, #C8, #C8]
#C10 = 16
#C11 = <dart.core::int*>[#C10, #C10, #C10]
}
@@ -61,7 +61,7 @@ worlds:
enum CompilationStrategy { direct, toKernel, toData, fromData }
expectedLibraryCount: 2
expectsRebuildBodiesOnly: true
expectsRebuildBodiesOnly: false # For now, libraries with errors cannot have bodies rebuild.
- entry: main.dart
useExperimentalInvalidation: true
worldType: updated
@@ -88,4 +88,4 @@ worlds:
enum CompilationStrategy { direct, toKernel, toData, fromData }
expectedLibraryCount: 2
expectsRebuildBodiesOnly: true
expectsRebuildBodiesOnly: false # For now, libraries with errors cannot have bodies rebuild.
@@ -3,19 +3,3 @@
# BSD-style license that can be found in the LICENSE.md file.
# Status file for the test suite ../test/incremental_load_from_dill_test.dart.
no_outline_change_1: Crash # Doesn't work on DillLibraryBuilders.
no_outline_change_2: Crash # Doesn't work on DillLibraryBuilders.
no_outline_change_6: Crash # Doesn't work on DillLibraryBuilders.
no_outline_change_7: Crash # Doesn't work on DillLibraryBuilders.
no_outline_change_9: Crash # Doesn't work on DillLibraryBuilders.
no_outline_change_10: Crash # Doesn't work on DillLibraryBuilders.
no_outline_change_11: Crash # Doesn't work on DillLibraryBuilders.
no_outline_change_12: Crash # Doesn't work on DillLibraryBuilders.
no_outline_change_13: Crash # Doesn't work on DillLibraryBuilders.
no_outline_change_14: Crash # Doesn't work on DillLibraryBuilders.
no_outline_change_21: Crash # Doesn't work on DillLibraryBuilders.
no_outline_change_24: Crash # Doesn't work on DillLibraryBuilders.
no_outline_change_27: Crash # Doesn't work on DillLibraryBuilders.
no_outline_change_33: Crash # Doesn't work on DillLibraryBuilders.
no_outline_change_34: Crash # Doesn't work on DillLibraryBuilders.