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