| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 dart2js.js_emitter; | 5 part of dart2js.js_emitter; |
| 6 | 6 |
| 7 class InterceptorEmitter extends CodeEmitterHelper { | 7 class InterceptorEmitter extends CodeEmitterHelper { |
| 8 final Set<String> interceptorInvocationNames = new Set<String>(); | 8 final Set<String> interceptorInvocationNames = new Set<String>(); |
| 9 | 9 |
| 10 void recordMangledNameOfMemberMethod(FunctionElement member, String name) { | 10 void recordMangledNameOfMemberMethod(FunctionElement member, String name) { |
| (...skipping 11 matching lines...) Expand all Loading... |
| 22 InterceptorConstant interceptorConstant = constant; | 22 InterceptorConstant interceptorConstant = constant; |
| 23 classes.add(interceptorConstant.dispatchedType.element); | 23 classes.add(interceptorConstant.dispatchedType.element); |
| 24 } | 24 } |
| 25 } | 25 } |
| 26 return classes; | 26 return classes; |
| 27 } | 27 } |
| 28 | 28 |
| 29 void emitGetInterceptorMethod(CodeBuffer buffer, | 29 void emitGetInterceptorMethod(CodeBuffer buffer, |
| 30 String key, | 30 String key, |
| 31 Set<ClassElement> classes) { | 31 Set<ClassElement> classes) { |
| 32 jsAst.Expression interceptorFor(ClassElement cls) { | 32 jsAst.Statement buildReturnInterceptor(ClassElement cls) { |
| 33 return js('#.prototype', namer.elementAccess(cls)); | 33 return js.return_(js(namer.isolateAccess(cls))['prototype']); |
| 34 } | 34 } |
| 35 | 35 |
| 36 /** | 36 /** |
| 37 * Build a JavaScrit AST node for doing a type check on | 37 * Build a JavaScrit AST node for doing a type check on |
| 38 * [cls]. [cls] must be a non-native interceptor class. | 38 * [cls]. [cls] must be an interceptor class. |
| 39 */ | 39 */ |
| 40 jsAst.Statement buildInterceptorCheck(ClassElement cls) { | 40 jsAst.Statement buildInterceptorCheck(ClassElement cls) { |
| 41 jsAst.Expression condition; | 41 jsAst.Expression condition; |
| 42 assert(backend.isInterceptorClass(cls)); | 42 assert(backend.isInterceptorClass(cls)); |
| 43 if (cls == backend.jsBoolClass) { | 43 if (cls == backend.jsBoolClass) { |
| 44 condition = js('(typeof receiver) == "boolean"'); | 44 condition = js('(typeof receiver) == "boolean"'); |
| 45 } else if (cls == backend.jsIntClass || | 45 } else if (cls == backend.jsIntClass || |
| 46 cls == backend.jsDoubleClass || | 46 cls == backend.jsDoubleClass || |
| 47 cls == backend.jsNumberClass) { | 47 cls == backend.jsNumberClass) { |
| 48 throw 'internal error'; | 48 throw 'internal error'; |
| 49 } else if (cls == backend.jsArrayClass || | 49 } else if (cls == backend.jsArrayClass || |
| 50 cls == backend.jsMutableArrayClass || | 50 cls == backend.jsMutableArrayClass || |
| 51 cls == backend.jsFixedArrayClass || | 51 cls == backend.jsFixedArrayClass || |
| 52 cls == backend.jsExtendableArrayClass) { | 52 cls == backend.jsExtendableArrayClass) { |
| 53 condition = js('receiver.constructor == Array'); | 53 condition = js('receiver.constructor == Array'); |
| 54 } else if (cls == backend.jsStringClass) { | 54 } else if (cls == backend.jsStringClass) { |
| 55 condition = js('(typeof receiver) == "string"'); | 55 condition = js('(typeof receiver) == "string"'); |
| 56 } else if (cls == backend.jsNullClass) { | 56 } else if (cls == backend.jsNullClass) { |
| 57 condition = js('receiver == null'); | 57 condition = js('receiver == null'); |
| 58 } else { | 58 } else { |
| 59 throw 'internal error'; | 59 throw 'internal error'; |
| 60 } | 60 } |
| 61 return js.statement('if (#) return #', [condition, interceptorFor(cls)]); | 61 return js.if_(condition, buildReturnInterceptor(cls)); |
| 62 } | 62 } |
| 63 | 63 |
| 64 bool hasArray = false; | 64 bool hasArray = false; |
| 65 bool hasBool = false; | 65 bool hasBool = false; |
| 66 bool hasDouble = false; | 66 bool hasDouble = false; |
| 67 bool hasInt = false; | 67 bool hasInt = false; |
| 68 bool hasNull = false; | 68 bool hasNull = false; |
| 69 bool hasNumber = false; | 69 bool hasNumber = false; |
| 70 bool hasString = false; | 70 bool hasString = false; |
| 71 bool hasNative = false; | 71 bool hasNative = false; |
| (...skipping 29 matching lines...) Expand all Loading... |
| 101 if (hasDouble) { | 101 if (hasDouble) { |
| 102 hasNumber = true; | 102 hasNumber = true; |
| 103 } | 103 } |
| 104 if (hasInt) hasNumber = true; | 104 if (hasInt) hasNumber = true; |
| 105 | 105 |
| 106 if (classes.containsAll(backend.interceptedClasses)) { | 106 if (classes.containsAll(backend.interceptedClasses)) { |
| 107 // I.e. this is the general interceptor. | 107 // I.e. this is the general interceptor. |
| 108 hasNative = anyNativeClasses; | 108 hasNative = anyNativeClasses; |
| 109 } | 109 } |
| 110 | 110 |
| 111 List<Statement> statements = <Statement>[]; | 111 jsAst.Block block = new jsAst.Block.empty(); |
| 112 | 112 |
| 113 if (hasNumber) { | 113 if (hasNumber) { |
| 114 jsAst.Statement whenNumber; | 114 jsAst.Statement whenNumber; |
| 115 | 115 |
| 116 /// Note: there are two number classes in play: Dart's [num], | 116 /// Note: there are two number classes in play: Dart's [num], |
| 117 /// and JavaScript's Number (typeof receiver == 'number'). This | 117 /// and JavaScript's Number (typeof receiver == 'number'). This |
| 118 /// is the fallback used when we have determined that receiver | 118 /// is the fallback used when we have determined that receiver |
| 119 /// is a JavaScript Number. | 119 /// is a JavaScript Number. |
| 120 jsAst.Expression interceptorForNumber = interceptorFor( | 120 jsAst.Return returnNumberClass = buildReturnInterceptor( |
| 121 hasDouble ? backend.jsDoubleClass : backend.jsNumberClass); | 121 hasDouble ? backend.jsDoubleClass : backend.jsNumberClass); |
| 122 | 122 |
| 123 if (hasInt) { | 123 if (hasInt) { |
| 124 whenNumber = js.statement('''{ | 124 jsAst.Expression isInt = js('Math.floor(receiver) == receiver'); |
| 125 if (Math.floor(receiver) == receiver) return #; | 125 whenNumber = js.block([ |
| 126 return #; | 126 js.if_(isInt, buildReturnInterceptor(backend.jsIntClass)), |
| 127 }''', [interceptorFor(backend.jsIntClass), interceptorForNumber]); | 127 returnNumberClass]); |
| 128 } else { | 128 } else { |
| 129 whenNumber = js.statement('return #', interceptorForNumber); | 129 whenNumber = returnNumberClass; |
| 130 } | 130 } |
| 131 statements.add( | 131 block.statements.add( |
| 132 js.statement('if (typeof receiver == "number") #;', whenNumber)); | 132 js.if_('(typeof receiver) == "number"', |
| 133 whenNumber)); |
| 133 } | 134 } |
| 134 | 135 |
| 135 if (hasString) { | 136 if (hasString) { |
| 136 statements.add(buildInterceptorCheck(backend.jsStringClass)); | 137 block.statements.add(buildInterceptorCheck(backend.jsStringClass)); |
| 137 } | 138 } |
| 138 if (hasNull) { | 139 if (hasNull) { |
| 139 statements.add(buildInterceptorCheck(backend.jsNullClass)); | 140 block.statements.add(buildInterceptorCheck(backend.jsNullClass)); |
| 140 } else { | 141 } else { |
| 141 // Returning "undefined" or "null" here will provoke a JavaScript | 142 // Returning "undefined" or "null" here will provoke a JavaScript |
| 142 // TypeError which is later identified as a null-error by | 143 // TypeError which is later identified as a null-error by |
| 143 // [unwrapException] in js_helper.dart. | 144 // [unwrapException] in js_helper.dart. |
| 144 statements.add( | 145 block.statements.add(js.if_('receiver == null', |
| 145 js.statement('if (receiver == null) return receiver')); | 146 js.return_(js('receiver')))); |
| 146 } | 147 } |
| 147 if (hasBool) { | 148 if (hasBool) { |
| 148 statements.add(buildInterceptorCheck(backend.jsBoolClass)); | 149 block.statements.add(buildInterceptorCheck(backend.jsBoolClass)); |
| 149 } | 150 } |
| 150 // TODO(ahe): It might be faster to check for Array before | 151 // TODO(ahe): It might be faster to check for Array before |
| 151 // function and bool. | 152 // function and bool. |
| 152 if (hasArray) { | 153 if (hasArray) { |
| 153 statements.add(buildInterceptorCheck(backend.jsArrayClass)); | 154 block.statements.add(buildInterceptorCheck(backend.jsArrayClass)); |
| 154 } | 155 } |
| 155 | 156 |
| 156 if (hasNative) { | 157 if (hasNative) { |
| 157 statements.add(js.statement(r'''{ | 158 block.statements.add( |
| 158 if (typeof receiver != "object") return receiver; | 159 js.if_( |
| 159 if (receiver instanceof #) return receiver; | 160 js('(typeof receiver) != "object"'), |
| 160 return #(receiver); | 161 js.return_(js('receiver')))); |
| 161 }''', [ | 162 |
| 162 namer.elementAccess(compiler.objectClass), | 163 // if (receiver instanceof $.Object) return receiver; |
| 163 namer.elementAccess(backend.getNativeInterceptorMethod)])); | 164 // return $.getNativeInterceptor(receiver); |
| 165 block.statements.add( |
| 166 js.if_(js('receiver instanceof #', |
| 167 js(namer.isolateAccess(compiler.objectClass))), |
| 168 js.return_(js('receiver')))); |
| 169 block.statements.add( |
| 170 js.return_( |
| 171 js(namer.isolateAccess(backend.getNativeInterceptorMethod))( |
| 172 ['receiver']))); |
| 164 | 173 |
| 165 } else { | 174 } else { |
| 166 ClassElement jsUnknown = backend.jsUnknownJavaScriptObjectClass; | 175 ClassElement jsUnknown = backend.jsUnknownJavaScriptObjectClass; |
| 167 if (compiler.codegenWorld.instantiatedClasses.contains(jsUnknown)) { | 176 if (compiler.codegenWorld.instantiatedClasses.contains(jsUnknown)) { |
| 168 statements.add( | 177 block.statements.add( |
| 169 js.statement('if (!(receiver instanceof #)) return #;', | 178 js.if_(js('!(receiver instanceof #)', |
| 170 [namer.elementAccess(compiler.objectClass), | 179 js(namer.isolateAccess(compiler.objectClass))), |
| 171 interceptorFor(jsUnknown)])); | 180 buildReturnInterceptor(jsUnknown))); |
| 172 } | 181 } |
| 173 | 182 |
| 174 statements.add(js.statement('return receiver')); | 183 block.statements.add(js.return_(js('receiver'))); |
| 175 } | 184 } |
| 176 | 185 |
| 177 buffer.write(jsAst.prettyPrint( | 186 buffer.write(jsAst.prettyPrint( |
| 178 js('''${namer.globalObjectFor(compiler.interceptorsLibrary)}.# = | 187 js('${namer.globalObjectFor(compiler.interceptorsLibrary)}.$key = #', |
| 179 function(receiver) { #; }''', | 188 js.fun(['receiver'], block)), |
| 180 [key, statements]), | |
| 181 compiler)); | 189 compiler)); |
| 182 buffer.write(N); | 190 buffer.write(N); |
| 183 } | 191 } |
| 184 | 192 |
| 185 /** | 193 /** |
| 186 * Emit all versions of the [:getInterceptor:] method. | 194 * Emit all versions of the [:getInterceptor:] method. |
| 187 */ | 195 */ |
| 188 void emitGetInterceptorMethods(CodeBuffer buffer) { | 196 void emitGetInterceptorMethods(CodeBuffer buffer) { |
| 189 task.addComment('getInterceptor methods', buffer); | 197 task.addComment('getInterceptor methods', buffer); |
| 190 Map<String, Set<ClassElement>> specializedGetInterceptors = | 198 Map<String, Set<ClassElement>> specializedGetInterceptors = |
| 191 backend.specializedGetInterceptors; | 199 backend.specializedGetInterceptors; |
| 192 for (String name in specializedGetInterceptors.keys.toList()..sort()) { | 200 for (String name in specializedGetInterceptors.keys.toList()..sort()) { |
| 193 Set<ClassElement> classes = specializedGetInterceptors[name]; | 201 Set<ClassElement> classes = specializedGetInterceptors[name]; |
| 194 emitGetInterceptorMethod(buffer, name, classes); | 202 emitGetInterceptorMethod(buffer, name, classes); |
| 195 } | 203 } |
| 196 } | 204 } |
| 197 | 205 |
| 198 // Returns a statement that takes care of performance critical | 206 // Returns a statement that takes care of performance critical |
| 199 // common case for a one-shot interceptor, or null if there is no | 207 // common case for a one-shot interceptor, or null if there is no |
| 200 // fast path. | 208 // fast path. |
| 201 jsAst.Statement fastPathForOneShotInterceptor(Selector selector, | 209 jsAst.Statement fastPathForOneShotInterceptor(Selector selector, |
| 202 Set<ClassElement> classes) { | 210 Set<ClassElement> classes) { |
| 211 jsAst.Expression isNumber(String variable) { |
| 212 return js('typeof $variable == "number"'); |
| 213 } |
| 214 |
| 215 jsAst.Expression isNotObject(String variable) { |
| 216 return js('typeof $variable != "object"'); |
| 217 } |
| 218 |
| 219 jsAst.Expression isInt(String variable) { |
| 220 return isNumber(variable).binary('&&', |
| 221 js('Math.floor($variable) == $variable')); |
| 222 } |
| 223 |
| 224 jsAst.Expression tripleShiftZero(jsAst.Expression receiver) { |
| 225 return receiver.binary('>>>', js('0')); |
| 226 } |
| 203 | 227 |
| 204 if (selector.isOperator()) { | 228 if (selector.isOperator()) { |
| 205 String name = selector.name; | 229 String name = selector.name; |
| 206 if (name == '==') { | 230 if (name == '==') { |
| 207 return js.statement('''{ | 231 // Unfolds to: |
| 208 if (receiver == null) return a0 == null; | 232 // if (receiver == null) return a0 == null; |
| 209 if (typeof receiver != "object") | 233 // if (typeof receiver != 'object') { |
| 210 return a0 != null && receiver === a0; | 234 // return a0 != null && receiver === a0; |
| 211 }'''); | 235 // } |
| 236 List<jsAst.Statement> body = <jsAst.Statement>[]; |
| 237 body.add(js.if_('receiver == null', js.return_(js('a0 == null')))); |
| 238 body.add(js.if_( |
| 239 isNotObject('receiver'), |
| 240 js.return_(js('a0 != null && receiver === a0')))); |
| 241 return new jsAst.Block(body); |
| 212 } | 242 } |
| 213 if (!classes.contains(backend.jsIntClass) | 243 if (!classes.contains(backend.jsIntClass) |
| 214 && !classes.contains(backend.jsNumberClass) | 244 && !classes.contains(backend.jsNumberClass) |
| 215 && !classes.contains(backend.jsDoubleClass)) { | 245 && !classes.contains(backend.jsDoubleClass)) { |
| 216 return null; | 246 return null; |
| 217 } | 247 } |
| 218 if (selector.argumentCount == 1) { | 248 if (selector.argumentCount == 1) { |
| 219 // The following operators do not map to a JavaScript operator. | 249 // The following operators do not map to a JavaScript |
| 250 // operator. |
| 220 if (name == '~/' || name == '<<' || name == '%' || name == '>>') { | 251 if (name == '~/' || name == '<<' || name == '%' || name == '>>') { |
| 221 return null; | 252 return null; |
| 222 } | 253 } |
| 223 jsAst.Expression result = js('receiver $name a0'); | 254 jsAst.Expression result = js('receiver').binary(name, js('a0')); |
| 224 if (name == '&' || name == '|' || name == '^') { | 255 if (name == '&' || name == '|' || name == '^') { |
| 225 result = js('# >>> 0', result); | 256 result = tripleShiftZero(result); |
| 226 } | 257 } |
| 227 return js.statement( | 258 // Unfolds to: |
| 228 'if (typeof receiver == "number" && typeof a0 == "number")' | 259 // if (typeof receiver == "number" && typeof a0 == "number") |
| 229 ' return #;', | 260 // return receiver op a0; |
| 230 result); | 261 return js.if_( |
| 262 isNumber('receiver').binary('&&', isNumber('a0')), |
| 263 js.return_(result)); |
| 231 } else if (name == 'unary-') { | 264 } else if (name == 'unary-') { |
| 232 return js.statement( | 265 // [: if (typeof receiver == "number") return -receiver :]. |
| 233 'if (typeof receiver == "number") return -receiver'); | 266 return js.if_(isNumber('receiver'), |
| 267 js.return_(js('-receiver'))); |
| 234 } else { | 268 } else { |
| 235 assert(name == '~'); | 269 assert(name == '~'); |
| 236 return js.statement(''' | 270 return js.if_(isInt('receiver'), |
| 237 if (typeof receiver == "number" && Math.floor(receiver) == receiver) | 271 js.return_(js('~receiver >>> 0'))); |
| 238 return (~receiver) >>> 0; | |
| 239 '''); | |
| 240 } | 272 } |
| 241 } else if (selector.isIndex() || selector.isIndexSet()) { | 273 } else if (selector.isIndex() || selector.isIndexSet()) { |
| 242 // For an index operation, this code generates: | 274 // For an index operation, this code generates: |
| 243 // | 275 // |
| 244 // if (receiver.constructor == Array || typeof receiver == "string") { | 276 // if (receiver.constructor == Array || typeof receiver == "string") { |
| 245 // if (a0 >>> 0 === a0 && a0 < receiver.length) { | 277 // if (a0 >>> 0 === a0 && a0 < receiver.length) { |
| 246 // return receiver[a0]; | 278 // return receiver[a0]; |
| 247 // } | 279 // } |
| 248 // } | 280 // } |
| 249 // | 281 // |
| (...skipping 13 matching lines...) Expand all Loading... |
| 263 // The index set operator requires a check on its set value in | 295 // The index set operator requires a check on its set value in |
| 264 // checked mode, so we don't optimize the interceptor if the | 296 // checked mode, so we don't optimize the interceptor if the |
| 265 // compiler has type assertions enabled. | 297 // compiler has type assertions enabled. |
| 266 if (selector.isIndexSet() | 298 if (selector.isIndexSet() |
| 267 && (compiler.enableTypeAssertions || !containsArray)) { | 299 && (compiler.enableTypeAssertions || !containsArray)) { |
| 268 return null; | 300 return null; |
| 269 } | 301 } |
| 270 if (!containsArray && !containsString) { | 302 if (!containsArray && !containsString) { |
| 271 return null; | 303 return null; |
| 272 } | 304 } |
| 305 jsAst.Expression isIntAndAboveZero = js('a0 >>> 0 === a0'); |
| 306 jsAst.Expression belowLength = js('a0 < receiver.length'); |
| 273 jsAst.Expression arrayCheck = js('receiver.constructor == Array'); | 307 jsAst.Expression arrayCheck = js('receiver.constructor == Array'); |
| 274 jsAst.Expression indexableCheck = | 308 jsAst.Expression indexableCheck = |
| 275 backend.generateIsJsIndexableCall(js('receiver'), js('receiver')); | 309 backend.generateIsJsIndexableCall(js('receiver'), js('receiver')); |
| 276 | 310 |
| 277 jsAst.Expression orExp(left, right) { | 311 jsAst.Expression orExp(left, right) { |
| 278 return left == null ? right : js('# || #', [left, right]); | 312 return left == null ? right : left.binary('||', right); |
| 279 } | 313 } |
| 280 | 314 |
| 281 if (selector.isIndex()) { | 315 if (selector.isIndex()) { |
| 316 jsAst.Expression stringCheck = js('typeof receiver == "string"'); |
| 282 jsAst.Expression typeCheck; | 317 jsAst.Expression typeCheck; |
| 283 if (containsArray) { | 318 if (containsArray) { |
| 284 typeCheck = arrayCheck; | 319 typeCheck = arrayCheck; |
| 285 } | 320 } |
| 286 | 321 |
| 287 if (containsString) { | 322 if (containsString) { |
| 288 typeCheck = orExp(typeCheck, js('typeof receiver == "string"')); | 323 typeCheck = orExp(typeCheck, stringCheck); |
| 289 } | 324 } |
| 290 | 325 |
| 291 if (containsJsIndexable) { | 326 if (containsJsIndexable) { |
| 292 typeCheck = orExp(typeCheck, indexableCheck); | 327 typeCheck = orExp(typeCheck, indexableCheck); |
| 293 } | 328 } |
| 294 | 329 |
| 295 return js.statement(''' | 330 return js.if_(typeCheck, |
| 296 if (#) | 331 js.if_(isIntAndAboveZero.binary('&&', belowLength), |
| 297 if ((a0 >>> 0) === a0 && a0 < receiver.length) | 332 js.return_(js('receiver[a0]')))); |
| 298 return receiver[a0]; | |
| 299 ''', typeCheck); | |
| 300 } else { | 333 } else { |
| 301 jsAst.Expression typeCheck; | 334 jsAst.Expression typeCheck; |
| 302 if (containsArray) { | 335 if (containsArray) { |
| 303 typeCheck = arrayCheck; | 336 typeCheck = arrayCheck; |
| 304 } | 337 } |
| 305 | 338 |
| 306 if (containsJsIndexable) { | 339 if (containsJsIndexable) { |
| 307 typeCheck = orExp(typeCheck, indexableCheck); | 340 typeCheck = orExp(typeCheck, indexableCheck); |
| 308 } | 341 } |
| 309 | 342 |
| 310 return js.statement(r''' | 343 jsAst.Expression isImmutableArray = typeCheck.binary( |
| 311 if (# && !receiver.immutable$list && | 344 '&&', js(r'!receiver.immutable$list')); |
| 312 (a0 >>> 0) === a0 && a0 < receiver.length) | 345 return js.if_(isImmutableArray.binary( |
| 313 return receiver[a0] = a1; | 346 '&&', isIntAndAboveZero.binary('&&', belowLength)), |
| 314 ''', typeCheck); | 347 js.return_(js('receiver[a0] = a1'))); |
| 315 } | 348 } |
| 316 } | 349 } |
| 317 return null; | 350 return null; |
| 318 } | 351 } |
| 319 | 352 |
| 320 void emitOneShotInterceptors(CodeBuffer buffer) { | 353 void emitOneShotInterceptors(CodeBuffer buffer) { |
| 321 List<String> names = backend.oneShotInterceptors.keys.toList(); | 354 List<String> names = backend.oneShotInterceptors.keys.toList(); |
| 322 names.sort(); | 355 names.sort(); |
| 323 for (String name in names) { | 356 for (String name in names) { |
| 324 Selector selector = backend.oneShotInterceptors[name]; | 357 Selector selector = backend.oneShotInterceptors[name]; |
| 325 Set<ClassElement> classes = | 358 Set<ClassElement> classes = |
| 326 backend.getInterceptedClassesOn(selector.name); | 359 backend.getInterceptedClassesOn(selector.name); |
| 327 String getInterceptorName = | 360 String getInterceptorName = |
| 328 namer.getInterceptorName(backend.getInterceptorMethod, classes); | 361 namer.getInterceptorName(backend.getInterceptorMethod, classes); |
| 329 | 362 |
| 330 List<String> parameterNames = <String>[]; | 363 List<jsAst.Parameter> parameters = <jsAst.Parameter>[]; |
| 331 parameterNames.add('receiver'); | 364 List<jsAst.Expression> arguments = <jsAst.Expression>[]; |
| 365 parameters.add(new jsAst.Parameter('receiver')); |
| 366 arguments.add(js('receiver')); |
| 332 | 367 |
| 333 if (selector.isSetter()) { | 368 if (selector.isSetter()) { |
| 334 parameterNames.add('value'); | 369 parameters.add(new jsAst.Parameter('value')); |
| 370 arguments.add(js('value')); |
| 335 } else { | 371 } else { |
| 336 for (int i = 0; i < selector.argumentCount; i++) { | 372 for (int i = 0; i < selector.argumentCount; i++) { |
| 337 parameterNames.add('a$i'); | 373 String argName = 'a$i'; |
| 374 parameters.add(new jsAst.Parameter(argName)); |
| 375 arguments.add(js(argName)); |
| 338 } | 376 } |
| 339 } | 377 } |
| 340 | 378 |
| 379 List<jsAst.Statement> body = <jsAst.Statement>[]; |
| 380 jsAst.Statement optimizedPath = |
| 381 fastPathForOneShotInterceptor(selector, classes); |
| 382 if (optimizedPath != null) { |
| 383 body.add(optimizedPath); |
| 384 } |
| 385 |
| 341 String invocationName = backend.namer.invocationName(selector); | 386 String invocationName = backend.namer.invocationName(selector); |
| 342 String globalObject = namer.globalObjectFor(compiler.interceptorsLibrary); | 387 String globalObject = namer.globalObjectFor(compiler.interceptorsLibrary); |
| 388 body.add(js.return_( |
| 389 js(globalObject)[getInterceptorName]('receiver')[invocationName]( |
| 390 arguments))); |
| 343 | 391 |
| 344 jsAst.Statement optimizedPath = | 392 jsAst.Expression assignment = |
| 345 fastPathForOneShotInterceptor(selector, classes); | 393 js('${globalObject}.$name = #', js.fun(parameters, body)); |
| 346 if (optimizedPath == null) optimizedPath = js.statement(';'); | |
| 347 | |
| 348 jsAst.Expression assignment = js('${globalObject}.# = function(#) {' | |
| 349 ' #;' | |
| 350 ' return #.#(receiver).#(#) }', | |
| 351 [name, parameterNames, | |
| 352 optimizedPath, | |
| 353 globalObject, getInterceptorName, invocationName, parameterNames]); | |
| 354 | 394 |
| 355 buffer.write(jsAst.prettyPrint(assignment, compiler)); | 395 buffer.write(jsAst.prettyPrint(assignment, compiler)); |
| 356 buffer.write(N); | 396 buffer.write(N); |
| 357 } | 397 } |
| 358 } | 398 } |
| 359 | 399 |
| 360 /** | 400 /** |
| 361 * If [JSInvocationMirror._invokeOn] has been compiled, emit all the | 401 * If [JSInvocationMirror._invokeOn] has been compiled, emit all the |
| 362 * possible selector names that are intercepted into the | 402 * possible selector names that are intercepted into the |
| 363 * [interceptedNames] top-level variable. The implementation of | 403 * [interceptedNames] top-level variable. The implementation of |
| (...skipping 14 matching lines...) Expand all Loading... |
| 378 var invocationNames = interceptorInvocationNames.toList()..sort(); | 418 var invocationNames = interceptorInvocationNames.toList()..sort(); |
| 379 List<jsAst.ArrayElement> elements = invocationNames.map( | 419 List<jsAst.ArrayElement> elements = invocationNames.map( |
| 380 (String invocationName) { | 420 (String invocationName) { |
| 381 jsAst.Literal str = js.string(invocationName); | 421 jsAst.Literal str = js.string(invocationName); |
| 382 return new jsAst.ArrayElement(index++, str); | 422 return new jsAst.ArrayElement(index++, str); |
| 383 }).toList(); | 423 }).toList(); |
| 384 jsAst.ArrayInitializer array = | 424 jsAst.ArrayInitializer array = |
| 385 new jsAst.ArrayInitializer(invocationNames.length, elements); | 425 new jsAst.ArrayInitializer(invocationNames.length, elements); |
| 386 | 426 |
| 387 jsAst.Expression assignment = | 427 jsAst.Expression assignment = |
| 388 js('${task.isolateProperties}.# = #', [name, array]); | 428 js('${task.isolateProperties}.$name = #', array); |
| 389 | 429 |
| 390 buffer.write(jsAst.prettyPrint(assignment, compiler)); | 430 buffer.write(jsAst.prettyPrint(assignment, compiler)); |
| 391 buffer.write(N); | 431 buffer.write(N); |
| 392 } | 432 } |
| 393 | 433 |
| 394 /** | 434 /** |
| 395 * Emit initializer for [mapTypeToInterceptor] data structure used by | 435 * Emit initializer for [mapTypeToInterceptor] data structure used by |
| 396 * [findInterceptorForType]. See declaration of [mapTypeToInterceptor] in | 436 * [findInterceptorForType]. See declaration of [mapTypeToInterceptor] in |
| 397 * `interceptors.dart`. | 437 * `interceptors.dart`. |
| 398 */ | 438 */ |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 443 var map = new jsAst.ObjectInitializer(properties); | 483 var map = new jsAst.ObjectInitializer(properties); |
| 444 elements.add(map); | 484 elements.add(map); |
| 445 } | 485 } |
| 446 } | 486 } |
| 447 } | 487 } |
| 448 | 488 |
| 449 jsAst.ArrayInitializer array = new jsAst.ArrayInitializer.from(elements); | 489 jsAst.ArrayInitializer array = new jsAst.ArrayInitializer.from(elements); |
| 450 String name = | 490 String name = |
| 451 backend.namer.getNameOfGlobalField(backend.mapTypeToInterceptor); | 491 backend.namer.getNameOfGlobalField(backend.mapTypeToInterceptor); |
| 452 jsAst.Expression assignment = | 492 jsAst.Expression assignment = |
| 453 js('${task.isolateProperties}.# = #', [name, array]); | 493 js('${task.isolateProperties}.$name = #', array); |
| 454 | 494 |
| 455 buffer.write(jsAst.prettyPrint(assignment, compiler)); | 495 buffer.write(jsAst.prettyPrint(assignment, compiler)); |
| 456 buffer.write(N); | 496 buffer.write(N); |
| 457 } | 497 } |
| 458 } | 498 } |
| OLD | NEW |