| OLD | NEW |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library dart2js.serialization_test_helper; | 5 library dart2js.serialization_test_helper; |
| 6 | 6 |
| 7 import 'dart:collection'; | 7 import 'dart:collection'; |
| 8 import 'package:compiler/src/common/resolution.dart'; | 8 import 'package:compiler/src/common/resolution.dart'; |
| 9 import 'package:compiler/src/constants/expressions.dart'; | 9 import 'package:compiler/src/constants/expressions.dart'; |
| 10 import 'package:compiler/src/constants/values.dart'; | 10 import 'package:compiler/src/constants/values.dart'; |
| 11 import 'package:compiler/src/dart_types.dart'; | 11 import 'package:compiler/src/dart_types.dart'; |
| 12 import 'package:compiler/src/compiler.dart'; | 12 import 'package:compiler/src/compiler.dart'; |
| 13 import 'package:compiler/src/elements/elements.dart'; | 13 import 'package:compiler/src/elements/elements.dart'; |
| 14 import 'package:compiler/src/serialization/equivalence.dart'; | 14 import 'package:compiler/src/serialization/equivalence.dart'; |
| 15 import 'package:compiler/src/tree/nodes.dart'; | 15 import 'package:compiler/src/tree/nodes.dart'; |
| 16 import 'package:expect/expect.dart'; | 16 import 'package:expect/expect.dart'; |
| 17 import 'test_data.dart'; | 17 import 'test_data.dart'; |
| 18 | 18 |
| 19 Check currentCheck; | 19 Check currentCheck; |
| 20 | 20 |
| 21 class Check { | 21 class Check { |
| 22 final Check parent; | 22 final Check parent; |
| 23 final Object object1; | 23 final Object object1; |
| 24 final Object object2; | 24 final Object object2; |
| 25 final String property; | 25 final String property; |
| 26 final Object value1; | 26 final Object value1; |
| 27 final Object value2; | 27 final Object value2; |
| 28 | 28 |
| 29 Check(this.parent, this.object1, this.object2, this.property, this.value1, thi
s.value2); | 29 Check(this.parent, this.object1, this.object2, this.property, this.value1, |
| 30 this.value2); |
| 30 | 31 |
| 31 String printOn(StringBuffer sb, String indent) { | 32 String printOn(StringBuffer sb, String indent) { |
| 32 if (parent != null) { | 33 if (parent != null) { |
| 33 indent = parent.printOn(sb, indent); | 34 indent = parent.printOn(sb, indent); |
| 34 sb.write('\n$indent|\n'); | 35 sb.write('\n$indent|\n'); |
| 35 } | 36 } |
| 36 sb.write("${indent}property='$property'\n "); | 37 sb.write("${indent}property='$property'\n "); |
| 37 sb.write("${indent}object1=$object1 (${object1.runtimeType})\n "); | 38 sb.write("${indent}object1=$object1 (${object1.runtimeType})\n "); |
| 38 sb.write("${indent}value=${value1 == null ? "null" : "'$value1'"} "); | 39 sb.write("${indent}value=${value1 == null ? "null" : "'$value1'"} "); |
| 39 sb.write("(${value1.runtimeType}) vs\n "); | 40 sb.write("(${value1.runtimeType}) vs\n "); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 52 | 53 |
| 53 /// Strategy for checking equivalence. | 54 /// Strategy for checking equivalence. |
| 54 /// | 55 /// |
| 55 /// Use this strategy to fail early with contextual information in the event of | 56 /// Use this strategy to fail early with contextual information in the event of |
| 56 /// inequivalence. | 57 /// inequivalence. |
| 57 class CheckStrategy implements TestStrategy { | 58 class CheckStrategy implements TestStrategy { |
| 58 const CheckStrategy(); | 59 const CheckStrategy(); |
| 59 | 60 |
| 60 @override | 61 @override |
| 61 bool test(var object1, var object2, String property, var value1, var value2, | 62 bool test(var object1, var object2, String property, var value1, var value2, |
| 62 [bool equivalence(a, b) = equality]) { | 63 [bool equivalence(a, b) = equality]) { |
| 63 return check(object1, object2, property, value1, value2, equivalence); | 64 return check(object1, object2, property, value1, value2, equivalence); |
| 64 } | 65 } |
| 65 | 66 |
| 66 @override | 67 @override |
| 67 bool testLists( | 68 bool testLists( |
| 68 Object object1, Object object2, String property, | 69 Object object1, Object object2, String property, List list1, List list2, |
| 69 List list1, List list2, | |
| 70 [bool elementEquivalence(a, b) = equality]) { | 70 [bool elementEquivalence(a, b) = equality]) { |
| 71 return checkListEquivalence( | 71 return checkListEquivalence(object1, object2, property, list1, list2, |
| 72 object1, object2, property, list1, list2, | |
| 73 (o1, o2, p, v1, v2) { | 72 (o1, o2, p, v1, v2) { |
| 74 if (!elementEquivalence(v1, v2)) { | 73 if (!elementEquivalence(v1, v2)) { |
| 75 throw "$o1.$p = '${v1}' <> " | 74 throw "$o1.$p = '${v1}' <> " |
| 76 "$o2.$p = '${v2}'"; | 75 "$o2.$p = '${v2}'"; |
| 77 } | 76 } |
| 78 return true; | 77 return true; |
| 79 }); | 78 }); |
| 80 } | 79 } |
| 81 | 80 |
| 82 @override | 81 @override |
| 83 bool testSets( | 82 bool testSets( |
| 84 var object1, var object2, String property, | 83 var object1, var object2, String property, Iterable set1, Iterable set2, |
| 85 Iterable set1, Iterable set2, | |
| 86 [bool elementEquivalence(a, b) = equality]) { | 84 [bool elementEquivalence(a, b) = equality]) { |
| 87 return checkSetEquivalence( | 85 return checkSetEquivalence( |
| 88 object1, object2,property, set1, set2, elementEquivalence); | 86 object1, object2, property, set1, set2, elementEquivalence); |
| 89 } | 87 } |
| 90 | 88 |
| 91 @override | 89 @override |
| 92 bool testMaps( | 90 bool testMaps(var object1, var object2, String property, Map map1, Map map2, |
| 93 var object1, var object2, String property, Map map1, Map map2, | |
| 94 [bool keyEquivalence(a, b) = equality, | 91 [bool keyEquivalence(a, b) = equality, |
| 95 bool valueEquivalence(a, b) = equality]) { | 92 bool valueEquivalence(a, b) = equality]) { |
| 96 return checkMapEquivalence(object1, object2, property, | 93 return checkMapEquivalence(object1, object2, property, map1, map2, |
| 97 map1, map2, keyEquivalence, valueEquivalence); | 94 keyEquivalence, valueEquivalence); |
| 98 } | 95 } |
| 99 | 96 |
| 100 @override | 97 @override |
| 101 bool testElements( | 98 bool testElements(Object object1, Object object2, String property, |
| 102 Object object1, Object object2, String property, | |
| 103 Element element1, Element element2) { | 99 Element element1, Element element2) { |
| 104 return checkElementIdentities( | 100 return checkElementIdentities( |
| 105 object1, object2, property, element1, element2); | 101 object1, object2, property, element1, element2); |
| 106 } | 102 } |
| 107 | 103 |
| 108 @override | 104 @override |
| 109 bool testTypes( | 105 bool testTypes(Object object1, Object object2, String property, |
| 110 Object object1, Object object2, String property, | |
| 111 DartType type1, DartType type2) { | 106 DartType type1, DartType type2) { |
| 112 return checkTypes(object1, object2, property, type1, type2); | 107 return checkTypes(object1, object2, property, type1, type2); |
| 113 } | 108 } |
| 114 | 109 |
| 115 @override | 110 @override |
| 116 bool testConstants( | 111 bool testConstants(Object object1, Object object2, String property, |
| 117 Object object1, Object object2, String property, | |
| 118 ConstantExpression exp1, ConstantExpression exp2) { | 112 ConstantExpression exp1, ConstantExpression exp2) { |
| 119 return checkConstants(object1, object2, property, exp1, exp2); | 113 return checkConstants(object1, object2, property, exp1, exp2); |
| 120 } | 114 } |
| 121 | 115 |
| 122 @override | 116 @override |
| 123 bool testConstantValues(Object object1, Object object2, String property, | 117 bool testConstantValues(Object object1, Object object2, String property, |
| 124 ConstantValue value1, ConstantValue value2) { | 118 ConstantValue value1, ConstantValue value2) { |
| 125 return areConstantValuesEquivalent(value1, value2); | 119 return areConstantValuesEquivalent(value1, value2); |
| 126 } | 120 } |
| 127 | 121 |
| 128 @override | 122 @override |
| 129 bool testTypeLists( | 123 bool testTypeLists(Object object1, Object object2, String property, |
| 130 Object object1, Object object2, String property, | |
| 131 List<DartType> list1, List<DartType> list2) { | 124 List<DartType> list1, List<DartType> list2) { |
| 132 return checkTypeLists(object1, object2, property, list1, list2); | 125 return checkTypeLists(object1, object2, property, list1, list2); |
| 133 } | 126 } |
| 134 | 127 |
| 135 @override | 128 @override |
| 136 bool testConstantLists( | 129 bool testConstantLists(Object object1, Object object2, String property, |
| 137 Object object1, Object object2, String property, | 130 List<ConstantExpression> list1, List<ConstantExpression> list2) { |
| 138 List<ConstantExpression> list1, | |
| 139 List<ConstantExpression> list2) { | |
| 140 return checkConstantLists(object1, object2, property, list1, list2); | 131 return checkConstantLists(object1, object2, property, list1, list2); |
| 141 } | 132 } |
| 142 | 133 |
| 143 @override | 134 @override |
| 144 bool testConstantValueLists(Object object1, Object object2, String property, | 135 bool testConstantValueLists(Object object1, Object object2, String property, |
| 145 List<ConstantValue> list1, List<ConstantValue> list2) { | 136 List<ConstantValue> list1, List<ConstantValue> list2) { |
| 146 return checkConstantValueLists(object1, object2, property, list1, list2); | 137 return checkConstantValueLists(object1, object2, property, list1, list2); |
| 147 } | 138 } |
| 148 | 139 |
| 149 @override | 140 @override |
| 150 bool testNodes(Object object1, Object object2, String property, | 141 bool testNodes( |
| 151 Node node1, Node node2) { | 142 Object object1, Object object2, String property, Node node1, Node node2) { |
| 152 return new NodeEquivalenceVisitor(this).testNodes( | 143 return new NodeEquivalenceVisitor(this) |
| 153 object1, object2, property, node1, node2); | 144 .testNodes(object1, object2, property, node1, node2); |
| 154 } | 145 } |
| 155 } | 146 } |
| 156 | 147 |
| 157 /// Check that the values [property] of [object1] and [object2], [value1] and | 148 /// Check that the values [property] of [object1] and [object2], [value1] and |
| 158 /// [value2] respectively, are equal and throw otherwise. | 149 /// [value2] respectively, are equal and throw otherwise. |
| 159 bool check(var object1, var object2, String property, var value1, var value2, | 150 bool check(var object1, var object2, String property, var value1, var value2, |
| 160 [bool equivalence(a, b) = equality]) { | 151 [bool equivalence(a, b) = equality]) { |
| 161 currentCheck = new Check( | 152 currentCheck = |
| 162 currentCheck, object1, object2, property, value1, value2); | 153 new Check(currentCheck, object1, object2, property, value1, value2); |
| 163 if (!equivalence(value1, value2)) { | 154 if (!equivalence(value1, value2)) { |
| 164 throw currentCheck; | 155 throw currentCheck; |
| 165 } | 156 } |
| 166 currentCheck = currentCheck.parent; | 157 currentCheck = currentCheck.parent; |
| 167 return true; | 158 return true; |
| 168 } | 159 } |
| 169 | 160 |
| 170 /// Check equivalence of the two lists, [list1] and [list2], using | 161 /// Check equivalence of the two lists, [list1] and [list2], using |
| 171 /// [checkEquivalence] to check the pair-wise equivalence. | 162 /// [checkEquivalence] to check the pair-wise equivalence. |
| 172 /// | 163 /// |
| 173 /// Uses [object1], [object2] and [property] to provide context for failures. | 164 /// Uses [object1], [object2] and [property] to provide context for failures. |
| 174 bool checkListEquivalence( | 165 bool checkListEquivalence( |
| 175 Object object1, Object object2, String property, | 166 Object object1, |
| 176 Iterable list1, Iterable list2, | 167 Object object2, |
| 168 String property, |
| 169 Iterable list1, |
| 170 Iterable list2, |
| 177 void checkEquivalence(o1, o2, property, a, b)) { | 171 void checkEquivalence(o1, o2, property, a, b)) { |
| 178 currentCheck = | 172 currentCheck = |
| 179 new Check(currentCheck, object1, object2, property, list1, list2); | 173 new Check(currentCheck, object1, object2, property, list1, list2); |
| 180 for (int i = 0; i < list1.length && i < list2.length; i++) { | 174 for (int i = 0; i < list1.length && i < list2.length; i++) { |
| 181 checkEquivalence( | 175 checkEquivalence( |
| 182 object1, object2, property, | 176 object1, object2, property, list1.elementAt(i), list2.elementAt(i)); |
| 183 list1.elementAt(i), list2.elementAt(i)); | |
| 184 } | 177 } |
| 185 for (int i = list1.length; i < list2.length; i++) { | 178 for (int i = list1.length; i < list2.length; i++) { |
| 186 throw | 179 throw 'Missing equivalent for element ' |
| 187 'Missing equivalent for element ' | |
| 188 '#$i ${list2.elementAt(i)} in `${property}` on $object2.\n' | 180 '#$i ${list2.elementAt(i)} in `${property}` on $object2.\n' |
| 189 '`${property}` on $object1:\n ${list1.join('\n ')}\n' | 181 '`${property}` on $object1:\n ${list1.join('\n ')}\n' |
| 190 '`${property}` on $object2:\n ${list2.join('\n ')}'; | 182 '`${property}` on $object2:\n ${list2.join('\n ')}'; |
| 191 } | 183 } |
| 192 for (int i = list2.length; i < list1.length; i++) { | 184 for (int i = list2.length; i < list1.length; i++) { |
| 193 throw | 185 throw 'Missing equivalent for element ' |
| 194 'Missing equivalent for element ' | |
| 195 '#$i ${list1.elementAt(i)} in `${property}` on $object1.\n' | 186 '#$i ${list1.elementAt(i)} in `${property}` on $object1.\n' |
| 196 '`${property}` on $object1:\n ${list1.join('\n ')}\n' | 187 '`${property}` on $object1:\n ${list1.join('\n ')}\n' |
| 197 '`${property}` on $object2:\n ${list2.join('\n ')}'; | 188 '`${property}` on $object2:\n ${list2.join('\n ')}'; |
| 198 } | 189 } |
| 199 currentCheck = currentCheck.parent; | 190 currentCheck = currentCheck.parent; |
| 200 return true; | 191 return true; |
| 201 } | 192 } |
| 202 | 193 |
| 203 /// Computes the set difference between [set1] and [set2] using | 194 /// Computes the set difference between [set1] and [set2] using |
| 204 /// [elementEquivalence] to determine element equivalence. | 195 /// [elementEquivalence] to determine element equivalence. |
| 205 /// | 196 /// |
| 206 /// Elements both in [set1] and [set2] are added to [common], elements in [set1] | 197 /// Elements both in [set1] and [set2] are added to [common], elements in [set1] |
| 207 /// but not in [set2] are added to [unfound], and the set of elements in [set2] | 198 /// but not in [set2] are added to [unfound], and the set of elements in [set2] |
| 208 /// but not in [set1] are returned. | 199 /// but not in [set1] are returned. |
| 209 Set computeSetDifference( | 200 Set computeSetDifference( |
| 210 Iterable set1, | 201 Iterable set1, Iterable set2, List<List> common, List unfound, |
| 211 Iterable set2, | 202 {bool sameElement(a, b): equality, void checkElements(a, b)}) { |
| 212 List<List> common, | |
| 213 List unfound, | |
| 214 {bool sameElement(a, b): equality, | |
| 215 void checkElements(a, b)}) { | |
| 216 // TODO(johnniwinther): Avoid the quadratic cost here. Some ideas: | 203 // TODO(johnniwinther): Avoid the quadratic cost here. Some ideas: |
| 217 // - convert each set to a list and sort it first, then compare by walking | 204 // - convert each set to a list and sort it first, then compare by walking |
| 218 // both lists in parallel | 205 // both lists in parallel |
| 219 // - map each element to a canonical object, create a map containing those | 206 // - map each element to a canonical object, create a map containing those |
| 220 // mappings, use the mapped sets to compare (then operations like | 207 // mappings, use the mapped sets to compare (then operations like |
| 221 // set.difference would work) | 208 // set.difference would work) |
| 222 Set remaining = set2.toSet(); | 209 Set remaining = set2.toSet(); |
| 223 for (var element1 in set1) { | 210 for (var element1 in set1) { |
| 224 var correspondingElement; | 211 var correspondingElement; |
| 225 for (var element2 in remaining) { | 212 for (var element2 in remaining) { |
| (...skipping 12 matching lines...) Expand all Loading... |
| 238 unfound.add(element1); | 225 unfound.add(element1); |
| 239 } | 226 } |
| 240 } | 227 } |
| 241 return remaining; | 228 return remaining; |
| 242 } | 229 } |
| 243 | 230 |
| 244 /// Check equivalence of the two iterables, [set1] and [set1], as sets using | 231 /// Check equivalence of the two iterables, [set1] and [set1], as sets using |
| 245 /// [elementEquivalence] to compute the pair-wise equivalence. | 232 /// [elementEquivalence] to compute the pair-wise equivalence. |
| 246 /// | 233 /// |
| 247 /// Uses [object1], [object2] and [property] to provide context for failures. | 234 /// Uses [object1], [object2] and [property] to provide context for failures. |
| 248 bool checkSetEquivalence( | 235 bool checkSetEquivalence(var object1, var object2, String property, |
| 249 var object1, | 236 Iterable set1, Iterable set2, bool sameElement(a, b), |
| 250 var object2, | |
| 251 String property, | |
| 252 Iterable set1, | |
| 253 Iterable set2, | |
| 254 bool sameElement(a, b), | |
| 255 {void onSameElement(a, b)}) { | 237 {void onSameElement(a, b)}) { |
| 256 List<List> common = <List>[]; | 238 List<List> common = <List>[]; |
| 257 List unfound = []; | 239 List unfound = []; |
| 258 Set remaining = | 240 Set remaining = computeSetDifference(set1, set2, common, unfound, |
| 259 computeSetDifference(set1, set2, common, unfound, | 241 sameElement: sameElement, checkElements: onSameElement); |
| 260 sameElement: sameElement, checkElements: onSameElement); | |
| 261 if (unfound.isNotEmpty || remaining.isNotEmpty) { | 242 if (unfound.isNotEmpty || remaining.isNotEmpty) { |
| 262 String message = | 243 String message = "Set mismatch for `$property` on $object1 vs $object2: \n" |
| 263 "Set mismatch for `$property` on $object1 vs $object2: \n" | |
| 264 "Common:\n ${common.join('\n ')}\n" | 244 "Common:\n ${common.join('\n ')}\n" |
| 265 "Unfound:\n ${unfound.join('\n ')}\n" | 245 "Unfound:\n ${unfound.join('\n ')}\n" |
| 266 "Extra: \n ${remaining.join('\n ')}"; | 246 "Extra: \n ${remaining.join('\n ')}"; |
| 267 throw message; | 247 throw message; |
| 268 } | 248 } |
| 269 return true; | 249 return true; |
| 270 } | 250 } |
| 271 | 251 |
| 272 /// Check equivalence of the two iterables, [set1] and [set1], as sets using | 252 /// Check equivalence of the two iterables, [set1] and [set1], as sets using |
| 273 /// [elementEquivalence] to compute the pair-wise equivalence. | 253 /// [elementEquivalence] to compute the pair-wise equivalence. |
| 274 /// | 254 /// |
| 275 /// Uses [object1], [object2] and [property] to provide context for failures. | 255 /// Uses [object1], [object2] and [property] to provide context for failures. |
| 276 bool checkMapEquivalence( | 256 bool checkMapEquivalence(var object1, var object2, String property, Map map1, |
| 277 var object1, | 257 Map map2, bool sameKey(a, b), bool sameValue(a, b)) { |
| 278 var object2, | |
| 279 String property, | |
| 280 Map map1, | |
| 281 Map map2, | |
| 282 bool sameKey(a, b), | |
| 283 bool sameValue(a, b)) { | |
| 284 List<List> common = <List>[]; | 258 List<List> common = <List>[]; |
| 285 List unfound = []; | 259 List unfound = []; |
| 286 Set remaining = | 260 Set remaining = computeSetDifference(map1.keys, map2.keys, common, unfound, |
| 287 computeSetDifference(map1.keys, map2.keys, common, unfound, | 261 sameElement: sameKey); |
| 288 sameElement: sameKey); | |
| 289 if (unfound.isNotEmpty || remaining.isNotEmpty) { | 262 if (unfound.isNotEmpty || remaining.isNotEmpty) { |
| 290 String message = | 263 String message = |
| 291 "Map key mismatch for `$property` on $object1 vs $object2: \n" | 264 "Map key mismatch for `$property` on $object1 vs $object2: \n" |
| 292 "Common:\n ${common.join('\n ')}\n" | 265 "Common:\n ${common.join('\n ')}\n" |
| 293 "Unfound:\n ${unfound.join('\n ')}\n" | 266 "Unfound:\n ${unfound.join('\n ')}\n" |
| 294 "Extra: \n ${remaining.join('\n ')}"; | 267 "Extra: \n ${remaining.join('\n ')}"; |
| 295 throw message; | 268 throw message; |
| 296 } | 269 } |
| 297 for (List pair in common) { | 270 for (List pair in common) { |
| 298 check(object1, object2, 'Map value for `$property`', | 271 check(object1, object2, 'Map value for `$property`', map1[pair[0]], |
| 299 map1[pair[0]], map2[pair[1]], sameValue); | 272 map2[pair[1]], sameValue); |
| 300 } | 273 } |
| 301 return true; | 274 return true; |
| 302 } | 275 } |
| 303 | 276 |
| 304 /// Checks the equivalence of the identity (but not properties) of [element1] | 277 /// Checks the equivalence of the identity (but not properties) of [element1] |
| 305 /// and [element2]. | 278 /// and [element2]. |
| 306 /// | 279 /// |
| 307 /// Uses [object1], [object2] and [property] to provide context for failures. | 280 /// Uses [object1], [object2] and [property] to provide context for failures. |
| 308 bool checkElementIdentities( | 281 bool checkElementIdentities(Object object1, Object object2, String property, |
| 309 Object object1, Object object2, String property, | |
| 310 Element element1, Element element2) { | 282 Element element1, Element element2) { |
| 311 if (identical(element1, element2)) return true; | 283 if (identical(element1, element2)) return true; |
| 312 return check(object1, object2, | 284 return check( |
| 313 property, element1, element2, areElementsEquivalent); | 285 object1, object2, property, element1, element2, areElementsEquivalent); |
| 314 } | 286 } |
| 315 | 287 |
| 316 /// Checks the pair-wise equivalence of the identity (but not properties) of the | 288 /// Checks the pair-wise equivalence of the identity (but not properties) of the |
| 317 /// elements in [list] and [list2]. | 289 /// elements in [list] and [list2]. |
| 318 /// | 290 /// |
| 319 /// Uses [object1], [object2] and [property] to provide context for failures. | 291 /// Uses [object1], [object2] and [property] to provide context for failures. |
| 320 bool checkElementListIdentities( | 292 bool checkElementListIdentities(Object object1, Object object2, String property, |
| 321 Object object1, Object object2, String property, | |
| 322 Iterable<Element> list1, Iterable<Element> list2) { | 293 Iterable<Element> list1, Iterable<Element> list2) { |
| 323 return checkListEquivalence( | 294 return checkListEquivalence( |
| 324 object1, object2, property, | 295 object1, object2, property, list1, list2, checkElementIdentities); |
| 325 list1, list2, checkElementIdentities); | |
| 326 } | 296 } |
| 327 | 297 |
| 328 /// Checks the equivalence of [type1] and [type2]. | 298 /// Checks the equivalence of [type1] and [type2]. |
| 329 /// | 299 /// |
| 330 /// Uses [object1], [object2] and [property] to provide context for failures. | 300 /// Uses [object1], [object2] and [property] to provide context for failures. |
| 331 bool checkTypes( | 301 bool checkTypes(Object object1, Object object2, String property, DartType type1, |
| 332 Object object1, Object object2, String property, | 302 DartType type2) { |
| 333 DartType type1, DartType type2) { | |
| 334 if (identical(type1, type2)) return true; | 303 if (identical(type1, type2)) return true; |
| 335 if (type1 == null || type2 == null) { | 304 if (type1 == null || type2 == null) { |
| 336 return check(object1, object2, property, type1, type2); | 305 return check(object1, object2, property, type1, type2); |
| 337 } else { | 306 } else { |
| 338 return check(object1, object2, property, type1, type2, | 307 return check(object1, object2, property, type1, type2, |
| 339 (a, b) => const TypeEquivalence(const CheckStrategy()).visit(a, b)); | 308 (a, b) => const TypeEquivalence(const CheckStrategy()).visit(a, b)); |
| 340 } | 309 } |
| 341 } | 310 } |
| 342 | 311 |
| 343 /// Checks the pair-wise equivalence of the types in [list1] and [list2]. | 312 /// Checks the pair-wise equivalence of the types in [list1] and [list2]. |
| 344 /// | 313 /// |
| 345 /// Uses [object1], [object2] and [property] to provide context for failures. | 314 /// Uses [object1], [object2] and [property] to provide context for failures. |
| 346 bool checkTypeLists( | 315 bool checkTypeLists(Object object1, Object object2, String property, |
| 347 Object object1, Object object2, String property, | |
| 348 List<DartType> list1, List<DartType> list2) { | 316 List<DartType> list1, List<DartType> list2) { |
| 349 return checkListEquivalence( | 317 return checkListEquivalence( |
| 350 object1, object2, property, list1, list2, checkTypes); | 318 object1, object2, property, list1, list2, checkTypes); |
| 351 } | 319 } |
| 352 | 320 |
| 353 /// Checks the equivalence of [exp1] and [exp2]. | 321 /// Checks the equivalence of [exp1] and [exp2]. |
| 354 /// | 322 /// |
| 355 /// Uses [object1], [object2] and [property] to provide context for failures. | 323 /// Uses [object1], [object2] and [property] to provide context for failures. |
| 356 bool checkConstants( | 324 bool checkConstants(Object object1, Object object2, String property, |
| 357 Object object1, Object object2, String property, | |
| 358 ConstantExpression exp1, ConstantExpression exp2) { | 325 ConstantExpression exp1, ConstantExpression exp2) { |
| 359 if (identical(exp1, exp2)) return true; | 326 if (identical(exp1, exp2)) return true; |
| 360 if (exp1 == null || exp2 == null) { | 327 if (exp1 == null || exp2 == null) { |
| 361 return check(object1, object2, property, exp1, exp2); | 328 return check(object1, object2, property, exp1, exp2); |
| 362 } else { | 329 } else { |
| 363 return check(object1, object2, property, exp1, exp2, | 330 return check(object1, object2, property, exp1, exp2, |
| 364 (a, b) => const ConstantEquivalence(const CheckStrategy()).visit(a, b)); | 331 (a, b) => const ConstantEquivalence(const CheckStrategy()).visit(a, b)); |
| 365 } | 332 } |
| 366 } | 333 } |
| 367 | 334 |
| 368 /// Checks the equivalence of [value1] and [value2]. | 335 /// Checks the equivalence of [value1] and [value2]. |
| 369 /// | 336 /// |
| 370 /// Uses [object1], [object2] and [property] to provide context for failures. | 337 /// Uses [object1], [object2] and [property] to provide context for failures. |
| 371 bool checkConstantValues( | 338 bool checkConstantValues(Object object1, Object object2, String property, |
| 372 Object object1, Object object2, String property, | |
| 373 ConstantValue value1, ConstantValue value2) { | 339 ConstantValue value1, ConstantValue value2) { |
| 374 if (identical(value1, value2)) return true; | 340 if (identical(value1, value2)) return true; |
| 375 if (value1 == null || value2 == null) { | 341 if (value1 == null || value2 == null) { |
| 376 return check(object1, object2, property, value1, value2); | 342 return check(object1, object2, property, value1, value2); |
| 377 } else { | 343 } else { |
| 378 return check(object1, object2, property, value1, value2, | 344 return check( |
| 379 (a, b) => const ConstantValueEquivalence( | 345 object1, |
| 380 const CheckStrategy()).visit(a, b)); | 346 object2, |
| 347 property, |
| 348 value1, |
| 349 value2, |
| 350 (a, b) => |
| 351 const ConstantValueEquivalence(const CheckStrategy()).visit(a, b)); |
| 381 } | 352 } |
| 382 } | 353 } |
| 383 | 354 |
| 384 /// Checks the pair-wise equivalence of the constants in [list1] and [list2]. | 355 /// Checks the pair-wise equivalence of the constants in [list1] and [list2]. |
| 385 /// | 356 /// |
| 386 /// Uses [object1], [object2] and [property] to provide context for failures. | 357 /// Uses [object1], [object2] and [property] to provide context for failures. |
| 387 bool checkConstantLists( | 358 bool checkConstantLists(Object object1, Object object2, String property, |
| 388 Object object1, Object object2, String property, | 359 List<ConstantExpression> list1, List<ConstantExpression> list2) { |
| 389 List<ConstantExpression> list1, | |
| 390 List<ConstantExpression> list2) { | |
| 391 return checkListEquivalence( | 360 return checkListEquivalence( |
| 392 object1, object2, property, | 361 object1, object2, property, list1, list2, checkConstants); |
| 393 list1, list2, checkConstants); | |
| 394 } | 362 } |
| 395 | 363 |
| 396 /// Checks the pair-wise equivalence of the constants values in [list1] and | 364 /// Checks the pair-wise equivalence of the constants values in [list1] and |
| 397 /// [list2]. | 365 /// [list2]. |
| 398 /// | 366 /// |
| 399 /// Uses [object1], [object2] and [property] to provide context for failures. | 367 /// Uses [object1], [object2] and [property] to provide context for failures. |
| 400 bool checkConstantValueLists( | 368 bool checkConstantValueLists(Object object1, Object object2, String property, |
| 401 Object object1, Object object2, String property, | 369 List<ConstantValue> list1, List<ConstantValue> list2) { |
| 402 List<ConstantValue> list1, | |
| 403 List<ConstantValue> list2) { | |
| 404 return checkListEquivalence( | 370 return checkListEquivalence( |
| 405 object1, object2, property, | 371 object1, object2, property, list1, list2, checkConstantValues); |
| 406 list1, list2, checkConstantValues); | |
| 407 } | 372 } |
| 408 | 373 |
| 409 /// Check member property equivalence between all members common to [compiler1] | 374 /// Check member property equivalence between all members common to [compiler1] |
| 410 /// and [compiler2]. | 375 /// and [compiler2]. |
| 411 void checkLoadedLibraryMembers( | 376 void checkLoadedLibraryMembers( |
| 412 Compiler compiler1, | 377 Compiler compiler1, |
| 413 Compiler compiler2, | 378 Compiler compiler2, |
| 414 bool hasProperty(Element member1), | 379 bool hasProperty(Element member1), |
| 415 void checkMemberProperties(Compiler compiler1, Element member1, | 380 void checkMemberProperties(Compiler compiler1, Element member1, |
| 416 Compiler compiler2, Element member2, | 381 Compiler compiler2, Element member2, |
| 417 {bool verbose}), | 382 {bool verbose}), |
| 418 {bool verbose: false}) { | 383 {bool verbose: false}) { |
| 419 | |
| 420 void checkMembers(Element member1, Element member2) { | 384 void checkMembers(Element member1, Element member2) { |
| 421 if (member1.isClass && member2.isClass) { | 385 if (member1.isClass && member2.isClass) { |
| 422 ClassElement class1 = member1; | 386 ClassElement class1 = member1; |
| 423 ClassElement class2 = member2; | 387 ClassElement class2 = member2; |
| 424 if (!class1.isResolved) return; | 388 if (!class1.isResolved) return; |
| 425 | 389 |
| 426 if (hasProperty(member1)) { | 390 if (hasProperty(member1)) { |
| 427 if (areElementsEquivalent(member1, member2)) { | 391 if (areElementsEquivalent(member1, member2)) { |
| 428 checkMemberProperties( | 392 checkMemberProperties(compiler1, member1, compiler2, member2, |
| 429 compiler1, member1, | |
| 430 compiler2, member2, | |
| 431 verbose: verbose); | 393 verbose: verbose); |
| 432 } | 394 } |
| 433 } | 395 } |
| 434 | 396 |
| 435 class1.forEachLocalMember((m1) { | 397 class1.forEachLocalMember((m1) { |
| 436 checkMembers(m1, class2.localLookup(m1.name)); | 398 checkMembers(m1, class2.localLookup(m1.name)); |
| 437 }); | 399 }); |
| 438 ClassElement superclass1 = class1.superclass; | 400 ClassElement superclass1 = class1.superclass; |
| 439 ClassElement superclass2 = class2.superclass; | 401 ClassElement superclass2 = class2.superclass; |
| 440 while (superclass1 != null && superclass1.isUnnamedMixinApplication) { | 402 while (superclass1 != null && superclass1.isUnnamedMixinApplication) { |
| 441 for (ConstructorElement c1 in superclass1.constructors) { | 403 for (ConstructorElement c1 in superclass1.constructors) { |
| 442 checkMembers(c1, superclass2.lookupConstructor(c1.name)); | 404 checkMembers(c1, superclass2.lookupConstructor(c1.name)); |
| 443 } | 405 } |
| 444 superclass1 = superclass1.superclass; | 406 superclass1 = superclass1.superclass; |
| 445 superclass2 = superclass2.superclass; | 407 superclass2 = superclass2.superclass; |
| 446 } | 408 } |
| 447 return; | 409 return; |
| 448 } | 410 } |
| 449 | 411 |
| 450 if (!hasProperty(member1)) { | 412 if (!hasProperty(member1)) { |
| 451 return; | 413 return; |
| 452 } | 414 } |
| 453 | 415 |
| 454 if (member2 == null) { | 416 if (member2 == null) { |
| 455 throw 'Missing member for ${member1}'; | 417 throw 'Missing member for ${member1}'; |
| 456 } | 418 } |
| 457 | 419 |
| 458 if (areElementsEquivalent(member1, member2)) { | 420 if (areElementsEquivalent(member1, member2)) { |
| 459 checkMemberProperties( | 421 checkMemberProperties(compiler1, member1, compiler2, member2, |
| 460 compiler1, member1, | |
| 461 compiler2, member2, | |
| 462 verbose: verbose); | 422 verbose: verbose); |
| 463 } | 423 } |
| 464 } | 424 } |
| 465 | 425 |
| 466 for (LibraryElement library1 in compiler1.libraryLoader.libraries) { | 426 for (LibraryElement library1 in compiler1.libraryLoader.libraries) { |
| 467 LibraryElement library2 = | 427 LibraryElement library2 = |
| 468 compiler2.libraryLoader.lookupLibrary(library1.canonicalUri); | 428 compiler2.libraryLoader.lookupLibrary(library1.canonicalUri); |
| 469 if (library2 != null) { | 429 if (library2 != null) { |
| 470 library1.forEachLocalMember((Element member1) { | 430 library1.forEachLocalMember((Element member1) { |
| 471 checkMembers(member1, library2.localLookup(member1.name)); | 431 checkMembers(member1, library2.localLookup(member1.name)); |
| 472 }); | 432 }); |
| 473 | |
| 474 } | 433 } |
| 475 } | 434 } |
| 476 } | 435 } |
| 477 | 436 |
| 478 /// Check equivalence of all resolution impacts. | 437 /// Check equivalence of all resolution impacts. |
| 479 void checkAllImpacts( | 438 void checkAllImpacts(Compiler compiler1, Compiler compiler2, |
| 480 Compiler compiler1, | |
| 481 Compiler compiler2, | |
| 482 {bool verbose: false}) { | 439 {bool verbose: false}) { |
| 483 checkLoadedLibraryMembers( | 440 checkLoadedLibraryMembers(compiler1, compiler2, (Element member1) { |
| 484 compiler1, | 441 return compiler1.resolution.hasResolutionImpact(member1); |
| 485 compiler2, | 442 }, checkImpacts, verbose: verbose); |
| 486 (Element member1) { | |
| 487 return compiler1.resolution.hasResolutionImpact(member1); | |
| 488 }, | |
| 489 checkImpacts, | |
| 490 verbose: verbose); | |
| 491 } | 443 } |
| 492 | 444 |
| 493 /// Check equivalence of resolution impact for [member1] and [member2]. | 445 /// Check equivalence of resolution impact for [member1] and [member2]. |
| 494 void checkImpacts(Compiler compiler1, Element member1, | 446 void checkImpacts( |
| 495 Compiler compiler2, Element member2, | 447 Compiler compiler1, Element member1, Compiler compiler2, Element member2, |
| 496 {bool verbose: false}) { | 448 {bool verbose: false}) { |
| 497 ResolutionImpact impact1 = compiler1.resolution.getResolutionImpact(member1); | 449 ResolutionImpact impact1 = compiler1.resolution.getResolutionImpact(member1); |
| 498 ResolutionImpact impact2 = compiler2.resolution.getResolutionImpact(member2); | 450 ResolutionImpact impact2 = compiler2.resolution.getResolutionImpact(member2); |
| 499 | 451 |
| 500 if (impact1 == null && impact2 == null) return; | 452 if (impact1 == null && impact2 == null) return; |
| 501 | 453 |
| 502 if (verbose) { | 454 if (verbose) { |
| 503 print('Checking impacts for $member1 vs $member2'); | 455 print('Checking impacts for $member1 vs $member2'); |
| 504 } | 456 } |
| 505 | 457 |
| 506 if (impact1 == null) { | 458 if (impact1 == null) { |
| 507 throw 'Missing impact for $member1. $member2 has $impact2'; | 459 throw 'Missing impact for $member1. $member2 has $impact2'; |
| 508 } | 460 } |
| 509 if (impact2 == null) { | 461 if (impact2 == null) { |
| 510 throw 'Missing impact for $member2. $member1 has $impact1'; | 462 throw 'Missing impact for $member2. $member1 has $impact1'; |
| 511 } | 463 } |
| 512 | 464 |
| 513 testResolutionImpactEquivalence(impact1, impact2, const CheckStrategy()); | 465 testResolutionImpactEquivalence(impact1, impact2, const CheckStrategy()); |
| 514 } | 466 } |
| 515 | 467 |
| 516 void checkSets( | 468 void checkSets( |
| 517 Iterable set1, | 469 Iterable set1, Iterable set2, String messagePrefix, bool sameElement(a, b), |
| 518 Iterable set2, | |
| 519 String messagePrefix, | |
| 520 bool sameElement(a, b), | |
| 521 {bool failOnUnfound: true, | 470 {bool failOnUnfound: true, |
| 522 bool failOnExtra: true, | 471 bool failOnExtra: true, |
| 523 bool verbose: false, | 472 bool verbose: false, |
| 524 void onSameElement(a, b), | 473 void onSameElement(a, b), |
| 525 void onUnfoundElement(a), | 474 void onUnfoundElement(a), |
| 526 void onExtraElement(b), | 475 void onExtraElement(b), |
| 527 String elementToString(key): defaultToString}) { | 476 String elementToString(key): defaultToString}) { |
| 528 List<List> common = <List>[]; | 477 List<List> common = <List>[]; |
| 529 List unfound = []; | 478 List unfound = []; |
| 530 Set remaining = computeSetDifference( | 479 Set remaining = computeSetDifference(set1, set2, common, unfound, |
| 531 set1, set2, common, unfound, | 480 sameElement: sameElement, checkElements: onSameElement); |
| 532 sameElement: sameElement, | |
| 533 checkElements: onSameElement); | |
| 534 if (onUnfoundElement != null) { | 481 if (onUnfoundElement != null) { |
| 535 unfound.forEach(onUnfoundElement); | 482 unfound.forEach(onUnfoundElement); |
| 536 } | 483 } |
| 537 if (onExtraElement != null) { | 484 if (onExtraElement != null) { |
| 538 remaining.forEach(onExtraElement); | 485 remaining.forEach(onExtraElement); |
| 539 } | 486 } |
| 540 StringBuffer sb = new StringBuffer(); | 487 StringBuffer sb = new StringBuffer(); |
| 541 sb.write("$messagePrefix:"); | 488 sb.write("$messagePrefix:"); |
| 542 if (verbose) { | 489 if (verbose) { |
| 543 sb.write("\n Common: \n"); | 490 sb.write("\n Common: \n"); |
| (...skipping 18 matching lines...) Expand all Loading... |
| 562 } else { | 509 } else { |
| 563 print(message); | 510 print(message); |
| 564 } | 511 } |
| 565 } else if (verbose) { | 512 } else if (verbose) { |
| 566 print(message); | 513 print(message); |
| 567 } | 514 } |
| 568 } | 515 } |
| 569 | 516 |
| 570 String defaultToString(obj) => '$obj'; | 517 String defaultToString(obj) => '$obj'; |
| 571 | 518 |
| 572 void checkMaps( | 519 void checkMaps(Map map1, Map map2, String messagePrefix, bool sameKey(a, b), |
| 573 Map map1, | |
| 574 Map map2, | |
| 575 String messagePrefix, | |
| 576 bool sameKey(a, b), | |
| 577 bool sameValue(a, b), | 520 bool sameValue(a, b), |
| 578 {bool failOnUnfound: true, | 521 {bool failOnUnfound: true, |
| 579 bool failOnMismatch: true, | 522 bool failOnMismatch: true, |
| 580 bool verbose: false, | 523 bool verbose: false, |
| 581 String keyToString(key): defaultToString, | 524 String keyToString(key): defaultToString, |
| 582 String valueToString(key): defaultToString}) { | 525 String valueToString(key): defaultToString}) { |
| 583 List<List> common = <List>[]; | 526 List<List> common = <List>[]; |
| 584 List unfound = []; | 527 List unfound = []; |
| 585 List<List> mismatch = <List>[]; | 528 List<List> mismatch = <List>[]; |
| 586 Set remaining = computeSetDifference( | 529 Set remaining = computeSetDifference(map1.keys, map2.keys, common, unfound, |
| 587 map1.keys, map2.keys, common, unfound, | 530 sameElement: sameKey, checkElements: (k1, k2) { |
| 588 sameElement: sameKey, | 531 var v1 = map1[k1]; |
| 589 checkElements: (k1, k2) { | 532 var v2 = map2[k2]; |
| 590 var v1 = map1[k1]; | 533 if (!sameValue(v1, v2)) { |
| 591 var v2 = map2[k2]; | 534 mismatch.add([k1, k2]); |
| 592 if (!sameValue(v1, v2)) { | 535 } |
| 593 mismatch.add([k1, k2]); | 536 }); |
| 594 } | |
| 595 }); | |
| 596 StringBuffer sb = new StringBuffer(); | 537 StringBuffer sb = new StringBuffer(); |
| 597 sb.write("$messagePrefix:"); | 538 sb.write("$messagePrefix:"); |
| 598 if (verbose) { | 539 if (verbose) { |
| 599 sb.write("\n Common: \n"); | 540 sb.write("\n Common: \n"); |
| 600 for (List pair in common) { | 541 for (List pair in common) { |
| 601 var k1 = pair[0]; | 542 var k1 = pair[0]; |
| 602 var k2 = pair[1]; | 543 var k2 = pair[1]; |
| 603 var v1 = map1[k1]; | 544 var v1 = map1[k1]; |
| 604 var v2 = map2[k2]; | 545 var v2 = map2[k2]; |
| 605 sb.write(" key1 =${keyToString(k1)}\n"); | 546 sb.write(" key1 =${keyToString(k1)}\n"); |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 644 remaining.isNotEmpty) { | 585 remaining.isNotEmpty) { |
| 645 Expect.fail(message); | 586 Expect.fail(message); |
| 646 } else { | 587 } else { |
| 647 print(message); | 588 print(message); |
| 648 } | 589 } |
| 649 } else if (verbose) { | 590 } else if (verbose) { |
| 650 print(message); | 591 print(message); |
| 651 } | 592 } |
| 652 } | 593 } |
| 653 | 594 |
| 654 void checkAllResolvedAsts( | 595 void checkAllResolvedAsts(Compiler compiler1, Compiler compiler2, |
| 655 Compiler compiler1, | |
| 656 Compiler compiler2, | |
| 657 {bool verbose: false}) { | 596 {bool verbose: false}) { |
| 658 checkLoadedLibraryMembers( | 597 checkLoadedLibraryMembers(compiler1, compiler2, (Element member1) { |
| 659 compiler1, | 598 return member1 is ExecutableElement && |
| 660 compiler2, | 599 compiler1.resolution.hasResolvedAst(member1); |
| 661 (Element member1) { | 600 }, checkResolvedAsts, verbose: verbose); |
| 662 return member1 is ExecutableElement && | |
| 663 compiler1.resolution.hasResolvedAst(member1); | |
| 664 }, | |
| 665 checkResolvedAsts, | |
| 666 verbose: verbose); | |
| 667 } | 601 } |
| 668 | 602 |
| 669 | |
| 670 /// Check equivalence of [impact1] and [impact2]. | 603 /// Check equivalence of [impact1] and [impact2]. |
| 671 void checkResolvedAsts(Compiler compiler1, Element member1, | 604 void checkResolvedAsts( |
| 672 Compiler compiler2, Element member2, | 605 Compiler compiler1, Element member1, Compiler compiler2, Element member2, |
| 673 {bool verbose: false}) { | 606 {bool verbose: false}) { |
| 674 if (!compiler2.serialization.isDeserialized(member2)) { | 607 if (!compiler2.serialization.isDeserialized(member2)) { |
| 675 return; | 608 return; |
| 676 } | 609 } |
| 677 ResolvedAst resolvedAst1 = compiler1.resolution.getResolvedAst(member1); | 610 ResolvedAst resolvedAst1 = compiler1.resolution.getResolvedAst(member1); |
| 678 ResolvedAst resolvedAst2 = compiler2.serialization.getResolvedAst(member2); | 611 ResolvedAst resolvedAst2 = compiler2.serialization.getResolvedAst(member2); |
| 679 | 612 |
| 680 if (resolvedAst1 == null || resolvedAst2 == null) return; | 613 if (resolvedAst1 == null || resolvedAst2 == null) return; |
| 681 | 614 |
| 682 if (verbose) { | 615 if (verbose) { |
| 683 print('Checking resolved asts for $member1 vs $member2'); | 616 print('Checking resolved asts for $member1 vs $member2'); |
| 684 } | 617 } |
| 685 | 618 |
| 686 testResolvedAstEquivalence( | 619 testResolvedAstEquivalence(resolvedAst1, resolvedAst2, const CheckStrategy()); |
| 687 resolvedAst1, resolvedAst2, const CheckStrategy()); | |
| 688 } | 620 } |
| 689 | 621 |
| 690 /// Returns the test arguments for testing the [index]th skipped test. The | 622 /// Returns the test arguments for testing the [index]th skipped test. The |
| 691 /// [skip] count is used to check that [index] is a valid index. | 623 /// [skip] count is used to check that [index] is a valid index. |
| 692 List<String> testSkipped(int index, int skip) { | 624 List<String> testSkipped(int index, int skip) { |
| 693 if (index < 0 || index >= skip) { | 625 if (index < 0 || index >= skip) { |
| 694 throw new ArgumentError('Invalid skip index $index'); | 626 throw new ArgumentError('Invalid skip index $index'); |
| 695 } | 627 } |
| 696 return ['${index}', '${index + 1}']; | 628 return ['${index}', '${index + 1}']; |
| 697 } | 629 } |
| 698 | 630 |
| 699 /// Return the test arguments for testing the [index]th segment (1-based) of | 631 /// Return the test arguments for testing the [index]th segment (1-based) of |
| 700 /// the [TESTS] split into [count] groups. The first [skip] tests are excluded | 632 /// the [TESTS] split into [count] groups. The first [skip] tests are excluded |
| 701 /// from the automatic grouping. | 633 /// from the automatic grouping. |
| 702 List<String> testSegment(int index, int count, int skip) { | 634 List<String> testSegment(int index, int count, int skip) { |
| 703 if (index < 0 || index > count) { | 635 if (index < 0 || index > count) { |
| 704 throw new ArgumentError('Invalid segment index $index'); | 636 throw new ArgumentError('Invalid segment index $index'); |
| 705 } | 637 } |
| 706 | 638 |
| 707 String segmentNumber(int i) { | 639 String segmentNumber(int i) { |
| 708 return '${skip + i * (TESTS.length - skip) ~/ count}'; | 640 return '${skip + i * (TESTS.length - skip) ~/ count}'; |
| 709 } | 641 } |
| 710 | 642 |
| 711 if (index == 1 && skip != 0) { | 643 if (index == 1 && skip != 0) { |
| 712 return ['${skip}', segmentNumber(index)]; | 644 return ['${skip}', segmentNumber(index)]; |
| 713 } else if (index == count) { | 645 } else if (index == count) { |
| 714 return [segmentNumber(index - 1)]; | 646 return [segmentNumber(index - 1)]; |
| 715 } else { | 647 } else { |
| 716 return [segmentNumber(index - 1), segmentNumber(index)]; | 648 return [segmentNumber(index - 1), segmentNumber(index)]; |
| 717 } | 649 } |
| 718 } | 650 } |
| OLD | NEW |