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

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: merge 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 # }',
98 : parameters; 96 [parameterNames, expression]);
99 return js.fun(parameters, js.return_(expression)); 97 } else {
98 return js(r'function(#) { return # }', [parameterNames, expression]);
99 }
100 } 100 }
101 101
102 for (String jsName in addedJsNames.keys.toList()..sort()) { 102 for (String jsName in addedJsNames.keys.toList()..sort()) {
103 Selector selector = addedJsNames[jsName]; 103 Selector selector = addedJsNames[jsName];
104 jsAst.Expression method = generateMethod(jsName, selector); 104 jsAst.Expression method = generateMethod(jsName, selector);
105 if (method != null) { 105 if (method != null) {
106 addProperty(jsName, method); 106 addProperty(jsName, method);
107 String reflectionName = task.getReflectionName(selector, jsName); 107 String reflectionName = task.getReflectionName(selector, jsName);
108 if (reflectionName != null) { 108 if (reflectionName != null) {
109 bool accessible = compiler.world.allFunctions.filter(selector).any( 109 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- 169 * as base 88 numbers. The difference is 2, which is "c" in lower-case-
170 * terminated base 26. 170 * terminated base 26.
171 * 171 *
172 * The reason we don't encode long minified names with this method is that 172 * 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. 173 * decoding the base 88 numbers would overflow JavaScript's puny integers.
174 * 174 *
175 * There are some selectors that have a special calling convention (because 175 * 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 176 * they are called with the receiver as the first argument). They need a
177 * slightly different noSuchMethod handler, so we handle these first. 177 * slightly different noSuchMethod handler, so we handle these first.
178 */ 178 */
179 void addTrivialNsmHandlers(List<jsAst.Node> statements) { 179 List<jsAst.Statement> buildTrivialNsmHandlers() {
180 if (trivialNsmHandlers.length == 0) return; 180 List<jsAst.Statement> statements = <jsAst.Statement>[];
181 if (trivialNsmHandlers.length == 0) return statements;
181 // Sort by calling convention, JS name length and by JS name. 182 // Sort by calling convention, JS name length and by JS name.
182 trivialNsmHandlers.sort((a, b) { 183 trivialNsmHandlers.sort((a, b) {
183 bool aIsIntercepted = backend.isInterceptedName(a.name); 184 bool aIsIntercepted = backend.isInterceptedName(a.name);
184 bool bIsIntercepted = backend.isInterceptedName(b.name); 185 bool bIsIntercepted = backend.isInterceptedName(b.name);
185 if (aIsIntercepted != bIsIntercepted) return aIsIntercepted ? -1 : 1; 186 if (aIsIntercepted != bIsIntercepted) return aIsIntercepted ? -1 : 1;
186 String aName = namer.invocationMirrorInternalName(a); 187 String aName = namer.invocationMirrorInternalName(a);
187 String bName = namer.invocationMirrorInternalName(b); 188 String bName = namer.invocationMirrorInternalName(b);
188 if (aName.length != bName.length) return aName.length - bName.length; 189 if (aName.length != bName.length) return aName.length - bName.length;
189 return aName.compareTo(bName); 190 return aName.compareTo(bName);
190 }); 191 });
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
261 diffEncoding.write(","); 262 diffEncoding.write(",");
262 } 263 }
263 diffEncoding.write(short); 264 diffEncoding.write(short);
264 } 265 }
265 nameCounter++; 266 nameCounter++;
266 } 267 }
267 268
268 // Startup code that loops over the method names and puts handlers on the 269 // Startup code that loops over the method names and puts handlers on the
269 // Object class to catch noSuchMethod invocations. 270 // Object class to catch noSuchMethod invocations.
270 ClassElement objectClass = compiler.objectClass; 271 ClassElement objectClass = compiler.objectClass;
271 String createInvocationMirror = namer.isolateAccess( 272 jsAst.Expression createInvocationMirror = namer.elementAccess(
272 backend.getCreateInvocationMirror()); 273 backend.getCreateInvocationMirror());
273 String noSuchMethodName = namer.publicInstanceMethodNameByArity( 274 String noSuchMethodName = namer.publicInstanceMethodNameByArity(
274 Compiler.NO_SUCH_METHOD, Compiler.NO_SUCH_METHOD_ARG_COUNT); 275 Compiler.NO_SUCH_METHOD, Compiler.NO_SUCH_METHOD_ARG_COUNT);
275 var type = 0; 276 var type = 0;
276 if (useDiffEncoding) { 277 if (useDiffEncoding) {
277 statements.addAll([ 278 statements.add(js.statement('''{
278 js('var objectClassObject = ' 279 var objectClassObject =
279 ' collectedClasses["${namer.getNameOfClass(objectClass)}"],' 280 collectedClasses[#], // # is name of class Object.
280 ' shortNames = "$diffEncoding".split(","),' 281 shortNames = #.split(","), // # is diffEncoding.
281 ' nameNumber = 0,' 282 nameNumber = 0,
282 ' diffEncodedString = shortNames[0],' 283 diffEncodedString = shortNames[0],
283 ' calculatedShortNames = [0, 1]'), // 0, 1 are args for splice. 284 calculatedShortNames = [0, 1]; // 0, 1 are args for splice.
284 // If we are loading a deferred library the object class will not be in 285 // If we are loading a deferred library the object class will not be i n
285 // the collectedClasses so objectClassObject is undefined, and we skip 286 // the collectedClasses so objectClassObject is undefined, and we skip
286 // setting up the names. 287 // setting up the names.
287 js.if_('objectClassObject', [ 288
288 js.if_('objectClassObject instanceof Array', 289 if (objectClassObject) {
289 js('objectClassObject = objectClassObject[1]')), 290 if (objectClassObject instanceof Array)
290 js.for_('var i = 0', 'i < diffEncodedString.length', 'i++', [ 291 objectClassObject = objectClassObject[1];
291 js('var codes = [],' 292 for (var i = 0; i < diffEncodedString.length; i++) {
292 ' diff = 0,' 293 var codes = [],
293 ' digit = diffEncodedString.charCodeAt(i)'), 294 diff = 0,
294 js.if_('digit == ${$PERIOD}', [ 295 digit = diffEncodedString.charCodeAt(i);
295 js('nameNumber = 0'), 296 if (digit == ${$PERIOD}) {
296 js('digit = diffEncodedString.charCodeAt(++i)') 297 nameNumber = 0;
297 ]), 298 digit = diffEncodedString.charCodeAt(++i);
298 js.while_('digit <= ${$Z}', [ 299 }
299 js('diff *= 26'), 300 for (; digit <= ${$Z};) {
300 js('diff += (digit - ${$A})'), 301 diff *= 26;
301 js('digit = diffEncodedString.charCodeAt(++i)') 302 diff += (digit - ${$A});
302 ]), 303 digit = diffEncodedString.charCodeAt(++i);
303 js('diff *= 26'), 304 }
304 js('diff += (digit - ${$a})'), 305 diff *= 26;
305 js('nameNumber += diff'), 306 diff += (digit - ${$a});
306 js.for_('var remaining = nameNumber', 307 nameNumber += diff;
307 'remaining > 0', 308 for (var remaining = nameNumber;
308 'remaining = (remaining / 88) | 0', [ 309 remaining > 0;
309 js('codes.unshift(${$HASH} + remaining % 88)') 310 remaining = (remaining / 88) | 0) {
310 ]), 311 codes.unshift(${$HASH} + remaining % 88);
311 js('calculatedShortNames.push(' 312 }
312 ' String.fromCharCode.apply(String, codes))') 313 calculatedShortNames.push(
313 ]), 314 String.fromCharCode.apply(String, codes));
314 js('shortNames.splice.apply(shortNames, calculatedShortNames)')]) 315 }
315 ]); 316 shortNames.splice.apply(shortNames, calculatedShortNames);
317 }
318 }''', [
319 js.string(namer.getNameOfClass(objectClass)),
320 js.string('$diffEncoding')]));
316 } else { 321 } else {
317 // No useDiffEncoding version. 322 // No useDiffEncoding version.
318 Iterable<String> longs = trivialNsmHandlers.map((selector) => 323 Iterable<String> longs = trivialNsmHandlers.map((selector) =>
319 selector.invocationMirrorMemberName); 324 selector.invocationMirrorMemberName);
320 String longNamesConstant = minify ? "" : 325 statements.add(js.statement(
321 ',longNames = "${longs.join(",")}".split(",")'; 326 'var objectClassObject = collectedClasses[#],'
322 statements.add( 327 ' shortNames = #.split(",")', [
323 js('var objectClassObject = ' 328 js.string(namer.getNameOfClass(objectClass)),
324 ' collectedClasses["${namer.getNameOfClass(objectClass)}"],' 329 js.string('$diffEncoding')]));
325 ' shortNames = "$diffEncoding".split(",")' 330 if (!minify) {
326 ' $longNamesConstant')); 331 statements.add(js.statement('var longNames = #.split(",")',
327 statements.add( 332 js.string(longs.join(','))));
328 js.if_('objectClassObject instanceof Array', 333 }
329 js('objectClassObject = objectClassObject[1]'))); 334 statements.add(js.statement(
335 'if (objectClassObject instanceof Array)'
336 ' objectClassObject = objectClassObject[1];'));
330 } 337 }
331 338
332 String sliceOffset = ', (j < $firstNormalSelector) ? 1 : 0'; 339 // 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 ? 340 String whatToPatch = task.nativeEmitter.handleNoSuchMethod ?
337 "Object.prototype" : 341 "Object.prototype" :
338 "objectClassObject"; 342 "objectClassObject";
339 343
340 var params = ['name', 'short', 'type']; 344 List<jsAst.Expression> sliceOffsetArguments =
341 var sliceOffsetParam = ''; 345 firstNormalSelector == 0
342 var slice = 'Array.prototype.slice.call'; 346 ? []
343 if (!sliceOffset.isEmpty) { 347 : (firstNormalSelector == shorts.length
344 sliceOffsetParam = ', sliceOffset'; 348 ? [js.number(1)]
345 params.add('sliceOffset'); 349 : [js('(j < #) ? 1 : 0', js.number(firstNormalSelector))]);
346 } 350
347 statements.addAll([ 351 var sliceOffsetParams = sliceOffsetArguments.isEmpty ? [] : ['sliceOffset'];
352
353 statements.add(js.statement('''
348 // If we are loading a deferred library the object class will not be in 354 // If we are loading a deferred library the object class will not be in
349 // the collectedClasses so objectClassObject is undefined, and we skip 355 // the collectedClasses so objectClassObject is undefined, and we skip
350 // setting up the names. 356 // setting up the names.
351 js.if_('objectClassObject', [ 357 if (objectClassObject) {
352 js.for_('var j = 0', 'j < shortNames.length', 'j++', [ 358 for (var j = 0; j < shortNames.length; j++) {
353 js('var type = 0'), 359 var type = 0;
354 js('var short = shortNames[j]'), 360 var short = shortNames[j];
355 js.if_('short[0] == "${namer.getterPrefix[0]}"', js('type = 1')), 361 if (short[0] == "${namer.getterPrefix[0]}") type = 1;
356 js.if_('short[0] == "${namer.setterPrefix[0]}"', js('type = 2')), 362 if (short[0] == "${namer.setterPrefix[0]}") type = 2;
357 // Generate call to: 363 // Generate call to:
358 // createInvocationMirror(String name, internalName, type, arguments, 364 //
359 // argumentNames) 365 // createInvocationMirror(String name, internalName, type,
360 js('$whatToPatch[short] = #(${minify ? "shortNames" : "longNames"}[j], ' 366 // arguments, argumentNames)
361 'short, type$sliceOffset)', 367 //
362 js.fun(params, [js.return_(js.fun([], 368 $whatToPatch[short] = (function(name, short, type, #) {
363 [js.return_(js( 369 return function() {
364 'this.$noSuchMethodName(' 370 return this.#(this,
365 'this, ' 371 #(name, short, type,
366 '$createInvocationMirror(' 372 Array.prototype.slice.call(arguments, #),
367 'name, short, type, ' 373 []));
368 '$slice(arguments$sliceOffsetParam), []))'))]))])) 374 }
369 ]) 375 })(#[j], short, type, #);
370 ]) 376 }
371 ]); 377 }''', [
378 sliceOffsetParams, // parameter
379 noSuchMethodName,
380 createInvocationMirror,
381 sliceOffsetParams, // argument to slice
382 minify ? 'shortNames' : 'longNames',
383 sliceOffsetArguments
384 ]));
385
386 return statements;
372 } 387 }
373 } 388 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698