| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
| 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. | |
| 4 | |
| 5 part of type_graph_inferrer; | |
| 6 | |
| 7 /** | |
| 8 * A set of selector names that [List] implements, that we know do not | |
| 9 * change the element type of the list, or let the list escape to code | |
| 10 * that might change the element type. | |
| 11 */ | |
| 12 Set<String> okListSelectorsSet = new Set<String>.from( | |
| 13 const <String>[ | |
| 14 // From Object. | |
| 15 '==', | |
| 16 'hashCode', | |
| 17 'toString', | |
| 18 'noSuchMethod', | |
| 19 'runtimeType', | |
| 20 | |
| 21 // From Iterable. | |
| 22 'iterator', | |
| 23 'map', | |
| 24 'where', | |
| 25 'expand', | |
| 26 'contains', | |
| 27 'forEach', | |
| 28 'reduce', | |
| 29 'fold', | |
| 30 'every', | |
| 31 'join', | |
| 32 'any', | |
| 33 'toList', | |
| 34 'toSet', | |
| 35 'length', | |
| 36 'isEmpty', | |
| 37 'isNotEmpty', | |
| 38 'take', | |
| 39 'takeWhile', | |
| 40 'skip', | |
| 41 'skipWhile', | |
| 42 'first', | |
| 43 'last', | |
| 44 'single', | |
| 45 'firstWhere', | |
| 46 'lastWhere', | |
| 47 'singleWhere', | |
| 48 'elementAt', | |
| 49 | |
| 50 // From List. | |
| 51 '[]', | |
| 52 'length', | |
| 53 'reversed', | |
| 54 'sort', | |
| 55 'indexOf', | |
| 56 'lastIndexOf', | |
| 57 'clear', | |
| 58 'remove', | |
| 59 'removeAt', | |
| 60 'removeLast', | |
| 61 'removeWhere', | |
| 62 'retainWhere', | |
| 63 'sublist', | |
| 64 'getRange', | |
| 65 'removeRange', | |
| 66 'asMap', | |
| 67 | |
| 68 // From JSArray. | |
| 69 'checkMutable', | |
| 70 'checkGrowable', | |
| 71 ]); | |
| 72 | |
| 73 Set<String> doNotChangeLengthSelectorsSet = new Set<String>.from( | |
| 74 const <String>[ | |
| 75 // From Object. | |
| 76 '==', | |
| 77 'hashCode', | |
| 78 'toString', | |
| 79 'noSuchMethod', | |
| 80 'runtimeType', | |
| 81 | |
| 82 // From Iterable. | |
| 83 'iterator', | |
| 84 'map', | |
| 85 'where', | |
| 86 'expand', | |
| 87 'contains', | |
| 88 'forEach', | |
| 89 'reduce', | |
| 90 'fold', | |
| 91 'every', | |
| 92 'join', | |
| 93 'any', | |
| 94 'toList', | |
| 95 'toSet', | |
| 96 'length', | |
| 97 'isEmpty', | |
| 98 'isNotEmpty', | |
| 99 'take', | |
| 100 'takeWhile', | |
| 101 'skip', | |
| 102 'skipWhile', | |
| 103 'first', | |
| 104 'last', | |
| 105 'single', | |
| 106 'firstWhere', | |
| 107 'lastWhere', | |
| 108 'singleWhere', | |
| 109 'elementAt', | |
| 110 | |
| 111 // From List. | |
| 112 '[]', | |
| 113 '[]=', | |
| 114 'length', | |
| 115 'reversed', | |
| 116 'sort', | |
| 117 'indexOf', | |
| 118 'lastIndexOf', | |
| 119 'sublist', | |
| 120 'getRange', | |
| 121 'asMap', | |
| 122 | |
| 123 // From JSArray. | |
| 124 'checkMutable', | |
| 125 'checkGrowable', | |
| 126 ]); | |
| 127 | |
| 128 | |
| 129 class ListTracerVisitor extends TracerVisitor<ListTypeInformation> { | |
| 130 // The [Set] of found assignments to the list. | |
| 131 Set<TypeInformation> assignments = new Setlet<TypeInformation>(); | |
| 132 bool callsGrowableMethod = false; | |
| 133 | |
| 134 ListTracerVisitor(tracedType, inferrer) : super(tracedType, inferrer); | |
| 135 | |
| 136 /** | |
| 137 * Returns [true] if the analysis completed successfully, [false] if it | |
| 138 * bailed out. In the former case, [assignments] holds a list of | |
| 139 * [TypeInformation] nodes that flow into the element type of this list. | |
| 140 */ | |
| 141 bool run() { | |
| 142 analyze(); | |
| 143 ListTypeInformation list = tracedType; | |
| 144 if (continueAnalyzing) { | |
| 145 if (!callsGrowableMethod && list.inferredLength == null) { | |
| 146 list.inferredLength = list.originalLength; | |
| 147 } | |
| 148 list.addFlowsIntoTargets(flowsInto); | |
| 149 return true; | |
| 150 } else { | |
| 151 callsGrowableMethod = true; | |
| 152 assignments = null; | |
| 153 return false; | |
| 154 } | |
| 155 } | |
| 156 | |
| 157 visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info) { | |
| 158 bailout('Passed to a closure'); | |
| 159 } | |
| 160 | |
| 161 visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { | |
| 162 super.visitStaticCallSiteTypeInformation(info); | |
| 163 Element called = info.calledElement; | |
| 164 if (called.isForeign(compiler.backend) && called.name == 'JS') { | |
| 165 bailout('Used in JS ${info.call}'); | |
| 166 } | |
| 167 } | |
| 168 | |
| 169 visitDynamicCallSiteTypeInformation(DynamicCallSiteTypeInformation info) { | |
| 170 super.visitDynamicCallSiteTypeInformation(info); | |
| 171 Selector selector = info.selector; | |
| 172 String selectorName = selector.name; | |
| 173 if (currentUser == info.receiver) { | |
| 174 if (!okListSelectorsSet.contains(selectorName)) { | |
| 175 if (selector.isCall) { | |
| 176 int positionalLength = info.arguments.positional.length; | |
| 177 if (selectorName == 'add') { | |
| 178 if (positionalLength == 1) { | |
| 179 assignments.add(info.arguments.positional[0]); | |
| 180 } | |
| 181 } else if (selectorName == 'insert') { | |
| 182 if (positionalLength == 2) { | |
| 183 assignments.add(info.arguments.positional[1]); | |
| 184 } | |
| 185 } else { | |
| 186 bailout('Used in a not-ok selector'); | |
| 187 return; | |
| 188 } | |
| 189 } else if (selector.isIndexSet) { | |
| 190 assignments.add(info.arguments.positional[1]); | |
| 191 } else if (!selector.isIndex) { | |
| 192 bailout('Used in a not-ok selector'); | |
| 193 return; | |
| 194 } | |
| 195 } | |
| 196 if (!doNotChangeLengthSelectorsSet.contains(selectorName)) { | |
| 197 callsGrowableMethod = true; | |
| 198 } | |
| 199 if (selectorName == 'length' && selector.isSetter) { | |
| 200 callsGrowableMethod = true; | |
| 201 assignments.add(inferrer.types.nullType); | |
| 202 } | |
| 203 } else if (selector.isCall && | |
| 204 !info.targets.every((element) => element.isFunction)) { | |
| 205 bailout('Passed to a closure'); | |
| 206 return; | |
| 207 } | |
| 208 } | |
| 209 } | |
| OLD | NEW |