| 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 } |
| 68 |
| 69 /// Returns [true] if the given element is a complex [noSuchMethod] |
| 70 /// implementation. An implementation is complex if it falls into |
| 71 /// category C, as described above. |
| 72 bool isComplex(FunctionElement element) { |
| 73 assert(element.name == Compiler.NO_SUCH_METHOD); |
| 74 return otherImpls.contains(element); |
| 67 } | 75 } |
| 68 | 76 |
| 69 NsmCategory _categorizeImpl(Element noSuchMethodElement) { | 77 NsmCategory _categorizeImpl(Element noSuchMethodElement) { |
| 70 assert(noSuchMethodElement.name == Compiler.NO_SUCH_METHOD); | 78 assert(noSuchMethodElement.name == Compiler.NO_SUCH_METHOD); |
| 71 if (defaultImpls.contains(noSuchMethodElement)) { | 79 if (defaultImpls.contains(noSuchMethodElement)) { |
| 72 return NsmCategory.DEFAULT; | 80 return NsmCategory.DEFAULT; |
| 73 } | 81 } |
| 74 if (throwingImpls.contains(noSuchMethodElement)) { | 82 if (throwingImpls.contains(noSuchMethodElement)) { |
| 75 return NsmCategory.THROWING; | 83 return NsmCategory.THROWING; |
| 76 } | 84 } |
| 77 if (otherImpls.contains(noSuchMethodElement)) { | 85 if (otherImpls.contains(noSuchMethodElement)) { |
| 78 return NsmCategory.OTHER; | 86 return NsmCategory.OTHER; |
| 79 } | 87 } |
| 80 if (noSuchMethodElement is! FunctionElement || | 88 if (noSuchMethodElement is! FunctionElement || |
| 81 !compiler.noSuchMethodSelector.signatureApplies(noSuchMethodElement)) { | 89 !_compiler.noSuchMethodSelector.signatureApplies(noSuchMethodElement)) { |
| 82 otherImpls.add(noSuchMethodElement); | 90 otherImpls.add(noSuchMethodElement); |
| 83 return NsmCategory.OTHER; | 91 return NsmCategory.OTHER; |
| 84 } | 92 } |
| 85 FunctionElement noSuchMethodFunc = noSuchMethodElement as FunctionElement; | 93 FunctionElement noSuchMethodFunc = noSuchMethodElement as FunctionElement; |
| 86 if (backend.isDefaultNoSuchMethodImplementation(noSuchMethodFunc)) { | 94 if (_isDefaultNoSuchMethodImplementation(noSuchMethodFunc)) { |
| 87 defaultImpls.add(noSuchMethodFunc); | 95 defaultImpls.add(noSuchMethodFunc); |
| 88 return NsmCategory.DEFAULT; | 96 return NsmCategory.DEFAULT; |
| 89 } else if (hasForwardingSyntax(noSuchMethodFunc)) { | 97 } else if (_hasForwardingSyntax(noSuchMethodFunc)) { |
| 90 // If the implementation is 'noSuchMethod(x) => super.noSuchMethod(x);' | 98 // If the implementation is 'noSuchMethod(x) => super.noSuchMethod(x);' |
| 91 // then it is in the same category as the super call. | 99 // then it is in the same category as the super call. |
| 92 Element superCall = noSuchMethodFunc.enclosingClass | 100 Element superCall = noSuchMethodFunc.enclosingClass |
| 93 .lookupSuperSelector(compiler.noSuchMethodSelector); | 101 .lookupSuperSelector(_compiler.noSuchMethodSelector); |
| 94 NsmCategory category = _categorizeImpl(superCall); | 102 NsmCategory category = _categorizeImpl(superCall); |
| 95 switch(category) { | 103 switch(category) { |
| 96 case NsmCategory.DEFAULT: | 104 case NsmCategory.DEFAULT: |
| 97 defaultImpls.add(noSuchMethodFunc); | 105 defaultImpls.add(noSuchMethodFunc); |
| 98 break; | 106 break; |
| 99 case NsmCategory.THROWING: | 107 case NsmCategory.THROWING: |
| 100 throwingImpls.add(noSuchMethodFunc); | 108 throwingImpls.add(noSuchMethodFunc); |
| 101 break; | 109 break; |
| 102 case NsmCategory.OTHER: | 110 case NsmCategory.OTHER: |
| 103 otherImpls.add(noSuchMethodFunc); | 111 otherImpls.add(noSuchMethodFunc); |
| 104 break; | 112 break; |
| 105 } | 113 } |
| 106 return category; | 114 return category; |
| 107 } else if (isThrowing(noSuchMethodFunc)) { | 115 } else if (_hasThrowingSyntax(noSuchMethodFunc)) { |
| 108 throwingImpls.add(noSuchMethodFunc); | 116 throwingImpls.add(noSuchMethodFunc); |
| 109 return NsmCategory.THROWING; | 117 return NsmCategory.THROWING; |
| 110 } else { | 118 } else { |
| 111 otherImpls.add(noSuchMethodFunc); | 119 otherImpls.add(noSuchMethodFunc); |
| 112 return NsmCategory.OTHER; | 120 return NsmCategory.OTHER; |
| 113 } | 121 } |
| 114 } | 122 } |
| 115 | 123 |
| 116 bool hasForwardingSyntax(FunctionElement element) { | 124 bool _isDefaultNoSuchMethodImplementation(Element element) { |
| 125 ClassElement classElement = element.enclosingClass; |
| 126 return classElement == _compiler.objectClass |
| 127 || classElement == _backend.jsInterceptorClass |
| 128 || classElement == _backend.jsNullClass; |
| 129 } |
| 130 |
| 131 bool _hasForwardingSyntax(FunctionElement element) { |
| 117 // At this point we know that this is signature-compatible with | 132 // 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 | 133 // Object.noSuchMethod, but it may have more than one argument as long as |
| 119 // it only has one required argument. | 134 // it only has one required argument. |
| 120 String param = element.parameters.first.name; | 135 String param = element.parameters.first.name; |
| 121 Statement body = element.node.body; | 136 Statement body = element.node.body; |
| 122 Expression expr; | 137 Expression expr; |
| 123 if (body is Return && body.isArrowBody) { | 138 if (body is Return && body.isArrowBody) { |
| 124 expr = body.expression; | 139 expr = body.expression; |
| 125 } else if (body is Block && | 140 } else if (body is Block && |
| 126 !body.statements.isEmpty && | 141 !body.statements.isEmpty && |
| (...skipping 13 matching lines...) Expand all Loading... |
| 140 arg.argumentsNode == null && | 155 arg.argumentsNode == null && |
| 141 arg.receiver == null && | 156 arg.receiver == null && |
| 142 arg.selector is Identifier && | 157 arg.selector is Identifier && |
| 143 arg.selector.source == param) { | 158 arg.selector.source == param) { |
| 144 return true; | 159 return true; |
| 145 } | 160 } |
| 146 } | 161 } |
| 147 return false; | 162 return false; |
| 148 } | 163 } |
| 149 | 164 |
| 150 bool isThrowing(FunctionElement element) { | 165 bool _hasThrowingSyntax(FunctionElement element) { |
| 151 Statement body = element.node.body; | 166 Statement body = element.node.body; |
| 152 if (body is Return && body.isArrowBody) { | 167 if (body is Return && body.isArrowBody) { |
| 153 if (body.expression is Throw) { | 168 if (body.expression is Throw) { |
| 154 return true; | 169 return true; |
| 155 } | 170 } |
| 156 } else if (body is Block && | 171 } else if (body is Block && |
| 157 !body.statements.isEmpty && | 172 !body.statements.isEmpty && |
| 158 body.statements.nodes.tail.isEmpty) { | 173 body.statements.nodes.tail.isEmpty) { |
| 159 if (body.statements.nodes.head is Throw) { | 174 if (body.statements.nodes.head is Throw) { |
| 160 return true; | 175 return true; |
| 161 } | 176 } |
| 162 } | 177 } |
| 163 return false; | 178 return false; |
| 164 } | 179 } |
| 165 } | 180 } |
| 166 | 181 |
| 167 enum NsmCategory { DEFAULT, THROWING, OTHER } | 182 enum NsmCategory { DEFAULT, THROWING, OTHER } |
| OLD | NEW |