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 |