11// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
22// for details. All rights reserved. Use of this source code is governed by a
33// BSD-style license that can be found in the LICENSE file.
4+ import "dart:convert" ;
45import "dart:io" ;
56import "package:kernel/kernel.dart" ;
67import "package:kernel/ast.dart" ;
78
8- main (List <String > args) {
9+ main (List <String > args) async {
910 // Ensure right args are passed.
1011 if (args.length < 1 ) {
1112 print ("usage: ${Platform .script } a.dill" );
@@ -19,8 +20,8 @@ main(List<String> args) {
1920 // Visit component.
2021 component.accept (visitor);
2122
22- // Print compiled data.
23- print ( visitor.classInfo );
23+ // Save data to file .
24+ visitor.saveDataToFile ( "dart2html_metrics.json" );
2425}
2526
2627/// Visits classes in libraries specified by `libraryFilter`
@@ -55,12 +56,13 @@ class MetricsVisitor extends RecursiveVisitor {
5556
5657 @override
5758 void visitProcedure (Procedure node) {
58- // If this method invokes super, track for class.
59- if (node.containsSuperCalls) {
60- classInfo[currentClass]
61- .methods
62- .add (ClassMetricsMethod (node.name.text, true ));
63- }
59+ classInfo[currentClass].methods.add (ClassMetricsMethod (
60+ node.name.text,
61+ node.containsSuperCalls,
62+ node.isInstanceMember,
63+ node.isExternal,
64+ node.isAbstract,
65+ node.kind.toString ()));
6466 }
6567
6668 @override
@@ -71,6 +73,11 @@ class MetricsVisitor extends RecursiveVisitor {
7173 currentClass = node.name;
7274 var metrics = ClassMetrics ();
7375
76+ // Check if class contains native members.
77+ if (node.annotations.any (_isNativeMarkerAnnotation)) {
78+ metrics.containsNativeMember = true ;
79+ }
80+
7481 // Check if Mixed.
7582 if (node.superclass? .isAnonymousMixin ?? false ) {
7683 metrics.mixed = true ;
@@ -83,18 +90,26 @@ class MetricsVisitor extends RecursiveVisitor {
8390 metrics.parent = unmangledParent;
8491 }
8592
93+ // Check for implemented classes.
94+ if (node.implementedTypes.length > 0 ) {
95+ var implementedTypes =
96+ node.implementedTypes.map ((type) => type.className.asClass.name);
97+ metrics.implementedTypes = implementedTypes.toList ();
98+ }
99+
86100 classInfo[currentClass] = metrics;
101+
87102 super .visitClass (node);
88103 }
89104 }
90105
91106 // Returns List of parsed mixins from superclass name.
92107 List <String > _filterMixins (String superWithMixins) {
93- var start = superWithMixins.indexOf (' with' ) + 4 ;
108+ var start = superWithMixins.indexOf (" with" ) + 4 ;
94109 var mixins = superWithMixins.substring (start);
95- mixins = mixins.replaceAll (' ' , '' );
110+ mixins = mixins.replaceAll (" " , "" );
96111
97- return mixins.split (',' );
112+ return mixins.split ("," );
98113 }
99114
100115 // Recursively searches superclasses, filtering anonymous mixins,
@@ -107,31 +122,76 @@ class MetricsVisitor extends RecursiveVisitor {
107122 return node.name;
108123 }
109124
110- // Passes through the aggregated data and does post processing,
111- // adding classes that inherit.
125+ // Returns true if a class Annotation is Native.
126+ bool _isNativeMarkerAnnotation (Expression annotation) {
127+ if (annotation is ConstructorInvocation ) {
128+ var type = annotation.constructedType;
129+ if (type.classNode.name == "Native" ) {
130+ return true ;
131+ }
132+ }
133+ return false ;
134+ }
135+
136+ // Passes through the aggregated data and processes,
137+ // adding child classes and overridden methods from parent.
112138 void _processData () {
113139 classInfo.keys.forEach ((className) {
114140 var parentName = classInfo[className].parent;
115-
116141 if (classInfo[parentName] != null ) {
117142 classInfo[parentName].inheritedBy.add (className);
143+
144+ var notOverridden = < String > [];
145+ var parentMethods = classInfo[parentName].methods.map ((m) => m.name);
146+ var classMethods = classInfo[className].methods.map ((m) => m.name);
147+
148+ parentMethods.forEach ((method) =>
149+ {if (! classMethods.contains (method)) notOverridden.add (method)});
150+
151+ // Update Method Info.
152+ classInfo[className].notOverriddenMethods = notOverridden;
118153 }
119154 });
120155 }
156+
157+ // Saves the data to file.
158+ void saveDataToFile (String filename) {
159+ var formatted = jsonFormat (classInfo);
160+
161+ File (filename).writeAsStringSync (formatted);
162+ }
163+
164+ // Converts the passed Map to a pretty print JSON string.
165+ String jsonFormat (Map <String , ClassMetrics > info) {
166+ JsonEncoder encoder = new JsonEncoder .withIndent (" " );
167+ return encoder.convert (info);
168+ }
121169}
122170
123171/// Tracks info compiled for a class.
124172class ClassMetrics {
125173 List <ClassMetricsMethod > methods;
126174 List <String > mixins;
175+ List <String > implementedTypes;
176+ List <String > notOverriddenMethods;
127177 List <String > inheritedBy;
128178 String parent;
129179 bool mixed;
180+ bool containsNativeMember;
130181
131182 ClassMetrics (
132- {this .parent, this .mixed = false , mixins, methods, inheritedBy}) {
133- this .mixins = mixins ?? [];
183+ {this .mixed = false ,
184+ this .containsNativeMember = false ,
185+ this .parent,
186+ methods,
187+ mixins,
188+ notOverridden,
189+ implementedTypes,
190+ inheritedBy}) {
134191 this .methods = methods ?? [];
192+ this .mixins = mixins ?? [];
193+ this .notOverriddenMethods = notOverridden ?? [];
194+ this .implementedTypes = implementedTypes ?? [];
135195 this .inheritedBy = inheritedBy ?? [];
136196 }
137197
@@ -151,18 +211,37 @@ class ClassMetrics {
151211 "mixins" : mixins,
152212 "parent" : parent,
153213 "inheritedBy" : inheritedBy,
214+ "containsNativeMember" : containsNativeMember,
215+ "notOverriddenMethods" : notOverriddenMethods,
216+ "implementedTypes" : implementedTypes
154217 };
155218 }
156219}
157220
158221/// Tracks info related to a specific method.
159222class ClassMetricsMethod {
160223 String name;
224+ String methodKind;
161225 bool invokesSuper;
226+ bool isInstanceMember;
227+ bool isExternal;
228+ bool isAbstract;
162229
163- ClassMetricsMethod (this .name, [this .invokesSuper = false ]);
230+ ClassMetricsMethod (this .name,
231+ [this .invokesSuper = false ,
232+ this .isInstanceMember = false ,
233+ this .isExternal = false ,
234+ this .isAbstract = false ,
235+ this .methodKind = "" ]);
164236
165237 Map <String , dynamic > toJson () {
166- return {"name" : name, "invokesSuper" : invokesSuper};
238+ return {
239+ "name" : name,
240+ "invokesSuper" : invokesSuper,
241+ "isInstanceMember" : isInstanceMember,
242+ "isExternal" : isExternal,
243+ "isAbstract" : isAbstract,
244+ "methodKind" : methodKind
245+ };
167246 }
168247}
0 commit comments