@@ -499,6 +499,9 @@ class KernelInfoCollector {
499
499
info.coverageId = '${field .hashCode }' ;
500
500
}
501
501
502
+ _addClosureInfo (info, field,
503
+ libraryEntity: fieldEntity.library, memberEntity: fieldEntity);
504
+
502
505
state.info.fields.add (info);
503
506
return info;
504
507
}
@@ -549,9 +552,10 @@ class KernelInfoCollector {
549
552
}
550
553
551
554
FunctionInfo visitFunction (ir.FunctionNode function,
552
- {FunctionEntity functionEntity}) {
553
- var parent = function.parent;
554
- String name = parent.toStringInternal ();
555
+ {FunctionEntity functionEntity, LocalFunctionInfo localFunctionInfo}) {
556
+ final parent = function.parent;
557
+ String name =
558
+ parent is ir.LocalFunction ? 'call' : parent.toStringInternal ();
555
559
bool isConstructor = parent is ir.Constructor ;
556
560
bool isFactory = parent is ir.Procedure && parent.isFactory;
557
561
// Kernel `isStatic` refers to static members, constructors, and top-level
@@ -632,6 +636,15 @@ class KernelInfoCollector {
632
636
outputUnit: null );
633
637
state.entityToInfo[functionEntity] = info;
634
638
639
+ if (function.parent is ir.Member )
640
+ _addClosureInfo (info, function.parent,
641
+ libraryEntity: functionEntity.library, memberEntity: functionEntity);
642
+ else {
643
+ // This branch is only reached when function is a 'call' method.
644
+ // TODO(markzipan): Ensure call methods never have children.
645
+ info.closures = [];
646
+ }
647
+
635
648
if (compiler.options.experimentCallInstrumentation) {
636
649
// We use function.hashCode because it is globally unique and it is
637
650
// available while we are doing codegen.
@@ -641,6 +654,41 @@ class KernelInfoCollector {
641
654
state.info.functions.add (info);
642
655
return info;
643
656
}
657
+
658
+ /// Adds closure information to [info] , using all nested closures in [member] .
659
+ void _addClosureInfo (Info info, ir.Member member,
660
+ {LibraryEntity libraryEntity, MemberEntity memberEntity}) {
661
+ final localFunctionInfoCollector = LocalFunctionInfoCollector ();
662
+ member.accept (localFunctionInfoCollector);
663
+ List <ClosureInfo > nestedClosures = < ClosureInfo > [];
664
+ localFunctionInfoCollector.localFunctions.forEach ((key, value) {
665
+ FunctionEntity closureEntity;
666
+ environment.forEachNestedClosure (memberEntity, (closure) {
667
+ if (closure.enclosingClass.name == value.name) {
668
+ closureEntity = closure;
669
+ }
670
+ });
671
+ final closureClassEntity = closureEntity.enclosingClass;
672
+ final closureInfo =
673
+ ClosureInfo (name: value.name, outputUnit: null , size: null );
674
+ state.entityToInfo[closureClassEntity] = closureInfo;
675
+
676
+ FunctionEntity callMethod = closedWorld.elementEnvironment
677
+ .lookupClassMember (closureClassEntity, Identifiers .call);
678
+ final functionInfo = visitFunction (key.function,
679
+ functionEntity: callMethod, localFunctionInfo: value);
680
+ state.entityToInfo[closureEntity] = functionInfo;
681
+
682
+ closureInfo.function = functionInfo;
683
+ functionInfo.parent = closureInfo;
684
+ state.info.closures.add (closureInfo);
685
+
686
+ closureInfo.parent = info;
687
+ nestedClosures.add (closureInfo);
688
+ });
689
+ if (info is FunctionInfo ) info.closures = nestedClosures;
690
+ if (info is FieldInfo ) info.closures = nestedClosures;
691
+ }
644
692
}
645
693
646
694
/// Annotates [KernelInfoCollector] with info extracted from closed-world
@@ -853,24 +901,25 @@ class DumpInfoAnnotator {
853
901
}
854
902
855
903
ClosureInfo visitClosureClass (ClassEntity element) {
856
- ClosureInfo closureInfo = ClosureInfo (
857
- name: element.name,
858
- outputUnit: _unitInfoForClass (element),
859
- size: dumpInfoTask.sizeOf (element));
860
- kernelInfo.state.entityToInfo[element] = closureInfo;
904
+ final kClosureInfos = kernelInfo.state.info.closures
905
+ .where ((info) => info.name == element.name)
906
+ .toList ();
907
+ assert (
908
+ kClosureInfos.length == 1 ,
909
+ 'Ambiguous closure resolution. '
910
+ 'Expected singleton, found $kClosureInfos ' );
911
+ final kClosureInfo = kClosureInfos.first;
912
+
913
+ kClosureInfo.outputUnit = _unitInfoForClass (element);
914
+ kClosureInfo.size = dumpInfoTask.sizeOf (element);
861
915
862
916
FunctionEntity callMethod = closedWorld.elementEnvironment
863
917
.lookupClassMember (element, Identifiers .call);
864
918
865
- FunctionInfo functionInfo = visitFunction (callMethod, element.name);
866
- if (functionInfo == null ) return null ;
867
-
868
- closureInfo.function = functionInfo;
869
- functionInfo.parent = closureInfo;
870
- closureInfo.treeShakenStatus = TreeShakenStatus .Live ;
919
+ visitFunction (callMethod, element.name);
871
920
872
- kernelInfo.state.info.closures. add (closureInfo) ;
873
- return closureInfo ;
921
+ kClosureInfo.treeShakenStatus = TreeShakenStatus . Live ;
922
+ return kClosureInfo ;
874
923
}
875
924
876
925
// TODO(markzipan): [parentName] is used for disambiguation, but this might
@@ -879,8 +928,6 @@ class DumpInfoAnnotator {
879
928
int size = dumpInfoTask.sizeOf (function);
880
929
// TODO(sigmund): consider adding a small info to represent unreachable
881
930
// code here.
882
- if (size == 0 && ! shouldKeep (function)) return null ;
883
-
884
931
var compareName = function.name;
885
932
if (function.isConstructor) {
886
933
compareName = compareName == ""
@@ -898,9 +945,10 @@ class DumpInfoAnnotator {
898
945
! (function.isSetter ^ i.modifiers.isSetter))
899
946
.toList ();
900
947
assert (
901
- kFunctionInfos.length = = 1 ,
948
+ kFunctionInfos.length < = 1 ,
902
949
'Ambiguous function resolution. '
903
- 'Expected singleton, found $kFunctionInfos ' );
950
+ 'Expected single or none, found $kFunctionInfos ' );
951
+ if (kFunctionInfos.length == 0 ) return null ;
904
952
final kFunctionInfo = kFunctionInfos.first;
905
953
906
954
List <CodeSpan > code = dumpInfoTask.codeOf (function);
@@ -943,19 +991,13 @@ class DumpInfoAnnotator {
943
991
int _addClosureInfo (BasicInfo info, MemberEntity member) {
944
992
assert (info is FunctionInfo || info is FieldInfo );
945
993
int size = 0 ;
946
- List <ClosureInfo > nestedClosures = < ClosureInfo > [];
947
994
environment.forEachNestedClosure (member, (closure) {
948
995
ClosureInfo closureInfo = visitClosureClass (closure.enclosingClass);
949
996
if (closureInfo != null ) {
950
- closureInfo.parent = info;
951
- closureInfo.treeShakenStatus = info.treeShakenStatus;
952
- nestedClosures.add (closureInfo);
997
+ closureInfo.treeShakenStatus = TreeShakenStatus .Live ;
953
998
size += closureInfo.size;
954
999
}
955
1000
});
956
- if (info is FunctionInfo ) info.closures = nestedClosures;
957
- if (info is FieldInfo ) info.closures = nestedClosures;
958
-
959
1001
return size;
960
1002
}
961
1003
@@ -1432,3 +1474,73 @@ class DumpInfoStateData {
1432
1474
1433
1475
DumpInfoStateData ();
1434
1476
}
1477
+
1478
+ class LocalFunctionInfo {
1479
+ final ir.LocalFunction localFunction;
1480
+ final List <ir.TreeNode > hierarchy;
1481
+ final String name;
1482
+ bool isInvoked = false ;
1483
+
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
+ }
1518
+
1519
+ return LocalFunctionInfo ._(localFunction, hierarchy, name);
1520
+ }
1521
+ }
1522
+
1523
+ class LocalFunctionInfoCollector extends ir.RecursiveVisitor <void > {
1524
+ final localFunctions = < ir.LocalFunction , LocalFunctionInfo > {};
1525
+
1526
+ @override
1527
+ void visitFunctionExpression (ir.FunctionExpression node) {
1528
+ assert (localFunctions[node] == null );
1529
+ localFunctions[node] = LocalFunctionInfo (node);
1530
+ defaultExpression (node);
1531
+ }
1532
+
1533
+ @override
1534
+ void visitFunctionDeclaration (ir.FunctionDeclaration node) {
1535
+ assert (localFunctions[node] == null );
1536
+ localFunctions[node] = LocalFunctionInfo (node);
1537
+ defaultStatement (node);
1538
+ }
1539
+
1540
+ @override
1541
+ void visitLocalFunctionInvocation (ir.LocalFunctionInvocation node) {
1542
+ if (localFunctions[node.localFunction] == null )
1543
+ visitFunctionDeclaration (node.localFunction);
1544
+ localFunctions[node.localFunction].isInvoked = true ;
1545
+ }
1546
+ }
0 commit comments