| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 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 | 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 library dart2js.js_emitter.interceptor_stub_generator; | 5 library dart2js.js_emitter.interceptor_stub_generator; |
| 6 | 6 |
| 7 import '../compiler.dart' show Compiler; | 7 import 'package:js_runtime/shared/embedded_names.dart' as embeddedNames; |
| 8 |
| 9 import '../common_elements.dart'; |
| 8 import '../constants/values.dart'; | 10 import '../constants/values.dart'; |
| 9 import '../elements/entities.dart'; | 11 import '../elements/entities.dart'; |
| 10 import '../elements/types.dart' show InterfaceType; | 12 import '../elements/types.dart' show InterfaceType; |
| 11 import '../js/js.dart' as jsAst; | 13 import '../js/js.dart' as jsAst; |
| 12 import '../js/js.dart' show js; | 14 import '../js/js.dart' show js; |
| 13 import '../js_backend/js_backend.dart' | 15 import '../js_backend/namer.dart' show Namer; |
| 14 show | 16 import '../js_backend/constant_handler_javascript.dart' |
| 15 CustomElementsCodegenAnalysis, | 17 show JavaScriptConstantCompiler; |
| 16 JavaScriptBackend, | 18 import '../js_backend/custom_elements_analysis.dart' |
| 17 JavaScriptConstantCompiler, | 19 show CustomElementsCodegenAnalysis; |
| 18 Namer; | 20 import '../js_backend/native_data.dart'; |
| 21 import '../js_backend/interceptor_data.dart'; |
| 22 import '../native/enqueue.dart'; |
| 23 import '../options.dart'; |
| 19 import '../universe/selector.dart' show Selector; | 24 import '../universe/selector.dart' show Selector; |
| 25 import '../universe/world_builder.dart' show CodegenWorldBuilder; |
| 20 import '../world.dart' show ClosedWorld; | 26 import '../world.dart' show ClosedWorld; |
| 21 | 27 |
| 22 import 'code_emitter_task.dart' show Emitter; | 28 import 'code_emitter_task.dart' show CodeEmitterTask, Emitter; |
| 23 | 29 |
| 24 class InterceptorStubGenerator { | 30 class InterceptorStubGenerator { |
| 25 final Compiler compiler; | 31 final CompilerOptions _options; |
| 26 final Namer namer; | 32 final CommonElements _commonElements; |
| 27 final JavaScriptBackend backend; | 33 final CodeEmitterTask _emitterTask; |
| 28 final ClosedWorld closedWorld; | 34 final NativeCodegenEnqueuer _nativeCodegenEnqueuer; |
| 35 final JavaScriptConstantCompiler _constants; |
| 36 final Namer _namer; |
| 37 final NativeData _nativeData; |
| 38 final InterceptorData _interceptorData; |
| 39 final OneShotInterceptorData _oneShotInterceptorData; |
| 40 final CustomElementsCodegenAnalysis _customElementsCodegenAnalysis; |
| 41 final CodegenWorldBuilder _codegenWorldBuilder; |
| 42 final ClosedWorld _closedWorld; |
| 29 | 43 |
| 30 InterceptorStubGenerator( | 44 InterceptorStubGenerator( |
| 31 this.compiler, this.namer, this.backend, this.closedWorld); | 45 this._options, |
| 46 this._commonElements, |
| 47 this._emitterTask, |
| 48 this._nativeCodegenEnqueuer, |
| 49 this._constants, |
| 50 this._namer, |
| 51 this._nativeData, |
| 52 this._interceptorData, |
| 53 this._oneShotInterceptorData, |
| 54 this._customElementsCodegenAnalysis, |
| 55 this._codegenWorldBuilder, |
| 56 this._closedWorld); |
| 32 | 57 |
| 33 Emitter get emitter => backend.emitter.emitter; | 58 Emitter get _emitter => _emitterTask.emitter; |
| 34 | 59 |
| 35 jsAst.Expression generateGetInterceptorMethod(Set<ClassEntity> classes) { | 60 jsAst.Expression generateGetInterceptorMethod(Set<ClassEntity> classes) { |
| 36 jsAst.Expression interceptorFor(ClassEntity cls) { | 61 jsAst.Expression interceptorFor(ClassEntity cls) { |
| 37 return backend.emitter.interceptorPrototypeAccess(cls); | 62 return _emitterTask.interceptorPrototypeAccess(cls); |
| 38 } | 63 } |
| 39 | 64 |
| 40 /** | 65 /** |
| 41 * Build a JavaScrit AST node for doing a type check on | 66 * Build a JavaScrit AST node for doing a type check on |
| 42 * [cls]. [cls] must be a non-native interceptor class. | 67 * [cls]. [cls] must be a non-native interceptor class. |
| 43 */ | 68 */ |
| 44 jsAst.Statement buildInterceptorCheck(ClassEntity cls) { | 69 jsAst.Statement buildInterceptorCheck(ClassEntity cls) { |
| 45 jsAst.Expression condition; | 70 jsAst.Expression condition; |
| 46 assert(backend.interceptorData.isInterceptedClass(cls)); | 71 assert(_interceptorData.isInterceptedClass(cls)); |
| 47 if (cls == compiler.commonElements.jsBoolClass) { | 72 if (cls == _commonElements.jsBoolClass) { |
| 48 condition = js('(typeof receiver) == "boolean"'); | 73 condition = js('(typeof receiver) == "boolean"'); |
| 49 } else if (cls == compiler.commonElements.jsIntClass || | 74 } else if (cls == _commonElements.jsIntClass || |
| 50 cls == compiler.commonElements.jsDoubleClass || | 75 cls == _commonElements.jsDoubleClass || |
| 51 cls == compiler.commonElements.jsNumberClass) { | 76 cls == _commonElements.jsNumberClass) { |
| 52 throw 'internal error'; | 77 throw 'internal error'; |
| 53 } else if (cls == compiler.commonElements.jsArrayClass || | 78 } else if (cls == _commonElements.jsArrayClass || |
| 54 cls == compiler.commonElements.jsMutableArrayClass || | 79 cls == _commonElements.jsMutableArrayClass || |
| 55 cls == compiler.commonElements.jsFixedArrayClass || | 80 cls == _commonElements.jsFixedArrayClass || |
| 56 cls == compiler.commonElements.jsExtendableArrayClass) { | 81 cls == _commonElements.jsExtendableArrayClass) { |
| 57 condition = js('receiver.constructor == Array'); | 82 condition = js('receiver.constructor == Array'); |
| 58 } else if (cls == compiler.commonElements.jsStringClass) { | 83 } else if (cls == _commonElements.jsStringClass) { |
| 59 condition = js('(typeof receiver) == "string"'); | 84 condition = js('(typeof receiver) == "string"'); |
| 60 } else if (cls == compiler.commonElements.jsNullClass) { | 85 } else if (cls == _commonElements.jsNullClass) { |
| 61 condition = js('receiver == null'); | 86 condition = js('receiver == null'); |
| 62 } else { | 87 } else { |
| 63 throw 'internal error'; | 88 throw 'internal error'; |
| 64 } | 89 } |
| 65 return js.statement('if (#) return #', [condition, interceptorFor(cls)]); | 90 return js.statement('if (#) return #', [condition, interceptorFor(cls)]); |
| 66 } | 91 } |
| 67 | 92 |
| 68 bool hasArray = false; | 93 bool hasArray = false; |
| 69 bool hasBool = false; | 94 bool hasBool = false; |
| 70 bool hasDouble = false; | 95 bool hasDouble = false; |
| 71 bool hasInt = false; | 96 bool hasInt = false; |
| 72 bool hasNull = false; | 97 bool hasNull = false; |
| 73 bool hasNumber = false; | 98 bool hasNumber = false; |
| 74 bool hasString = false; | 99 bool hasString = false; |
| 75 bool hasNative = false; | 100 bool hasNative = false; |
| 76 bool anyNativeClasses = | 101 bool anyNativeClasses = _nativeCodegenEnqueuer.hasInstantiatedNativeClasses; |
| 77 backend.nativeCodegenEnqueuer.hasInstantiatedNativeClasses; | |
| 78 | 102 |
| 79 for (ClassEntity cls in classes) { | 103 for (ClassEntity cls in classes) { |
| 80 if (cls == compiler.commonElements.jsArrayClass || | 104 if (cls == _commonElements.jsArrayClass || |
| 81 cls == compiler.commonElements.jsMutableArrayClass || | 105 cls == _commonElements.jsMutableArrayClass || |
| 82 cls == compiler.commonElements.jsFixedArrayClass || | 106 cls == _commonElements.jsFixedArrayClass || |
| 83 cls == compiler.commonElements.jsExtendableArrayClass) | 107 cls == _commonElements.jsExtendableArrayClass) |
| 84 hasArray = true; | 108 hasArray = true; |
| 85 else if (cls == compiler.commonElements.jsBoolClass) | 109 else if (cls == _commonElements.jsBoolClass) |
| 86 hasBool = true; | 110 hasBool = true; |
| 87 else if (cls == compiler.commonElements.jsDoubleClass) | 111 else if (cls == _commonElements.jsDoubleClass) |
| 88 hasDouble = true; | 112 hasDouble = true; |
| 89 else if (cls == compiler.commonElements.jsIntClass) | 113 else if (cls == _commonElements.jsIntClass) |
| 90 hasInt = true; | 114 hasInt = true; |
| 91 else if (cls == compiler.commonElements.jsNullClass) | 115 else if (cls == _commonElements.jsNullClass) |
| 92 hasNull = true; | 116 hasNull = true; |
| 93 else if (cls == compiler.commonElements.jsNumberClass) | 117 else if (cls == _commonElements.jsNumberClass) |
| 94 hasNumber = true; | 118 hasNumber = true; |
| 95 else if (cls == compiler.commonElements.jsStringClass) | 119 else if (cls == _commonElements.jsStringClass) |
| 96 hasString = true; | 120 hasString = true; |
| 97 else { | 121 else { |
| 98 // The set of classes includes classes mixed-in to interceptor classes | 122 // The set of classes includes classes mixed-in to interceptor classes |
| 99 // and user extensions of native classes. | 123 // and user extensions of native classes. |
| 100 // | 124 // |
| 101 // The set of classes also includes the 'primitive' interceptor | 125 // The set of classes also includes the 'primitive' interceptor |
| 102 // PlainJavaScriptObject even when it has not been resolved, since it is | 126 // PlainJavaScriptObject even when it has not been resolved, since it is |
| 103 // only resolved through the reference in getNativeInterceptor when | 127 // only resolved through the reference in getNativeInterceptor when |
| 104 // getNativeInterceptor is marked as used. Guard against probing | 128 // getNativeInterceptor is marked as used. Guard against probing |
| 105 // unresolved PlainJavaScriptObject by testing for anyNativeClasses. | 129 // unresolved PlainJavaScriptObject by testing for anyNativeClasses. |
| 106 | 130 |
| 107 if (anyNativeClasses) { | 131 if (anyNativeClasses) { |
| 108 if (backend.nativeData.isNativeOrExtendsNative(cls)) hasNative = true; | 132 if (_nativeData.isNativeOrExtendsNative(cls)) hasNative = true; |
| 109 } | 133 } |
| 110 } | 134 } |
| 111 } | 135 } |
| 112 if (hasDouble) { | 136 if (hasDouble) { |
| 113 hasNumber = true; | 137 hasNumber = true; |
| 114 } | 138 } |
| 115 if (hasInt) hasNumber = true; | 139 if (hasInt) hasNumber = true; |
| 116 | 140 |
| 117 if (classes.containsAll(backend.interceptorData.interceptedClasses)) { | 141 if (classes.containsAll(_interceptorData.interceptedClasses)) { |
| 118 // I.e. this is the general interceptor. | 142 // I.e. this is the general interceptor. |
| 119 hasNative = anyNativeClasses; | 143 hasNative = anyNativeClasses; |
| 120 } | 144 } |
| 121 | 145 |
| 122 List<jsAst.Statement> statements = <jsAst.Statement>[]; | 146 List<jsAst.Statement> statements = <jsAst.Statement>[]; |
| 123 | 147 |
| 124 if (hasNumber) { | 148 if (hasNumber) { |
| 125 jsAst.Statement whenNumber; | 149 jsAst.Statement whenNumber; |
| 126 | 150 |
| 127 /// Note: there are two number classes in play: Dart's [num], | 151 /// Note: there are two number classes in play: Dart's [num], |
| 128 /// and JavaScript's Number (typeof receiver == 'number'). This | 152 /// and JavaScript's Number (typeof receiver == 'number'). This |
| 129 /// is the fallback used when we have determined that receiver | 153 /// is the fallback used when we have determined that receiver |
| 130 /// is a JavaScript Number. | 154 /// is a JavaScript Number. |
| 131 jsAst.Expression interceptorForNumber = interceptorFor(hasDouble | 155 jsAst.Expression interceptorForNumber = interceptorFor(hasDouble |
| 132 ? compiler.commonElements.jsDoubleClass | 156 ? _commonElements.jsDoubleClass |
| 133 : compiler.commonElements.jsNumberClass); | 157 : _commonElements.jsNumberClass); |
| 134 | 158 |
| 135 if (hasInt) { | 159 if (hasInt) { |
| 136 whenNumber = js.statement( | 160 whenNumber = js.statement( |
| 137 '''{ | 161 '''{ |
| 138 if (Math.floor(receiver) == receiver) return #; | 162 if (Math.floor(receiver) == receiver) return #; |
| 139 return #; | 163 return #; |
| 140 }''', | 164 }''', |
| 141 [ | 165 [interceptorFor(_commonElements.jsIntClass), interceptorForNumber]); |
| 142 interceptorFor(compiler.commonElements.jsIntClass), | |
| 143 interceptorForNumber | |
| 144 ]); | |
| 145 } else { | 166 } else { |
| 146 whenNumber = js.statement('return #', interceptorForNumber); | 167 whenNumber = js.statement('return #', interceptorForNumber); |
| 147 } | 168 } |
| 148 statements | 169 statements |
| 149 .add(js.statement('if (typeof receiver == "number") #;', whenNumber)); | 170 .add(js.statement('if (typeof receiver == "number") #;', whenNumber)); |
| 150 } | 171 } |
| 151 | 172 |
| 152 if (hasString) { | 173 if (hasString) { |
| 153 statements | 174 statements.add(buildInterceptorCheck(_commonElements.jsStringClass)); |
| 154 .add(buildInterceptorCheck(compiler.commonElements.jsStringClass)); | |
| 155 } | 175 } |
| 156 if (hasNull) { | 176 if (hasNull) { |
| 157 statements | 177 statements.add(buildInterceptorCheck(_commonElements.jsNullClass)); |
| 158 .add(buildInterceptorCheck(compiler.commonElements.jsNullClass)); | |
| 159 } else { | 178 } else { |
| 160 // Returning "undefined" or "null" here will provoke a JavaScript | 179 // Returning "undefined" or "null" here will provoke a JavaScript |
| 161 // TypeError which is later identified as a null-error by | 180 // TypeError which is later identified as a null-error by |
| 162 // [unwrapException] in js_helper.dart. | 181 // [unwrapException] in js_helper.dart. |
| 163 statements.add(js.statement('if (receiver == null) return receiver')); | 182 statements.add(js.statement('if (receiver == null) return receiver')); |
| 164 } | 183 } |
| 165 if (hasBool) { | 184 if (hasBool) { |
| 166 statements | 185 statements.add(buildInterceptorCheck(_commonElements.jsBoolClass)); |
| 167 .add(buildInterceptorCheck(compiler.commonElements.jsBoolClass)); | |
| 168 } | 186 } |
| 169 // TODO(ahe): It might be faster to check for Array before | 187 // TODO(ahe): It might be faster to check for Array before |
| 170 // function and bool. | 188 // function and bool. |
| 171 if (hasArray) { | 189 if (hasArray) { |
| 172 statements | 190 statements.add(buildInterceptorCheck(_commonElements.jsArrayClass)); |
| 173 .add(buildInterceptorCheck(compiler.commonElements.jsArrayClass)); | |
| 174 } | 191 } |
| 175 | 192 |
| 176 if (hasNative) { | 193 if (hasNative) { |
| 177 statements.add(js.statement( | 194 statements.add(js.statement( |
| 178 r'''{ | 195 r'''{ |
| 179 if (typeof receiver != "object") { | 196 if (typeof receiver != "object") { |
| 180 if (typeof receiver == "function" ) return #; | 197 if (typeof receiver == "function" ) return #; |
| 181 return receiver; | 198 return receiver; |
| 182 } | 199 } |
| 183 if (receiver instanceof #) return receiver; | 200 if (receiver instanceof #) return receiver; |
| 184 return #(receiver); | 201 return #(receiver); |
| 185 }''', | 202 }''', |
| 186 [ | 203 [ |
| 187 interceptorFor(compiler.commonElements.jsJavaScriptFunctionClass), | 204 interceptorFor(_commonElements.jsJavaScriptFunctionClass), |
| 188 backend.emitter | 205 _emitter.constructorAccess(_commonElements.objectClass), |
| 189 .constructorAccess(compiler.commonElements.objectClass), | 206 _emitter.staticFunctionAccess( |
| 190 backend.emitter.staticFunctionAccess( | 207 _commonElements.getNativeInterceptorMethod) |
| 191 compiler.commonElements.getNativeInterceptorMethod) | |
| 192 ])); | 208 ])); |
| 193 } else { | 209 } else { |
| 194 ClassEntity jsUnknown = | 210 ClassEntity jsUnknown = _commonElements.jsUnknownJavaScriptObjectClass; |
| 195 compiler.commonElements.jsUnknownJavaScriptObjectClass; | 211 if (_codegenWorldBuilder.directlyInstantiatedClasses |
| 196 if (compiler.codegenWorldBuilder.directlyInstantiatedClasses | |
| 197 .contains(jsUnknown)) { | 212 .contains(jsUnknown)) { |
| 198 statements.add(js.statement('if (!(receiver instanceof #)) return #;', [ | 213 statements.add(js.statement('if (!(receiver instanceof #)) return #;', [ |
| 199 backend.emitter | 214 _emitter.constructorAccess(_commonElements.objectClass), |
| 200 .constructorAccess(compiler.commonElements.objectClass), | |
| 201 interceptorFor(jsUnknown) | 215 interceptorFor(jsUnknown) |
| 202 ])); | 216 ])); |
| 203 } | 217 } |
| 204 | 218 |
| 205 statements.add(js.statement('return receiver')); | 219 statements.add(js.statement('return receiver')); |
| 206 } | 220 } |
| 207 | 221 |
| 208 return js('''function(receiver) { #; }''', new jsAst.Block(statements)); | 222 return js('''function(receiver) { #; }''', new jsAst.Block(statements)); |
| 209 } | 223 } |
| 210 | 224 |
| 225 jsAst.Call _generateIsJsIndexableCall( |
| 226 jsAst.Expression use1, jsAst.Expression use2) { |
| 227 String dispatchPropertyName = embeddedNames.DISPATCH_PROPERTY_NAME; |
| 228 jsAst.Expression dispatchProperty = |
| 229 _emitter.generateEmbeddedGlobalAccess(dispatchPropertyName); |
| 230 |
| 231 // We pass the dispatch property record to the isJsIndexable |
| 232 // helper rather than reading it inside the helper to increase the |
| 233 // chance of making the dispatch record access monomorphic. |
| 234 jsAst.PropertyAccess record = |
| 235 new jsAst.PropertyAccess(use2, dispatchProperty); |
| 236 |
| 237 List<jsAst.Expression> arguments = <jsAst.Expression>[use1, record]; |
| 238 FunctionEntity helper = _commonElements.isJsIndexable; |
| 239 jsAst.Expression helperExpression = _emitter.staticFunctionAccess(helper); |
| 240 return new jsAst.Call(helperExpression, arguments); |
| 241 } |
| 242 |
| 211 // Returns a statement that takes care of performance critical | 243 // Returns a statement that takes care of performance critical |
| 212 // common case for a one-shot interceptor, or null if there is no | 244 // common case for a one-shot interceptor, or null if there is no |
| 213 // fast path. | 245 // fast path. |
| 214 jsAst.Statement _fastPathForOneShotInterceptor( | 246 jsAst.Statement _fastPathForOneShotInterceptor( |
| 215 Selector selector, Set<ClassEntity> classes) { | 247 Selector selector, Set<ClassEntity> classes) { |
| 216 if (selector.isOperator) { | 248 if (selector.isOperator) { |
| 217 String name = selector.name; | 249 String name = selector.name; |
| 218 if (name == '==') { | 250 if (name == '==') { |
| 219 return js.statement('''{ | 251 return js.statement('''{ |
| 220 if (receiver == null) return a0 == null; | 252 if (receiver == null) return a0 == null; |
| 221 if (typeof receiver != "object") | 253 if (typeof receiver != "object") |
| 222 return a0 != null && receiver === a0; | 254 return a0 != null && receiver === a0; |
| 223 }'''); | 255 }'''); |
| 224 } | 256 } |
| 225 if (!classes.contains(compiler.commonElements.jsIntClass) && | 257 if (!classes.contains(_commonElements.jsIntClass) && |
| 226 !classes.contains(compiler.commonElements.jsNumberClass) && | 258 !classes.contains(_commonElements.jsNumberClass) && |
| 227 !classes.contains(compiler.commonElements.jsDoubleClass)) { | 259 !classes.contains(_commonElements.jsDoubleClass)) { |
| 228 return null; | 260 return null; |
| 229 } | 261 } |
| 230 if (selector.argumentCount == 1) { | 262 if (selector.argumentCount == 1) { |
| 231 // The following operators do not map to a JavaScript operator. | 263 // The following operators do not map to a JavaScript operator. |
| 232 if (name == '~/' || name == '<<' || name == '%' || name == '>>') { | 264 if (name == '~/' || name == '<<' || name == '%' || name == '>>') { |
| 233 return null; | 265 return null; |
| 234 } | 266 } |
| 235 jsAst.Expression result = js('receiver $name a0'); | 267 jsAst.Expression result = js('receiver $name a0'); |
| 236 if (name == '&' || name == '|' || name == '^') { | 268 if (name == '&' || name == '|' || name == '^') { |
| 237 result = js('# >>> 0', result); | 269 result = js('# >>> 0', result); |
| (...skipping 26 matching lines...) Expand all Loading... |
| 264 // | 296 // |
| 265 // For an index set operation, this code generates: | 297 // For an index set operation, this code generates: |
| 266 // | 298 // |
| 267 // if (typeof a0 === "number") { | 299 // if (typeof a0 === "number") { |
| 268 // if (receiver.constructor == Array && !receiver.immutable$list) { | 300 // if (receiver.constructor == Array && !receiver.immutable$list) { |
| 269 // if (a0 >>> 0 === a0 && a0 < receiver.length) { | 301 // if (a0 >>> 0 === a0 && a0 < receiver.length) { |
| 270 // return receiver[a0] = a1; | 302 // return receiver[a0] = a1; |
| 271 // } | 303 // } |
| 272 // } | 304 // } |
| 273 // } | 305 // } |
| 274 bool containsArray = | 306 bool containsArray = classes.contains(_commonElements.jsArrayClass); |
| 275 classes.contains(compiler.commonElements.jsArrayClass); | 307 bool containsString = classes.contains(_commonElements.jsStringClass); |
| 276 bool containsString = | 308 bool containsJsIndexable = _closedWorld |
| 277 classes.contains(compiler.commonElements.jsStringClass); | 309 .isImplemented(_commonElements.jsIndexingBehaviorInterface) && |
| 278 bool containsJsIndexable = closedWorld.isImplemented( | |
| 279 compiler.commonElements.jsIndexingBehaviorInterface) && | |
| 280 classes.any((cls) { | 310 classes.any((cls) { |
| 281 return closedWorld.isSubtypeOf( | 311 return _closedWorld.isSubtypeOf( |
| 282 cls, compiler.commonElements.jsIndexingBehaviorInterface); | 312 cls, _commonElements.jsIndexingBehaviorInterface); |
| 283 }); | 313 }); |
| 284 // The index set operator requires a check on its set value in | 314 // The index set operator requires a check on its set value in |
| 285 // checked mode, so we don't optimize the interceptor if the | 315 // checked mode, so we don't optimize the interceptor if the |
| 286 // compiler has type assertions enabled. | 316 // _compiler has type assertions enabled. |
| 287 if (selector.isIndexSet && | 317 if (selector.isIndexSet && |
| 288 (compiler.options.enableTypeAssertions || !containsArray)) { | 318 (_options.enableTypeAssertions || !containsArray)) { |
| 289 return null; | 319 return null; |
| 290 } | 320 } |
| 291 if (!containsArray && !containsString) { | 321 if (!containsArray && !containsString) { |
| 292 return null; | 322 return null; |
| 293 } | 323 } |
| 294 jsAst.Expression arrayCheck = js('receiver.constructor == Array'); | 324 jsAst.Expression arrayCheck = js('receiver.constructor == Array'); |
| 295 jsAst.Expression indexableCheck = | 325 jsAst.Expression indexableCheck = |
| 296 backend.generateIsJsIndexableCall(js('receiver'), js('receiver')); | 326 _generateIsJsIndexableCall(js('receiver'), js('receiver')); |
| 297 | 327 |
| 298 jsAst.Expression orExp(left, right) { | 328 jsAst.Expression orExp(left, right) { |
| 299 return left == null ? right : js('# || #', [left, right]); | 329 return left == null ? right : js('# || #', [left, right]); |
| 300 } | 330 } |
| 301 | 331 |
| 302 if (selector.isIndex) { | 332 if (selector.isIndex) { |
| 303 jsAst.Expression typeCheck; | 333 jsAst.Expression typeCheck; |
| 304 if (containsArray) { | 334 if (containsArray) { |
| 305 typeCheck = arrayCheck; | 335 typeCheck = arrayCheck; |
| 306 } | 336 } |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 339 return receiver[a0] = a1; | 369 return receiver[a0] = a1; |
| 340 ''', | 370 ''', |
| 341 typeCheck); | 371 typeCheck); |
| 342 } | 372 } |
| 343 } | 373 } |
| 344 return null; | 374 return null; |
| 345 } | 375 } |
| 346 | 376 |
| 347 jsAst.Expression generateOneShotInterceptor(jsAst.Name name) { | 377 jsAst.Expression generateOneShotInterceptor(jsAst.Name name) { |
| 348 Selector selector = | 378 Selector selector = |
| 349 backend.oneShotInterceptorData.getOneShotInterceptorSelector(name); | 379 _oneShotInterceptorData.getOneShotInterceptorSelector(name); |
| 350 Set<ClassEntity> classes = | 380 Set<ClassEntity> classes = |
| 351 backend.interceptorData.getInterceptedClassesOn(selector.name); | 381 _interceptorData.getInterceptedClassesOn(selector.name); |
| 352 jsAst.Name getInterceptorName = namer.nameForGetInterceptor(classes); | 382 jsAst.Name getInterceptorName = _namer.nameForGetInterceptor(classes); |
| 353 | 383 |
| 354 List<String> parameterNames = <String>[]; | 384 List<String> parameterNames = <String>[]; |
| 355 parameterNames.add('receiver'); | 385 parameterNames.add('receiver'); |
| 356 | 386 |
| 357 if (selector.isSetter) { | 387 if (selector.isSetter) { |
| 358 parameterNames.add('value'); | 388 parameterNames.add('value'); |
| 359 } else { | 389 } else { |
| 360 for (int i = 0; i < selector.argumentCount; i++) { | 390 for (int i = 0; i < selector.argumentCount; i++) { |
| 361 parameterNames.add('a$i'); | 391 parameterNames.add('a$i'); |
| 362 } | 392 } |
| 363 } | 393 } |
| 364 | 394 |
| 365 jsAst.Name invocationName = backend.namer.invocationName(selector); | 395 jsAst.Name invocationName = _namer.invocationName(selector); |
| 366 String globalObject = namer | 396 String globalObject = |
| 367 .globalObjectForLibrary(compiler.commonElements.interceptorsLibrary); | 397 _namer.globalObjectForLibrary(_commonElements.interceptorsLibrary); |
| 368 | 398 |
| 369 jsAst.Statement optimizedPath = | 399 jsAst.Statement optimizedPath = |
| 370 _fastPathForOneShotInterceptor(selector, classes); | 400 _fastPathForOneShotInterceptor(selector, classes); |
| 371 if (optimizedPath == null) optimizedPath = js.statement(';'); | 401 if (optimizedPath == null) optimizedPath = js.statement(';'); |
| 372 | 402 |
| 373 return js('function(#) { #; return #.#(receiver).#(#) }', [ | 403 return js('function(#) { #; return #.#(receiver).#(#) }', [ |
| 374 parameterNames, | 404 parameterNames, |
| 375 optimizedPath, | 405 optimizedPath, |
| 376 globalObject, | 406 globalObject, |
| 377 getInterceptorName, | 407 getInterceptorName, |
| 378 invocationName, | 408 invocationName, |
| 379 parameterNames | 409 parameterNames |
| 380 ]); | 410 ]); |
| 381 } | 411 } |
| 382 | 412 |
| 383 jsAst.ArrayInitializer generateTypeToInterceptorMap() { | 413 jsAst.ArrayInitializer generateTypeToInterceptorMap() { |
| 384 // TODO(sra): Perhaps inject a constant instead? | 414 // TODO(sra): Perhaps inject a constant instead? |
| 385 CustomElementsCodegenAnalysis analysis = | 415 CustomElementsCodegenAnalysis analysis = _customElementsCodegenAnalysis; |
| 386 backend.customElementsCodegenAnalysis; | |
| 387 if (!analysis.needsTable) return null; | 416 if (!analysis.needsTable) return null; |
| 388 | 417 |
| 389 List<jsAst.Expression> elements = <jsAst.Expression>[]; | 418 List<jsAst.Expression> elements = <jsAst.Expression>[]; |
| 390 JavaScriptConstantCompiler handler = backend.constants; | |
| 391 List<ConstantValue> constants = | 419 List<ConstantValue> constants = |
| 392 handler.getConstantsForEmission(emitter.compareConstants); | 420 _constants.getConstantsForEmission(_emitter.compareConstants); |
| 393 for (ConstantValue constant in constants) { | 421 for (ConstantValue constant in constants) { |
| 394 if (constant is TypeConstantValue && | 422 if (constant is TypeConstantValue && |
| 395 constant.representedType is InterfaceType) { | 423 constant.representedType is InterfaceType) { |
| 396 InterfaceType type = constant.representedType; | 424 InterfaceType type = constant.representedType; |
| 397 ClassEntity classElement = type.element; | 425 ClassEntity classElement = type.element; |
| 398 if (!analysis.needsClass(classElement)) continue; | 426 if (!analysis.needsClass(classElement)) continue; |
| 399 | 427 |
| 400 elements.add(emitter.constantReference(constant)); | 428 elements.add(_emitter.constantReference(constant)); |
| 401 elements.add(backend.emitter.interceptorClassAccess(classElement)); | 429 elements.add(_emitter.interceptorClassAccess(classElement)); |
| 402 | 430 |
| 403 // Create JavaScript Object map for by-name lookup of generative | 431 // Create JavaScript Object map for by-name lookup of generative |
| 404 // constructors. For example, the class A has three generative | 432 // constructors. For example, the class A has three generative |
| 405 // constructors | 433 // constructors |
| 406 // | 434 // |
| 407 // class A { | 435 // class A { |
| 408 // A() {} | 436 // A() {} |
| 409 // A.foo() {} | 437 // A.foo() {} |
| 410 // A.bar() {} | 438 // A.bar() {} |
| 411 // } | 439 // } |
| 412 // | 440 // |
| 413 // Which are described by the map | 441 // Which are described by the map |
| 414 // | 442 // |
| 415 // {"": A.A$, "foo": A.A$foo, "bar": A.A$bar} | 443 // {"": A.A$, "foo": A.A$foo, "bar": A.A$bar} |
| 416 // | 444 // |
| 417 // We expect most of the time the map will be a singleton. | 445 // We expect most of the time the map will be a singleton. |
| 418 var properties = []; | 446 var properties = []; |
| 419 for (ConstructorEntity member in analysis.constructors(classElement)) { | 447 for (ConstructorEntity member in analysis.constructors(classElement)) { |
| 420 properties.add(new jsAst.Property(js.string(member.name), | 448 properties.add(new jsAst.Property( |
| 421 backend.emitter.staticFunctionAccess(member))); | 449 js.string(member.name), _emitter.staticFunctionAccess(member))); |
| 422 } | 450 } |
| 423 | 451 |
| 424 var map = new jsAst.ObjectInitializer(properties); | 452 var map = new jsAst.ObjectInitializer(properties); |
| 425 elements.add(map); | 453 elements.add(map); |
| 426 } | 454 } |
| 427 } | 455 } |
| 428 | 456 |
| 429 return new jsAst.ArrayInitializer(elements); | 457 return new jsAst.ArrayInitializer(elements); |
| 430 } | 458 } |
| 431 } | 459 } |
| OLD | NEW |