Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 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 | 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 part of js_backend; | 5 part of js_backend; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * Categorizes `noSuchMethod` implementations. | 8 * Categorizes `noSuchMethod` implementations. |
| 9 * | 9 * |
| 10 * If user code includes `noSuchMethod` implementations, type inference is | 10 * If user code includes `noSuchMethod` implementations, type inference is |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 38 */ | 38 */ |
| 39 class NoSuchMethodRegistry { | 39 class NoSuchMethodRegistry { |
| 40 /// The implementations that fall into category A, described above. | 40 /// The implementations that fall into category A, described above. |
| 41 final Set<FunctionElement> defaultImpls = new Set<FunctionElement>(); | 41 final Set<FunctionElement> defaultImpls = new Set<FunctionElement>(); |
| 42 /// The implementations that fall into category B, described above. | 42 /// The implementations that fall into category B, described above. |
| 43 final Set<FunctionElement> throwingImpls = new Set<FunctionElement>(); | 43 final Set<FunctionElement> throwingImpls = new Set<FunctionElement>(); |
| 44 /// The implementations that fall into category C, described above. | 44 /// The implementations that fall into category C, described above. |
| 45 final Set<Element> otherImpls = new Set<Element>(); | 45 final Set<Element> otherImpls = new Set<Element>(); |
| 46 | 46 |
| 47 /// The implementations that have not yet been categorized. | 47 /// The implementations that have not yet been categorized. |
| 48 final Set<Element> uncategorizedImpls = new Set<Element>(); | 48 final Set<Element> _uncategorizedImpls = new Set<Element>(); |
| 49 | 49 |
| 50 final JavaScriptBackend backend; | 50 final JavaScriptBackend _backend; |
| 51 final Compiler compiler; | 51 final Compiler _compiler; |
| 52 | 52 |
| 53 NoSuchMethodRegistry(JavaScriptBackend backend) | 53 NoSuchMethodRegistry(JavaScriptBackend backend) |
| 54 : this.backend = backend, | 54 : this._backend = backend, |
| 55 this.compiler = backend.compiler; | 55 this._compiler = backend.compiler; |
| 56 | 56 |
| 57 bool get hasThrowingNoSuchMethod => throwingImpls.isNotEmpty; | 57 bool get hasThrowingNoSuchMethod => throwingImpls.isNotEmpty; |
| 58 bool get hasComplexNoSuchMethod => otherImpls.isNotEmpty; | 58 bool get hasComplexNoSuchMethod => otherImpls.isNotEmpty; |
| 59 | 59 |
| 60 void registerNoSuchMethod(Element noSuchMethodElement) { | 60 void registerNoSuchMethod(Element noSuchMethodElement) { |
| 61 uncategorizedImpls.add(noSuchMethodElement); | 61 _uncategorizedImpls.add(noSuchMethodElement); |
| 62 } | 62 } |
| 63 | 63 |
| 64 void onQueueEmpty() { | 64 void onQueueEmpty() { |
| 65 uncategorizedImpls.forEach(_categorizeImpl); | 65 _uncategorizedImpls.forEach(_categorizeImpl); |
| 66 uncategorizedImpls.clear(); | 66 _uncategorizedImpls.clear(); |
| 67 } | 67 } |
| 68 | 68 |
| 69 bool isComplex(FunctionElement element) => otherImpls.contains(element); | |
|
floitsch
2015/03/23 22:34:42
Add dartdoc.
assert, that the element is a nsm.
Harry Terkelsen
2015/03/24 00:10:29
Done.
| |
| 70 | |
| 69 NsmCategory _categorizeImpl(Element noSuchMethodElement) { | 71 NsmCategory _categorizeImpl(Element noSuchMethodElement) { |
| 70 assert(noSuchMethodElement.name == Compiler.NO_SUCH_METHOD); | 72 assert(noSuchMethodElement.name == Compiler.NO_SUCH_METHOD); |
| 71 if (defaultImpls.contains(noSuchMethodElement)) { | 73 if (defaultImpls.contains(noSuchMethodElement)) { |
| 72 return NsmCategory.DEFAULT; | 74 return NsmCategory.DEFAULT; |
| 73 } | 75 } |
| 74 if (throwingImpls.contains(noSuchMethodElement)) { | 76 if (throwingImpls.contains(noSuchMethodElement)) { |
| 75 return NsmCategory.THROWING; | 77 return NsmCategory.THROWING; |
| 76 } | 78 } |
| 77 if (otherImpls.contains(noSuchMethodElement)) { | 79 if (otherImpls.contains(noSuchMethodElement)) { |
| 78 return NsmCategory.OTHER; | 80 return NsmCategory.OTHER; |
| 79 } | 81 } |
| 80 if (noSuchMethodElement is! FunctionElement || | 82 if (noSuchMethodElement is! FunctionElement || |
| 81 !compiler.noSuchMethodSelector.signatureApplies(noSuchMethodElement)) { | 83 !_compiler.noSuchMethodSelector.signatureApplies(noSuchMethodElement)) { |
| 82 otherImpls.add(noSuchMethodElement); | 84 otherImpls.add(noSuchMethodElement); |
| 83 return NsmCategory.OTHER; | 85 return NsmCategory.OTHER; |
| 84 } | 86 } |
| 85 FunctionElement noSuchMethodFunc = noSuchMethodElement as FunctionElement; | 87 FunctionElement noSuchMethodFunc = noSuchMethodElement as FunctionElement; |
| 86 if (backend.isDefaultNoSuchMethodImplementation(noSuchMethodFunc)) { | 88 if (_isDefaultNoSuchMethodImplementation(noSuchMethodFunc)) { |
| 87 defaultImpls.add(noSuchMethodFunc); | 89 defaultImpls.add(noSuchMethodFunc); |
| 88 return NsmCategory.DEFAULT; | 90 return NsmCategory.DEFAULT; |
| 89 } else if (hasForwardingSyntax(noSuchMethodFunc)) { | 91 } else if (_hasForwardingSyntax(noSuchMethodFunc)) { |
| 90 // If the implementation is 'noSuchMethod(x) => super.noSuchMethod(x);' | 92 // If the implementation is 'noSuchMethod(x) => super.noSuchMethod(x);' |
| 91 // then it is in the same category as the super call. | 93 // then it is in the same category as the super call. |
| 92 Element superCall = noSuchMethodFunc.enclosingClass | 94 Element superCall = noSuchMethodFunc.enclosingClass |
| 93 .lookupSuperSelector(compiler.noSuchMethodSelector); | 95 .lookupSuperSelector(_compiler.noSuchMethodSelector); |
| 94 NsmCategory category = _categorizeImpl(superCall); | 96 NsmCategory category = _categorizeImpl(superCall); |
| 95 switch(category) { | 97 switch(category) { |
| 96 case NsmCategory.DEFAULT: | 98 case NsmCategory.DEFAULT: |
| 97 defaultImpls.add(noSuchMethodFunc); | 99 defaultImpls.add(noSuchMethodFunc); |
| 98 break; | 100 break; |
| 99 case NsmCategory.THROWING: | 101 case NsmCategory.THROWING: |
| 100 throwingImpls.add(noSuchMethodFunc); | 102 throwingImpls.add(noSuchMethodFunc); |
| 101 break; | 103 break; |
| 102 case NsmCategory.OTHER: | 104 case NsmCategory.OTHER: |
| 103 otherImpls.add(noSuchMethodFunc); | 105 otherImpls.add(noSuchMethodFunc); |
| 104 break; | 106 break; |
| 105 } | 107 } |
| 106 return category; | 108 return category; |
| 107 } else if (isThrowing(noSuchMethodFunc)) { | 109 } else if (_hasThrowingSyntax(noSuchMethodFunc)) { |
| 108 throwingImpls.add(noSuchMethodFunc); | 110 throwingImpls.add(noSuchMethodFunc); |
| 109 return NsmCategory.THROWING; | 111 return NsmCategory.THROWING; |
| 110 } else { | 112 } else { |
| 111 otherImpls.add(noSuchMethodFunc); | 113 otherImpls.add(noSuchMethodFunc); |
| 112 return NsmCategory.OTHER; | 114 return NsmCategory.OTHER; |
| 113 } | 115 } |
| 114 } | 116 } |
| 115 | 117 |
| 116 bool hasForwardingSyntax(FunctionElement element) { | 118 bool _isDefaultNoSuchMethodImplementation(Element element) { |
| 119 ClassElement classElement = element.enclosingClass; | |
| 120 return classElement == _compiler.objectClass | |
| 121 || classElement == _backend.jsInterceptorClass | |
| 122 || classElement == _backend.jsNullClass; | |
| 123 } | |
| 124 | |
| 125 bool _hasForwardingSyntax(FunctionElement element) { | |
| 117 // At this point we know that this is signature-compatible with | 126 // At this point we know that this is signature-compatible with |
| 118 // Object.noSuchMethod, but it may have more than one argument as long as | 127 // Object.noSuchMethod, but it may have more than one argument as long as |
| 119 // it only has one required argument. | 128 // it only has one required argument. |
| 120 String param = element.parameters.first.name; | 129 String param = element.parameters.first.name; |
| 121 Statement body = element.node.body; | 130 Statement body = element.node.body; |
| 122 Expression expr; | 131 Expression expr; |
| 123 if (body is Return && body.isArrowBody) { | 132 if (body is Return && body.isArrowBody) { |
| 124 expr = body.expression; | 133 expr = body.expression; |
| 125 } else if (body is Block && | 134 } else if (body is Block && |
| 126 !body.statements.isEmpty && | 135 !body.statements.isEmpty && |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 140 arg.argumentsNode == null && | 149 arg.argumentsNode == null && |
| 141 arg.receiver == null && | 150 arg.receiver == null && |
| 142 arg.selector is Identifier && | 151 arg.selector is Identifier && |
| 143 arg.selector.source == param) { | 152 arg.selector.source == param) { |
| 144 return true; | 153 return true; |
| 145 } | 154 } |
| 146 } | 155 } |
| 147 return false; | 156 return false; |
| 148 } | 157 } |
| 149 | 158 |
| 150 bool isThrowing(FunctionElement element) { | 159 bool _hasThrowingSyntax(FunctionElement element) { |
| 151 Statement body = element.node.body; | 160 Statement body = element.node.body; |
| 152 if (body is Return && body.isArrowBody) { | 161 if (body is Return && body.isArrowBody) { |
| 153 if (body.expression is Throw) { | 162 if (body.expression is Throw) { |
| 154 return true; | 163 return true; |
| 155 } | 164 } |
| 156 } else if (body is Block && | 165 } else if (body is Block && |
| 157 !body.statements.isEmpty && | 166 !body.statements.isEmpty && |
| 158 body.statements.nodes.tail.isEmpty) { | 167 body.statements.nodes.tail.isEmpty) { |
| 159 if (body.statements.nodes.head is Throw) { | 168 if (body.statements.nodes.head is Throw) { |
| 160 return true; | 169 return true; |
| 161 } | 170 } |
| 162 } | 171 } |
| 163 return false; | 172 return false; |
| 164 } | 173 } |
| 165 } | 174 } |
| 166 | 175 |
| 167 enum NsmCategory { DEFAULT, THROWING, OTHER } | 176 enum NsmCategory { DEFAULT, THROWING, OTHER } |
| OLD | NEW |