| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2014, 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 Set<String> okMapSelectorsSet = new Set.from( | |
| 8 const <String>[ | |
| 9 // From Object. | |
| 10 "==", | |
| 11 "hashCode", | |
| 12 "toString", | |
| 13 "noSuchMethod", | |
| 14 "runtimeType", | |
| 15 // From Map | |
| 16 "[]", | |
| 17 "isEmpty", | |
| 18 "isNotEmpty", | |
| 19 "keys", | |
| 20 "length", | |
| 21 "values", | |
| 22 "clear", | |
| 23 "containsKey", | |
| 24 "containsValue", | |
| 25 "forEach", | |
| 26 "remove"]); | |
| 27 | |
| 28 class MapTracerVisitor extends TracerVisitor<MapTypeInformation> { | |
| 29 // These lists are used to keep track of newly discovered assignments to | |
| 30 // the map. Note that elements at corresponding indices are expected to | |
| 31 // belong to the same assignment operation. | |
| 32 List<TypeInformation> keyAssignments = <TypeInformation>[]; | |
| 33 List<TypeInformation> valueAssignments = <TypeInformation>[]; | |
| 34 // This list is used to keep track of assignments of entire maps to | |
| 35 // this map. | |
| 36 List<MapTypeInformation> mapAssignments = <MapTypeInformation>[]; | |
| 37 | |
| 38 MapTracerVisitor(tracedType, inferrer) : super(tracedType, inferrer); | |
| 39 | |
| 40 /** | |
| 41 * Returns [true] if the analysis completed successfully, [false] | |
| 42 * if it bailed out. In the former case, [keyAssignments] and | |
| 43 * [valueAssignments] hold a list of [TypeInformation] nodes that | |
| 44 * flow into the key and value types of this map. | |
| 45 */ | |
| 46 bool run() { | |
| 47 analyze(); | |
| 48 MapTypeInformation map = tracedType; | |
| 49 if (continueAnalyzing) { | |
| 50 map.addFlowsIntoTargets(flowsInto); | |
| 51 return true; | |
| 52 } | |
| 53 keyAssignments = valueAssignments = mapAssignments = null; | |
| 54 return false; | |
| 55 } | |
| 56 | |
| 57 visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info) { | |
| 58 bailout('Passed to a closure'); | |
| 59 } | |
| 60 | |
| 61 visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { | |
| 62 super.visitStaticCallSiteTypeInformation(info); | |
| 63 Element called = info.calledElement; | |
| 64 if (called.isForeign(compiler.backend) && called.name == 'JS') { | |
| 65 bailout('Used in JS ${info.call}'); | |
| 66 } | |
| 67 } | |
| 68 | |
| 69 visitDynamicCallSiteTypeInformation(DynamicCallSiteTypeInformation info) { | |
| 70 super.visitDynamicCallSiteTypeInformation(info); | |
| 71 Selector selector = info.selector; | |
| 72 String selectorName = selector.name; | |
| 73 if (currentUser == info.receiver) { | |
| 74 if (!okMapSelectorsSet.contains(selectorName)) { | |
| 75 if (selector.isCall) { | |
| 76 int positionalLength = info.arguments.positional.length; | |
| 77 if (selectorName == 'addAll') { | |
| 78 // All keys and values from the argument flow into | |
| 79 // the map. | |
| 80 TypeInformation map = info.arguments.positional[0]; | |
| 81 if (map is MapTypeInformation) { | |
| 82 inferrer.analyzeMapAndEnqueue(map); | |
| 83 mapAssignments.add(map); | |
| 84 } else { | |
| 85 // If we could select a component from a [TypeInformation], | |
| 86 // like the keytype or valuetype in this case, we could | |
| 87 // propagate more here. | |
| 88 // TODO(herhut): implement selection on [TypeInformation]. | |
| 89 bailout('Adding map with unknown typeinfo to current map'); | |
| 90 } | |
| 91 } else if (selectorName == 'putIfAbsent') { | |
| 92 // The first argument is a new key, the result type of | |
| 93 // the second argument becomes a new value. | |
| 94 // Unfortunately, the type information does not | |
| 95 // explicitly track the return type, yet, so we have | |
| 96 // to go to dynamic. | |
| 97 // TODO(herhut,16507): Use return type of closure in | |
| 98 // Map.putIfAbsent. | |
| 99 keyAssignments.add(info.arguments.positional[0]); | |
| 100 valueAssignments.add(inferrer.types.dynamicType); | |
| 101 } else { | |
| 102 // It would be nice to handle [Map.keys] and [Map.values], too. | |
| 103 // However, currently those calls do not trigger the creation | |
| 104 // of a [ListTypeInformation], so I have nowhere to propagate | |
| 105 // that information. | |
| 106 // TODO(herhut): add support for Map.keys and Map.values. | |
| 107 bailout('Map used in a not-ok selector [$selectorName]'); | |
| 108 return; | |
| 109 } | |
| 110 } else if (selector.isIndexSet) { | |
| 111 keyAssignments.add(info.arguments.positional[0]); | |
| 112 valueAssignments.add(info.arguments.positional[1]); | |
| 113 } else if (!selector.isIndex) { | |
| 114 bailout('Map used in a not-ok selector [$selectorName]'); | |
| 115 return; | |
| 116 } | |
| 117 } | |
| 118 } else if (selector.isCall && | |
| 119 !info.targets.every((element) => element.isFunction)) { | |
| 120 bailout('Passed to a closure'); | |
| 121 return; | |
| 122 } | |
| 123 } | |
| 124 } | |
| OLD | NEW |