Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2015, 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 js_backend; | |
| 6 | |
| 7 /** | |
| 8 * Categorizes `noSuchMethod` implementations. | |
| 9 * | |
| 10 * If user code includes `noSuchMethod` implementations, type inference is | |
| 11 * hindered because (for instance) any selector where the type of the | |
| 12 * receiver is not known all implementations of `noSuchMethod` must be taken | |
| 13 * into account when inferring the return type. | |
| 14 * | |
| 15 * The situation can be ameliorated with some heuristics for disregarding some | |
| 16 * `noSuchMethod` implementations during type inference. We can partition | |
| 17 * `noSuchMethod` implementations into 3 categories. | |
| 18 * | |
| 19 * Implementations in category A have the form | |
| 20 * | |
| 21 * noSuchMethod(x) => super.noSuchMethod(x); | |
| 22 * | |
| 23 * where the `super.noSuchMethod` hits `Object.noSuchMethod`, | |
| 24 * `Interceptor.noSuchMethod`, or another implementation in category A. | |
| 25 * | |
| 26 * Implementations in category B immediately throw, and all other | |
| 27 * implementations are in category C. A and B implementations may be safely | |
| 28 * ignored during type inference. After type inference, we may further partition | |
| 29 * C into C1 and C2, where C1 implementations have no return type (they are | |
| 30 * guaranteed to throw) and C2, those with return types. | |
| 31 */ | |
| 32 class NoSuchMethodRegistry { | |
| 33 /// The implementations that fall into category A, described above. | |
| 34 final Set<FunctionElement> forwardingImpls = new Set<FunctionElement>(); | |
| 35 /// The implementations that fall into category B, described above. | |
| 36 final Set<FunctionElement> throwingImpls = new Set<FunctionElement>(); | |
| 37 /// The implementations that fall into category C, described above. | |
| 38 final Set<Element> otherImpls = new Set<Element>(); | |
| 39 | |
| 40 /// The implementations that have not yet been categorized. | |
| 41 final Set<Element> uncategorizedImpls = new Set<Element>(); | |
| 42 | |
| 43 final JavaScriptBackend backend; | |
| 44 final Compiler compiler; | |
| 45 | |
| 46 NoSuchMethodRegistry(JavaScriptBackend backend) | |
| 47 : this.backend = backend, | |
| 48 this.compiler = backend.compiler; | |
| 49 | |
| 50 void registerNoSuchMethod(Element noSuchMethodElement) { | |
| 51 uncategorizedImpls.add(noSuchMethodElement); | |
| 52 } | |
| 53 | |
| 54 void onQueueEmpty(Enqueuer enqueuer) { | |
| 55 uncategorizedImpls.forEach(_categorizeImpl); | |
| 56 uncategorizedImpls.clear(); | |
| 57 } | |
| 58 | |
| 59 NsmCategory _categorizeImpl(Element noSuchMethodElement) { | |
| 60 assert(noSuchMethodElement.name == Compiler.NO_SUCH_METHOD); | |
| 61 if (forwardingImpls.contains(noSuchMethodElement)) { | |
| 62 return NsmCategory.FORWARDING; | |
| 63 } | |
| 64 if (throwingImpls.contains(noSuchMethodElement)) { | |
| 65 return NsmCategory.THROWING; | |
| 66 } | |
| 67 if (otherImpls.contains(noSuchMethodElement)) { | |
| 68 return NsmCategory.OTHER; | |
| 69 } | |
| 70 if (noSuchMethodElement is! FunctionElement || | |
| 71 !compiler.noSuchMethodSelector.signatureApplies(noSuchMethodElement)) { | |
| 72 otherImpls.add(noSuchMethodElement); | |
| 73 return NsmCategory.OTHER; | |
| 74 } | |
| 75 FunctionElement noSuchMethodFunc = noSuchMethodElement as FunctionElement; | |
| 76 if (isForwarding(noSuchMethodFunc)) { | |
| 77 forwardingImpls.add(noSuchMethodFunc); | |
| 78 return NsmCategory.FORWARDING; | |
| 79 } else if (isThrowing(noSuchMethodFunc)) { | |
| 80 throwingImpls.add(noSuchMethodFunc); | |
| 81 return NsmCategory.THROWING; | |
| 82 } else { | |
| 83 otherImpls.add(noSuchMethodFunc); | |
| 84 return NsmCategory.OTHER; | |
| 85 } | |
| 86 } | |
| 87 | |
| 88 bool isForwarding(FunctionElement element) { | |
| 89 if (backend.isDefaultNoSuchMethodImplementation(element)) { | |
| 90 return true; | |
| 91 } | |
| 92 | |
| 93 if (!hasForwardingSyntax(element)) { | |
| 94 return false; | |
| 95 } | |
| 96 | |
| 97 // Check if the super call hits a forwarding implementation | |
| 98 Element superCall = element.enclosingClass | |
| 99 .lookupSuperSelector(compiler.noSuchMethodSelector); | |
| 100 var category = _categorizeImpl(superCall); | |
|
Johnni Winther
2015/03/18 10:47:16
Nit: Replace 'var' with 'NsmCategory'. We only use
Harry Terkelsen
2015/03/18 20:54:50
Done.
| |
| 101 return category == NsmCategory.FORWARDING; | |
| 102 } | |
| 103 | |
| 104 bool hasForwardingSyntax(FunctionElement element) { | |
| 105 String param = element.parameters.single.name; | |
| 106 Statement body = element.node.body; | |
| 107 Expression expr; | |
| 108 if (body is Return && body.isArrowBody) { | |
| 109 expr = body.expression; | |
| 110 } else if (body is Block && | |
| 111 !body.statements.isEmpty && | |
| 112 body.statements.nodes.tail.isEmpty) { | |
| 113 Statement stmt = body.statements.nodes.head; | |
| 114 if (stmt is Return && stmt.hasExpression) { | |
| 115 expr = stmt.expression; | |
| 116 } | |
| 117 } | |
| 118 if (expr is Send && | |
| 119 expr.isSuperCall && | |
| 120 expr.selector is Identifier && | |
| 121 (expr.selector as Identifier).source == Compiler.NO_SUCH_METHOD) { | |
| 122 var arg = expr.arguments.head; | |
| 123 if (arg is Send && | |
| 124 arg.argumentsNode == null && | |
| 125 arg.receiver == null && | |
| 126 arg.selector is Identifier && | |
| 127 arg.selector.source == param) { | |
| 128 return true; | |
| 129 } | |
| 130 } | |
| 131 return false; | |
| 132 } | |
| 133 | |
| 134 bool isThrowing(FunctionElement element) { | |
| 135 Statement body = element.node.body; | |
| 136 if (body is Return && body.isArrowBody) { | |
| 137 if (body.expression is Throw) { | |
| 138 return true; | |
| 139 } | |
| 140 } else if (body is Block && | |
| 141 !body.statements.isEmpty && | |
| 142 body.statements.nodes.tail.isEmpty) { | |
| 143 if (body.statements.nodes.head is Throw) { | |
| 144 return true; | |
| 145 } | |
| 146 } | |
| 147 return false; | |
| 148 } | |
| 149 } | |
| 150 | |
| 151 enum NsmCategory { FORWARDING, THROWING, OTHER } | |
| OLD | NEW |