Skip to content

Commit 1c27154

Browse files
MarkzipanCommit Bot
authored and
Commit Bot
committed
[dart2js] Adding closure dump info tests + fixing sibling logic in kernel dump info
- Reconciles some logic for closure/LocalFunction name resolution between dump info and the element model - Handles closure disambiguation (j world <-> k world) by assuming fixed visit order Change-Id: Ic9a61208ed93372633e118541b71f20543e250a3 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/245600 Commit-Queue: Mark Zhou <[email protected]> Reviewed-by: Joshua Litt <[email protected]>
1 parent 19d0d88 commit 1c27154

File tree

2 files changed

+321
-50
lines changed

2 files changed

+321
-50
lines changed

pkg/compiler/lib/src/dump_info.dart

Lines changed: 105 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import 'constants/values.dart' show ConstantValue, InterceptorConstantValue;
2424
import 'deferred_load/output_unit.dart' show OutputUnit, deferredPartFileName;
2525
import 'dump_info_javascript_monitor.dart';
2626
import 'elements/entities.dart';
27+
import 'elements/entity_utils.dart' as entity_utils;
2728
import 'inferrer/abstract_value_domain.dart';
2829
import 'inferrer/types.dart'
2930
show GlobalTypeInferenceMemberResult, GlobalTypeInferenceResults;
@@ -663,14 +664,16 @@ class KernelInfoCollector {
663664
List<ClosureInfo> nestedClosures = <ClosureInfo>[];
664665
localFunctionInfoCollector.localFunctions.forEach((key, value) {
665666
FunctionEntity closureEntity;
667+
int closureOrder = value.order;
666668
environment.forEachNestedClosure(memberEntity, (closure) {
667-
if (closure.enclosingClass.name == value.name) {
669+
if (closure.enclosingClass.name == value.name &&
670+
(closureOrder-- == 0)) {
668671
closureEntity = closure;
669672
}
670673
});
671674
final closureClassEntity = closureEntity.enclosingClass;
672-
final closureInfo =
673-
ClosureInfo(name: value.name, outputUnit: null, size: null);
675+
final closureInfo = ClosureInfo(
676+
name: value.disambiguatedName, outputUnit: null, size: null);
674677
state.entityToInfo[closureClassEntity] = closureInfo;
675678

676679
FunctionEntity callMethod = closedWorld.elementEnvironment
@@ -691,6 +694,32 @@ class KernelInfoCollector {
691694
}
692695
}
693696

697+
/// Maps JWorld Entity objects to disambiguated names in order to map them
698+
/// to/from Kernel.
699+
///
700+
/// This is primarily used for naming closure objects, which rely on Entity
701+
/// object identity to determine uniqueness.
702+
///
703+
/// Note: this relies on the Kernel traversal order to determine order, which
704+
/// may change in the future.
705+
class EntityDisambiguator {
706+
final nameFrequencies = <String, int>{};
707+
final entityNames = <Entity, String>{};
708+
709+
String name(Entity entity) {
710+
final disambiguatedName = entityNames[entity];
711+
if (disambiguatedName != null) {
712+
return disambiguatedName;
713+
}
714+
nameFrequencies[entity.name] = (nameFrequencies[entity.name] ?? -1) + 1;
715+
final order = nameFrequencies[entity.name];
716+
entityNames[entity] =
717+
order == 0 ? entity.name : '${entity.name}%${order - 1}';
718+
719+
return entityNames[entity];
720+
}
721+
}
722+
694723
/// Annotates [KernelInfoCollector] with info extracted from closed-world
695724
/// analysis.
696725
class DumpInfoAnnotator {
@@ -699,6 +728,7 @@ class DumpInfoAnnotator {
699728
final JClosedWorld closedWorld;
700729
final GlobalTypeInferenceResults _globalInferenceResults;
701730
final DumpInfoTask dumpInfoTask;
731+
final entityDisambiguator = EntityDisambiguator();
702732

703733
JElementEnvironment get environment => closedWorld.elementEnvironment;
704734

@@ -901,8 +931,9 @@ class DumpInfoAnnotator {
901931
}
902932

903933
ClosureInfo visitClosureClass(ClassEntity element) {
934+
final disambiguatedElementName = entityDisambiguator.name(element);
904935
final kClosureInfos = kernelInfo.state.info.closures
905-
.where((info) => info.name == element.name)
936+
.where((info) => info.name == disambiguatedElementName)
906937
.toList();
907938
assert(
908939
kClosureInfos.length == 1,
@@ -916,7 +947,8 @@ class DumpInfoAnnotator {
916947
FunctionEntity callMethod = closedWorld.elementEnvironment
917948
.lookupClassMember(element, Identifiers.call);
918949

919-
visitFunction(callMethod, element.name);
950+
final functionInfo = visitFunction(callMethod, disambiguatedElementName);
951+
if (functionInfo == null) return null;
920952

921953
kClosureInfo.treeShakenStatus = TreeShakenStatus.Live;
922954
return kClosureInfo;
@@ -926,8 +958,8 @@ class DumpInfoAnnotator {
926958
// not always be valid. Check and validate later.
927959
FunctionInfo visitFunction(FunctionEntity function, String parentName) {
928960
int size = dumpInfoTask.sizeOf(function);
929-
// TODO(sigmund): consider adding a small info to represent unreachable
930-
// code here.
961+
if (size == 0 && !shouldKeep(function)) return null;
962+
931963
var compareName = function.name;
932964
if (function.isConstructor) {
933965
compareName = compareName == ""
@@ -1477,63 +1509,36 @@ class DumpInfoStateData {
14771509

14781510
class LocalFunctionInfo {
14791511
final ir.LocalFunction localFunction;
1480-
final List<ir.TreeNode> hierarchy;
14811512
final String name;
1513+
final int order;
14821514
bool isInvoked = false;
14831515

1484-
LocalFunctionInfo._(this.localFunction, this.hierarchy, this.name);
1485-
1486-
factory LocalFunctionInfo(ir.LocalFunction localFunction) {
1487-
String name = '';
1488-
ir.TreeNode node = localFunction;
1489-
final hierarchy = <ir.TreeNode>[];
1490-
bool inClosure = false;
1491-
while (node != null) {
1492-
// Only consider nodes used for resolving a closure's full name.
1493-
if (node is ir.FunctionDeclaration) {
1494-
hierarchy.add(node);
1495-
name = '_${node.variable.name}' + name;
1496-
inClosure = false;
1497-
} else if (node is ir.FunctionExpression) {
1498-
hierarchy.add(node);
1499-
name = (inClosure ? '_' : '_closure') + name;
1500-
inClosure = true;
1501-
} else if (node is ir.Member) {
1502-
hierarchy.add(node);
1503-
var cleanName = node.toStringInternal();
1504-
if (cleanName.endsWith('.'))
1505-
cleanName = cleanName.substring(0, cleanName.length - 1);
1506-
final isFactory = node is ir.Procedure && node.isFactory;
1507-
if (isFactory) {
1508-
cleanName = cleanName.replaceAll('.', '\$');
1509-
cleanName = '${node.enclosingClass.toStringInternal()}_' + cleanName;
1510-
} else {
1511-
cleanName = cleanName.replaceAll('.', '_');
1512-
}
1513-
name = cleanName + name;
1514-
inClosure = false;
1515-
}
1516-
node = node.parent;
1517-
}
1516+
LocalFunctionInfo(this.localFunction, this.name, this.order);
15181517

1519-
return LocalFunctionInfo._(localFunction, hierarchy, name);
1520-
}
1518+
get disambiguatedName => order == 0 ? name : '$name%${order - 1}';
15211519
}
15221520

15231521
class LocalFunctionInfoCollector extends ir.RecursiveVisitor<void> {
15241522
final localFunctions = <ir.LocalFunction, LocalFunctionInfo>{};
1523+
final localFunctionNames = <String, int>{};
1524+
1525+
LocalFunctionInfo generateLocalFunctionInfo(ir.LocalFunction localFunction) {
1526+
final name = _computeClosureName(localFunction);
1527+
localFunctionNames[name] = (localFunctionNames[name] ?? -1) + 1;
1528+
return LocalFunctionInfo(localFunction, name, localFunctionNames[name]);
1529+
}
15251530

15261531
@override
15271532
void visitFunctionExpression(ir.FunctionExpression node) {
15281533
assert(localFunctions[node] == null);
1529-
localFunctions[node] = LocalFunctionInfo(node);
1534+
localFunctions[node] = generateLocalFunctionInfo(node);
15301535
defaultExpression(node);
15311536
}
15321537

15331538
@override
15341539
void visitFunctionDeclaration(ir.FunctionDeclaration node) {
15351540
assert(localFunctions[node] == null);
1536-
localFunctions[node] = LocalFunctionInfo(node);
1541+
localFunctions[node] = generateLocalFunctionInfo(node);
15371542
defaultStatement(node);
15381543
}
15391544

@@ -1544,3 +1549,57 @@ class LocalFunctionInfoCollector extends ir.RecursiveVisitor<void> {
15441549
localFunctions[node.localFunction].isInvoked = true;
15451550
}
15461551
}
1552+
1553+
// Returns a non-unique name for the given closure element.
1554+
//
1555+
// Must be kept logically identical to js_model/element_map_impl.dart.
1556+
String _computeClosureName(ir.TreeNode treeNode) {
1557+
String reconstructConstructorName(ir.Member node) {
1558+
String className = node.enclosingClass.name;
1559+
if (node.name.text == '') {
1560+
return className;
1561+
} else {
1562+
return '$className\$${node.name}';
1563+
}
1564+
}
1565+
1566+
var parts = <String>[];
1567+
// First anonymous is called 'closure', outer ones called '' to give a
1568+
// compound name where increasing nesting level corresponds to extra
1569+
// underscores.
1570+
var anonymous = 'closure';
1571+
ir.TreeNode current = treeNode;
1572+
while (current != null) {
1573+
var node = current;
1574+
if (node is ir.FunctionExpression) {
1575+
parts.add(anonymous);
1576+
anonymous = '';
1577+
} else if (node is ir.FunctionDeclaration) {
1578+
String name = node.variable.name;
1579+
if (name != null && name != "") {
1580+
parts.add(entity_utils.operatorNameToIdentifier(name));
1581+
} else {
1582+
parts.add(anonymous);
1583+
anonymous = '';
1584+
}
1585+
} else if (node is ir.Class) {
1586+
parts.add(node.name);
1587+
break;
1588+
} else if (node is ir.Procedure) {
1589+
if (node.kind == ir.ProcedureKind.Factory) {
1590+
parts.add(reconstructConstructorName(node));
1591+
} else {
1592+
parts.add(entity_utils.operatorNameToIdentifier(node.name.text));
1593+
}
1594+
} else if (node is ir.Constructor) {
1595+
parts.add(reconstructConstructorName(node));
1596+
break;
1597+
} else if (node is ir.Field) {
1598+
// Add the field name for closures in field initializers.
1599+
String name = node.name?.text;
1600+
if (name != null) parts.add(name);
1601+
}
1602+
current = current.parent;
1603+
}
1604+
return parts.reversed.join('_');
1605+
}

0 commit comments

Comments
 (0)