| Index: lib/src/measurements.dart
|
| diff --git a/lib/src/measurements.dart b/lib/src/measurements.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..499f00173aa3cad09a6a0e8a6644b83d77881f40
|
| --- /dev/null
|
| +++ b/lib/src/measurements.dart
|
| @@ -0,0 +1,251 @@
|
| +// Copyright (c) 2015, 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.
|
| +
|
| +/// Measurements collected about individual functions. Currently we compute
|
| +/// data about "sends", to classify whether we know the target or not.
|
| +library dart2js_info.src.measurements;
|
| +
|
| +/// Top-level set of metrics
|
| +const List<Metric> _topLevelMetrics = const [
|
| + Metric.functions,
|
| + Metric.send,
|
| +];
|
| +
|
| +/// Apply `f` on each metric in DFS order on the metric tree. [Metric.functions]
|
| +/// and [Metric.send] are the top level metrics. See those declarations for
|
| +/// details on the subtrees.
|
| +visitAllMetrics(f) {
|
| + var parentsStack = [];
|
| + helper(Metric m) {
|
| + f(m, parentsStack);
|
| + if (m is GroupedMetric) {
|
| + parentsStack.add(m);
|
| + m.submetrics.forEach(helper);
|
| + parentsStack.removeLast();
|
| + }
|
| + }
|
| + _topLevelMetrics.forEach(helper);
|
| +}
|
| +
|
| +/// A metric we intend to measure.
|
| +class Metric {
|
| + /// Name for the metric.
|
| + final String name;
|
| +
|
| + const Metric(this.name);
|
| +
|
| + String toString() => name;
|
| +
|
| + /// Total functions in a library/package/program. Parent of
|
| + /// [reachableFunction].
|
| + static const Metric functions = const GroupedMetric('functions', const [
|
| + reachableFunctions,
|
| + ]);
|
| +
|
| + /// Subset of the functions that are reachable.
|
| + static const Metric reachableFunctions = const Metric('reachable functions');
|
| +
|
| + /// Parent of all send metrics. We classify sends as follows:
|
| + ///
|
| + /// sends
|
| + /// |- monomorphic
|
| + /// | |- static (top-levels, statics)
|
| + /// | |- super
|
| + /// | |- local (access to a local var, call local function)
|
| + /// | |- constructor (like factory ctros)
|
| + /// | |- type variable (reading a type variable)
|
| + /// | |- nsm (known no such method exception)
|
| + /// | |- single-nsm-call (known no such method call, single target)
|
| + /// | |- instance (non-interceptor, only one possible target)
|
| + /// | '- interceptor (interceptor, known)
|
| + /// |
|
| + /// '- polymorphic
|
| + /// |- multi-nsm (known to be nSM, but not sure if error, or call, or
|
| + /// which call)
|
| + /// |- virtual (traditional virtual call, polymorphic equivalent of
|
| + /// | `instance`, no-interceptor)
|
| + /// |- multi-interceptor (1 of n possible interceptors)
|
| + /// '- dynamic (any combination of the above)
|
| + ///
|
| + static const Metric send = const GroupedMetric('send', const [
|
| + monomorphicSend,
|
| + polymorphicSend,
|
| + ]);
|
| +
|
| + /// Parent of monomorphic sends, see [send] for details.
|
| + static const Metric monomorphicSend = const GroupedMetric('monomorphic',
|
| + const [
|
| + staticSend,
|
| + superSend,
|
| + localSend,
|
| + constructorSend,
|
| + typeVariableSend,
|
| + nsmErrorSend,
|
| + singleNsmCallSend,
|
| + instanceSend,
|
| + interceptorSend,
|
| + ]);
|
| +
|
| + /// Metric for static calls, see [send] for details.
|
| + static const Metric staticSend = const Metric('static');
|
| +
|
| + /// Metric for super calls, see [send] for details.
|
| + static const Metric superSend = const Metric('super');
|
| +
|
| + /// Metric for local variable sends, see [send] for details.
|
| + static const Metric localSend = const Metric('local');
|
| +
|
| + /// Metric for constructor sends, see [send] for details.
|
| + static const Metric constructorSend = const Metric('constructor');
|
| +
|
| + /// Metric for type-variable sends, see [send] for details.
|
| + // TODO(sigmund): delete? is mainly associated with compile-time errors
|
| + static const Metric typeVariableSend = const Metric('type variable');
|
| +
|
| + /// Metric for no-such-method errors, see [send] for details.
|
| + static const Metric nsmErrorSend = const Metric('nSM error');
|
| +
|
| + /// Metric for calls to noSuchMethod methods with a known target, see [send]
|
| + /// for details.
|
| + static const Metric singleNsmCallSend = const Metric('nSM call single');
|
| +
|
| + /// Metric for calls to a precisely known instance method, see [send] for
|
| + /// details.
|
| + static const Metric instanceSend = const Metric('instance');
|
| +
|
| + /// Metric for calls to a precisely known interceptor method, see [send] for
|
| + /// details.
|
| + static const Metric interceptorSend = const Metric('interceptor');
|
| +
|
| + /// Parent of polymorphic sends, see [send] for details.
|
| + static const Metric polymorphicSend = const GroupedMetric('polymorphic',
|
| + const [
|
| + multiNsmCallSend,
|
| + virtualSend,
|
| + multiInterceptorSend,
|
| + dynamicSend,
|
| + ]);
|
| +
|
| + /// Metric for calls to noSuchMethod methods with more than one possible
|
| + /// target, see [send] for details.
|
| + static const Metric multiNsmCallSend = const Metric('nSM call multi');
|
| +
|
| + /// Metric for calls that are dispatched virtually ar runtime, see [send] for
|
| + /// details.
|
| + static const Metric virtualSend = const Metric('virtual');
|
| +
|
| + /// Metyric for calls to more than one possible interceptor, see [send] for
|
| + /// details.
|
| + static const Metric multiInterceptorSend = const Metric('interceptor multi');
|
| +
|
| + /// Metyric for dynamic calls for which we know nothing about the target
|
| + /// method. See [send] for details.
|
| + static const Metric dynamicSend = const Metric('dynamic');
|
| +
|
| + String toJson() => name;
|
| + static Map<String, Metric> _nameToMetricMap = () {
|
| + var res = {};
|
| + visitAllMetrics((m, _) => res[m.name] = m);
|
| + return res;
|
| + }();
|
| +
|
| + static Metric fromJson(String name) => _nameToMetricMap[name];
|
| +}
|
| +
|
| +/// A metric that is subdivided in smaller metrics.
|
| +class GroupedMetric extends Metric {
|
| + final List<Metric> submetrics;
|
| +
|
| + const GroupedMetric(String name, this.submetrics) : super(name);
|
| +}
|
| +
|
| +/// A measurement entry (practically a source-span location where the
|
| +/// measurement was seen).
|
| +class Entry {
|
| + final int begin;
|
| + final int end;
|
| + Entry(this.begin, this.end);
|
| +}
|
| +
|
| +/// A collection of data points for each metric. Used to summarize a single
|
| +/// function, a library, a package, or an entire program.
|
| +class Measurements {
|
| + final Uri uri;
|
| + final Map<Metric, List<Entry>> entries;
|
| + final Map<Metric, int> counters;
|
| +
|
| + Measurements([this.uri])
|
| + : entries = <Metric, List<Entry>>{},
|
| + counters = <Metric, int>{};
|
| +
|
| + const Measurements.unreachableFunction()
|
| + : counters = const { Metric.functions: 1}, entries = const {}, uri = null;
|
| +
|
| + Measurements.reachableFunction([this.uri])
|
| + : counters = { Metric.functions: 1, Metric.reachableFunctions: 1},
|
| + entries = {};
|
| +
|
| + /// Record [metric] was seen. The optional [begin] and [end] offsets are
|
| + /// included for metrics that correspond to a source range. Intended to be
|
| + /// used by `StatsBuilder`.
|
| + record(Metric metric, [int begin, int end]) {
|
| + if (begin != null && end != null) {
|
| + assert(uri != null);
|
| + entries.putIfAbsent(metric, () => []).add(new Entry(begin, end));
|
| + }
|
| + counters.putIfAbsent(metric, () => 0);
|
| + counters[metric]++;
|
| + }
|
| +
|
| + /// Removes a previously added entry. Intended only to be used by
|
| + /// `StatsBuilder`. Internally `StatsBuilder` computes redundant information
|
| + /// in order to check for coverage and validate invariants with
|
| + /// [checkInvariant]. This is used to adjust some of the redundant
|
| + /// information.
|
| + popLast(Metric metric) {
|
| + assert(entries[metric] != null && entries[metric].isNotEmpty);
|
| + entries[metric].removeLast();
|
| + counters[metric]--;
|
| + }
|
| +
|
| + /// Add the counters from [other] into this set of measurements.
|
| + addFrom(Measurements other) {
|
| + other.counters.forEach((metric, value) {
|
| + var current = counters[metric];
|
| + counters[metric] = current == null ? value : current + value;
|
| + });
|
| + }
|
| +
|
| + /// Check that every grouped metric totals the individual counts of it's
|
| + /// submetric.
|
| + bool checkInvariant(GroupedMetric key) {
|
| + int total = counters[key] ?? 0;
|
| + int submetricTotal = 0;
|
| + for (var metric in key.submetrics) {
|
| + var n = counters[metric];
|
| + if (n != null) submetricTotal += n;
|
| + }
|
| + return total == submetricTotal;
|
| + }
|
| +
|
| + Map toJson() {
|
| + var jsonEntries = <String, List<Map>>{};
|
| + entries.forEach((metric, values) {
|
| + jsonEntries[metric.toJson()] =
|
| + values.expand((e) => [e.begin, e.end]).toList();
|
| + });
|
| + var json = {'entries': jsonEntries};
|
| + // TODO(sigmund): encode uri as an offset of the URIs available in the parts
|
| + // of the library info.
|
| + if (uri != null) json['sourceFile'] = '$uri';
|
| + if (counters[Metric.functions] != null) {
|
| + json[Metric.functions.toJson()] = counters[Metric.functions];
|
| + }
|
| + if (counters[Metric.reachableFunctions] != null) {
|
| + json[Metric.reachableFunctions.toJson()] =
|
| + counters[Metric.reachableFunctions];
|
| + }
|
| + return json;
|
| + }
|
| +}
|
|
|