From 670c642701c960e1f861ce08a25e3c0b46aef6af Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Tue, 12 Dec 2017 13:35:15 -0800 Subject: [PATCH 01/16] New class memoizer for simplifying Dartdoc's caching --- lib/src/model_utils.dart | 135 +++++++++++++++++++++++++++++++++++++ test/model_utils_test.dart | 72 ++++++++++++++++++++ 2 files changed, 207 insertions(+) diff --git a/lib/src/model_utils.dart b/lib/src/model_utils.dart index 0b8dd946a3..1271b8feb7 100644 --- a/lib/src/model_utils.dart +++ b/lib/src/model_utils.dart @@ -4,6 +4,7 @@ library dartdoc.model_utils; +import 'dart:collection'; import 'dart:convert'; import 'dart:io'; @@ -12,6 +13,8 @@ import 'package:analyzer/src/generated/engine.dart'; import 'package:analyzer/src/generated/sdk.dart'; import 'package:analyzer/src/generated/source_io.dart'; import 'package:dartdoc/src/model.dart'; +import 'package:quiver_hashcode/hashcode.dart'; +import 'package:tuple/tuple.dart'; import 'config.dart'; @@ -166,3 +169,135 @@ String crossdartifySource( } return newSource; } + +/// An UnmodifiableListView that computes equality and hashCode based on the +/// equality and hashCode of its contained objects. +class HashableList extends UnmodifiableListView { + HashableList(Iterable iterable) : super(iterable); + + @override + bool operator==(other) { + if (this.length == other.length) { + for (var index = 0; index < length; ++index) { + if (this[index] != other[index]) return false; + } + return true; + } + return false; + } + + @override + get hashCode => hashObjects(this); +} + +/// Extend or use as a mixin to track object-specific cached values, or +/// instantiate directly to track other values. +class MethodMemoizer { + /// Map of a function and its positional parameters (if any), to a value. + Map, dynamic> _memoizationTable; + + MethodMemoizer() { + invalidateMemos(); + } + + /// Reset the memoization table, forcing calls of the underlying functions. + void invalidateMemos() { _memoizationTable = new Map(); } + + /// Calls and caches the return value of [f]() if not in the cache, then + /// returns the cached value of [f](). + R memoized(Function f) { + Tuple2 key = new Tuple2(f, new HashableList([])); + if (!_memoizationTable.containsKey(key)) { + _memoizationTable[key] = f(); + } + return _memoizationTable[key]; + } + + /// Calls and caches the return value of [f]([param1]) if not in the cache, then + /// returns the cached value of [f]([param1]). + R memoized1(R Function(A) f, A param1) { + Tuple2 key = new Tuple2(f, new HashableList([param1])); + if (!_memoizationTable.containsKey(key)) { + _memoizationTable[key] = f(param1); + } + return _memoizationTable[key]; + } + + /// Calls and caches the return value of [f]([param1], [param2]) if not in the + /// cache, then returns the cached value of [f]([param1], [param2]). + R memoized2(R Function(A, B) f, A param1, B param2) { + Tuple2 key = new Tuple2(f, new HashableList([param1, param2])); + if (!_memoizationTable.containsKey(key)) { + _memoizationTable[key] = f(param1, param2); + } + return _memoizationTable[key]; + } + + /// Calls and caches the return value of [f]([param1], [param2], [param3]) if + /// not in the cache, then returns the cached value of [f]([param1], + /// [param2], [param3]). + R memoized3(R Function(A, B, C) f, A param1, B param2, C param3) { + Tuple2 key = new Tuple2(f, new HashableList([param1, param2, param3])); + if (!_memoizationTable.containsKey(key)) { + _memoizationTable[key] = f(param1, param2, param3); + } + return _memoizationTable[key]; + } + + /// Calls and caches the return value of [f]([param1], [param2], [param3], + /// [param4]) if not in the cache, then returns the cached value of + /// [f]([param1], [param2], [param3], [param4]). + R memoized4(R Function(A, B, C, D) f, A param1, B param2, C param3, D param4) { + Tuple2 key = new Tuple2(f, new HashableList([param1, param2, param3, param4])); + if (!_memoizationTable.containsKey(key)) { + _memoizationTable[key] = f(param1, param2, param3, param4); + } + return _memoizationTable[key]; + } + + /// Calls and caches the return value of [f]([param1], [param2], [param3], + /// [param4], [param5]) if not in the cache, then returns the cached value of [f]( + /// [param1], [param2], [param3], [param4], [param5]). + R memoized5(R Function(A, B, C, D, E) f, A param1, B param2, C param3, D param4, E param5) { + Tuple2 key = new Tuple2(f, new HashableList([param1, param2, param3, param4, param5])); + if (!_memoizationTable.containsKey(key)) { + _memoizationTable[key] = f(param1, param2, param3, param4, param5); + } + return _memoizationTable[key]; + } + + /// Calls and caches the return value of [f]([param1], [param2], [param3], + /// [param4], [param5], [param6]) if not in the cache, then returns the cached + /// value of [f]([param1], [param2], [param3], [param4], [param5], [param6]). + R memoized6(R Function(A, B, C, D, E, F) f, A param1, B param2, C param3, D param4, E param5, F param6) { + Tuple2 key = new Tuple2(f, new HashableList([param1, param2, param3, param4, param5, param6])); + if (!_memoizationTable.containsKey(key)) { + _memoizationTable[key] = f(param1, param2, param3, param4, param5, param6); + } + return _memoizationTable[key]; + } + + /// Calls and caches the return value of [f]([param1], [param2], [param3], + /// [param4], [param5], [param6], [param7]) if not in the cache, then returns + /// the cached value of [f]([param1], [param2], [param3], [param4], [param5], + /// [param6], [param7]). + R memoized7(R Function(A, B, C, D, E, F, G) f, A param1, B param2, C param3, D param4, E param5, F param6, G param7) { + Tuple2 key = new Tuple2(f, new HashableList([param1, param2, param3, param4, param5, param6, param7])); + if (!_memoizationTable.containsKey(key)) { + _memoizationTable[key] = f(param1, param2, param3, param4, param5, param6, param7); + } + return _memoizationTable[key]; + } + + /// Calls and caches the return value of [f]([param1], [param2], [param3], + /// [param4], [param5], [param6], [param7], [param8]) if not in the cache, + /// then returns the cached value of [f]([param1], [param2], [param3], + /// [param4], [param5], [param6], [param7], [param8]). + R memoized8(R Function(A, B, C, D, E, F, G, H) f, A param1, B param2, C param3, D param4, E param5, F param6, G param7, H param8) { + Tuple2 key = new Tuple2(f, new HashableList([param1, param2, param3, param4, param5, param6, param7, param8])); + if (!_memoizationTable.containsKey(key)) { + _memoizationTable[key] = f(param1, param2, param3, param4, param5, param6, param7, param8); + } + return _memoizationTable[key]; + } +} \ No newline at end of file diff --git a/test/model_utils_test.dart b/test/model_utils_test.dart index 7bc35cdfe2..cb87b2d16d 100644 --- a/test/model_utils_test.dart +++ b/test/model_utils_test.dart @@ -7,6 +7,38 @@ library dartdoc.model_utils_test; import 'package:dartdoc/src/model_utils.dart'; import 'package:test/test.dart'; +class MemoizerUser extends MethodMemoizer { + int foo = 0; + // These are actually not things you would ordinarily memoize, because + // they change. But they are useful for testing. + int _toMemoize() { return foo++; } + int get toMemoize => memoized(_toMemoize); + + String _memoizedParameter1(String param) => "${foo++} ${param}"; + String memoizedParameter1(String param) => memoized1(_memoizedParameter1, param); + + String _memoizedParameter2(String param, String param2) => "${foo++} ${param} ${param2}"; + String memoizedParameter2(String param, String param2) => memoized2(_memoizedParameter2, param, param2); + + String _memoizedParameter3(String param, String param2, String param3) => "${foo++} ${param} ${param2} ${param3}"; + String memoizedParameter3(String param, String param2, String param3) => memoized3(_memoizedParameter3, param, param2, param3); + + String _memoizedParameter4(String param, String param2, String param3, String param4) => "${foo++} ${param} ${param2} ${param3} ${param4}"; + String memoizedParameter4(String param, String param2, String param3, String param4) => memoized4(_memoizedParameter4, param, param2, param3, param4); + + String _memoizedParameter5(String param, String param2, String param3, String param4, String param5) => "${foo++} ${param} ${param2} ${param3} ${param4} ${param5}"; + String memoizedParameter5(String param, String param2, String param3, String param4, String param5) => memoized5(_memoizedParameter5, param, param2, param3, param4, param5); + + String _memoizedParameter6(String param, String param2, String param3, String param4, String param5, String param6) => "${foo++} ${param} ${param2} ${param3} ${param4} ${param5} ${param6}"; + String memoizedParameter6(String param, String param2, String param3, String param4, String param5, String param6) => memoized6(_memoizedParameter6, param, param2, param3, param4, param5, param6); + + String _memoizedParameter7(String param, String param2, String param3, String param4, String param5, String param6, String param7) => "${foo++} ${param} ${param2} ${param3} ${param4} ${param5} ${param6} ${param7}"; + String memoizedParameter7(String param, String param2, String param3, String param4, String param5, String param6, String param7) => memoized7(_memoizedParameter7, param, param2, param3, param4, param5, param6, param7); + + String _memoizedParameter8(String param, String param2, String param3, String param4, String param5, String param6, String param7, String param8) => "${foo++} ${param} ${param2} ${param3} ${param4} ${param5} ${param6} ${param7} ${param8}"; + String memoizedParameter8(String param, String param2, String param3, String param4, String param5, String param6, String param7, String param8) => memoized8(_memoizedParameter8, param, param2, param3, param4, param5, param6, param7, param8); +} + void main() { group('model_utils stripIndentFromSource', () { test('no indent', () { @@ -52,4 +84,44 @@ void main() { 'void foo() {\n print(1);\n}\n'); }); }); + + group('model_utils MethodMemoizer', () { + test('basic memoization and invalidation', () { + var m = new MemoizerUser(); + expect(m.toMemoize, equals(0), reason: "initialization problem"); + expect(m.toMemoize, equals(0), reason: "failed to memoize"); + m.invalidateMemos(); + expect(m.toMemoize, equals(1), reason: "failed to invalidate"); + }); + + test('memoization of a method with parameter', () { + var m = new MemoizerUser(); + expect(m.memoizedParameter1("hello"), equals("0 hello"), reason: "initialization problem"); + expect(m.memoizedParameter1("hello"), equals("0 hello"), reason: "failed to memoize"); + expect(m.memoizedParameter1("goodbye"), equals("1 goodbye")); + expect(m.memoizedParameter1("something"), equals("2 something")); + m.invalidateMemos(); + expect(m.memoizedParameter1("hello"), equals("3 hello"), reason: "failed to invalidate"); + }); + + test('memoization of many parameters', () { + var m = new MemoizerUser(); + expect(m.memoizedParameter1("hello"), equals("0 hello")); + expect(m.memoizedParameter2("hello", "obi"), equals("1 hello obi")); + expect(m.memoizedParameter3("hello", "obi", "wan"), equals("2 hello obi wan")); + expect(m.memoizedParameter4("hello", "obi", "wan", "how"), equals("3 hello obi wan how")); + expect(m.memoizedParameter5("hello", "obi", "wan", "how", "are"), equals("4 hello obi wan how are")); + expect(m.memoizedParameter6("hello", "obi", "wan", "how", "are", "you"), equals("5 hello obi wan how are you")); + expect(m.memoizedParameter7("hello", "obi", "wan", "how", "are", "you", "doing"), equals("6 hello obi wan how are you doing")); + expect(m.memoizedParameter8("hello", "obi", "wan", "how", "are", "you", "doing", "today"), equals("7 hello obi wan how are you doing today")); + expect(m.memoizedParameter1("hello"), equals("0 hello")); + expect(m.memoizedParameter2("hello", "obi"), equals("1 hello obi")); + expect(m.memoizedParameter3("hello", "obi", "wan"), equals("2 hello obi wan")); + expect(m.memoizedParameter4("hello", "obi", "wan", "how"), equals("3 hello obi wan how")); + expect(m.memoizedParameter5("hello", "obi", "wan", "how", "are"), equals("4 hello obi wan how are")); + expect(m.memoizedParameter6("hello", "obi", "wan", "how", "are", "you"), equals("5 hello obi wan how are you")); + expect(m.memoizedParameter7("hello", "obi", "wan", "how", "are", "you", "doing"), equals("6 hello obi wan how are you doing")); + expect(m.memoizedParameter8("hello", "obi", "wan", "how", "are", "you", "doing", "today"), equals("7 hello obi wan how are you doing today")); + }); + }); } From 851cfb03e62e9c75803576c874817acc8235aa95 Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Tue, 12 Dec 2017 13:59:38 -0800 Subject: [PATCH 02/16] Reduce copy-pasting to bare minimum --- lib/src/model_utils.dart | 75 ++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 45 deletions(-) diff --git a/lib/src/model_utils.dart b/lib/src/model_utils.dart index 1271b8feb7..31c48d6314 100644 --- a/lib/src/model_utils.dart +++ b/lib/src/model_utils.dart @@ -190,11 +190,15 @@ class HashableList extends UnmodifiableListView { get hashCode => hashObjects(this); } +class _MemoKey extends Tuple2 { + _MemoKey(Function f, HashableList l) : super(f, l) {} +} + /// Extend or use as a mixin to track object-specific cached values, or /// instantiate directly to track other values. class MethodMemoizer { /// Map of a function and its positional parameters (if any), to a value. - Map, dynamic> _memoizationTable; + Map<_MemoKey, dynamic> _memoizationTable; MethodMemoizer() { invalidateMemos(); @@ -203,78 +207,65 @@ class MethodMemoizer { /// Reset the memoization table, forcing calls of the underlying functions. void invalidateMemos() { _memoizationTable = new Map(); } - /// Calls and caches the return value of [f]() if not in the cache, then - /// returns the cached value of [f](). - R memoized(Function f) { - Tuple2 key = new Tuple2(f, new HashableList([])); + // Never write this if statement again. + R _getOrUpdateCache(R Function() f, _MemoKey key) { if (!_memoizationTable.containsKey(key)) { _memoizationTable[key] = f(); } return _memoizationTable[key]; } + /// Calls and caches the return value of [f]() if not in the cache, then + /// returns the cached value of [f](). + R memoized(Function f) { + _MemoKey key = new _MemoKey(f, new HashableList([])); + return _getOrUpdateCache(() => f(), key); + } + /// Calls and caches the return value of [f]([param1]) if not in the cache, then /// returns the cached value of [f]([param1]). R memoized1(R Function(A) f, A param1) { - Tuple2 key = new Tuple2(f, new HashableList([param1])); - if (!_memoizationTable.containsKey(key)) { - _memoizationTable[key] = f(param1); - } - return _memoizationTable[key]; + _MemoKey key = new _MemoKey(f, new HashableList([param1])); + return _getOrUpdateCache(() => f(param1), key); } /// Calls and caches the return value of [f]([param1], [param2]) if not in the /// cache, then returns the cached value of [f]([param1], [param2]). R memoized2(R Function(A, B) f, A param1, B param2) { - Tuple2 key = new Tuple2(f, new HashableList([param1, param2])); - if (!_memoizationTable.containsKey(key)) { - _memoizationTable[key] = f(param1, param2); - } - return _memoizationTable[key]; + _MemoKey key = new _MemoKey(f, new HashableList([param1, param2])); + return _getOrUpdateCache(() => f(param1, param2), key); } /// Calls and caches the return value of [f]([param1], [param2], [param3]) if /// not in the cache, then returns the cached value of [f]([param1], /// [param2], [param3]). R memoized3(R Function(A, B, C) f, A param1, B param2, C param3) { - Tuple2 key = new Tuple2(f, new HashableList([param1, param2, param3])); - if (!_memoizationTable.containsKey(key)) { - _memoizationTable[key] = f(param1, param2, param3); - } - return _memoizationTable[key]; + _MemoKey key = new _MemoKey(f, new HashableList([param1, param2, param3])); + return _getOrUpdateCache(() => f(param1, param2, param3), key); } /// Calls and caches the return value of [f]([param1], [param2], [param3], /// [param4]) if not in the cache, then returns the cached value of /// [f]([param1], [param2], [param3], [param4]). R memoized4(R Function(A, B, C, D) f, A param1, B param2, C param3, D param4) { - Tuple2 key = new Tuple2(f, new HashableList([param1, param2, param3, param4])); - if (!_memoizationTable.containsKey(key)) { - _memoizationTable[key] = f(param1, param2, param3, param4); - } - return _memoizationTable[key]; + _MemoKey key = new _MemoKey(f, new HashableList([param1, param2, param3, param4])); + return _getOrUpdateCache(() => f(param1, param2, param3, param4), key); } /// Calls and caches the return value of [f]([param1], [param2], [param3], /// [param4], [param5]) if not in the cache, then returns the cached value of [f]( /// [param1], [param2], [param3], [param4], [param5]). R memoized5(R Function(A, B, C, D, E) f, A param1, B param2, C param3, D param4, E param5) { - Tuple2 key = new Tuple2(f, new HashableList([param1, param2, param3, param4, param5])); - if (!_memoizationTable.containsKey(key)) { - _memoizationTable[key] = f(param1, param2, param3, param4, param5); - } - return _memoizationTable[key]; + _MemoKey key = new _MemoKey(f, new HashableList([param1, param2, param3, param4, param5])); + return _getOrUpdateCache(() => f(param1, param2, param3, param4, param5), key); } /// Calls and caches the return value of [f]([param1], [param2], [param3], /// [param4], [param5], [param6]) if not in the cache, then returns the cached /// value of [f]([param1], [param2], [param3], [param4], [param5], [param6]). R memoized6(R Function(A, B, C, D, E, F) f, A param1, B param2, C param3, D param4, E param5, F param6) { - Tuple2 key = new Tuple2(f, new HashableList([param1, param2, param3, param4, param5, param6])); - if (!_memoizationTable.containsKey(key)) { - _memoizationTable[key] = f(param1, param2, param3, param4, param5, param6); - } - return _memoizationTable[key]; + _MemoKey key = new _MemoKey(f, new HashableList([param1, param2, param3, param4, param5, param6])); + return _getOrUpdateCache(() => f(param1, param2, param3, param4, param5, param6), key); } /// Calls and caches the return value of [f]([param1], [param2], [param3], @@ -282,11 +273,8 @@ class MethodMemoizer { /// the cached value of [f]([param1], [param2], [param3], [param4], [param5], /// [param6], [param7]). R memoized7(R Function(A, B, C, D, E, F, G) f, A param1, B param2, C param3, D param4, E param5, F param6, G param7) { - Tuple2 key = new Tuple2(f, new HashableList([param1, param2, param3, param4, param5, param6, param7])); - if (!_memoizationTable.containsKey(key)) { - _memoizationTable[key] = f(param1, param2, param3, param4, param5, param6, param7); - } - return _memoizationTable[key]; + _MemoKey key = new _MemoKey(f, new HashableList([param1, param2, param3, param4, param5, param6, param7])); + return _getOrUpdateCache(() => f(param1, param2, param3, param4, param5, param6, param7), key); } /// Calls and caches the return value of [f]([param1], [param2], [param3], @@ -294,10 +282,7 @@ class MethodMemoizer { /// then returns the cached value of [f]([param1], [param2], [param3], /// [param4], [param5], [param6], [param7], [param8]). R memoized8(R Function(A, B, C, D, E, F, G, H) f, A param1, B param2, C param3, D param4, E param5, F param6, G param7, H param8) { - Tuple2 key = new Tuple2(f, new HashableList([param1, param2, param3, param4, param5, param6, param7, param8])); - if (!_memoizationTable.containsKey(key)) { - _memoizationTable[key] = f(param1, param2, param3, param4, param5, param6, param7, param8); - } - return _memoizationTable[key]; + _MemoKey key = new _MemoKey(f, new HashableList([param1, param2, param3, param4, param5, param6, param7, param8])); + return _getOrUpdateCache(() => f(param1, param2, param3, param4, param5, param6, param7, param8), key); } } \ No newline at end of file From e3b9a91c1cfb96391af1ec90a918fcd3640b934c Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Tue, 12 Dec 2017 14:00:26 -0800 Subject: [PATCH 03/16] One more tweak --- lib/src/model_utils.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/model_utils.dart b/lib/src/model_utils.dart index 31c48d6314..16c8c2caa4 100644 --- a/lib/src/model_utils.dart +++ b/lib/src/model_utils.dart @@ -219,7 +219,7 @@ class MethodMemoizer { /// returns the cached value of [f](). R memoized(Function f) { _MemoKey key = new _MemoKey(f, new HashableList([])); - return _getOrUpdateCache(() => f(), key); + return _getOrUpdateCache(f, key); } /// Calls and caches the return value of [f]([param1]) if not in the cache, then From f583d2ec49dca77c396b11c8fbef013c0999fbb0 Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Tue, 12 Dec 2017 14:02:57 -0800 Subject: [PATCH 04/16] dartfmt --- lib/src/model_utils.dart | 63 +++++++++++----- test/model_utils_test.dart | 144 +++++++++++++++++++++++++++---------- 2 files changed, 152 insertions(+), 55 deletions(-) diff --git a/lib/src/model_utils.dart b/lib/src/model_utils.dart index 16c8c2caa4..929ac4b904 100644 --- a/lib/src/model_utils.dart +++ b/lib/src/model_utils.dart @@ -176,7 +176,7 @@ class HashableList extends UnmodifiableListView { HashableList(Iterable iterable) : super(iterable); @override - bool operator==(other) { + bool operator ==(other) { if (this.length == other.length) { for (var index = 0; index < length; ++index) { if (this[index] != other[index]) return false; @@ -205,7 +205,9 @@ class MethodMemoizer { } /// Reset the memoization table, forcing calls of the underlying functions. - void invalidateMemos() { _memoizationTable = new Map(); } + void invalidateMemos() { + _memoizationTable = new Map(); + } // Never write this if statement again. R _getOrUpdateCache(R Function() f, _MemoKey key) { @@ -247,42 +249,69 @@ class MethodMemoizer { /// Calls and caches the return value of [f]([param1], [param2], [param3], /// [param4]) if not in the cache, then returns the cached value of /// [f]([param1], [param2], [param3], [param4]). - R memoized4(R Function(A, B, C, D) f, A param1, B param2, C param3, D param4) { - _MemoKey key = new _MemoKey(f, new HashableList([param1, param2, param3, param4])); + R memoized4( + R Function(A, B, C, D) f, A param1, B param2, C param3, D param4) { + _MemoKey key = + new _MemoKey(f, new HashableList([param1, param2, param3, param4])); return _getOrUpdateCache(() => f(param1, param2, param3, param4), key); } /// Calls and caches the return value of [f]([param1], [param2], [param3], /// [param4], [param5]) if not in the cache, then returns the cached value of [f]( /// [param1], [param2], [param3], [param4], [param5]). - R memoized5(R Function(A, B, C, D, E) f, A param1, B param2, C param3, D param4, E param5) { - _MemoKey key = new _MemoKey(f, new HashableList([param1, param2, param3, param4, param5])); - return _getOrUpdateCache(() => f(param1, param2, param3, param4, param5), key); + R memoized5(R Function(A, B, C, D, E) f, A param1, B param2, + C param3, D param4, E param5) { + _MemoKey key = new _MemoKey( + f, new HashableList([param1, param2, param3, param4, param5])); + return _getOrUpdateCache( + () => f(param1, param2, param3, param4, param5), key); } /// Calls and caches the return value of [f]([param1], [param2], [param3], /// [param4], [param5], [param6]) if not in the cache, then returns the cached /// value of [f]([param1], [param2], [param3], [param4], [param5], [param6]). - R memoized6(R Function(A, B, C, D, E, F) f, A param1, B param2, C param3, D param4, E param5, F param6) { - _MemoKey key = new _MemoKey(f, new HashableList([param1, param2, param3, param4, param5, param6])); - return _getOrUpdateCache(() => f(param1, param2, param3, param4, param5, param6), key); + R memoized6(R Function(A, B, C, D, E, F) f, A param1, + B param2, C param3, D param4, E param5, F param6) { + _MemoKey key = new _MemoKey( + f, new HashableList([param1, param2, param3, param4, param5, param6])); + return _getOrUpdateCache( + () => f(param1, param2, param3, param4, param5, param6), key); } /// Calls and caches the return value of [f]([param1], [param2], [param3], /// [param4], [param5], [param6], [param7]) if not in the cache, then returns /// the cached value of [f]([param1], [param2], [param3], [param4], [param5], /// [param6], [param7]). - R memoized7(R Function(A, B, C, D, E, F, G) f, A param1, B param2, C param3, D param4, E param5, F param6, G param7) { - _MemoKey key = new _MemoKey(f, new HashableList([param1, param2, param3, param4, param5, param6, param7])); - return _getOrUpdateCache(() => f(param1, param2, param3, param4, param5, param6, param7), key); + R memoized7(R Function(A, B, C, D, E, F, G) f, + A param1, B param2, C param3, D param4, E param5, F param6, G param7) { + _MemoKey key = new _MemoKey( + f, + new HashableList( + [param1, param2, param3, param4, param5, param6, param7])); + return _getOrUpdateCache( + () => f(param1, param2, param3, param4, param5, param6, param7), key); } /// Calls and caches the return value of [f]([param1], [param2], [param3], /// [param4], [param5], [param6], [param7], [param8]) if not in the cache, /// then returns the cached value of [f]([param1], [param2], [param3], /// [param4], [param5], [param6], [param7], [param8]). - R memoized8(R Function(A, B, C, D, E, F, G, H) f, A param1, B param2, C param3, D param4, E param5, F param6, G param7, H param8) { - _MemoKey key = new _MemoKey(f, new HashableList([param1, param2, param3, param4, param5, param6, param7, param8])); - return _getOrUpdateCache(() => f(param1, param2, param3, param4, param5, param6, param7, param8), key); + R memoized8( + R Function(A, B, C, D, E, F, G, H) f, + A param1, + B param2, + C param3, + D param4, + E param5, + F param6, + G param7, + H param8) { + _MemoKey key = new _MemoKey( + f, + new HashableList( + [param1, param2, param3, param4, param5, param6, param7, param8])); + return _getOrUpdateCache( + () => f(param1, param2, param3, param4, param5, param6, param7, param8), + key); } -} \ No newline at end of file +} diff --git a/test/model_utils_test.dart b/test/model_utils_test.dart index cb87b2d16d..a992ea02c5 100644 --- a/test/model_utils_test.dart +++ b/test/model_utils_test.dart @@ -11,32 +11,77 @@ class MemoizerUser extends MethodMemoizer { int foo = 0; // These are actually not things you would ordinarily memoize, because // they change. But they are useful for testing. - int _toMemoize() { return foo++; } + int _toMemoize() { + return foo++; + } + int get toMemoize => memoized(_toMemoize); String _memoizedParameter1(String param) => "${foo++} ${param}"; - String memoizedParameter1(String param) => memoized1(_memoizedParameter1, param); - - String _memoizedParameter2(String param, String param2) => "${foo++} ${param} ${param2}"; - String memoizedParameter2(String param, String param2) => memoized2(_memoizedParameter2, param, param2); - - String _memoizedParameter3(String param, String param2, String param3) => "${foo++} ${param} ${param2} ${param3}"; - String memoizedParameter3(String param, String param2, String param3) => memoized3(_memoizedParameter3, param, param2, param3); - - String _memoizedParameter4(String param, String param2, String param3, String param4) => "${foo++} ${param} ${param2} ${param3} ${param4}"; - String memoizedParameter4(String param, String param2, String param3, String param4) => memoized4(_memoizedParameter4, param, param2, param3, param4); - - String _memoizedParameter5(String param, String param2, String param3, String param4, String param5) => "${foo++} ${param} ${param2} ${param3} ${param4} ${param5}"; - String memoizedParameter5(String param, String param2, String param3, String param4, String param5) => memoized5(_memoizedParameter5, param, param2, param3, param4, param5); - - String _memoizedParameter6(String param, String param2, String param3, String param4, String param5, String param6) => "${foo++} ${param} ${param2} ${param3} ${param4} ${param5} ${param6}"; - String memoizedParameter6(String param, String param2, String param3, String param4, String param5, String param6) => memoized6(_memoizedParameter6, param, param2, param3, param4, param5, param6); - - String _memoizedParameter7(String param, String param2, String param3, String param4, String param5, String param6, String param7) => "${foo++} ${param} ${param2} ${param3} ${param4} ${param5} ${param6} ${param7}"; - String memoizedParameter7(String param, String param2, String param3, String param4, String param5, String param6, String param7) => memoized7(_memoizedParameter7, param, param2, param3, param4, param5, param6, param7); - - String _memoizedParameter8(String param, String param2, String param3, String param4, String param5, String param6, String param7, String param8) => "${foo++} ${param} ${param2} ${param3} ${param4} ${param5} ${param6} ${param7} ${param8}"; - String memoizedParameter8(String param, String param2, String param3, String param4, String param5, String param6, String param7, String param8) => memoized8(_memoizedParameter8, param, param2, param3, param4, param5, param6, param7, param8); + String memoizedParameter1(String param) => + memoized1(_memoizedParameter1, param); + + String _memoizedParameter2(String param, String param2) => + "${foo++} ${param} ${param2}"; + String memoizedParameter2(String param, String param2) => + memoized2(_memoizedParameter2, param, param2); + + String _memoizedParameter3(String param, String param2, String param3) => + "${foo++} ${param} ${param2} ${param3}"; + String memoizedParameter3(String param, String param2, String param3) => + memoized3(_memoizedParameter3, param, param2, param3); + + String _memoizedParameter4( + String param, String param2, String param3, String param4) => + "${foo++} ${param} ${param2} ${param3} ${param4}"; + String memoizedParameter4( + String param, String param2, String param3, String param4) => + memoized4(_memoizedParameter4, param, param2, param3, param4); + + String _memoizedParameter5(String param, String param2, String param3, + String param4, String param5) => + "${foo++} ${param} ${param2} ${param3} ${param4} ${param5}"; + String memoizedParameter5(String param, String param2, String param3, + String param4, String param5) => + memoized5(_memoizedParameter5, param, param2, param3, param4, param5); + + String _memoizedParameter6(String param, String param2, String param3, + String param4, String param5, String param6) => + "${foo++} ${param} ${param2} ${param3} ${param4} ${param5} ${param6}"; + String memoizedParameter6(String param, String param2, String param3, + String param4, String param5, String param6) => + memoized6( + _memoizedParameter6, param, param2, param3, param4, param5, param6); + + String _memoizedParameter7(String param, String param2, String param3, + String param4, String param5, String param6, String param7) => + "${foo++} ${param} ${param2} ${param3} ${param4} ${param5} ${param6} ${param7}"; + String memoizedParameter7(String param, String param2, String param3, + String param4, String param5, String param6, String param7) => + memoized7(_memoizedParameter7, param, param2, param3, param4, param5, + param6, param7); + + String _memoizedParameter8( + String param, + String param2, + String param3, + String param4, + String param5, + String param6, + String param7, + String param8) => + "${foo++} ${param} ${param2} ${param3} ${param4} ${param5} ${param6} ${param7} ${param8}"; + String memoizedParameter8( + String param, + String param2, + String param3, + String param4, + String param5, + String param6, + String param7, + String param8) => + memoized8(_memoizedParameter8, param, param2, param3, param4, param5, + param6, param7, param8); } void main() { @@ -96,32 +141,55 @@ void main() { test('memoization of a method with parameter', () { var m = new MemoizerUser(); - expect(m.memoizedParameter1("hello"), equals("0 hello"), reason: "initialization problem"); - expect(m.memoizedParameter1("hello"), equals("0 hello"), reason: "failed to memoize"); + expect(m.memoizedParameter1("hello"), equals("0 hello"), + reason: "initialization problem"); + expect(m.memoizedParameter1("hello"), equals("0 hello"), + reason: "failed to memoize"); expect(m.memoizedParameter1("goodbye"), equals("1 goodbye")); expect(m.memoizedParameter1("something"), equals("2 something")); m.invalidateMemos(); - expect(m.memoizedParameter1("hello"), equals("3 hello"), reason: "failed to invalidate"); + expect(m.memoizedParameter1("hello"), equals("3 hello"), + reason: "failed to invalidate"); }); test('memoization of many parameters', () { var m = new MemoizerUser(); expect(m.memoizedParameter1("hello"), equals("0 hello")); expect(m.memoizedParameter2("hello", "obi"), equals("1 hello obi")); - expect(m.memoizedParameter3("hello", "obi", "wan"), equals("2 hello obi wan")); - expect(m.memoizedParameter4("hello", "obi", "wan", "how"), equals("3 hello obi wan how")); - expect(m.memoizedParameter5("hello", "obi", "wan", "how", "are"), equals("4 hello obi wan how are")); - expect(m.memoizedParameter6("hello", "obi", "wan", "how", "are", "you"), equals("5 hello obi wan how are you")); - expect(m.memoizedParameter7("hello", "obi", "wan", "how", "are", "you", "doing"), equals("6 hello obi wan how are you doing")); - expect(m.memoizedParameter8("hello", "obi", "wan", "how", "are", "you", "doing", "today"), equals("7 hello obi wan how are you doing today")); + expect(m.memoizedParameter3("hello", "obi", "wan"), + equals("2 hello obi wan")); + expect(m.memoizedParameter4("hello", "obi", "wan", "how"), + equals("3 hello obi wan how")); + expect(m.memoizedParameter5("hello", "obi", "wan", "how", "are"), + equals("4 hello obi wan how are")); + expect(m.memoizedParameter6("hello", "obi", "wan", "how", "are", "you"), + equals("5 hello obi wan how are you")); + expect( + m.memoizedParameter7( + "hello", "obi", "wan", "how", "are", "you", "doing"), + equals("6 hello obi wan how are you doing")); + expect( + m.memoizedParameter8( + "hello", "obi", "wan", "how", "are", "you", "doing", "today"), + equals("7 hello obi wan how are you doing today")); expect(m.memoizedParameter1("hello"), equals("0 hello")); expect(m.memoizedParameter2("hello", "obi"), equals("1 hello obi")); - expect(m.memoizedParameter3("hello", "obi", "wan"), equals("2 hello obi wan")); - expect(m.memoizedParameter4("hello", "obi", "wan", "how"), equals("3 hello obi wan how")); - expect(m.memoizedParameter5("hello", "obi", "wan", "how", "are"), equals("4 hello obi wan how are")); - expect(m.memoizedParameter6("hello", "obi", "wan", "how", "are", "you"), equals("5 hello obi wan how are you")); - expect(m.memoizedParameter7("hello", "obi", "wan", "how", "are", "you", "doing"), equals("6 hello obi wan how are you doing")); - expect(m.memoizedParameter8("hello", "obi", "wan", "how", "are", "you", "doing", "today"), equals("7 hello obi wan how are you doing today")); + expect(m.memoizedParameter3("hello", "obi", "wan"), + equals("2 hello obi wan")); + expect(m.memoizedParameter4("hello", "obi", "wan", "how"), + equals("3 hello obi wan how")); + expect(m.memoizedParameter5("hello", "obi", "wan", "how", "are"), + equals("4 hello obi wan how are")); + expect(m.memoizedParameter6("hello", "obi", "wan", "how", "are", "you"), + equals("5 hello obi wan how are you")); + expect( + m.memoizedParameter7( + "hello", "obi", "wan", "how", "are", "you", "doing"), + equals("6 hello obi wan how are you doing")); + expect( + m.memoizedParameter8( + "hello", "obi", "wan", "how", "are", "you", "doing", "today"), + equals("7 hello obi wan how are you doing today")); }); }); } From ac450e6a958e054029a8b3ef086a543bbb9df8e5 Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Tue, 12 Dec 2017 14:20:43 -0800 Subject: [PATCH 05/16] Add the motivating case --- lib/src/model_utils.dart | 5 +++-- test/model_utils_test.dart | 2 +- tool/grind.dart | 30 ++++++++---------------------- 3 files changed, 12 insertions(+), 25 deletions(-) diff --git a/lib/src/model_utils.dart b/lib/src/model_utils.dart index 929ac4b904..bca64addaa 100644 --- a/lib/src/model_utils.dart +++ b/lib/src/model_utils.dart @@ -190,17 +190,18 @@ class HashableList extends UnmodifiableListView { get hashCode => hashObjects(this); } +/// A type alias for [Tuple2]``. class _MemoKey extends Tuple2 { _MemoKey(Function f, HashableList l) : super(f, l) {} } /// Extend or use as a mixin to track object-specific cached values, or /// instantiate directly to track other values. -class MethodMemoizer { +class Memoizer { /// Map of a function and its positional parameters (if any), to a value. Map<_MemoKey, dynamic> _memoizationTable; - MethodMemoizer() { + Memoizer() { invalidateMemos(); } diff --git a/test/model_utils_test.dart b/test/model_utils_test.dart index a992ea02c5..9fe5c42a51 100644 --- a/test/model_utils_test.dart +++ b/test/model_utils_test.dart @@ -7,7 +7,7 @@ library dartdoc.model_utils_test; import 'package:dartdoc/src/model_utils.dart'; import 'package:test/test.dart'; -class MemoizerUser extends MethodMemoizer { +class MemoizerUser extends Memoizer { int foo = 0; // These are actually not things you would ordinarily memoize, because // they change. But they are useful for testing. diff --git a/tool/grind.dart b/tool/grind.dart index 3601561f9c..3b7d53edb2 100644 --- a/tool/grind.dart +++ b/tool/grind.dart @@ -7,35 +7,21 @@ import 'dart:convert'; import 'dart:io' hide ProcessException; import 'package:dartdoc/src/io_utils.dart'; +import 'package:dartdoc/src/model_utils.dart'; import 'package:grinder/grinder.dart'; import 'package:path/path.dart' as path; import 'package:yaml/yaml.dart' as yaml; main([List args]) => grind(args); -Directory _dartdocDocsDir; -Directory get dartdocDocsDir { - if (_dartdocDocsDir == null) { - _dartdocDocsDir = Directory.systemTemp.createTempSync('dartdoc'); - } - return _dartdocDocsDir; -} +final Memoizer tempdirsCache = new Memoizer(); -Directory _sdkDocsDir; -Directory get sdkDocsDir { - if (_sdkDocsDir == null) { - _sdkDocsDir = Directory.systemTemp.createTempSync('sdkdocs'); - } - return _sdkDocsDir; -} - -Directory _flutterDir; -Directory get flutterDir { - if (_flutterDir == null) { - _flutterDir = Directory.systemTemp.createTempSync('flutter'); - } - return _flutterDir; -} +Directory get dartdocDocsDir => + tempdirsCache.memoized1(Directory.systemTemp.createTempSync, 'dartdoc'); +Directory get sdkDocsDir => + tempdirsCache.memoized1(Directory.systemTemp.createTempSync, 'sdkdocs'); +Directory get flutterDir => + tempdirsCache.memoized1(Directory.systemTemp.createTempSync, 'flutter'); final Directory flutterDirDevTools = new Directory(path.join(flutterDir.path, 'dev', 'tools')); From afe4aa6335162060d3f5db209742e5f4ac096e06 Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Tue, 12 Dec 2017 15:13:33 -0800 Subject: [PATCH 06/16] systemTemp was not a constant --- tool/grind.dart | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tool/grind.dart b/tool/grind.dart index 3b7d53edb2..5429089ed8 100644 --- a/tool/grind.dart +++ b/tool/grind.dart @@ -14,14 +14,16 @@ import 'package:yaml/yaml.dart' as yaml; main([List args]) => grind(args); +// Directory.systemTemp is not a constant. So wrap it. +Directory createTempSync(String prefix) => + Directory.systemTemp.createTempSync(prefix); + final Memoizer tempdirsCache = new Memoizer(); Directory get dartdocDocsDir => - tempdirsCache.memoized1(Directory.systemTemp.createTempSync, 'dartdoc'); -Directory get sdkDocsDir => - tempdirsCache.memoized1(Directory.systemTemp.createTempSync, 'sdkdocs'); -Directory get flutterDir => - tempdirsCache.memoized1(Directory.systemTemp.createTempSync, 'flutter'); + tempdirsCache.memoized1(createTempSync, 'dartdoc'); +Directory get sdkDocsDir => tempdirsCache.memoized1(createTempSync, 'sdkdocs'); +Directory get flutterDir => tempdirsCache.memoized1(createTempSync, 'flutter'); final Directory flutterDirDevTools = new Directory(path.join(flutterDir.path, 'dev', 'tools')); @@ -130,6 +132,8 @@ analyze() async { @Depends(analyze, test, testDartdoc) buildbot() => null; +void anOrdinaryFunction() {} + @Task('Generate docs for the Dart SDK') Future buildSdkDocs() async { log('building SDK docs'); From 5f66b7abb4a2d55b41e8d0b0e6960cfe8bd12d30 Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Tue, 12 Dec 2017 15:16:10 -0800 Subject: [PATCH 07/16] Cleanup --- tool/grind.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/tool/grind.dart b/tool/grind.dart index 5429089ed8..d84e372b72 100644 --- a/tool/grind.dart +++ b/tool/grind.dart @@ -132,8 +132,6 @@ analyze() async { @Depends(analyze, test, testDartdoc) buildbot() => null; -void anOrdinaryFunction() {} - @Task('Generate docs for the Dart SDK') Future buildSdkDocs() async { log('building SDK docs'); From 5a22c16f0021dd9a9ccca5fd04b5cdcde456a66b Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Wed, 13 Dec 2017 09:21:15 -0800 Subject: [PATCH 08/16] Review comments --- lib/src/model_utils.dart | 80 ++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/lib/src/model_utils.dart b/lib/src/model_utils.dart index bca64addaa..2aed1855ef 100644 --- a/lib/src/model_utils.dart +++ b/lib/src/model_utils.dart @@ -14,7 +14,6 @@ import 'package:analyzer/src/generated/sdk.dart'; import 'package:analyzer/src/generated/source_io.dart'; import 'package:dartdoc/src/model.dart'; import 'package:quiver_hashcode/hashcode.dart'; -import 'package:tuple/tuple.dart'; import 'config.dart'; @@ -172,16 +171,18 @@ String crossdartifySource( /// An UnmodifiableListView that computes equality and hashCode based on the /// equality and hashCode of its contained objects. -class HashableList extends UnmodifiableListView { - HashableList(Iterable iterable) : super(iterable); +class _HashableList extends UnmodifiableListView { + _HashableList(Iterable iterable) : super(iterable); @override bool operator ==(other) { - if (this.length == other.length) { - for (var index = 0; index < length; ++index) { - if (this[index] != other[index]) return false; + if (other is _HashableList) { + if (this.length == other.length) { + for (var index = 0; index < length; ++index) { + if (this[index] != other[index]) return false; + } + return true; } - return true; } return false; } @@ -190,16 +191,35 @@ class HashableList extends UnmodifiableListView { get hashCode => hashObjects(this); } -/// A type alias for [Tuple2]``. -class _MemoKey extends Tuple2 { - _MemoKey(Function f, HashableList l) : super(f, l) {} -} - /// Extend or use as a mixin to track object-specific cached values, or /// instantiate directly to track other values. +/// +/// For all methods in this class, the parameter [f] must be a tear-off method +/// or top level function (not an inline closure) for memoization to work. +/// [Memoizer] depends on the equality operator on the given function to detect +/// when we are calling the same function. +/// +/// Use: +/// +/// ```dart +/// String aTestFunction(String greeting, String name) => "${greeting}, ${name}"; +/// int aSlowFunction() { doSome(); return expensiveCalculations(); } +/// +/// myMemoizer.memoized2(aTestFunction, "Hello, "world"); +/// myMemoizer.memoized(aSlowFunction); +/// ``` +/// +/// *Not*: +/// +/// ```dart +/// String aTestFunction(String greeting, String name) => "${greeting}, ${name}"; +/// +/// myMemoizer.memoized2((a, b) => aTestFunction(a, b), "Hello", "world"); +/// myMemoizer.memoized(() => aSlowFunction());; +/// ``` class Memoizer { /// Map of a function and its positional parameters (if any), to a value. - Map<_MemoKey, dynamic> _memoizationTable; + Map<_HashableList, dynamic> _memoizationTable; Memoizer() { invalidateMemos(); @@ -211,7 +231,7 @@ class Memoizer { } // Never write this if statement again. - R _getOrUpdateCache(R Function() f, _MemoKey key) { + R _getOrUpdateCache(R Function() f, _HashableList key) { if (!_memoizationTable.containsKey(key)) { _memoizationTable[key] = f(); } @@ -220,22 +240,24 @@ class Memoizer { /// Calls and caches the return value of [f]() if not in the cache, then /// returns the cached value of [f](). + /// + R memoized(Function f) { - _MemoKey key = new _MemoKey(f, new HashableList([])); + _HashableList key = new _HashableList([f]); return _getOrUpdateCache(f, key); } /// Calls and caches the return value of [f]([param1]) if not in the cache, then /// returns the cached value of [f]([param1]). R memoized1(R Function(A) f, A param1) { - _MemoKey key = new _MemoKey(f, new HashableList([param1])); + _HashableList key = new _HashableList([f, param1]); return _getOrUpdateCache(() => f(param1), key); } /// Calls and caches the return value of [f]([param1], [param2]) if not in the /// cache, then returns the cached value of [f]([param1], [param2]). R memoized2(R Function(A, B) f, A param1, B param2) { - _MemoKey key = new _MemoKey(f, new HashableList([param1, param2])); + _HashableList key = new _HashableList([f, param1, param2]); return _getOrUpdateCache(() => f(param1, param2), key); } @@ -243,7 +265,7 @@ class Memoizer { /// not in the cache, then returns the cached value of [f]([param1], /// [param2], [param3]). R memoized3(R Function(A, B, C) f, A param1, B param2, C param3) { - _MemoKey key = new _MemoKey(f, new HashableList([param1, param2, param3])); + _HashableList key = new _HashableList([f, param1, param2, param3]); return _getOrUpdateCache(() => f(param1, param2, param3), key); } @@ -252,8 +274,8 @@ class Memoizer { /// [f]([param1], [param2], [param3], [param4]). R memoized4( R Function(A, B, C, D) f, A param1, B param2, C param3, D param4) { - _MemoKey key = - new _MemoKey(f, new HashableList([param1, param2, param3, param4])); + _HashableList key = + new _HashableList([f, param1, param2, param3, param4]); return _getOrUpdateCache(() => f(param1, param2, param3, param4), key); } @@ -262,8 +284,7 @@ class Memoizer { /// [param1], [param2], [param3], [param4], [param5]). R memoized5(R Function(A, B, C, D, E) f, A param1, B param2, C param3, D param4, E param5) { - _MemoKey key = new _MemoKey( - f, new HashableList([param1, param2, param3, param4, param5])); + _HashableList key = new _HashableList([f, param1, param2, param3, param4, param5]); return _getOrUpdateCache( () => f(param1, param2, param3, param4, param5), key); } @@ -273,8 +294,7 @@ class Memoizer { /// value of [f]([param1], [param2], [param3], [param4], [param5], [param6]). R memoized6(R Function(A, B, C, D, E, F) f, A param1, B param2, C param3, D param4, E param5, F param6) { - _MemoKey key = new _MemoKey( - f, new HashableList([param1, param2, param3, param4, param5, param6])); + _HashableList key = new _HashableList([f, param1, param2, param3, param4, param5, param6]); return _getOrUpdateCache( () => f(param1, param2, param3, param4, param5, param6), key); } @@ -285,10 +305,8 @@ class Memoizer { /// [param6], [param7]). R memoized7(R Function(A, B, C, D, E, F, G) f, A param1, B param2, C param3, D param4, E param5, F param6, G param7) { - _MemoKey key = new _MemoKey( - f, - new HashableList( - [param1, param2, param3, param4, param5, param6, param7])); + _HashableList key = new _HashableList( + [f, param1, param2, param3, param4, param5, param6, param7]); return _getOrUpdateCache( () => f(param1, param2, param3, param4, param5, param6, param7), key); } @@ -307,10 +325,8 @@ class Memoizer { F param6, G param7, H param8) { - _MemoKey key = new _MemoKey( - f, - new HashableList( - [param1, param2, param3, param4, param5, param6, param7, param8])); + _HashableList key = new _HashableList( + [f, param1, param2, param3, param4, param5, param6, param7, param8]); return _getOrUpdateCache( () => f(param1, param2, param3, param4, param5, param6, param7, param8), key); From a05f3954915f586a0183be9f9b30bc19638aaca6 Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Wed, 13 Dec 2017 09:21:43 -0800 Subject: [PATCH 09/16] dartfmt --- lib/src/model_utils.dart | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/src/model_utils.dart b/lib/src/model_utils.dart index 2aed1855ef..983d06c3e2 100644 --- a/lib/src/model_utils.dart +++ b/lib/src/model_utils.dart @@ -243,21 +243,21 @@ class Memoizer { /// R memoized(Function f) { - _HashableList key = new _HashableList([f]); + _HashableList key = new _HashableList([f]); return _getOrUpdateCache(f, key); } /// Calls and caches the return value of [f]([param1]) if not in the cache, then /// returns the cached value of [f]([param1]). R memoized1(R Function(A) f, A param1) { - _HashableList key = new _HashableList([f, param1]); + _HashableList key = new _HashableList([f, param1]); return _getOrUpdateCache(() => f(param1), key); } /// Calls and caches the return value of [f]([param1], [param2]) if not in the /// cache, then returns the cached value of [f]([param1], [param2]). R memoized2(R Function(A, B) f, A param1, B param2) { - _HashableList key = new _HashableList([f, param1, param2]); + _HashableList key = new _HashableList([f, param1, param2]); return _getOrUpdateCache(() => f(param1, param2), key); } @@ -265,7 +265,7 @@ class Memoizer { /// not in the cache, then returns the cached value of [f]([param1], /// [param2], [param3]). R memoized3(R Function(A, B, C) f, A param1, B param2, C param3) { - _HashableList key = new _HashableList([f, param1, param2, param3]); + _HashableList key = new _HashableList([f, param1, param2, param3]); return _getOrUpdateCache(() => f(param1, param2, param3), key); } @@ -274,8 +274,7 @@ class Memoizer { /// [f]([param1], [param2], [param3], [param4]). R memoized4( R Function(A, B, C, D) f, A param1, B param2, C param3, D param4) { - _HashableList key = - new _HashableList([f, param1, param2, param3, param4]); + _HashableList key = new _HashableList([f, param1, param2, param3, param4]); return _getOrUpdateCache(() => f(param1, param2, param3, param4), key); } @@ -284,7 +283,8 @@ class Memoizer { /// [param1], [param2], [param3], [param4], [param5]). R memoized5(R Function(A, B, C, D, E) f, A param1, B param2, C param3, D param4, E param5) { - _HashableList key = new _HashableList([f, param1, param2, param3, param4, param5]); + _HashableList key = + new _HashableList([f, param1, param2, param3, param4, param5]); return _getOrUpdateCache( () => f(param1, param2, param3, param4, param5), key); } @@ -294,7 +294,8 @@ class Memoizer { /// value of [f]([param1], [param2], [param3], [param4], [param5], [param6]). R memoized6(R Function(A, B, C, D, E, F) f, A param1, B param2, C param3, D param4, E param5, F param6) { - _HashableList key = new _HashableList([f, param1, param2, param3, param4, param5, param6]); + _HashableList key = + new _HashableList([f, param1, param2, param3, param4, param5, param6]); return _getOrUpdateCache( () => f(param1, param2, param3, param4, param5, param6), key); } @@ -306,7 +307,7 @@ class Memoizer { R memoized7(R Function(A, B, C, D, E, F, G) f, A param1, B param2, C param3, D param4, E param5, F param6, G param7) { _HashableList key = new _HashableList( - [f, param1, param2, param3, param4, param5, param6, param7]); + [f, param1, param2, param3, param4, param5, param6, param7]); return _getOrUpdateCache( () => f(param1, param2, param3, param4, param5, param6, param7), key); } @@ -326,7 +327,7 @@ class Memoizer { G param7, H param8) { _HashableList key = new _HashableList( - [f, param1, param2, param3, param4, param5, param6, param7, param8]); + [f, param1, param2, param3, param4, param5, param6, param7, param8]); return _getOrUpdateCache( () => f(param1, param2, param3, param4, param5, param6, param7, param8), key); From 91ced783b16801f839601d68bb66e0ad1b2df313 Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Wed, 13 Dec 2017 14:50:23 -0800 Subject: [PATCH 10/16] First version of SDK warnings comparison --- pubspec.lock | 2 +- tool/grind.dart | 175 +++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 153 insertions(+), 24 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 566f015931..d505112a10 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -374,4 +374,4 @@ packages: source: hosted version: "2.1.13" sdks: - dart: ">=1.23.0 <=2.0.0-dev.10.0" + dart: ">=1.23.0 <=2.0.0-dev.12.0" diff --git a/tool/grind.dart b/tool/grind.dart index d84e372b72..40420859f1 100644 --- a/tool/grind.dart +++ b/tool/grind.dart @@ -25,20 +25,34 @@ Directory get dartdocDocsDir => Directory get sdkDocsDir => tempdirsCache.memoized1(createTempSync, 'sdkdocs'); Directory get flutterDir => tempdirsCache.memoized1(createTempSync, 'flutter'); +/// Version of dartdoc we should use when making comparisons. +String get dartdocOriginalBranch { + String branch = 'master'; + if (Platform.environment.containsKey('DARTDOC_ORIGINAL')) { + branch = Platform.environment['DARTDOC_ORIGINAL']; + } + return branch; +} + + final Directory flutterDirDevTools = new Directory(path.join(flutterDir.path, 'dev', 'tools')); final RegExp quotables = new RegExp(r'[ "\r\n\$]'); // from flutter:dev/tools/dartdoc.dart, modified void _printStream(Stream> stream, Stdout output, - {String prefix: ''}) { + {String prefix: '', Iterable Function(String line) filter}) { assert(prefix != null); + if (filter == null) filter = (line) => [line]; stream .transform(UTF8.decoder) .transform(const LineSplitter()) + .expand(filter) .listen((String line) { - output.write('$prefix$line'.trim()); - output.write('\n'); + if (line != null) { + output.write('$prefix$line'.trim()); + output.write('\n'); + } }); } @@ -71,13 +85,37 @@ class _SubprocessLauncher { /// A wrapper around start/await process.exitCode that will display the /// output of the executable continuously and fail on non-zero exit codes. + /// It will also parse any valid JSON objects (one per line) it encounters + /// on stdout/stderr, and return them. + /// /// Makes running programs in grinder similar to set -ex for bash, even on /// Windows (though some of the bashisms will no longer make sense). /// TODO(jcollins-g): move this to grinder? - Future runStreamed(String executable, List arguments, + Future> runStreamed(String executable, List arguments, {String workingDirectory}) async { + List jsonObjects = new List(); + + /// Allow us to pretend we didn't pass the JSON flag in to dartdoc by + /// printing what dartdoc would have printed without it, yet storing + /// json objects into [jsonObjects]. + Iterable jsonCallback(String line) { + Map result; + try { + result = json.decoder.convert(line); + } catch (FormatException) {} + if (result != null) { + jsonObjects.add(result); + if (result.containsKey('message')) { + line = result['message']; + } else if (result.containsKey('data')) { + line = result['data']['text']; + } + } + return line.split('\n'); + } + stderr.write('$prefix+ '); - if (workingDirectory != null) stderr.write('cd "$workingDirectory" && '); + if (workingDirectory != null) stderr.write('(cd "$workingDirectory" && '); if (environment != null) { stderr.write(environment.keys.map((String key) { if (environment[key].contains(quotables)) { @@ -103,14 +141,16 @@ class _SubprocessLauncher { Process process = await Process.start(executable, arguments, workingDirectory: workingDirectory, environment: environment); - _printStream(process.stdout, stdout, prefix: prefix); - _printStream(process.stderr, stderr, prefix: prefix); + _printStream(process.stdout, stdout, prefix: prefix, filter: jsonCallback); + _printStream(process.stderr, stderr, prefix: prefix, filter: jsonCallback); await process.exitCode; int exitCode = await process.exitCode; if (exitCode != 0) { fail("exitCode: $exitCode"); } + + return jsonObjects; } } @@ -135,15 +175,99 @@ buildbot() => null; @Task('Generate docs for the Dart SDK') Future buildSdkDocs() async { log('building SDK docs'); - var launcher = new _SubprocessLauncher('build-sdk-docs'); - await launcher.runStreamed(Platform.resolvedExecutable, [ + await _buildSdkDocs(sdkDocsDir.path, new Future.value(Directory.current.path)); +} + +/// Returns a map of warning texts to the number of times each has been seen. +Map jsonMessageIterableToWarnings(Iterable messageIterable) { + Map warningTexts = new Map(); + for (Map message in messageIterable) { + if (message.containsKey('level') && message['level'] == 'WARNING' && message.containsKey('data')) { + warningTexts.putIfAbsent(message['data']['text'], () => 0); + warningTexts[message['data']['text']]++; + } + } + return warningTexts; +} + +void printWarningDelta(String title, Map original, Map current) { + Set quantityChangedOuts = new Set(); + Set onlyOriginal = new Set(); + Set onlyCurrent = new Set(); + Set allKeys = new Set.from([]..addAll(original.keys)..addAll(current.keys)); + + for (String key in allKeys) { + if (original.containsKey(key) && !current.containsKey(key)) { + onlyOriginal.add(key); + } else if (!original.containsKey(key) && current.containsKey(key)) { + onlyCurrent.add(key); + } else if (original.containsKey(key) && current.containsKey(key) && + original[key] != current[key]) { + quantityChangedOuts.add(key); + } + } + + if (onlyOriginal.isNotEmpty) { + print('*** $title : ${onlyOriginal.length} warnings from original ($dartdocOriginalBranch) missing in current:'); + onlyOriginal.forEach((warning) => print(warning)); + } + if (onlyCurrent.isNotEmpty) { + print('*** $title : ${onlyCurrent.length} new warnings not in original ($dartdocOriginalBranch)'); + onlyCurrent.forEach((warning) => print(warning)); + } + if (quantityChangedOuts.isNotEmpty) { + print('*** $title : Identical warning quantity changed'); + for (String key in quantityChangedOuts) { + print("* Appeared ${original[key]} times in original ($dartdocOriginalBranch), now ${current[key]}:"); + print(key); + } + } + if (onlyOriginal.isEmpty && onlyCurrent.isEmpty && quantityChangedOuts.isEmpty) { + print('*** $title : No difference in warning output from original ($dartdocOriginalBranch)${allKeys.isEmpty ? "" : " (${allKeys.length} warnings found)"}'); + } +} + +@Task('Display delta in SDK warnings') +Future compareSdkWarnings() async { + Directory originalDartdocSdkDocs = Directory.systemTemp.createTempSync('dartdoc-comparison-sdkdocs'); + Future originalDartdoc = createComparisonDartdoc(); + Future currentDartdocSdkBuild = _buildSdkDocs(sdkDocsDir.path, new Future.value(Directory.current.path), 'current'); + Future originalDartdocSdkBuild = _buildSdkDocs(originalDartdocSdkDocs.path, originalDartdoc, 'original'); + Map currentDartdocWarnings = jsonMessageIterableToWarnings(await currentDartdocSdkBuild); + Map originalDartdocWarnings = jsonMessageIterableToWarnings(await originalDartdocSdkBuild); + + printWarningDelta('SDK docs', originalDartdocWarnings, currentDartdocWarnings); +} + +/// Helper function to create a clean version of dartdoc (based on the current +/// directory, assumed to be a git repository). Uses [dartdocOriginalBranch] +/// to checkout a branch or tag. +Future createComparisonDartdoc() async { + var launcher = new _SubprocessLauncher('create-comparison-dartdoc'); + Directory dartdocClean = Directory.systemTemp.createTempSync('dartdoc-comparison'); + await launcher.runStreamed('git', + ['clone', Directory.current.path, dartdocClean.path]); + await launcher.runStreamed('git', + ['checkout', dartdocOriginalBranch], workingDirectory: dartdocClean.path); + await launcher.runStreamed(sdkBin('pub'), ['get'], workingDirectory: dartdocClean.path); + return dartdocClean.path; +} + +Future> _buildSdkDocs(String sdkDocsPath, Future futureCwd, [String label]) async { + if (label == null) label = ''; + if (label != '') label = '-$label'; + var launcher = new _SubprocessLauncher('build-sdk-docs$label'); + String cwd = await futureCwd; + await launcher.runStreamed(sdkBin('pub'), ['get'], workingDirectory: cwd); + return await launcher.runStreamed(Platform.resolvedExecutable, [ '--checked', 'bin/dartdoc.dart', '--output', '${sdkDocsDir.path}', '--sdk-docs', - '--show-progress' - ]); + '--json', + '--show-progress', + ], workingDirectory: cwd); } @Task('Serve generated SDK docs locally with dhttpd on port 8000') @@ -180,42 +304,47 @@ Future serveFlutterDocs() async { @Task('Build flutter docs') Future buildFlutterDocs() async { log('building flutter docs into: $flutterDir'); + await _buildFlutterDocs(flutterDir.path); + String index = + new File(path.join(flutterDir.path, 'dev', 'docs', 'doc', 'index.html')) + .readAsStringSync(); + stdout.write(index); +} + +Future _buildFlutterDocs(String flutterPath, [String label]) async { var launcher = - new _SubprocessLauncher('build-flutter-docs', _createThrowawayPubCache()); + new _SubprocessLauncher('build-flutter-docs${label == null ? "" : "-$label"}', _createThrowawayPubCache()); await launcher.runStreamed('git', ['clone', '--depth', '1', 'https://github.com/flutter/flutter.git', '.'], - workingDirectory: flutterDir.path); + workingDirectory: flutterPath); String flutterBin = path.join('bin', 'flutter'); String flutterCacheDart = - path.join(flutterDir.path, 'bin', 'cache', 'dart-sdk', 'bin', 'dart'); + path.join(flutterPath, 'bin', 'cache', 'dart-sdk', 'bin', 'dart'); String flutterCachePub = - path.join(flutterDir.path, 'bin', 'cache', 'dart-sdk', 'bin', 'pub'); + path.join(flutterPath, 'bin', 'cache', 'dart-sdk', 'bin', 'pub'); await launcher.runStreamed( flutterBin, ['--version'], - workingDirectory: flutterDir.path, + workingDirectory: flutterPath, ); await launcher.runStreamed( flutterBin, ['precache'], - workingDirectory: flutterDir.path, + workingDirectory: flutterPath, ); await launcher.runStreamed( flutterCachePub, ['get'], - workingDirectory: path.join(flutterDir.path, 'dev', 'tools'), + workingDirectory: path.join(flutterPath, 'dev', 'tools'), ); await launcher .runStreamed(flutterCachePub, ['global', 'activate', '-spath', '.']); await launcher.runStreamed( flutterCacheDart, [path.join('dev', 'tools', 'dartdoc.dart')], - workingDirectory: flutterDir.path, + workingDirectory: flutterPath, ); - String index = - new File(path.join(flutterDir.path, 'dev', 'docs', 'doc', 'index.html')) - .readAsStringSync(); - stdout.write(index); + } @Task('Checks that CHANGELOG mentions current version') From ba9b5564cb90110623d73ccb20402a9949154d9e Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Wed, 13 Dec 2017 15:04:12 -0800 Subject: [PATCH 11/16] dartfmt --- tool/grind.dart | 99 ++++++++++++++++++++++++++++++------------------- 1 file changed, 61 insertions(+), 38 deletions(-) diff --git a/tool/grind.dart b/tool/grind.dart index 40420859f1..c999d2daff 100644 --- a/tool/grind.dart +++ b/tool/grind.dart @@ -34,7 +34,6 @@ String get dartdocOriginalBranch { return branch; } - final Directory flutterDirDevTools = new Directory(path.join(flutterDir.path, 'dev', 'tools')); @@ -175,14 +174,17 @@ buildbot() => null; @Task('Generate docs for the Dart SDK') Future buildSdkDocs() async { log('building SDK docs'); - await _buildSdkDocs(sdkDocsDir.path, new Future.value(Directory.current.path)); + await _buildSdkDocs( + sdkDocsDir.path, new Future.value(Directory.current.path)); } /// Returns a map of warning texts to the number of times each has been seen. Map jsonMessageIterableToWarnings(Iterable messageIterable) { Map warningTexts = new Map(); for (Map message in messageIterable) { - if (message.containsKey('level') && message['level'] == 'WARNING' && message.containsKey('data')) { + if (message.containsKey('level') && + message['level'] == 'WARNING' && + message.containsKey('data')) { warningTexts.putIfAbsent(message['data']['text'], () => 0); warningTexts[message['data']['text']]++; } @@ -190,53 +192,68 @@ Map jsonMessageIterableToWarnings(Iterable messageIterable) { return warningTexts; } -void printWarningDelta(String title, Map original, Map current) { +void printWarningDelta( + String title, Map original, Map current) { Set quantityChangedOuts = new Set(); Set onlyOriginal = new Set(); Set onlyCurrent = new Set(); - Set allKeys = new Set.from([]..addAll(original.keys)..addAll(current.keys)); + Set allKeys = + new Set.from([]..addAll(original.keys)..addAll(current.keys)); for (String key in allKeys) { if (original.containsKey(key) && !current.containsKey(key)) { onlyOriginal.add(key); } else if (!original.containsKey(key) && current.containsKey(key)) { onlyCurrent.add(key); - } else if (original.containsKey(key) && current.containsKey(key) && - original[key] != current[key]) { + } else if (original.containsKey(key) && + current.containsKey(key) && + original[key] != current[key]) { quantityChangedOuts.add(key); } } if (onlyOriginal.isNotEmpty) { - print('*** $title : ${onlyOriginal.length} warnings from original ($dartdocOriginalBranch) missing in current:'); + print( + '*** $title : ${onlyOriginal.length} warnings from original ($dartdocOriginalBranch) missing in current:'); onlyOriginal.forEach((warning) => print(warning)); } if (onlyCurrent.isNotEmpty) { - print('*** $title : ${onlyCurrent.length} new warnings not in original ($dartdocOriginalBranch)'); + print( + '*** $title : ${onlyCurrent.length} new warnings not in original ($dartdocOriginalBranch)'); onlyCurrent.forEach((warning) => print(warning)); } if (quantityChangedOuts.isNotEmpty) { print('*** $title : Identical warning quantity changed'); for (String key in quantityChangedOuts) { - print("* Appeared ${original[key]} times in original ($dartdocOriginalBranch), now ${current[key]}:"); + print( + "* Appeared ${original[key]} times in original ($dartdocOriginalBranch), now ${current[key]}:"); print(key); } } - if (onlyOriginal.isEmpty && onlyCurrent.isEmpty && quantityChangedOuts.isEmpty) { - print('*** $title : No difference in warning output from original ($dartdocOriginalBranch)${allKeys.isEmpty ? "" : " (${allKeys.length} warnings found)"}'); + if (onlyOriginal.isEmpty && + onlyCurrent.isEmpty && + quantityChangedOuts.isEmpty) { + print( + '*** $title : No difference in warning output from original ($dartdocOriginalBranch)${allKeys.isEmpty ? "" : " (${allKeys.length} warnings found)"}'); } } @Task('Display delta in SDK warnings') Future compareSdkWarnings() async { - Directory originalDartdocSdkDocs = Directory.systemTemp.createTempSync('dartdoc-comparison-sdkdocs'); + Directory originalDartdocSdkDocs = + Directory.systemTemp.createTempSync('dartdoc-comparison-sdkdocs'); Future originalDartdoc = createComparisonDartdoc(); - Future currentDartdocSdkBuild = _buildSdkDocs(sdkDocsDir.path, new Future.value(Directory.current.path), 'current'); - Future originalDartdocSdkBuild = _buildSdkDocs(originalDartdocSdkDocs.path, originalDartdoc, 'original'); - Map currentDartdocWarnings = jsonMessageIterableToWarnings(await currentDartdocSdkBuild); - Map originalDartdocWarnings = jsonMessageIterableToWarnings(await originalDartdocSdkBuild); - - printWarningDelta('SDK docs', originalDartdocWarnings, currentDartdocWarnings); + Future currentDartdocSdkBuild = _buildSdkDocs( + sdkDocsDir.path, new Future.value(Directory.current.path), 'current'); + Future originalDartdocSdkBuild = + _buildSdkDocs(originalDartdocSdkDocs.path, originalDartdoc, 'original'); + Map currentDartdocWarnings = + jsonMessageIterableToWarnings(await currentDartdocSdkBuild); + Map originalDartdocWarnings = + jsonMessageIterableToWarnings(await originalDartdocSdkBuild); + + printWarningDelta( + 'SDK docs', originalDartdocWarnings, currentDartdocWarnings); } /// Helper function to create a clean version of dartdoc (based on the current @@ -244,30 +261,36 @@ Future compareSdkWarnings() async { /// to checkout a branch or tag. Future createComparisonDartdoc() async { var launcher = new _SubprocessLauncher('create-comparison-dartdoc'); - Directory dartdocClean = Directory.systemTemp.createTempSync('dartdoc-comparison'); - await launcher.runStreamed('git', - ['clone', Directory.current.path, dartdocClean.path]); - await launcher.runStreamed('git', - ['checkout', dartdocOriginalBranch], workingDirectory: dartdocClean.path); - await launcher.runStreamed(sdkBin('pub'), ['get'], workingDirectory: dartdocClean.path); + Directory dartdocClean = + Directory.systemTemp.createTempSync('dartdoc-comparison'); + await launcher + .runStreamed('git', ['clone', Directory.current.path, dartdocClean.path]); + await launcher.runStreamed('git', ['checkout', dartdocOriginalBranch], + workingDirectory: dartdocClean.path); + await launcher.runStreamed(sdkBin('pub'), ['get'], + workingDirectory: dartdocClean.path); return dartdocClean.path; } -Future> _buildSdkDocs(String sdkDocsPath, Future futureCwd, [String label]) async { +Future> _buildSdkDocs(String sdkDocsPath, Future futureCwd, + [String label]) async { if (label == null) label = ''; if (label != '') label = '-$label'; var launcher = new _SubprocessLauncher('build-sdk-docs$label'); String cwd = await futureCwd; await launcher.runStreamed(sdkBin('pub'), ['get'], workingDirectory: cwd); - return await launcher.runStreamed(Platform.resolvedExecutable, [ - '--checked', - 'bin/dartdoc.dart', - '--output', - '${sdkDocsDir.path}', - '--sdk-docs', - '--json', - '--show-progress', - ], workingDirectory: cwd); + return await launcher.runStreamed( + Platform.resolvedExecutable, + [ + '--checked', + 'bin/dartdoc.dart', + '--output', + '${sdkDocsDir.path}', + '--sdk-docs', + '--json', + '--show-progress', + ], + workingDirectory: cwd); } @Task('Serve generated SDK docs locally with dhttpd on port 8000') @@ -312,8 +335,9 @@ Future buildFlutterDocs() async { } Future _buildFlutterDocs(String flutterPath, [String label]) async { - var launcher = - new _SubprocessLauncher('build-flutter-docs${label == null ? "" : "-$label"}', _createThrowawayPubCache()); + var launcher = new _SubprocessLauncher( + 'build-flutter-docs${label == null ? "" : "-$label"}', + _createThrowawayPubCache()); await launcher.runStreamed('git', ['clone', '--depth', '1', 'https://github.com/flutter/flutter.git', '.'], workingDirectory: flutterPath); @@ -344,7 +368,6 @@ Future _buildFlutterDocs(String flutterPath, [String label]) async { [path.join('dev', 'tools', 'dartdoc.dart')], workingDirectory: flutterPath, ); - } @Task('Checks that CHANGELOG mentions current version') From bb07ee3136fedd8cab663a41c43ccf100c99d3d5 Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Wed, 13 Dec 2017 15:24:54 -0800 Subject: [PATCH 12/16] Eliminate the if statement altogether. --- lib/src/model_utils.dart | 37 +++++++++++++------------------------ pubspec.lock | 2 +- 2 files changed, 14 insertions(+), 25 deletions(-) diff --git a/lib/src/model_utils.dart b/lib/src/model_utils.dart index 983d06c3e2..c997032549 100644 --- a/lib/src/model_utils.dart +++ b/lib/src/model_utils.dart @@ -230,35 +230,25 @@ class Memoizer { _memoizationTable = new Map(); } - // Never write this if statement again. - R _getOrUpdateCache(R Function() f, _HashableList key) { - if (!_memoizationTable.containsKey(key)) { - _memoizationTable[key] = f(); - } - return _memoizationTable[key]; - } - /// Calls and caches the return value of [f]() if not in the cache, then /// returns the cached value of [f](). - /// - R memoized(Function f) { _HashableList key = new _HashableList([f]); - return _getOrUpdateCache(f, key); + return _memoizationTable.putIfAbsent(key, f); } /// Calls and caches the return value of [f]([param1]) if not in the cache, then /// returns the cached value of [f]([param1]). R memoized1(R Function(A) f, A param1) { _HashableList key = new _HashableList([f, param1]); - return _getOrUpdateCache(() => f(param1), key); + return _memoizationTable.putIfAbsent(key, () => f(param1)); } /// Calls and caches the return value of [f]([param1], [param2]) if not in the /// cache, then returns the cached value of [f]([param1], [param2]). R memoized2(R Function(A, B) f, A param1, B param2) { _HashableList key = new _HashableList([f, param1, param2]); - return _getOrUpdateCache(() => f(param1, param2), key); + return _memoizationTable.putIfAbsent(key, () => f(param1, param2)); } /// Calls and caches the return value of [f]([param1], [param2], [param3]) if @@ -266,7 +256,7 @@ class Memoizer { /// [param2], [param3]). R memoized3(R Function(A, B, C) f, A param1, B param2, C param3) { _HashableList key = new _HashableList([f, param1, param2, param3]); - return _getOrUpdateCache(() => f(param1, param2, param3), key); + return _memoizationTable.putIfAbsent(key, () => f(param1, param2, param3)); } /// Calls and caches the return value of [f]([param1], [param2], [param3], @@ -275,7 +265,7 @@ class Memoizer { R memoized4( R Function(A, B, C, D) f, A param1, B param2, C param3, D param4) { _HashableList key = new _HashableList([f, param1, param2, param3, param4]); - return _getOrUpdateCache(() => f(param1, param2, param3, param4), key); + return _memoizationTable.putIfAbsent(key, () => f(param1, param2, param3, param4)); } /// Calls and caches the return value of [f]([param1], [param2], [param3], @@ -285,8 +275,8 @@ class Memoizer { C param3, D param4, E param5) { _HashableList key = new _HashableList([f, param1, param2, param3, param4, param5]); - return _getOrUpdateCache( - () => f(param1, param2, param3, param4, param5), key); + return _memoizationTable.putIfAbsent(key, + () => f(param1, param2, param3, param4, param5)); } /// Calls and caches the return value of [f]([param1], [param2], [param3], @@ -296,8 +286,8 @@ class Memoizer { B param2, C param3, D param4, E param5, F param6) { _HashableList key = new _HashableList([f, param1, param2, param3, param4, param5, param6]); - return _getOrUpdateCache( - () => f(param1, param2, param3, param4, param5, param6), key); + return _memoizationTable.putIfAbsent(key, + () => f(param1, param2, param3, param4, param5, param6)); } /// Calls and caches the return value of [f]([param1], [param2], [param3], @@ -308,8 +298,8 @@ class Memoizer { A param1, B param2, C param3, D param4, E param5, F param6, G param7) { _HashableList key = new _HashableList( [f, param1, param2, param3, param4, param5, param6, param7]); - return _getOrUpdateCache( - () => f(param1, param2, param3, param4, param5, param6, param7), key); + return _memoizationTable.putIfAbsent(key, + () => f(param1, param2, param3, param4, param5, param6, param7)); } /// Calls and caches the return value of [f]([param1], [param2], [param3], @@ -328,8 +318,7 @@ class Memoizer { H param8) { _HashableList key = new _HashableList( [f, param1, param2, param3, param4, param5, param6, param7, param8]); - return _getOrUpdateCache( - () => f(param1, param2, param3, param4, param5, param6, param7, param8), - key); + return _memoizationTable.putIfAbsent(key, + () => f(param1, param2, param3, param4, param5, param6, param7, param8)); } } diff --git a/pubspec.lock b/pubspec.lock index 566f015931..d505112a10 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -374,4 +374,4 @@ packages: source: hosted version: "2.1.13" sdks: - dart: ">=1.23.0 <=2.0.0-dev.10.0" + dart: ">=1.23.0 <=2.0.0-dev.12.0" From 3eb76c6bcfde97f3b4796baeeac4738ef64e5f39 Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Wed, 13 Dec 2017 15:26:46 -0800 Subject: [PATCH 13/16] dartfmt --- lib/src/model_utils.dart | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/src/model_utils.dart b/lib/src/model_utils.dart index c997032549..db2e211bf2 100644 --- a/lib/src/model_utils.dart +++ b/lib/src/model_utils.dart @@ -265,7 +265,8 @@ class Memoizer { R memoized4( R Function(A, B, C, D) f, A param1, B param2, C param3, D param4) { _HashableList key = new _HashableList([f, param1, param2, param3, param4]); - return _memoizationTable.putIfAbsent(key, () => f(param1, param2, param3, param4)); + return _memoizationTable.putIfAbsent( + key, () => f(param1, param2, param3, param4)); } /// Calls and caches the return value of [f]([param1], [param2], [param3], @@ -275,8 +276,8 @@ class Memoizer { C param3, D param4, E param5) { _HashableList key = new _HashableList([f, param1, param2, param3, param4, param5]); - return _memoizationTable.putIfAbsent(key, - () => f(param1, param2, param3, param4, param5)); + return _memoizationTable.putIfAbsent( + key, () => f(param1, param2, param3, param4, param5)); } /// Calls and caches the return value of [f]([param1], [param2], [param3], @@ -286,8 +287,8 @@ class Memoizer { B param2, C param3, D param4, E param5, F param6) { _HashableList key = new _HashableList([f, param1, param2, param3, param4, param5, param6]); - return _memoizationTable.putIfAbsent(key, - () => f(param1, param2, param3, param4, param5, param6)); + return _memoizationTable.putIfAbsent( + key, () => f(param1, param2, param3, param4, param5, param6)); } /// Calls and caches the return value of [f]([param1], [param2], [param3], @@ -298,8 +299,8 @@ class Memoizer { A param1, B param2, C param3, D param4, E param5, F param6, G param7) { _HashableList key = new _HashableList( [f, param1, param2, param3, param4, param5, param6, param7]); - return _memoizationTable.putIfAbsent(key, - () => f(param1, param2, param3, param4, param5, param6, param7)); + return _memoizationTable.putIfAbsent( + key, () => f(param1, param2, param3, param4, param5, param6, param7)); } /// Calls and caches the return value of [f]([param1], [param2], [param3], @@ -318,7 +319,9 @@ class Memoizer { H param8) { _HashableList key = new _HashableList( [f, param1, param2, param3, param4, param5, param6, param7, param8]); - return _memoizationTable.putIfAbsent(key, - () => f(param1, param2, param3, param4, param5, param6, param7, param8)); + return _memoizationTable.putIfAbsent( + key, + () => + f(param1, param2, param3, param4, param5, param6, param7, param8)); } } From 17d37d331200113524974c8a1d443230fce8184e Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Thu, 14 Dec 2017 13:57:37 -0800 Subject: [PATCH 14/16] Reorganize code a bit --- lib/src/io_utils.dart | 156 +++++++++++++++++++++++++++++++++++++ test/io_utils_test.dart | 27 +++++++ tool/grind.dart | 168 +++------------------------------------- 3 files changed, 195 insertions(+), 156 deletions(-) diff --git a/lib/src/io_utils.dart b/lib/src/io_utils.dart index b0d02225a6..2d9b3c2fb4 100644 --- a/lib/src/io_utils.dart +++ b/lib/src/io_utils.dart @@ -5,6 +5,8 @@ /// This is a helper library to make working with io easier. library dartdoc.io_utils; +import 'dart:async'; +import 'dart:convert'; import 'dart:io'; import 'package:path/path.dart' as path; @@ -60,3 +62,157 @@ String getFileNameFor(String name) => final libraryNameRegexp = new RegExp('[.:]'); final partOfRegexp = new RegExp('part of '); final newLinePartOfRegexp = new RegExp('\npart of '); + +final RegExp quotables = new RegExp(r'[ "\r\n\$]'); + +class SubprocessLauncher { + final String context; + Map _environment; + + Map get environment => _environment; + + String get prefix => context.isNotEmpty ? '$context: ' : ''; + + // from flutter:dev/tools/dartdoc.dart, modified + static void _printStream(Stream> stream, Stdout output, + {String prefix: '', Iterable Function(String line) filter}) { + assert(prefix != null); + if (filter == null) filter = (line) => [line]; + stream + .transform(UTF8.decoder) + .transform(const LineSplitter()) + .expand(filter) + .listen((String line) { + if (line != null) { + output.write('$prefix$line'.trim()); + output.write('\n'); + } + }); + } + + SubprocessLauncher(this.context, [Map environment]) { + if (environment == null) this._environment = new Map(); + } + + /// A wrapper around start/await process.exitCode that will display the + /// output of the executable continuously and fail on non-zero exit codes. + /// It will also parse any valid JSON objects (one per line) it encounters + /// on stdout/stderr, and return them. Returns null if no JSON objects + /// were encountered. + /// + /// Makes running programs in grinder similar to set -ex for bash, even on + /// Windows (though some of the bashisms will no longer make sense). + /// TODO(jcollins-g): move this to grinder? + Future> runStreamed(String executable, List arguments, + {String workingDirectory}) async { + List jsonObjects; + + /// Allow us to pretend we didn't pass the JSON flag in to dartdoc by + /// printing what dartdoc would have printed without it, yet storing + /// json objects into [jsonObjects]. + Iterable jsonCallback(String line) { + Map result; + try { + result = json.decoder.convert(line); + } catch (FormatException) {} + if (result != null) { + if (jsonObjects == null) { + jsonObjects = new List(); + } + jsonObjects.add(result); + if (result.containsKey('message')) { + line = result['message']; + } else if (result.containsKey('data')) { + line = result['data']['text']; + } + } + return line.split('\n'); + } + + stderr.write('$prefix+ '); + if (workingDirectory != null) stderr.write('(cd "$workingDirectory" && '); + if (environment != null) { + stderr.write(environment.keys.map((String key) { + if (environment[key].contains(quotables)) { + return "$key='${environment[key]}'"; + } else { + return "$key=${environment[key]}"; + } + }).join(' ')); + stderr.write(' '); + } + stderr.write('$executable'); + if (arguments.isNotEmpty) { + for (String arg in arguments) { + if (arg.contains(quotables)) { + stderr.write(" '$arg'"); + } else { + stderr.write(" $arg"); + } + } + } + if (workingDirectory != null) stderr.write(')'); + stderr.write('\n'); + Process process = await Process.start(executable, arguments, + workingDirectory: workingDirectory, environment: environment); + + _printStream(process.stdout, stdout, prefix: prefix, filter: jsonCallback); + _printStream(process.stderr, stderr, prefix: prefix, filter: jsonCallback); + await process.exitCode; + + int exitCode = await process.exitCode; + if (exitCode != 0) { + throw new ProcessException(executable, arguments, "SubprocessLauncher got non-zero exitCode", exitCode); + } + return jsonObjects; + } +} + +/// Output formatter for comparing warnings. +String printWarningDelta( + String title, String dartdocOriginalBranch, Map original, Map current) { + StringBuffer printBuffer = new StringBuffer(); + Set quantityChangedOuts = new Set(); + Set onlyOriginal = new Set(); + Set onlyCurrent = new Set(); + Set allKeys = + new Set.from([]..addAll(original.keys)..addAll(current.keys)); + + for (String key in allKeys) { + if (original.containsKey(key) && !current.containsKey(key)) { + onlyOriginal.add(key); + } else if (!original.containsKey(key) && current.containsKey(key)) { + onlyCurrent.add(key); + } else if (original.containsKey(key) && + current.containsKey(key) && + original[key] != current[key]) { + quantityChangedOuts.add(key); + } + } + + if (onlyOriginal.isNotEmpty) { + printBuffer.writeln( + '*** $title : ${onlyOriginal.length} warnings from original ($dartdocOriginalBranch) missing in current:'); + onlyOriginal.forEach((warning) => printBuffer.writeln(warning)); + } + if (onlyCurrent.isNotEmpty) { + printBuffer.writeln( + '*** $title : ${onlyCurrent.length} new warnings not in original ($dartdocOriginalBranch)'); + onlyCurrent.forEach((warning) => printBuffer.writeln(warning)); + } + if (quantityChangedOuts.isNotEmpty) { + printBuffer.writeln('*** $title : Identical warning quantity changed'); + for (String key in quantityChangedOuts) { + printBuffer.writeln( + "* Appeared ${original[key]} times in original ($dartdocOriginalBranch), now ${current[key]}:"); + printBuffer.writeln(key); + } + } + if (onlyOriginal.isEmpty && + onlyCurrent.isEmpty && + quantityChangedOuts.isEmpty) { + printBuffer.writeln( + '*** $title : No difference in warning output from original ($dartdocOriginalBranch)${allKeys.isEmpty ? "" : " (${allKeys.length} warnings found)"}'); + } + return printBuffer.toString(); +} \ No newline at end of file diff --git a/test/io_utils_test.dart b/test/io_utils_test.dart index 5b0548c4ca..aa33aab744 100644 --- a/test/io_utils_test.dart +++ b/test/io_utils_test.dart @@ -17,4 +17,31 @@ void main() { expect(getFileNameFor('dartdoc.generator'), 'dartdoc-generator.html'); }); }); + + group('printWarningDelta', () { + Map original; + Map current; + setUp(() { + original = new Map.fromIterables(["originalwarning", "morewarning", "duplicateoriginalwarning"], + [1, 1, 2]); + current = new Map.fromIterables(["newwarning", "morewarning", "duplicateoriginalwarning"], + [1, 1, 1]); + }); + + test('verify output of printWarningDelta', () { + expect(printWarningDelta('Diff Title', 'oldbranch', original, current), + equals('*** Diff Title : 1 warnings from original (oldbranch) missing in current:\n' + 'originalwarning\n' + '*** Diff Title : 1 new warnings not in original (oldbranch)\n' + 'newwarning\n' + '*** Diff Title : Identical warning quantity changed\n' + '* Appeared 2 times in original (oldbranch), now 1:\n' + 'duplicateoriginalwarning\n')); + }); + + test('verify output when nothing changes', () { + expect(printWarningDelta('Diff Title 2', 'oldbranch2', original, original), + equals('*** Diff Title 2 : No difference in warning output from original (oldbranch2) (3 warnings found)\n')); + }); + }); } diff --git a/tool/grind.dart b/tool/grind.dart index c999d2daff..f2d0370cf0 100644 --- a/tool/grind.dart +++ b/tool/grind.dart @@ -3,7 +3,6 @@ // BSD-style license that can be found in the LICENSE file. import 'dart:async'; -import 'dart:convert'; import 'dart:io' hide ProcessException; import 'package:dartdoc/src/io_utils.dart'; @@ -37,23 +36,7 @@ String get dartdocOriginalBranch { final Directory flutterDirDevTools = new Directory(path.join(flutterDir.path, 'dev', 'tools')); -final RegExp quotables = new RegExp(r'[ "\r\n\$]'); -// from flutter:dev/tools/dartdoc.dart, modified -void _printStream(Stream> stream, Stdout output, - {String prefix: '', Iterable Function(String line) filter}) { - assert(prefix != null); - if (filter == null) filter = (line) => [line]; - stream - .transform(UTF8.decoder) - .transform(const LineSplitter()) - .expand(filter) - .listen((String line) { - if (line != null) { - output.write('$prefix$line'.trim()); - output.write('\n'); - } - }); -} + /// Creates a throwaway pub cache and returns the environment variables /// necessary to use it. @@ -70,92 +53,9 @@ Map _createThrowawayPubCache() { ]); } -class _SubprocessLauncher { - final String context; - Map _environment; - - Map get environment => _environment; - - String get prefix => context.isNotEmpty ? '$context: ' : ''; - - _SubprocessLauncher(this.context, [Map environment]) { - if (environment == null) this._environment = new Map(); - } - - /// A wrapper around start/await process.exitCode that will display the - /// output of the executable continuously and fail on non-zero exit codes. - /// It will also parse any valid JSON objects (one per line) it encounters - /// on stdout/stderr, and return them. - /// - /// Makes running programs in grinder similar to set -ex for bash, even on - /// Windows (though some of the bashisms will no longer make sense). - /// TODO(jcollins-g): move this to grinder? - Future> runStreamed(String executable, List arguments, - {String workingDirectory}) async { - List jsonObjects = new List(); - - /// Allow us to pretend we didn't pass the JSON flag in to dartdoc by - /// printing what dartdoc would have printed without it, yet storing - /// json objects into [jsonObjects]. - Iterable jsonCallback(String line) { - Map result; - try { - result = json.decoder.convert(line); - } catch (FormatException) {} - if (result != null) { - jsonObjects.add(result); - if (result.containsKey('message')) { - line = result['message']; - } else if (result.containsKey('data')) { - line = result['data']['text']; - } - } - return line.split('\n'); - } - - stderr.write('$prefix+ '); - if (workingDirectory != null) stderr.write('(cd "$workingDirectory" && '); - if (environment != null) { - stderr.write(environment.keys.map((String key) { - if (environment[key].contains(quotables)) { - return "$key='${environment[key]}'"; - } else { - return "$key=${environment[key]}"; - } - }).join(' ')); - stderr.write(' '); - } - stderr.write('$executable'); - if (arguments.isNotEmpty) { - for (String arg in arguments) { - if (arg.contains(quotables)) { - stderr.write(" '$arg'"); - } else { - stderr.write(" $arg"); - } - } - } - if (workingDirectory != null) stderr.write(')'); - stderr.write('\n'); - Process process = await Process.start(executable, arguments, - workingDirectory: workingDirectory, environment: environment); - - _printStream(process.stdout, stdout, prefix: prefix, filter: jsonCallback); - _printStream(process.stderr, stderr, prefix: prefix, filter: jsonCallback); - await process.exitCode; - - int exitCode = await process.exitCode; - if (exitCode != 0) { - fail("exitCode: $exitCode"); - } - - return jsonObjects; - } -} - @Task('Analyze dartdoc to ensure there are no errors and warnings') analyze() async { - await new _SubprocessLauncher('analyze').runStreamed( + await new SubprocessLauncher('analyze').runStreamed( sdkBin('dartanalyzer'), [ '--fatal-warnings', @@ -192,51 +92,7 @@ Map jsonMessageIterableToWarnings(Iterable messageIterable) { return warningTexts; } -void printWarningDelta( - String title, Map original, Map current) { - Set quantityChangedOuts = new Set(); - Set onlyOriginal = new Set(); - Set onlyCurrent = new Set(); - Set allKeys = - new Set.from([]..addAll(original.keys)..addAll(current.keys)); - - for (String key in allKeys) { - if (original.containsKey(key) && !current.containsKey(key)) { - onlyOriginal.add(key); - } else if (!original.containsKey(key) && current.containsKey(key)) { - onlyCurrent.add(key); - } else if (original.containsKey(key) && - current.containsKey(key) && - original[key] != current[key]) { - quantityChangedOuts.add(key); - } - } - if (onlyOriginal.isNotEmpty) { - print( - '*** $title : ${onlyOriginal.length} warnings from original ($dartdocOriginalBranch) missing in current:'); - onlyOriginal.forEach((warning) => print(warning)); - } - if (onlyCurrent.isNotEmpty) { - print( - '*** $title : ${onlyCurrent.length} new warnings not in original ($dartdocOriginalBranch)'); - onlyCurrent.forEach((warning) => print(warning)); - } - if (quantityChangedOuts.isNotEmpty) { - print('*** $title : Identical warning quantity changed'); - for (String key in quantityChangedOuts) { - print( - "* Appeared ${original[key]} times in original ($dartdocOriginalBranch), now ${current[key]}:"); - print(key); - } - } - if (onlyOriginal.isEmpty && - onlyCurrent.isEmpty && - quantityChangedOuts.isEmpty) { - print( - '*** $title : No difference in warning output from original ($dartdocOriginalBranch)${allKeys.isEmpty ? "" : " (${allKeys.length} warnings found)"}'); - } -} @Task('Display delta in SDK warnings') Future compareSdkWarnings() async { @@ -252,15 +108,15 @@ Future compareSdkWarnings() async { Map originalDartdocWarnings = jsonMessageIterableToWarnings(await originalDartdocSdkBuild); - printWarningDelta( - 'SDK docs', originalDartdocWarnings, currentDartdocWarnings); + print(printWarningDelta( + 'SDK docs', dartdocOriginalBranch, originalDartdocWarnings, currentDartdocWarnings)); } /// Helper function to create a clean version of dartdoc (based on the current /// directory, assumed to be a git repository). Uses [dartdocOriginalBranch] /// to checkout a branch or tag. Future createComparisonDartdoc() async { - var launcher = new _SubprocessLauncher('create-comparison-dartdoc'); + var launcher = new SubprocessLauncher('create-comparison-dartdoc'); Directory dartdocClean = Directory.systemTemp.createTempSync('dartdoc-comparison'); await launcher @@ -276,7 +132,7 @@ Future> _buildSdkDocs(String sdkDocsPath, Future futureCwd, [String label]) async { if (label == null) label = ''; if (label != '') label = '-$label'; - var launcher = new _SubprocessLauncher('build-sdk-docs$label'); + var launcher = new SubprocessLauncher('build-sdk-docs$label'); String cwd = await futureCwd; await launcher.runStreamed(sdkBin('pub'), ['get'], workingDirectory: cwd); return await launcher.runStreamed( @@ -297,7 +153,7 @@ Future> _buildSdkDocs(String sdkDocsPath, Future futureCwd, @Depends(buildSdkDocs) Future serveSdkDocs() async { log('launching dhttpd on port 8000 for SDK'); - var launcher = new _SubprocessLauncher('serve-sdk-docs'); + var launcher = new SubprocessLauncher('serve-sdk-docs'); await launcher.runStreamed(sdkBin('pub'), [ 'run', 'dhttpd', @@ -312,7 +168,7 @@ Future serveSdkDocs() async { @Depends(buildFlutterDocs) Future serveFlutterDocs() async { log('launching dhttpd on port 8001 for Flutter'); - var launcher = new _SubprocessLauncher('serve-flutter-docs'); + var launcher = new SubprocessLauncher('serve-flutter-docs'); await launcher.runStreamed(sdkBin('pub'), ['get']); await launcher.runStreamed(sdkBin('pub'), [ 'run', @@ -335,7 +191,7 @@ Future buildFlutterDocs() async { } Future _buildFlutterDocs(String flutterPath, [String label]) async { - var launcher = new _SubprocessLauncher( + var launcher = new SubprocessLauncher( 'build-flutter-docs${label == null ? "" : "-$label"}', _createThrowawayPubCache()); await launcher.runStreamed('git', @@ -478,13 +334,13 @@ publish() async { test() async { // `pub run test` is a bit slower than running an `test_all.dart` script // But it provides more useful output in the case of failures. - await new _SubprocessLauncher('test') + await new SubprocessLauncher('test') .runStreamed(sdkBin('pub'), ['run', 'test']); } @Task('Generate docs for dartdoc') testDartdoc() async { - var launcher = new _SubprocessLauncher('test-dartdoc'); + var launcher = new SubprocessLauncher('test-dartdoc'); await launcher.runStreamed(Platform.resolvedExecutable, ['--checked', 'bin/dartdoc.dart', '--output', dartdocDocsDir.path]); File indexHtml = joinFile(dartdocDocsDir, ['index.html']); @@ -493,7 +349,7 @@ testDartdoc() async { @Task('update test_package_docs') updateTestPackageDocs() async { - var launcher = new _SubprocessLauncher('update-test-package-docs'); + var launcher = new SubprocessLauncher('update-test-package-docs'); var testPackageDocs = new Directory(path.join('testing', 'test_package_docs')); var testPackage = new Directory(path.join('testing', 'test_package')); From 5a657cbfb10a8b146f5f6bd615db0334ecf7a315 Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Thu, 14 Dec 2017 13:58:09 -0800 Subject: [PATCH 15/16] dartfmt --- lib/src/io_utils.dart | 11 ++++++----- test/io_utils_test.dart | 21 +++++++++++++-------- tool/grind.dart | 8 ++------ 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/lib/src/io_utils.dart b/lib/src/io_utils.dart index 2d9b3c2fb4..207fb3dc8a 100644 --- a/lib/src/io_utils.dart +++ b/lib/src/io_utils.dart @@ -162,21 +162,22 @@ class SubprocessLauncher { int exitCode = await process.exitCode; if (exitCode != 0) { - throw new ProcessException(executable, arguments, "SubprocessLauncher got non-zero exitCode", exitCode); + throw new ProcessException(executable, arguments, + "SubprocessLauncher got non-zero exitCode", exitCode); } return jsonObjects; } } /// Output formatter for comparing warnings. -String printWarningDelta( - String title, String dartdocOriginalBranch, Map original, Map current) { +String printWarningDelta(String title, String dartdocOriginalBranch, + Map original, Map current) { StringBuffer printBuffer = new StringBuffer(); Set quantityChangedOuts = new Set(); Set onlyOriginal = new Set(); Set onlyCurrent = new Set(); Set allKeys = - new Set.from([]..addAll(original.keys)..addAll(current.keys)); + new Set.from([]..addAll(original.keys)..addAll(current.keys)); for (String key in allKeys) { if (original.containsKey(key) && !current.containsKey(key)) { @@ -215,4 +216,4 @@ String printWarningDelta( '*** $title : No difference in warning output from original ($dartdocOriginalBranch)${allKeys.isEmpty ? "" : " (${allKeys.length} warnings found)"}'); } return printBuffer.toString(); -} \ No newline at end of file +} diff --git a/test/io_utils_test.dart b/test/io_utils_test.dart index aa33aab744..a550e249fe 100644 --- a/test/io_utils_test.dart +++ b/test/io_utils_test.dart @@ -22,15 +22,18 @@ void main() { Map original; Map current; setUp(() { - original = new Map.fromIterables(["originalwarning", "morewarning", "duplicateoriginalwarning"], - [1, 1, 2]); - current = new Map.fromIterables(["newwarning", "morewarning", "duplicateoriginalwarning"], - [1, 1, 1]); + original = new Map.fromIterables( + ["originalwarning", "morewarning", "duplicateoriginalwarning"], + [1, 1, 2]); + current = new Map.fromIterables( + ["newwarning", "morewarning", "duplicateoriginalwarning"], [1, 1, 1]); }); test('verify output of printWarningDelta', () { - expect(printWarningDelta('Diff Title', 'oldbranch', original, current), - equals('*** Diff Title : 1 warnings from original (oldbranch) missing in current:\n' + expect( + printWarningDelta('Diff Title', 'oldbranch', original, current), + equals( + '*** Diff Title : 1 warnings from original (oldbranch) missing in current:\n' 'originalwarning\n' '*** Diff Title : 1 new warnings not in original (oldbranch)\n' 'newwarning\n' @@ -40,8 +43,10 @@ void main() { }); test('verify output when nothing changes', () { - expect(printWarningDelta('Diff Title 2', 'oldbranch2', original, original), - equals('*** Diff Title 2 : No difference in warning output from original (oldbranch2) (3 warnings found)\n')); + expect( + printWarningDelta('Diff Title 2', 'oldbranch2', original, original), + equals( + '*** Diff Title 2 : No difference in warning output from original (oldbranch2) (3 warnings found)\n')); }); }); } diff --git a/tool/grind.dart b/tool/grind.dart index f2d0370cf0..9a293d5987 100644 --- a/tool/grind.dart +++ b/tool/grind.dart @@ -36,8 +36,6 @@ String get dartdocOriginalBranch { final Directory flutterDirDevTools = new Directory(path.join(flutterDir.path, 'dev', 'tools')); - - /// Creates a throwaway pub cache and returns the environment variables /// necessary to use it. Map _createThrowawayPubCache() { @@ -92,8 +90,6 @@ Map jsonMessageIterableToWarnings(Iterable messageIterable) { return warningTexts; } - - @Task('Display delta in SDK warnings') Future compareSdkWarnings() async { Directory originalDartdocSdkDocs = @@ -108,8 +104,8 @@ Future compareSdkWarnings() async { Map originalDartdocWarnings = jsonMessageIterableToWarnings(await originalDartdocSdkBuild); - print(printWarningDelta( - 'SDK docs', dartdocOriginalBranch, originalDartdocWarnings, currentDartdocWarnings)); + print(printWarningDelta('SDK docs', dartdocOriginalBranch, + originalDartdocWarnings, currentDartdocWarnings)); } /// Helper function to create a clean version of dartdoc (based on the current From ccc05c6983c26e0ae4440c5de7cb441f03a98f5d Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Wed, 20 Dec 2017 11:49:11 -0800 Subject: [PATCH 16/16] review comments --- tool/grind.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/tool/grind.dart b/tool/grind.dart index 9a293d5987..f7278379d6 100644 --- a/tool/grind.dart +++ b/tool/grind.dart @@ -29,6 +29,7 @@ String get dartdocOriginalBranch { String branch = 'master'; if (Platform.environment.containsKey('DARTDOC_ORIGINAL')) { branch = Platform.environment['DARTDOC_ORIGINAL']; + log('using branch/tag: $branch for comparison from \$DARTDOC_ORIGINAL'); } return branch; }