Chromium Code Reviews| 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 NsmEmitter extends CodeEmitterHelper { | 7 class NsmEmitter extends CodeEmitterHelper { |
| 8 final List<Selector> trivialNsmHandlers = <Selector>[]; | 8 final List<Selector> trivialNsmHandlers = <Selector>[]; |
| 9 | 9 |
| 10 /// If this is true then we can generate the noSuchMethod handlers at startup | 10 /// If this is true then we can generate the noSuchMethod handlers at startup |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 56 | 56 |
| 57 // Set flag used by generateMethod helper below. If we have very few | 57 // Set flag used by generateMethod helper below. If we have very few |
| 58 // handlers we use addProperty for them all, rather than try to generate | 58 // handlers we use addProperty for them all, rather than try to generate |
| 59 // them at runtime. | 59 // them at runtime. |
| 60 bool haveVeryFewNoSuchMemberHandlers = | 60 bool haveVeryFewNoSuchMemberHandlers = |
| 61 (addedJsNames.length < VERY_FEW_NO_SUCH_METHOD_HANDLERS); | 61 (addedJsNames.length < VERY_FEW_NO_SUCH_METHOD_HANDLERS); |
| 62 | 62 |
| 63 jsAst.Expression generateMethod(String jsName, Selector selector) { | 63 jsAst.Expression generateMethod(String jsName, Selector selector) { |
| 64 // Values match JSInvocationMirror in js-helper library. | 64 // Values match JSInvocationMirror in js-helper library. |
| 65 int type = selector.invocationMirrorKind; | 65 int type = selector.invocationMirrorKind; |
| 66 List<jsAst.Parameter> parameters = <jsAst.Parameter>[]; | 66 List<String> parameterNames = |
| 67 for (int i = 0; i < selector.argumentCount; i++) { | 67 new List.generate(selector.argumentCount, (i) => '\$$i'); |
| 68 parameters.add(new jsAst.Parameter('\$$i')); | |
| 69 } | |
| 70 | 68 |
| 71 List<jsAst.Expression> argNames = | 69 List<jsAst.Expression> argNames = |
| 72 selector.getOrderedNamedArguments().map((String name) => | 70 selector.getOrderedNamedArguments().map((String name) => |
| 73 js.string(name)).toList(); | 71 js.string(name)).toList(); |
| 74 | 72 |
| 75 String methodName = selector.invocationMirrorMemberName; | 73 String methodName = selector.invocationMirrorMemberName; |
| 76 String internalName = namer.invocationMirrorInternalName(selector); | 74 String internalName = namer.invocationMirrorInternalName(selector); |
| 77 String reflectionName = task.getReflectionName(selector, internalName); | 75 String reflectionName = task.getReflectionName(selector, internalName); |
| 78 if (!haveVeryFewNoSuchMemberHandlers && | 76 if (!haveVeryFewNoSuchMemberHandlers && |
| 79 isTrivialNsmHandler(type, argNames, selector, internalName) && | 77 isTrivialNsmHandler(type, argNames, selector, internalName) && |
| 80 reflectionName == null) { | 78 reflectionName == null) { |
| 81 trivialNsmHandlers.add(selector); | 79 trivialNsmHandlers.add(selector); |
| 82 return null; | 80 return null; |
| 83 } | 81 } |
| 84 | 82 |
| 85 assert(backend.isInterceptedName(Compiler.NO_SUCH_METHOD)); | 83 assert(backend.isInterceptedName(Compiler.NO_SUCH_METHOD)); |
| 86 jsAst.Expression expression = js('this.$noSuchMethodName')( | 84 jsAst.Expression expression = js('this.#(this, #(#, #, #, #, #))', [ |
| 87 [js('this'), | 85 noSuchMethodName, |
| 88 namer.elementAccess(backend.getCreateInvocationMirror())([ | 86 namer.elementAccess(backend.getCreateInvocationMirror()), |
| 89 js.string(compiler.enableMinification ? | 87 js.string(compiler.enableMinification ? |
| 90 internalName : methodName), | 88 internalName : methodName), |
| 91 js.string(internalName), | 89 js.string(internalName), |
| 92 type, | 90 js.number(type), |
| 93 new jsAst.ArrayInitializer.from( | 91 new jsAst.ArrayInitializer.from(parameterNames.map(js)), |
| 94 parameters.map((param) => js(param.name)).toList()), | 92 new jsAst.ArrayInitializer.from(argNames)]); |
| 95 new jsAst.ArrayInitializer.from(argNames)])]); | 93 |
| 96 parameters = backend.isInterceptedName(selector.name) | 94 if (backend.isInterceptedName(selector.name)) { |
| 97 ? ([new jsAst.Parameter('\$receiver')]..addAll(parameters)) | 95 return js(r'function($receiver, #) { return # }', [parameterNames, expre ssion]); |
|
floitsch
2014/04/22 16:11:18
long line.
sra1
2014/04/23 02:33:50
Done.
| |
| 98 : parameters; | 96 } else { |
| 99 return js.fun(parameters, js.return_(expression)); | 97 return js(r'function(#) { return # }', [parameterNames, expression]); |
| 98 } | |
| 100 } | 99 } |
| 101 | 100 |
| 102 for (String jsName in addedJsNames.keys.toList()..sort()) { | 101 for (String jsName in addedJsNames.keys.toList()..sort()) { |
| 103 Selector selector = addedJsNames[jsName]; | 102 Selector selector = addedJsNames[jsName]; |
| 104 jsAst.Expression method = generateMethod(jsName, selector); | 103 jsAst.Expression method = generateMethod(jsName, selector); |
| 105 if (method != null) { | 104 if (method != null) { |
| 106 addProperty(jsName, method); | 105 addProperty(jsName, method); |
| 107 String reflectionName = task.getReflectionName(selector, jsName); | 106 String reflectionName = task.getReflectionName(selector, jsName); |
| 108 if (reflectionName != null) { | 107 if (reflectionName != null) { |
| 109 bool accessible = compiler.world.allFunctions.filter(selector).any( | 108 bool accessible = compiler.world.allFunctions.filter(selector).any( |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 169 * as base 88 numbers. The difference is 2, which is "c" in lower-case- | 168 * as base 88 numbers. The difference is 2, which is "c" in lower-case- |
| 170 * terminated base 26. | 169 * terminated base 26. |
| 171 * | 170 * |
| 172 * The reason we don't encode long minified names with this method is that | 171 * The reason we don't encode long minified names with this method is that |
| 173 * decoding the base 88 numbers would overflow JavaScript's puny integers. | 172 * decoding the base 88 numbers would overflow JavaScript's puny integers. |
| 174 * | 173 * |
| 175 * There are some selectors that have a special calling convention (because | 174 * There are some selectors that have a special calling convention (because |
| 176 * they are called with the receiver as the first argument). They need a | 175 * they are called with the receiver as the first argument). They need a |
| 177 * slightly different noSuchMethod handler, so we handle these first. | 176 * slightly different noSuchMethod handler, so we handle these first. |
| 178 */ | 177 */ |
| 179 void addTrivialNsmHandlers(List<jsAst.Node> statements) { | 178 List<jsAst.Statement> buildTrivialNsmHandlers() { |
| 180 if (trivialNsmHandlers.length == 0) return; | 179 List<jsAst.Statement> statements = <jsAst.Statement>[]; |
| 180 if (trivialNsmHandlers.length == 0) return statements; | |
| 181 // Sort by calling convention, JS name length and by JS name. | 181 // Sort by calling convention, JS name length and by JS name. |
| 182 trivialNsmHandlers.sort((a, b) { | 182 trivialNsmHandlers.sort((a, b) { |
| 183 bool aIsIntercepted = backend.isInterceptedName(a.name); | 183 bool aIsIntercepted = backend.isInterceptedName(a.name); |
| 184 bool bIsIntercepted = backend.isInterceptedName(b.name); | 184 bool bIsIntercepted = backend.isInterceptedName(b.name); |
| 185 if (aIsIntercepted != bIsIntercepted) return aIsIntercepted ? -1 : 1; | 185 if (aIsIntercepted != bIsIntercepted) return aIsIntercepted ? -1 : 1; |
| 186 String aName = namer.invocationMirrorInternalName(a); | 186 String aName = namer.invocationMirrorInternalName(a); |
| 187 String bName = namer.invocationMirrorInternalName(b); | 187 String bName = namer.invocationMirrorInternalName(b); |
| 188 if (aName.length != bName.length) return aName.length - bName.length; | 188 if (aName.length != bName.length) return aName.length - bName.length; |
| 189 return aName.compareTo(bName); | 189 return aName.compareTo(bName); |
| 190 }); | 190 }); |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 261 diffEncoding.write(","); | 261 diffEncoding.write(","); |
| 262 } | 262 } |
| 263 diffEncoding.write(short); | 263 diffEncoding.write(short); |
| 264 } | 264 } |
| 265 nameCounter++; | 265 nameCounter++; |
| 266 } | 266 } |
| 267 | 267 |
| 268 // Startup code that loops over the method names and puts handlers on the | 268 // Startup code that loops over the method names and puts handlers on the |
| 269 // Object class to catch noSuchMethod invocations. | 269 // Object class to catch noSuchMethod invocations. |
| 270 ClassElement objectClass = compiler.objectClass; | 270 ClassElement objectClass = compiler.objectClass; |
| 271 String createInvocationMirror = namer.isolateAccess( | 271 jsAst.Expression createInvocationMirror = namer.elementAccess( |
| 272 backend.getCreateInvocationMirror()); | 272 backend.getCreateInvocationMirror()); |
| 273 String noSuchMethodName = namer.publicInstanceMethodNameByArity( | 273 String noSuchMethodName = namer.publicInstanceMethodNameByArity( |
| 274 Compiler.NO_SUCH_METHOD, Compiler.NO_SUCH_METHOD_ARG_COUNT); | 274 Compiler.NO_SUCH_METHOD, Compiler.NO_SUCH_METHOD_ARG_COUNT); |
| 275 var type = 0; | 275 var type = 0; |
| 276 if (useDiffEncoding) { | 276 if (useDiffEncoding) { |
| 277 statements.addAll([ | 277 statements.add(js.statement('''{ |
| 278 js('var objectClassObject = ' | 278 var objectClassObject = |
| 279 ' collectedClasses["${namer.getNameOfClass(objectClass)}"],' | 279 collectedClasses[#], // # is name of class Object. |
| 280 ' shortNames = "$diffEncoding".split(","),' | 280 shortNames = #.split(","), // # is diffEncoding. |
| 281 ' nameNumber = 0,' | 281 nameNumber = 0, |
| 282 ' diffEncodedString = shortNames[0],' | 282 diffEncodedString = shortNames[0], |
| 283 ' calculatedShortNames = [0, 1]'), // 0, 1 are args for splice. | 283 calculatedShortNames = [0, 1]; // 0, 1 are args for splice. |
| 284 // If we are loading a deferred library the object class will not be in | 284 // If we are loading a deferred library the object class will not be i n |
|
floitsch
2014/04/22 16:11:18
long line.
| |
| 285 // the collectedClasses so objectClassObject is undefined, and we skip | 285 // the collectedClasses so objectClassObject is undefined, and we skip |
| 286 // setting up the names. | 286 // setting up the names. |
| 287 js.if_('objectClassObject', [ | 287 |
| 288 js.if_('objectClassObject instanceof Array', | 288 if (objectClassObject) { |
| 289 js('objectClassObject = objectClassObject[1]')), | 289 if (objectClassObject instanceof Array) |
| 290 js.for_('var i = 0', 'i < diffEncodedString.length', 'i++', [ | 290 objectClassObject = objectClassObject[1]; |
| 291 js('var codes = [],' | 291 for (var i = 0; i < diffEncodedString.length; i++) { |
| 292 ' diff = 0,' | 292 var codes = [], |
| 293 ' digit = diffEncodedString.charCodeAt(i)'), | 293 diff = 0, |
| 294 js.if_('digit == ${$PERIOD}', [ | 294 digit = diffEncodedString.charCodeAt(i); |
| 295 js('nameNumber = 0'), | 295 if (digit == ${$PERIOD}) { |
| 296 js('digit = diffEncodedString.charCodeAt(++i)') | 296 nameNumber = 0; |
| 297 ]), | 297 digit = diffEncodedString.charCodeAt(++i); |
| 298 js.while_('digit <= ${$Z}', [ | 298 } |
| 299 js('diff *= 26'), | 299 for (; digit <= ${$Z};) { |
| 300 js('diff += (digit - ${$A})'), | 300 diff *= 26; |
| 301 js('digit = diffEncodedString.charCodeAt(++i)') | 301 diff += (digit - ${$A}); |
| 302 ]), | 302 digit = diffEncodedString.charCodeAt(++i); |
| 303 js('diff *= 26'), | 303 } |
| 304 js('diff += (digit - ${$a})'), | 304 diff *= 26; |
| 305 js('nameNumber += diff'), | 305 diff += (digit - ${$a}); |
| 306 js.for_('var remaining = nameNumber', | 306 nameNumber += diff; |
| 307 'remaining > 0', | 307 for (var remaining = nameNumber; |
| 308 'remaining = (remaining / 88) | 0', [ | 308 remaining > 0; |
| 309 js('codes.unshift(${$HASH} + remaining % 88)') | 309 remaining = (remaining / 88) | 0) { |
| 310 ]), | 310 codes.unshift(${$HASH} + remaining % 88); |
| 311 js('calculatedShortNames.push(' | 311 } |
| 312 ' String.fromCharCode.apply(String, codes))') | 312 calculatedShortNames.push( |
| 313 ]), | 313 String.fromCharCode.apply(String, codes)); |
| 314 js('shortNames.splice.apply(shortNames, calculatedShortNames)')]) | 314 } |
| 315 ]); | 315 shortNames.splice.apply(shortNames, calculatedShortNames); |
| 316 } | |
| 317 }''', [ | |
| 318 js.string(namer.getNameOfClass(objectClass)), | |
| 319 js.string('$diffEncoding')])); | |
| 316 } else { | 320 } else { |
| 317 // No useDiffEncoding version. | 321 // No useDiffEncoding version. |
| 318 Iterable<String> longs = trivialNsmHandlers.map((selector) => | 322 Iterable<String> longs = trivialNsmHandlers.map((selector) => |
| 319 selector.invocationMirrorMemberName); | 323 selector.invocationMirrorMemberName); |
| 320 String longNamesConstant = minify ? "" : | 324 statements.add(js.statement( |
| 321 ',longNames = "${longs.join(",")}".split(",")'; | 325 'var objectClassObject = collectedClasses[#],' |
| 322 statements.add( | 326 ' shortNames = #.split(",")', [ |
| 323 js('var objectClassObject = ' | 327 js.string(namer.getNameOfClass(objectClass)), |
| 324 ' collectedClasses["${namer.getNameOfClass(objectClass)}"],' | 328 js.string('$diffEncoding')])); |
| 325 ' shortNames = "$diffEncoding".split(",")' | 329 if (!minify) { |
| 326 ' $longNamesConstant')); | 330 statements.add(js.statement('var longNames = #.split(",")', |
| 327 statements.add( | 331 js.string(longs.join(',')))); |
| 328 js.if_('objectClassObject instanceof Array', | 332 } |
| 329 js('objectClassObject = objectClassObject[1]'))); | 333 statements.add(js.statement( |
| 334 'if (objectClassObject instanceof Array)' | |
| 335 ' objectClassObject = objectClassObject[1];')); | |
| 330 } | 336 } |
| 331 | 337 |
| 332 String sliceOffset = ', (j < $firstNormalSelector) ? 1 : 0'; | 338 // TODO(9631): This is no longer valid for native methods. |
| 333 if (firstNormalSelector == 0) sliceOffset = ''; | |
| 334 if (firstNormalSelector == shorts.length) sliceOffset = ', 1'; | |
| 335 | |
| 336 String whatToPatch = task.nativeEmitter.handleNoSuchMethod ? | 339 String whatToPatch = task.nativeEmitter.handleNoSuchMethod ? |
| 337 "Object.prototype" : | 340 "Object.prototype" : |
| 338 "objectClassObject"; | 341 "objectClassObject"; |
| 339 | 342 |
| 340 var params = ['name', 'short', 'type']; | 343 List<jsAst.Expression> sliceOffsetArguments = |
| 341 var sliceOffsetParam = ''; | 344 firstNormalSelector == 0 |
| 342 var slice = 'Array.prototype.slice.call'; | 345 ? [] |
| 343 if (!sliceOffset.isEmpty) { | 346 : (firstNormalSelector == shorts.length |
| 344 sliceOffsetParam = ', sliceOffset'; | 347 ? [js.number(1)] |
| 345 params.add('sliceOffset'); | 348 : [js('(j < #) ? 1 : 0', js.number(firstNormalSelector))]); |
| 346 } | 349 |
| 347 statements.addAll([ | 350 var sliceOffsetParams = sliceOffsetArguments.isEmpty ? [] : ['sliceOffset']; |
| 351 | |
| 352 statements.add(js.statement(''' | |
| 348 // If we are loading a deferred library the object class will not be in | 353 // If we are loading a deferred library the object class will not be in |
| 349 // the collectedClasses so objectClassObject is undefined, and we skip | 354 // the collectedClasses so objectClassObject is undefined, and we skip |
| 350 // setting up the names. | 355 // setting up the names. |
| 351 js.if_('objectClassObject', [ | 356 if (objectClassObject) { |
| 352 js.for_('var j = 0', 'j < shortNames.length', 'j++', [ | 357 for (var j = 0; j < shortNames.length; j++) { |
| 353 js('var type = 0'), | 358 var type = 0; |
| 354 js('var short = shortNames[j]'), | 359 var short = shortNames[j]; |
| 355 js.if_('short[0] == "${namer.getterPrefix[0]}"', js('type = 1')), | 360 if (short[0] == "${namer.getterPrefix[0]}") type = 1; |
| 356 js.if_('short[0] == "${namer.setterPrefix[0]}"', js('type = 2')), | 361 if (short[0] == "${namer.setterPrefix[0]}") type = 2; |
| 357 // Generate call to: | 362 // Generate call to: |
| 358 // createInvocationMirror(String name, internalName, type, arguments, | 363 // |
| 359 // argumentNames) | 364 // createInvocationMirror(String name, internalName, type, |
| 360 js('$whatToPatch[short] = #(${minify ? "shortNames" : "longNames"}[j], ' | 365 // arguments, argumentNames) |
| 361 'short, type$sliceOffset)', | 366 // |
| 362 js.fun(params, [js.return_(js.fun([], | 367 $whatToPatch[short] = (function(name, short, type, #) { |
| 363 [js.return_(js( | 368 return function() { |
| 364 'this.$noSuchMethodName(' | 369 return this.#(this, |
| 365 'this, ' | 370 #(name, short, type, |
| 366 '$createInvocationMirror(' | 371 Array.prototype.slice.call(arguments, #), |
| 367 'name, short, type, ' | 372 [])); |
| 368 '$slice(arguments$sliceOffsetParam), []))'))]))])) | 373 } |
| 369 ]) | 374 })(#[j], short, type, #); |
| 370 ]) | 375 } |
| 371 ]); | 376 }''', [ |
| 377 sliceOffsetParams, // parameter | |
| 378 noSuchMethodName, | |
| 379 createInvocationMirror, | |
| 380 sliceOffsetParams, // argument to slice | |
| 381 minify ? 'shortNames' : 'longNames', | |
| 382 sliceOffsetArguments | |
| 383 ])); | |
| 384 | |
| 385 return statements; | |
| 372 } | 386 } |
| 373 } | 387 } |
| OLD | NEW |