[dart2js] Adding kernel closure information to dump info.
Change-Id: I7880d64b3c7ff62a1b1ad50ba5157f252e311aa2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/245280
Reviewed-by: Joshua Litt <joshualitt@google.com>
Commit-Queue: Mark Zhou <markzipan@google.com>
diff --git a/pkg/compiler/lib/src/dump_info.dart b/pkg/compiler/lib/src/dump_info.dart
index 184e44e..e84fc46 100644
--- a/pkg/compiler/lib/src/dump_info.dart
+++ b/pkg/compiler/lib/src/dump_info.dart
@@ -499,6 +499,9 @@
info.coverageId = '${field.hashCode}';
}
+ _addClosureInfo(info, field,
+ libraryEntity: fieldEntity.library, memberEntity: fieldEntity);
+
state.info.fields.add(info);
return info;
}
@@ -549,9 +552,10 @@
}
FunctionInfo visitFunction(ir.FunctionNode function,
- {FunctionEntity functionEntity}) {
- var parent = function.parent;
- String name = parent.toStringInternal();
+ {FunctionEntity functionEntity, LocalFunctionInfo localFunctionInfo}) {
+ final parent = function.parent;
+ String name =
+ parent is ir.LocalFunction ? 'call' : parent.toStringInternal();
bool isConstructor = parent is ir.Constructor;
bool isFactory = parent is ir.Procedure && parent.isFactory;
// Kernel `isStatic` refers to static members, constructors, and top-level
@@ -632,6 +636,15 @@
outputUnit: null);
state.entityToInfo[functionEntity] = info;
+ if (function.parent is ir.Member)
+ _addClosureInfo(info, function.parent,
+ libraryEntity: functionEntity.library, memberEntity: functionEntity);
+ else {
+ // This branch is only reached when function is a 'call' method.
+ // TODO(markzipan): Ensure call methods never have children.
+ info.closures = [];
+ }
+
if (compiler.options.experimentCallInstrumentation) {
// We use function.hashCode because it is globally unique and it is
// available while we are doing codegen.
@@ -641,6 +654,41 @@
state.info.functions.add(info);
return info;
}
+
+ /// Adds closure information to [info], using all nested closures in [member].
+ void _addClosureInfo(Info info, ir.Member member,
+ {LibraryEntity libraryEntity, MemberEntity memberEntity}) {
+ final localFunctionInfoCollector = LocalFunctionInfoCollector();
+ member.accept(localFunctionInfoCollector);
+ List<ClosureInfo> nestedClosures = <ClosureInfo>[];
+ localFunctionInfoCollector.localFunctions.forEach((key, value) {
+ FunctionEntity closureEntity;
+ environment.forEachNestedClosure(memberEntity, (closure) {
+ if (closure.enclosingClass.name == value.name) {
+ closureEntity = closure;
+ }
+ });
+ final closureClassEntity = closureEntity.enclosingClass;
+ final closureInfo =
+ ClosureInfo(name: value.name, outputUnit: null, size: null);
+ state.entityToInfo[closureClassEntity] = closureInfo;
+
+ FunctionEntity callMethod = closedWorld.elementEnvironment
+ .lookupClassMember(closureClassEntity, Identifiers.call);
+ final functionInfo = visitFunction(key.function,
+ functionEntity: callMethod, localFunctionInfo: value);
+ state.entityToInfo[closureEntity] = functionInfo;
+
+ closureInfo.function = functionInfo;
+ functionInfo.parent = closureInfo;
+ state.info.closures.add(closureInfo);
+
+ closureInfo.parent = info;
+ nestedClosures.add(closureInfo);
+ });
+ if (info is FunctionInfo) info.closures = nestedClosures;
+ if (info is FieldInfo) info.closures = nestedClosures;
+ }
}
/// Annotates [KernelInfoCollector] with info extracted from closed-world
@@ -853,24 +901,25 @@
}
ClosureInfo visitClosureClass(ClassEntity element) {
- ClosureInfo closureInfo = ClosureInfo(
- name: element.name,
- outputUnit: _unitInfoForClass(element),
- size: dumpInfoTask.sizeOf(element));
- kernelInfo.state.entityToInfo[element] = closureInfo;
+ final kClosureInfos = kernelInfo.state.info.closures
+ .where((info) => info.name == element.name)
+ .toList();
+ assert(
+ kClosureInfos.length == 1,
+ 'Ambiguous closure resolution. '
+ 'Expected singleton, found $kClosureInfos');
+ final kClosureInfo = kClosureInfos.first;
+
+ kClosureInfo.outputUnit = _unitInfoForClass(element);
+ kClosureInfo.size = dumpInfoTask.sizeOf(element);
FunctionEntity callMethod = closedWorld.elementEnvironment
.lookupClassMember(element, Identifiers.call);
- FunctionInfo functionInfo = visitFunction(callMethod, element.name);
- if (functionInfo == null) return null;
+ visitFunction(callMethod, element.name);
- closureInfo.function = functionInfo;
- functionInfo.parent = closureInfo;
- closureInfo.treeShakenStatus = TreeShakenStatus.Live;
-
- kernelInfo.state.info.closures.add(closureInfo);
- return closureInfo;
+ kClosureInfo.treeShakenStatus = TreeShakenStatus.Live;
+ return kClosureInfo;
}
// TODO(markzipan): [parentName] is used for disambiguation, but this might
@@ -879,8 +928,6 @@
int size = dumpInfoTask.sizeOf(function);
// TODO(sigmund): consider adding a small info to represent unreachable
// code here.
- if (size == 0 && !shouldKeep(function)) return null;
-
var compareName = function.name;
if (function.isConstructor) {
compareName = compareName == ""
@@ -898,9 +945,10 @@
!(function.isSetter ^ i.modifiers.isSetter))
.toList();
assert(
- kFunctionInfos.length == 1,
+ kFunctionInfos.length <= 1,
'Ambiguous function resolution. '
- 'Expected singleton, found $kFunctionInfos');
+ 'Expected single or none, found $kFunctionInfos');
+ if (kFunctionInfos.length == 0) return null;
final kFunctionInfo = kFunctionInfos.first;
List<CodeSpan> code = dumpInfoTask.codeOf(function);
@@ -943,19 +991,13 @@
int _addClosureInfo(BasicInfo info, MemberEntity member) {
assert(info is FunctionInfo || info is FieldInfo);
int size = 0;
- List<ClosureInfo> nestedClosures = <ClosureInfo>[];
environment.forEachNestedClosure(member, (closure) {
ClosureInfo closureInfo = visitClosureClass(closure.enclosingClass);
if (closureInfo != null) {
- closureInfo.parent = info;
- closureInfo.treeShakenStatus = info.treeShakenStatus;
- nestedClosures.add(closureInfo);
+ closureInfo.treeShakenStatus = TreeShakenStatus.Live;
size += closureInfo.size;
}
});
- if (info is FunctionInfo) info.closures = nestedClosures;
- if (info is FieldInfo) info.closures = nestedClosures;
-
return size;
}
@@ -1432,3 +1474,73 @@
DumpInfoStateData();
}
+
+class LocalFunctionInfo {
+ final ir.LocalFunction localFunction;
+ final List<ir.TreeNode> hierarchy;
+ final String name;
+ bool isInvoked = false;
+
+ LocalFunctionInfo._(this.localFunction, this.hierarchy, this.name);
+
+ factory LocalFunctionInfo(ir.LocalFunction localFunction) {
+ String name = '';
+ ir.TreeNode node = localFunction;
+ final hierarchy = <ir.TreeNode>[];
+ bool inClosure = false;
+ while (node != null) {
+ // Only consider nodes used for resolving a closure's full name.
+ if (node is ir.FunctionDeclaration) {
+ hierarchy.add(node);
+ name = '_${node.variable.name}' + name;
+ inClosure = false;
+ } else if (node is ir.FunctionExpression) {
+ hierarchy.add(node);
+ name = (inClosure ? '_' : '_closure') + name;
+ inClosure = true;
+ } else if (node is ir.Member) {
+ hierarchy.add(node);
+ var cleanName = node.toStringInternal();
+ if (cleanName.endsWith('.'))
+ cleanName = cleanName.substring(0, cleanName.length - 1);
+ final isFactory = node is ir.Procedure && node.isFactory;
+ if (isFactory) {
+ cleanName = cleanName.replaceAll('.', '\$');
+ cleanName = '${node.enclosingClass.toStringInternal()}_' + cleanName;
+ } else {
+ cleanName = cleanName.replaceAll('.', '_');
+ }
+ name = cleanName + name;
+ inClosure = false;
+ }
+ node = node.parent;
+ }
+
+ return LocalFunctionInfo._(localFunction, hierarchy, name);
+ }
+}
+
+class LocalFunctionInfoCollector extends ir.RecursiveVisitor<void> {
+ final localFunctions = <ir.LocalFunction, LocalFunctionInfo>{};
+
+ @override
+ void visitFunctionExpression(ir.FunctionExpression node) {
+ assert(localFunctions[node] == null);
+ localFunctions[node] = LocalFunctionInfo(node);
+ defaultExpression(node);
+ }
+
+ @override
+ void visitFunctionDeclaration(ir.FunctionDeclaration node) {
+ assert(localFunctions[node] == null);
+ localFunctions[node] = LocalFunctionInfo(node);
+ defaultStatement(node);
+ }
+
+ @override
+ void visitLocalFunctionInvocation(ir.LocalFunctionInvocation node) {
+ if (localFunctions[node.localFunction] == null)
+ visitFunctionDeclaration(node.localFunction);
+ localFunctions[node.localFunction].isInvoked = true;
+ }
+}