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 * Analyzes `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 NoSuchMethodAnalysis { | |
|
floitsch
2015/03/13 15:51:49
I wouldn't call this "Analysis".
In the end it's a
Harry Terkelsen
2015/03/17 21:43:11
Done.
| |
| 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 NoSuchMethodAnalysis(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 if (!compiler.enabledNoSuchMethod && | |
|
Johnni Winther
2015/03/16 14:20:43
Move this to the `CodegenEnqueuer.registerNoSuchMe
Harry Terkelsen
2015/03/17 21:43:11
See earlier comment about how the enqueuers and ba
| |
| 58 (throwingImpls.isNotEmpty || otherImpls.isNotEmpty)) { | |
| 59 backend.enableNoSuchMethod(enqueuer); | |
|
floitsch
2015/03/13 15:51:49
I would prefer if the "analysis" didn't have any s
Harry Terkelsen
2015/03/17 21:43:11
Done.
| |
| 60 compiler.enabledNoSuchMethod = true; | |
| 61 } | |
| 62 } | |
| 63 | |
| 64 void _categorizeImpl(Element noSuchMethodElement) { | |
| 65 assert(noSuchMethodElement.name == Compiler.NO_SUCH_METHOD); | |
| 66 if (forwardingImpls.contains(noSuchMethodElement) || | |
| 67 throwingImpls.contains(noSuchMethodElement) || | |
| 68 otherImpls.contains(noSuchMethodElement)) return; | |
| 69 if (noSuchMethodElement is! FunctionElement || | |
| 70 !compiler.noSuchMethodSelector.signatureApplies(noSuchMethodElement)) { | |
| 71 otherImpls.add(noSuchMethodElement); | |
| 72 } | |
| 73 FunctionElement noSuchMethodFunc = noSuchMethodElement as FunctionElement; | |
| 74 if (isForwarding(noSuchMethodFunc)) { | |
| 75 forwardingImpls.add(noSuchMethodFunc); | |
| 76 } else if (isThrowing(noSuchMethodFunc)) { | |
| 77 throwingImpls.add(noSuchMethodFunc); | |
| 78 } else { | |
| 79 otherImpls.add(noSuchMethodFunc); | |
| 80 } | |
| 81 } | |
| 82 | |
| 83 bool isForwarding(FunctionElement element) { | |
| 84 if (backend.isDefaultNoSuchMethodImplementation(element)) { | |
| 85 return true; | |
| 86 } | |
| 87 | |
| 88 if (!hasForwardingSyntax(element)) { | |
| 89 return false; | |
| 90 } | |
| 91 | |
| 92 // Check if the super call hits a forwarding implementation | |
| 93 Element superCall = element.enclosingClass | |
| 94 .lookupSuperSelector(compiler.noSuchMethodSelector); | |
| 95 _categorizeImpl(superCall); | |
|
Johnni Winther
2015/03/16 14:20:43
Maybe [_categorizeImpl] should return an enum resu
Harry Terkelsen
2015/03/17 21:55:16
Done.
| |
| 96 return forwardingImpls.contains(superCall); | |
| 97 } | |
| 98 | |
| 99 bool hasForwardingSyntax(FunctionElement element) { | |
| 100 String param = element.parameters.single.name; | |
| 101 Statement body = element.node.body; | |
| 102 Expression expr; | |
| 103 if (body is Return && body.isArrowBody) { | |
| 104 expr = body.expression; | |
| 105 } else if (body is Block && | |
| 106 !body.statements.isEmpty && | |
| 107 body.statements.nodes.tail.isEmpty) { | |
| 108 Statement stmt = body.statements.nodes.head; | |
| 109 if (stmt is Return && stmt.hasExpression) { | |
| 110 expr = stmt.expression; | |
| 111 } | |
| 112 } | |
| 113 if (expr is Send && | |
| 114 expr.isSuperCall && | |
| 115 expr.selector is Identifier && | |
| 116 (expr.selector as Identifier).source == Compiler.NO_SUCH_METHOD) { | |
| 117 var arg = expr.arguments.head; | |
| 118 if (arg is Send && | |
| 119 arg.argumentsNode == null && | |
| 120 arg.receiver == null && | |
| 121 arg.selector is Identifier && | |
| 122 arg.selector.source == param) { | |
| 123 return true; | |
| 124 } | |
| 125 } | |
| 126 return false; | |
| 127 } | |
| 128 | |
| 129 bool isThrowing(FunctionElement element) { | |
| 130 Statement body = element.node.body; | |
| 131 if (body is Return && body.isArrowBody) { | |
| 132 if (body.expression is Throw) { | |
| 133 return true; | |
| 134 } | |
| 135 } else if (body is Block && | |
| 136 !body.statements.isEmpty && | |
| 137 body.statements.nodes.tail.isEmpty) { | |
| 138 if (body.statements.nodes.head is Throw) { | |
| 139 return true; | |
| 140 } | |
| 141 } | |
| 142 return false; | |
| 143 } | |
| 144 } | |
| OLD | NEW |