Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(503)

Side by Side Diff: sdk/lib/_internal/compiler/implementation/js_emitter/nsm_emitter.dart

Issue 237583014: JS templates (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: cleanup Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698