Added checks to gather method data
Change-Id: I809e05ea3554ae9768d3f9cb25c824bbe36ca2bb
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/203600
Commit-Queue: Gabriel Castro <gabrielmcastro@google.com>
Reviewed-by: Nicholas Shahan <nshahan@google.com>
Reviewed-by: Srujan Gaddam <srujzs@google.com>
diff --git a/pkg/compiler/tool/kernel_visitor/dart_html_metrics_visitor.dart b/pkg/compiler/tool/kernel_visitor/dart_html_metrics_visitor.dart
index 353d7d1..13cc373 100644
--- a/pkg/compiler/tool/kernel_visitor/dart_html_metrics_visitor.dart
+++ b/pkg/compiler/tool/kernel_visitor/dart_html_metrics_visitor.dart
@@ -1,11 +1,12 @@
// Copyright (c) 2021, 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 file.
+import "dart:convert";
import "dart:io";
import "package:kernel/kernel.dart";
import "package:kernel/ast.dart";
-main(List<String> args) {
+main(List<String> args) async {
// Ensure right args are passed.
if (args.length < 1) {
print("usage: ${Platform.script} a.dill");
@@ -19,8 +20,8 @@
// Visit component.
component.accept(visitor);
- // Print compiled data.
- print(visitor.classInfo);
+ // Save data to file.
+ visitor.saveDataToFile("dart2html_metrics.json");
}
/// Visits classes in libraries specified by `libraryFilter`
@@ -55,12 +56,13 @@
@override
void visitProcedure(Procedure node) {
- // If this method invokes super, track for class.
- if (node.containsSuperCalls) {
- classInfo[currentClass]
- .methods
- .add(ClassMetricsMethod(node.name.text, true));
- }
+ classInfo[currentClass].methods.add(ClassMetricsMethod(
+ node.name.text,
+ node.containsSuperCalls,
+ node.isInstanceMember,
+ node.isExternal,
+ node.isAbstract,
+ node.kind.toString()));
}
@override
@@ -71,6 +73,11 @@
currentClass = node.name;
var metrics = ClassMetrics();
+ // Check if class contains native members.
+ if (node.annotations.any(_isNativeMarkerAnnotation)) {
+ metrics.containsNativeMember = true;
+ }
+
// Check if Mixed.
if (node.superclass?.isAnonymousMixin ?? false) {
metrics.mixed = true;
@@ -83,18 +90,26 @@
metrics.parent = unmangledParent;
}
+ // Check for implemented classes.
+ if (node.implementedTypes.length > 0) {
+ var implementedTypes =
+ node.implementedTypes.map((type) => type.className.asClass.name);
+ metrics.implementedTypes = implementedTypes.toList();
+ }
+
classInfo[currentClass] = metrics;
+
super.visitClass(node);
}
}
// Returns List of parsed mixins from superclass name.
List<String> _filterMixins(String superWithMixins) {
- var start = superWithMixins.indexOf('with') + 4;
+ var start = superWithMixins.indexOf("with") + 4;
var mixins = superWithMixins.substring(start);
- mixins = mixins.replaceAll(' ', '');
+ mixins = mixins.replaceAll(" ", "");
- return mixins.split(',');
+ return mixins.split(",");
}
// Recursively searches superclasses, filtering anonymous mixins,
@@ -107,31 +122,76 @@
return node.name;
}
- // Passes through the aggregated data and does post processing,
- // adding classes that inherit.
+ // Returns true if a class Annotation is Native.
+ bool _isNativeMarkerAnnotation(Expression annotation) {
+ if (annotation is ConstructorInvocation) {
+ var type = annotation.constructedType;
+ if (type.classNode.name == "Native") {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Passes through the aggregated data and processes,
+ // adding child classes and overridden methods from parent.
void _processData() {
classInfo.keys.forEach((className) {
var parentName = classInfo[className].parent;
-
if (classInfo[parentName] != null) {
classInfo[parentName].inheritedBy.add(className);
+
+ var notOverridden = <String>[];
+ var parentMethods = classInfo[parentName].methods.map((m) => m.name);
+ var classMethods = classInfo[className].methods.map((m) => m.name);
+
+ parentMethods.forEach((method) =>
+ {if (!classMethods.contains(method)) notOverridden.add(method)});
+
+ // Update Method Info.
+ classInfo[className].notOverriddenMethods = notOverridden;
}
});
}
+
+ // Saves the data to file.
+ void saveDataToFile(String filename) {
+ var formatted = jsonFormat(classInfo);
+
+ File(filename).writeAsStringSync(formatted);
+ }
+
+ // Converts the passed Map to a pretty print JSON string.
+ String jsonFormat(Map<String, ClassMetrics> info) {
+ JsonEncoder encoder = new JsonEncoder.withIndent(" ");
+ return encoder.convert(info);
+ }
}
/// Tracks info compiled for a class.
class ClassMetrics {
List<ClassMetricsMethod> methods;
List<String> mixins;
+ List<String> implementedTypes;
+ List<String> notOverriddenMethods;
List<String> inheritedBy;
String parent;
bool mixed;
+ bool containsNativeMember;
ClassMetrics(
- {this.parent, this.mixed = false, mixins, methods, inheritedBy}) {
- this.mixins = mixins ?? [];
+ {this.mixed = false,
+ this.containsNativeMember = false,
+ this.parent,
+ methods,
+ mixins,
+ notOverridden,
+ implementedTypes,
+ inheritedBy}) {
this.methods = methods ?? [];
+ this.mixins = mixins ?? [];
+ this.notOverriddenMethods = notOverridden ?? [];
+ this.implementedTypes = implementedTypes ?? [];
this.inheritedBy = inheritedBy ?? [];
}
@@ -151,6 +211,9 @@
"mixins": mixins,
"parent": parent,
"inheritedBy": inheritedBy,
+ "containsNativeMember": containsNativeMember,
+ "notOverriddenMethods": notOverriddenMethods,
+ "implementedTypes": implementedTypes
};
}
}
@@ -158,11 +221,27 @@
/// Tracks info related to a specific method.
class ClassMetricsMethod {
String name;
+ String methodKind;
bool invokesSuper;
+ bool isInstanceMember;
+ bool isExternal;
+ bool isAbstract;
- ClassMetricsMethod(this.name, [this.invokesSuper = false]);
+ ClassMetricsMethod(this.name,
+ [this.invokesSuper = false,
+ this.isInstanceMember = false,
+ this.isExternal = false,
+ this.isAbstract = false,
+ this.methodKind = ""]);
Map<String, dynamic> toJson() {
- return {"name": name, "invokesSuper": invokesSuper};
+ return {
+ "name": name,
+ "invokesSuper": invokesSuper,
+ "isInstanceMember": isInstanceMember,
+ "isExternal": isExternal,
+ "isAbstract": isAbstract,
+ "methodKind": methodKind
+ };
}
}
diff --git a/pkg/compiler/tool/kernel_visitor/test/info_visitor_test.dart b/pkg/compiler/tool/kernel_visitor/test/info_visitor_test.dart
index 9b20b89..1e24648 100644
--- a/pkg/compiler/tool/kernel_visitor/test/info_visitor_test.dart
+++ b/pkg/compiler/tool/kernel_visitor/test/info_visitor_test.dart
@@ -84,4 +84,13 @@
Expect.equals(visitor.classInfo["F"].mixed, true);
Expect.deepEquals(visitor.classInfo["F"].mixins, ["Mix1", "Mix2"]);
});
+
+ test("Class E implements A", () {
+ Expect.equals(visitor.classInfo["E"].implementedTypes.contains("A"), true);
+ });
+
+ test("Class G extends A but fails to override getValue()", () {
+ Expect.equals(
+ visitor.classInfo["G"].notOverriddenMethods.contains("getValue"), true);
+ });
}
diff --git a/pkg/compiler/tool/kernel_visitor/test/test_classes.dart b/pkg/compiler/tool/kernel_visitor/test/test_classes.dart
index 226aa2d..2b02ef8 100644
--- a/pkg/compiler/tool/kernel_visitor/test/test_classes.dart
+++ b/pkg/compiler/tool/kernel_visitor/test/test_classes.dart
@@ -43,3 +43,18 @@
class F extends B with Mix1, Mix2 {
F();
}
+
+// Test class with interface
+class E implements A {
+ E();
+
+ @override
+ getValue() {
+ return "E Value";
+ }
+}
+
+// Test class with unoverriden superclass method
+class G extends A {
+ G();
+}