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

Side by Side Diff: sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart

Issue 12499005: dart2js: Create noSuchMethod handlers at runtime to reduce overhead. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 9 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) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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 js_backend; 5 part of js_backend;
6 6
7 /** 7 /**
8 * A function element that represents a closure call. The signature is copied 8 * A function element that represents a closure call. The signature is copied
9 * from the given element. 9 * from the given element.
10 */ 10 */
(...skipping 259 matching lines...) Expand 10 before | Expand all | Expand 10 after
270 js['var constructor'], 270 js['var constructor'],
271 271
272 // if (typeof fields == "function") { 272 // if (typeof fields == "function") {
273 js.if_(js['typeof fields == "function"'], [ 273 js.if_(js['typeof fields == "function"'], [
274 js['constructor = fields'] 274 js['constructor = fields']
275 ], /* else */ [ 275 ], /* else */ [
276 js['var str = "function " + cls + "("'], 276 js['var str = "function " + cls + "("'],
277 js['var body = ""'], 277 js['var body = ""'],
278 278
279 // for (var i = 0; i < fields.length; i++) { 279 // for (var i = 0; i < fields.length; i++) {
280 js.for_(js['var i = 0'], js['i < fields.length'], js['i++'], [ 280 js.for_('var i = 0', 'i < fields.length', 'i++', [
281 // if (i != 0) str += ", "; 281 // if (i != 0) str += ", ";
282 js.if_(js['i != 0'], js['str += ", "']), 282 js.if_('i != 0', js['str += ", "']),
283 283
284 js['var field = fields[i]'], 284 js['var field = fields[i]'],
285 js['field = generateAccessor(field, prototype)'], 285 js['field = generateAccessor(field, prototype)'],
286 js['str += field'], 286 js['str += field'],
287 js['body += ("this." + field + " = " + field + ";\\n")'] 287 js['body += ("this." + field + " = " + field + ";\\n")']
288 ]), 288 ]),
289 289
290 js['str += (") {" + body + "}\\nreturn " + cls)'], 290 js['str += (") {" + body + "}\\nreturn " + cls)'],
291 291
292 js['constructor = (new Function(str))()'] 292 js['constructor = (new Function(str))()']
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
325 325
326 js.if_(js['tmp.__proto__'], [ 326 js.if_(js['tmp.__proto__'], [
327 js['tmp.__proto__ = {}'], 327 js['tmp.__proto__ = {}'],
328 js.if_(js[r'typeof tmp.get$f != "undefined"'], 328 js.if_(js[r'typeof tmp.get$f != "undefined"'],
329 js['$supportsProtoName = true']) 329 js['$supportsProtoName = true'])
330 330
331 ]) 331 ])
332 ]; 332 ];
333 } 333 }
334 334
335 const MAX_MINIFIED_LENGTH_FOR_DIFF_ENCODING = 4;
336 /**
337 * Adds (at runtime) the handlers to the Object class which catch calls to
338 * methods that the object does not have. The handlers create an invocation
339 * mirror object.
340 *
341 * The current version only gives you the minified name when minifying (when
342 * not minifying this method is not called).
343 *
344 * In order to generate the noSuchMethod handlers we only need the minified
345 * name of the method. We test the first character of the minified name to
346 * determine if it is a getter or a setter, and we use the arguments array at
347 * runtime to get the number of arguments and their values. If the method
348 * involves named arguments etc. then we don't handle it here, but emit the
349 * handler method directly on the Object class.
350 *
351 * The minified names are mostly 1-4 character names, which we emit in sorted
352 * order (primary key is length, secondary ordering is lexicographic). This
353 * gives an order like ... dD dI dX da ...
354 *
355 * Gzip is good with repeated text, but it can't diff-encode, so we do that
356 * for it. We encode the minified names in a comma-separated string, but all
357 * the 1-4 character names are encoded before the first comma as a series of
358 * base 26 numbers. The last digit of each number is lower case, the others
359 * are upper case, so 1 is "b" and 26 is "Ba".
360 *
361 * We think of the minified names as base 88 numbers using the ASCII
362 * characters from # to z. The base 26 numbers each encode the delta from
363 * the previous minified name to the next. So if there is a minified name
364 * called Df and the next is Dh, then they are 2971 and 2973 when thought of
365 * as base 88 numbers. The difference is 2, which is "c" in lower-case-
366 * terminated base 26.
367 *
368 * The reason we don't encode long minified names with this method is that
369 * decoding the base 88 numbers would overflow JavaScript's puny integers.
370 *
371 * There are some selectors that have a special calling convention (because
372 * they are called with the receiver as the first argument). They need a
373 * slightly different noSuchMethod handler, so we handle these first.
374 */
375 void addTrivialNsmHandlers(List<jsAst.Node> statements,
376 List<Selector> selectors) {
377 if (selectors.length == 0) return;
378 // Sort by calling convention, JS name length and by JS name.
379 selectors.sort((a, b) {
380 bool aIsIntercepted = backend.isInterceptedName(a.name);
381 bool bIsIntercepted = backend.isInterceptedName(b.name);
382 if (aIsIntercepted != bIsIntercepted) return aIsIntercepted ? -1 : 1;
383 String aName = namer.invocationMirrorInternalName(a);
384 String bName = namer.invocationMirrorInternalName(b);
385 if (aName.length != bName.length) return aName.length - bName.length;
386 return aName.compareTo(bName);
387 });
388
389 // Find out how many selectors there are with the special calling
390 // convention.
391 int firstNormalSelector = selectors.length;
392 for (int i = 0; i < selectors.length; i++) {
393 if (!backend.isInterceptedName(selectors[i].name)) {
394 firstNormalSelector = i;
395 break;
396 }
397 }
398
399 // Get the short names (JS names, perhaps minified).
400 Iterable<String> shorts = selectors.map((selector) =>
401 namer.invocationMirrorInternalName(selector));
402 final diffShorts = <String>[];
403 var diffEncoding = new StringBuffer();
404
405 // Treat string as a number in base 88 with digits in ASCII order from # to
406 // z. The short name sorting is based on length, and uses ASCII order for
407 // equal length strings so this means that names are ascending. The hash
408 // character, #, is never given as input, but we need it because it's the
409 // implicit leading zero (otherwise we could not code names with leading
410 // dollar signs).
411 int fromBase88(String x) {
412 int answer = 0;
413 for (int i = 0; i < x.length; i++) {
414 int c = x.codeUnitAt(i);
415 // No support for Unicode minified identifiers in JS.
416 assert(c >= $$ && c <= $z);
417 answer *= 88;
418 answer += c - $HASH;
419 }
420 return answer;
421 }
422
423 // Big endian encoding, A = 0, B = 1, etc. terminate with lower case.
424 String toBase26(int x) {
425 int c = x;
426 var encodingChars = <int>[];
427 encodingChars.add($a + (c % 26));
428 while (true) {
429 c ~/= 26;
430 if (c == 0) break;
431 encodingChars.add($A + (c % 26));
432 }
433 return new String.fromCharCodes(encodingChars.reversed.toList());
434 }
435
436 bool minify = compiler.enableMinification;
437 bool useDiffEncoding = minify && shorts.length > 30;
438
439 int previous = 0;
440 int nameCounter = 0;
441 for (String short in shorts) {
442 // Emit period that resets the diff base to zero when we switch to normal
443 // calling convention (this avoids the need to code negative diffs).
444 if (useDiffEncoding && nameCounter == firstNormalSelector) {
445 diffEncoding.write(".");
446 previous = 0;
447 }
448 if (short.length <= MAX_MINIFIED_LENGTH_FOR_DIFF_ENCODING &&
449 useDiffEncoding) {
450 int base63 = fromBase88(short);
451 int diff = base63 - previous;
452 previous = base63;
453 String base26Diff = toBase26(diff);
454 diffEncoding.write(base26Diff);
455 } else {
456 if (useDiffEncoding || diffEncoding.length != 0) {
457 diffEncoding.write(",");
458 }
459 diffEncoding.write(short);
460 }
461 nameCounter++;
462 }
463
464 // Startup code that loops over the method names and puts handlers on the
465 // Object class to catch noSuchMethod invocations.
466 ClassElement objectClass = compiler.objectClass;
467 String createInvocationMirror = namer.getName(
468 compiler.createInvocationMirrorElement);
469 String noSuchMethodName = namer.publicInstanceMethodNameByArity(
470 Compiler.NO_SUCH_METHOD, Compiler.NO_SUCH_METHOD_ARG_COUNT);
471 var type = 0;
472 if (useDiffEncoding) {
473 statements.addAll([
474 js['var objectClassObject = '
475 ' collectedClasses["${namer.getName(objectClass)}"],'
476 ' shortNames = "$diffEncoding".split(","),'
477 ' nameNumber = 0,'
478 ' diffEncodedString = shortNames[0],'
479 ' calculatedShortNames = [0, 1]'], // 0, 1 are args for splice.
480 js.for_('var i = 0', 'i < diffEncodedString.length', 'i++', [
481 js['var codes = [],'
482 ' diff = 0,'
483 ' digit = diffEncodedString.charCodeAt(i)'],
484 js.if_('digit == ${$PERIOD}', [
485 js['nameNumber = 0'],
486 js['digit = diffEncodedString.charCodeAt(++i)']
487 ]),
488 js.while_('digit <= ${$Z}', [
489 js['diff *= 26'],
490 js['diff += (digit - ${$A})'],
491 js['digit = diffEncodedString.charCodeAt(++i)']
492 ]),
493 js['diff *= 26'],
494 js['diff += (digit - ${$a})'],
495 js['nameNumber += diff'],
496 js.for_('var remaining = nameNumber',
497 'remaining > 0',
498 'remaining = ((remaining / 88) | 0)', [
499 js['codes.unshift(${$HASH} + (remaining % 88))']
500 ]),
501 js['calculatedShortNames.push('
502 ' String.fromCharCode.apply(String, codes))']
503 ]),
504 js['shortNames.splice.apply(shortNames, calculatedShortNames)']
505 ]);
506 } else {
507 // No useDiffEncoding version.
508 Iterable<String> longs = selectors.map((selector) =>
509 selector.invocationMirrorMemberName);
510 String longNamesConstant = minify ? "" :
511 ',longNames = "${longs.join(",")}".split(",")';
512 statements.add(
513 js['var objectClassObject = '
514 ' collectedClasses["${namer.getName(objectClass)}"],'
515 ' shortNames = "$diffEncoding".split(",")'
516 ' $longNamesConstant']);
517 }
518
519 String sliceOffset = '," + (j < $firstNormalSelector ? 1 : 0)';
520 if (firstNormalSelector == 0) sliceOffset = '"';
521 if (firstNormalSelector == shorts.length) sliceOffset = ', 1"';
522
523 String whatToPatch = nativeEmitter.objectToPutNoSuchMethodHandlersOn;
ngeoffray 2013/03/11 09:41:27 Instead of having these [objectToPush..] getters,
erikcorry 2013/03/13 08:55:45 Done.
524
525 statements.addAll([
526 js.for_('var j = 0', 'j < shortNames.length', 'j++', [
527 js['var type = 0'],
528 js['var short = shortNames[j]'],
529 js.if_('short[0] == "${namer.getterPrefix[0]}"', js['type = 1']),
530 js.if_('short[0] == "${namer.setterPrefix[0]}"', js['type = 2']),
531 js['$whatToPatch[short] = Function("'
532 'return this.$noSuchMethodName('
533 'this,'
534 '${namer.CURRENT_ISOLATE}.$createInvocationMirror(\'"'
535 ' + ${minify ? "shortNames" : "longNames"}[j]'
536 ' + "\',\'" + short + "\',"'
537 ' + type'
538 ' + ",Array.prototype.slice.call(arguments'
539 '$sliceOffset'
540 ' + "),[]))")']
541 ])
542 ]);
543 }
544
545 String get objectToPutNoSuchMethodHandlersOn {
546 return "objectClassObject";
ngeoffray 2013/03/11 09:41:27 What is this?
erikcorry 2013/03/13 08:55:45 Gone.
547 }
548
335 jsAst.Fun get finishClassesFunction { 549 jsAst.Fun get finishClassesFunction {
336 // Class descriptions are collected in a JS object. 550 // Class descriptions are collected in a JS object.
337 // 'finishClasses' takes all collected descriptions and sets up 551 // 'finishClasses' takes all collected descriptions and sets up
338 // the prototype. 552 // the prototype.
339 // Once set up, the constructors prototype field satisfy: 553 // Once set up, the constructors prototype field satisfy:
340 // - it contains all (local) members. 554 // - it contains all (local) members.
341 // - its internal prototype (__proto__) points to the superclass' 555 // - its internal prototype (__proto__) points to the superclass'
342 // prototype field. 556 // prototype field.
343 // - the prototype's constructor field points to the JavaScript 557 // - the prototype's constructor field points to the JavaScript
344 // constructor. 558 // constructor.
345 // For engines where we have access to the '__proto__' we can manipulate 559 // For engines where we have access to the '__proto__' we can manipulate
346 // the object literal directly. For other engines we have to create a new 560 // the object literal directly. For other engines we have to create a new
347 // object and copy over the members. 561 // object and copy over the members.
348 562
349 // function(collectedClasses, 563 List<jsAst.Node> statements = [
350 // isolateProperties,
351 // existingIsolateProperties) {
352 return js.fun(['collectedClasses', 'isolateProperties',
353 'existingIsolateProperties'], [
354 js['var pendingClasses = {}'], 564 js['var pendingClasses = {}'],
355 565
356 js['var hasOwnProperty = Object.prototype.hasOwnProperty'], 566 js['var hasOwnProperty = Object.prototype.hasOwnProperty'],
357 567
358 // for (var cls in collectedClasses) { 568 // for (var cls in collectedClasses) {
359 js.forIn('cls', 'collectedClasses', [ 569 js.forIn('cls', 'collectedClasses', [
360 // if (hasOwnProperty.call(collectedClasses, cls)) { 570 // if (hasOwnProperty.call(collectedClasses, cls)) {
361 js.if_(js['hasOwnProperty.call(collectedClasses, cls)'], [ 571 js.if_('hasOwnProperty.call(collectedClasses, cls)', [
362 js['var desc = collectedClasses[cls]'], 572 js['var desc = collectedClasses[cls]'],
363 573
364 /* The 'fields' are either a constructor function or a 574 /* The 'fields' are either a constructor function or a
365 * string encoding fields, constructor and superclass. Get 575 * string encoding fields, constructor and superclass. Get
366 * the superclass and the fields in the format 576 * the superclass and the fields in the format
367 * Super;field1,field2 from the null-string property on the 577 * Super;field1,field2 from the null-string property on the
368 * descriptor. 578 * descriptor.
369 */ 579 */
370 // var fields = desc[""], supr; 580 // var fields = desc[""], supr;
371 js['var fields = desc[""], supr'], 581 js['var fields = desc[""], supr'],
372 582
373 js.if_(js['typeof fields == "string"'], [ 583 js.if_('typeof fields == "string"', [
374 js['var s = fields.split(";")'], 584 js['var s = fields.split(";")'],
375 js['supr = s[0]'], 585 js['supr = s[0]'],
376 js['fields = s[1] == "" ? [] : s[1].split(",")'], 586 js['fields = s[1] == "" ? [] : s[1].split(",")'],
377 ], /* else */ [ 587 ], /* else */ [
378 js['supr = desc.super'] 588 js['supr = desc.super']
379 ]), 589 ]),
380 590
381 js['isolateProperties[cls] = defineClass(cls, fields, desc)'], 591 js['isolateProperties[cls] = defineClass(cls, fields, desc)'],
382 592
383 // if (supr) pendingClasses[cls] = supr; 593 // if (supr) pendingClasses[cls] = supr;
384 js.if_(js['supr'], js['pendingClasses[cls] = supr']) 594 js.if_('supr', js['pendingClasses[cls] = supr'])
385 ]) 595 ])
386 ]), 596 ]),
387 597
388 js['var finishedClasses = {}'], 598 js['var finishedClasses = {}'],
389 599
390 // function finishClass(cls) { ... } 600 // function finishClass(cls) { ... }
391 buildFinishClass(), 601 buildFinishClass(),
602 ];
392 603
604 addTrivialNsmHandlers(statements, compiler.codegenWorld.trivialNsmHandlers);
605
606 statements.add(
393 // for (var cls in pendingClasses) finishClass(cls); 607 // for (var cls in pendingClasses) finishClass(cls);
394 js.forIn('cls', 'pendingClasses', js['finishClass']('cls')) 608 js.forIn('cls', 'pendingClasses', js['finishClass(cls)'])
395 ]); 609 );
610 // function(collectedClasses,
611 // isolateProperties,
612 // existingIsolateProperties) {
613 return js.fun(['collectedClasses', 'isolateProperties',
614 'existingIsolateProperties'], statements);
396 } 615 }
397 616
398 jsAst.FunctionDeclaration buildFinishClass() { 617 jsAst.FunctionDeclaration buildFinishClass() {
399 // function finishClass(cls) { 618 // function finishClass(cls) {
400 jsAst.Fun fun = js.fun(['cls'], [ 619 jsAst.Fun fun = js.fun(['cls'], [
401 620
402 // TODO(8540): Remove this work around. 621 // TODO(8540): Remove this work around.
403 /* Opera does not support 'getOwnPropertyNames'. Therefore we use 622 /* Opera does not support 'getOwnPropertyNames'. Therefore we use
404 hasOwnProperty instead. */ 623 hasOwnProperty instead. */
405 js['var hasOwnProperty = Object.prototype.hasOwnProperty'], 624 js['var hasOwnProperty = Object.prototype.hasOwnProperty'],
406 625
407 // if (hasOwnProperty.call(finishedClasses, cls)) return; 626 // if (hasOwnProperty.call(finishedClasses, cls)) return;
408 js.if_(js['hasOwnProperty.call(finishedClasses, cls)'], 627 js.if_('hasOwnProperty.call(finishedClasses, cls)',
409 js.return_()), 628 js.return_()),
410 629
411 js['finishedClasses[cls] = true'], 630 js['finishedClasses[cls] = true'],
631
412 js['var superclass = pendingClasses[cls]'], 632 js['var superclass = pendingClasses[cls]'],
413 633
414 /* The superclass is only false (empty string) for Dart's Object class. */ 634 // The superclass is only false (empty string) for Dart's Object class.
415 js.if_(js['!superclass'], js.return_()), 635 // The minifier together with noSuchMethod can methods on the
ngeoffray 2013/03/11 09:41:27 can methods -> can put methods
erikcorry 2013/03/13 08:55:45 Done.
636 // Object.prototype object, and they show through here, so we check that
637 // we have a string.
638 js.if_('!superclass || typeof superclass != "string"', js.return_()),
416 js['finishClass(superclass)'], 639 js['finishClass(superclass)'],
417 js['var constructor = isolateProperties[cls]'], 640 js['var constructor = isolateProperties[cls]'],
418 js['var superConstructor = isolateProperties[superclass]'], 641 js['var superConstructor = isolateProperties[superclass]'],
419 642
420 // if (!superConstructor) 643 // if (!superConstructor)
421 // superConstructor = existingIsolateProperties[superclass]; 644 // superConstructor = existingIsolateProperties[superclass];
422 js.if_(js['superConstructor'].not, 645 js.if_(js['superConstructor'].not,
423 js['superConstructor'].assign( 646 js['superConstructor'].assign(
424 js['existingIsolateProperties'][js['superclass']])), 647 js['existingIsolateProperties'][js['superclass']])),
425 648
(...skipping 13 matching lines...) Expand all
439 js['tmp.prototype = superConstructor.prototype'], 662 js['tmp.prototype = superConstructor.prototype'],
440 js['var newPrototype = new tmp()'], 663 js['var newPrototype = new tmp()'],
441 664
442 js['constructor.prototype = newPrototype'], 665 js['constructor.prototype = newPrototype'],
443 js['newPrototype.constructor = constructor'], 666 js['newPrototype.constructor = constructor'],
444 667
445 // for (var member in prototype) { 668 // for (var member in prototype) {
446 js.forIn('member', 'prototype', [ 669 js.forIn('member', 'prototype', [
447 /* Short version of: if (member == '') */ 670 /* Short version of: if (member == '') */
448 // if (!member) continue; 671 // if (!member) continue;
449 js.if_(js['!member'], new jsAst.Continue(null)), 672 js.if_('!member', new jsAst.Continue(null)),
450 673
451 // if (hasOwnProperty.call(prototype, member)) { 674 // if (hasOwnProperty.call(prototype, member)) {
452 js.if_(js['hasOwnProperty.call(prototype, member)'], [ 675 js.if_('hasOwnProperty.call(prototype, member)', [
453 js['newPrototype[member] = prototype[member]'] 676 js['newPrototype[member] = prototype[member]']
454 ]) 677 ])
455 ]) 678 ])
456 679
457 ]) 680 ])
458 ]); 681 ]);
459 682
460 return new jsAst.FunctionDeclaration( 683 return new jsAst.FunctionDeclaration(
461 new jsAst.VariableDeclaration('finishClass'), 684 new jsAst.VariableDeclaration('finishClass'),
462 fun); 685 fun);
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
504 'null'], 727 'null'],
505 728
506 js['var isolatePrototype = oldIsolate.prototype'], 729 js['var isolatePrototype = oldIsolate.prototype'],
507 js['var str = "{\\n"'], 730 js['var str = "{\\n"'],
508 js['str += ' 731 js['str += '
509 '"var properties = $isolate.${namer.isolatePropertiesName};\\n"'], 732 '"var properties = $isolate.${namer.isolatePropertiesName};\\n"'],
510 js['var hasOwnProperty = Object.prototype.hasOwnProperty'], 733 js['var hasOwnProperty = Object.prototype.hasOwnProperty'],
511 734
512 // for (var staticName in isolateProperties) { 735 // for (var staticName in isolateProperties) {
513 js.forIn('staticName', 'isolateProperties', [ 736 js.forIn('staticName', 'isolateProperties', [
514 js.if_(js['hasOwnProperty.call(isolateProperties, staticName)'], [ 737 js.if_('hasOwnProperty.call(isolateProperties, staticName)', [
515 js['str += ("this." + staticName + "= properties." + staticName + ' 738 js['str += ("this." + staticName + "= properties." + staticName + '
516 '";\\n")'] 739 '";\\n")']
517 ]) 740 ])
518 ]), 741 ]),
519 742
520 js['str += "}\\n"'], 743 js['str += "}\\n"'],
521 744
522 js['var newIsolate = new Function(str)'], 745 js['var newIsolate = new Function(str)'],
523 js['newIsolate.prototype = isolatePrototype'], 746 js['newIsolate.prototype = isolatePrototype'],
524 js['isolatePrototype.constructor = newIsolate'], 747 js['isolatePrototype.constructor = newIsolate'],
(...skipping 26 matching lines...) Expand all
551 js['var sentinelUndefined = {}'], 774 js['var sentinelUndefined = {}'],
552 js['var sentinelInProgress = {}'], 775 js['var sentinelInProgress = {}'],
553 js['prototype[fieldName] = sentinelUndefined'], 776 js['prototype[fieldName] = sentinelUndefined'],
554 777
555 // prototype[getterName] = function() { 778 // prototype[getterName] = function() {
556 js['prototype'][js['getterName']].assign(js.fun([], [ 779 js['prototype'][js['getterName']].assign(js.fun([], [
557 js['var result = $isolate[fieldName]'], 780 js['var result = $isolate[fieldName]'],
558 781
559 // try { 782 // try {
560 js.try_([ 783 js.try_([
561 js.if_(js['result === sentinelUndefined'], [ 784 js.if_('result === sentinelUndefined', [
562 js['$isolate[fieldName] = sentinelInProgress'], 785 js['$isolate[fieldName] = sentinelInProgress'],
563 786
564 // try { 787 // try {
565 js.try_([ 788 js.try_([
566 js['result = $isolate[fieldName] = lazyValue()'], 789 js['result = $isolate[fieldName] = lazyValue()'],
567 790
568 ], finallyPart: [ 791 ], finallyPart: [
569 // Use try-finally, not try-catch/throw as it destroys the 792 // Use try-finally, not try-catch/throw as it destroys the
570 // stack trace. 793 // stack trace.
571 794
572 // if (result === sentinelUndefined) { 795 // if (result === sentinelUndefined) {
573 js.if_(js['result === sentinelUndefined'], [ 796 js.if_('result === sentinelUndefined', [
574 // if ($isolate[fieldName] === sentinelInProgress) { 797 // if ($isolate[fieldName] === sentinelInProgress) {
575 js.if_(js['$isolate[fieldName] === sentinelInProgress'], [ 798 js.if_('$isolate[fieldName] === sentinelInProgress', [
576 js['$isolate[fieldName] = null'], 799 js['$isolate[fieldName] = null'],
577 ]) 800 ])
578 ]) 801 ])
579 ]) 802 ])
580 ], /* else */ [ 803 ], /* else */ [
581 js.if_(js['result === sentinelInProgress'], 804 js.if_('result === sentinelInProgress',
582 js['$cyclicThrow(staticName)'] 805 js['$cyclicThrow(staticName)']
583 ) 806 )
584 ]), 807 ]),
585 808
586 // return result; 809 // return result;
587 js.return_('result') 810 js.return_('result')
588 811
589 ], finallyPart: [ 812 ], finallyPart: [
590 js['$isolate[getterName] = getter'] 813 js['$isolate[getterName] = getter']
591 ]) 814 ])
(...skipping 557 matching lines...) Expand 10 before | Expand all | Expand 10 after
1149 visitField, 1372 visitField,
1150 includeBackendMembers: true, 1373 includeBackendMembers: true,
1151 includeSuperMembers: isInstantiated && !classElement.isNative()); 1374 includeSuperMembers: isInstantiated && !classElement.isNative());
1152 } 1375 }
1153 1376
1154 void generateGetter(Element member, String fieldName, String accessorName, 1377 void generateGetter(Element member, String fieldName, String accessorName,
1155 ClassBuilder builder) { 1378 ClassBuilder builder) {
1156 assert(!backend.isInterceptorClass(member)); 1379 assert(!backend.isInterceptorClass(member));
1157 String getterName = namer.getterNameFromAccessorName(accessorName); 1380 String getterName = namer.getterNameFromAccessorName(accessorName);
1158 builder.addProperty(getterName, 1381 builder.addProperty(getterName,
1159 js.fun([], js.return_(js['this'][fieldName]))); 1382 js.fun([], js.return_(js['this.$fieldName'])));
1160 } 1383 }
1161 1384
1162 void generateSetter(Element member, String fieldName, String accessorName, 1385 void generateSetter(Element member, String fieldName, String accessorName,
1163 ClassBuilder builder) { 1386 ClassBuilder builder) {
1164 assert(!backend.isInterceptorClass(member)); 1387 assert(!backend.isInterceptorClass(member));
1165 String setterName = namer.setterNameFromAccessorName(accessorName); 1388 String setterName = namer.setterNameFromAccessorName(accessorName);
1166 List<String> args = backend.isInterceptedMethod(member) 1389 List<String> args = backend.isInterceptedMethod(member)
1167 ? ['receiver', 'v'] 1390 ? ['receiver', 'v']
1168 : ['v']; 1391 : ['v'];
1169 builder.addProperty(setterName, 1392 builder.addProperty(setterName,
(...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after
1339 emitInstanceMembers(classElement, builder); 1562 emitInstanceMembers(classElement, builder);
1340 1563
1341 jsAst.Expression init = 1564 jsAst.Expression init =
1342 js[classesCollector][className].assign(builder.toObjectInitializer()); 1565 js[classesCollector][className].assign(builder.toObjectInitializer());
1343 buffer.add(jsAst.prettyPrint(init, compiler)); 1566 buffer.add(jsAst.prettyPrint(init, compiler));
1344 buffer.add('$N$n'); 1567 buffer.add('$N$n');
1345 } 1568 }
1346 1569
1347 bool get getterAndSetterCanBeImplementedByFieldSpec => true; 1570 bool get getterAndSetterCanBeImplementedByFieldSpec => true;
1348 1571
1572 /// If this is true then we can generate the noSuchMethod handlers at startup
1573 /// time, instead of them being emitted as part of the Object class.
1574 bool get generateTrivialNsmHandlers => true;
1575
1349 int _selectorRank(Selector selector) { 1576 int _selectorRank(Selector selector) {
1350 int arity = selector.argumentCount * 3; 1577 int arity = selector.argumentCount * 3;
1351 if (selector.isGetter()) return arity + 2; 1578 if (selector.isGetter()) return arity + 2;
1352 if (selector.isSetter()) return arity + 1; 1579 if (selector.isSetter()) return arity + 1;
1353 return arity; 1580 return arity;
1354 } 1581 }
1355 1582
1356 int _compareSelectorNames(Selector selector1, Selector selector2) { 1583 int _compareSelectorNames(Selector selector1, Selector selector2) {
1357 String name1 = selector1.name.toString(); 1584 String name1 = selector1.name.toString();
1358 String name2 = selector2.name.toString(); 1585 String name2 = selector2.name.toString();
(...skipping 602 matching lines...) Expand 10 before | Expand all | Expand 10 after
1961 if (selectors != null && !selectors.isEmpty) { 2188 if (selectors != null && !selectors.isEmpty) {
1962 emitCallStubForGetter(member, selectors, builder.addProperty); 2189 emitCallStubForGetter(member, selectors, builder.addProperty);
1963 } 2190 }
1964 } else if (member.isFunction()) { 2191 } else if (member.isFunction()) {
1965 if (compiler.codegenWorld.hasInvokedGetter(member, compiler)) { 2192 if (compiler.codegenWorld.hasInvokedGetter(member, compiler)) {
1966 emitDynamicFunctionGetter(member, builder.addProperty); 2193 emitDynamicFunctionGetter(member, builder.addProperty);
1967 } 2194 }
1968 } 2195 }
1969 } 2196 }
1970 2197
2198 // Identify the noSuchMethod handlers that are so simple that we can
2199 // generate them programatically.
2200 bool trivialNsmHandler(
ngeoffray 2013/03/11 09:41:27 boolify the name? eg isTrivialNsmHandler?
erikcorry 2013/03/13 08:55:45 Done.
erikcorry 2013/03/13 08:55:45 Done.
2201 int type, List argNames, Selector selector, String internalName) {
2202 if (!generateTrivialNsmHandlers) return false;
2203 // Check for wrong calling convention.
2204 if (backend.isInterceptedName(selector.name)) {
2205 // We can handle the strange calling convention used by intercepted names
2206 // in the diff encoding, but we don't use that for non-minified code.
2207 if (!compiler.enableMinification) return false;
2208 if (namer.invocationMirrorInternalName(selector).length >
2209 MAX_MINIFIED_LENGTH_FOR_DIFF_ENCODING) return false;
ngeoffray 2013/03/11 09:41:27 Strange indentation. I would create a local variab
erikcorry 2013/03/13 08:55:45 Done.
2210 }
2211 // Check for named arguments.
2212 if (argNames.length != 0) return false;
2213 // Check for unexpected name (this doesn't really happen).
2214 if (internalName.startsWith(namer.getterPrefix[0])) return type == 1;
2215 if (internalName.startsWith(namer.setterPrefix[0])) return type == 2;
2216 return type == 0;
2217 }
2218
1971 void emitNoSuchMethodHandlers(DefineStubFunction defineStub) { 2219 void emitNoSuchMethodHandlers(DefineStubFunction defineStub) {
1972 // Do not generate no such method handlers if there is no class. 2220 // Do not generate no such method handlers if there is no class.
1973 if (compiler.codegenWorld.instantiatedClasses.isEmpty) return; 2221 if (compiler.codegenWorld.instantiatedClasses.isEmpty) return;
1974 2222
1975 String noSuchMethodName = namer.publicInstanceMethodNameByArity( 2223 String noSuchMethodName = namer.publicInstanceMethodNameByArity(
1976 Compiler.NO_SUCH_METHOD, Compiler.NO_SUCH_METHOD_ARG_COUNT); 2224 Compiler.NO_SUCH_METHOD, Compiler.NO_SUCH_METHOD_ARG_COUNT);
1977 2225
1978 Element createInvocationMirrorElement = 2226 Element createInvocationMirrorElement =
1979 compiler.findHelper(const SourceString("createInvocationMirror")); 2227 compiler.findHelper(const SourceString("createInvocationMirror"));
1980 String createInvocationMirrorName = 2228 String createInvocationMirrorName =
1981 namer.getName(createInvocationMirrorElement); 2229 namer.getName(createInvocationMirrorElement);
1982 2230
1983 // Keep track of the JavaScript names we've already added so we 2231 // Keep track of the JavaScript names we've already added so we
1984 // do not introduce duplicates (bad for code size). 2232 // do not introduce duplicates (bad for code size).
1985 Set<String> addedJsNames = new Set<String>(); 2233 Map<String, Selector> addedJsNames = new Map<String, Selector>();
1986 2234
1987 jsAst.Expression generateMethod(String jsName, Selector selector) { 2235 jsAst.Expression generateMethod(String jsName,
2236 Selector selector,
2237 bool veryFewNoSuchMemberHandlers) {
1988 // Values match JSInvocationMirror in js-helper library. 2238 // Values match JSInvocationMirror in js-helper library.
1989 int type = selector.invocationMirrorKind; 2239 int type = selector.invocationMirrorKind;
1990 String methodName = selector.invocationMirrorMemberName;
1991 List<jsAst.Parameter> parameters = <jsAst.Parameter>[]; 2240 List<jsAst.Parameter> parameters = <jsAst.Parameter>[];
1992 CodeBuffer args = new CodeBuffer(); 2241 CodeBuffer args = new CodeBuffer();
1993 for (int i = 0; i < selector.argumentCount; i++) { 2242 for (int i = 0; i < selector.argumentCount; i++) {
1994 parameters.add(new jsAst.Parameter('\$$i')); 2243 parameters.add(new jsAst.Parameter('\$$i'));
1995 } 2244 }
1996 2245
1997 List<jsAst.Expression> argNames = 2246 List<jsAst.Expression> argNames =
1998 selector.getOrderedNamedArguments().map((SourceString name) => 2247 selector.getOrderedNamedArguments().map((SourceString name) =>
1999 js.string(name.slowToString())).toList(); 2248 js.string(name.slowToString())).toList();
2000 2249
2250 String methodName = selector.invocationMirrorMemberName;
2001 String internalName = namer.invocationMirrorInternalName(selector); 2251 String internalName = namer.invocationMirrorInternalName(selector);
2252 if (!veryFewNoSuchMemberHandlers &&
2253 trivialNsmHandler(type, argNames, selector, internalName)) {
2254 compiler.codegenWorld.trivialNsmHandlers.add(selector);
2255 return null;
2256 }
2002 2257
2003 String createInvocationMirror = namer.getName( 2258 String createInvocationMirror = namer.getName(
2004 compiler.createInvocationMirrorElement); 2259 compiler.createInvocationMirrorElement);
2005 2260
2006 assert(backend.isInterceptedName(Compiler.NO_SUCH_METHOD)); 2261 assert(backend.isInterceptedName(Compiler.NO_SUCH_METHOD));
2007 jsAst.Expression expression = js['this.$noSuchMethodName']( 2262 jsAst.Expression expression = js['this.$noSuchMethodName'](
2008 [js['this'], 2263 [js['this'],
2009 js[namer.CURRENT_ISOLATE][createInvocationMirror]([ 2264 js[namer.CURRENT_ISOLATE][createInvocationMirror]([
2010 js.string(methodName), 2265 js.string(compiler.enableMinification ?
ngeoffray 2013/03/11 09:41:27 Is that to keep the behavior consistent? Ie, alway
erikcorry 2013/03/13 08:55:45 Yes.
2266 internalName : methodName),
2011 js.string(internalName), 2267 js.string(internalName),
2012 type, 2268 type,
2013 new jsAst.ArrayInitializer.from( 2269 new jsAst.ArrayInitializer.from(
2014 parameters.map((param) => js[param.name]).toList()), 2270 parameters.map((param) => js[param.name]).toList()),
2015 new jsAst.ArrayInitializer.from(argNames)])]); 2271 new jsAst.ArrayInitializer.from(argNames)])]);
2016 parameters = backend.isInterceptedName(selector.name) 2272 parameters = backend.isInterceptedName(selector.name)
2017 ? ([new jsAst.Parameter('\$receiver')]..addAll(parameters)) 2273 ? ([new jsAst.Parameter('\$receiver')]..addAll(parameters))
2018 : parameters; 2274 : parameters;
2019 return js.fun(parameters, js.return_(expression)); 2275 return js.fun(parameters, js.return_(expression));
2020 } 2276 }
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
2086 // the handler either because all objects of type D implement 2342 // the handler either because all objects of type D implement
2087 // bar through inheritance. 2343 // bar through inheritance.
2088 // 2344 //
2089 // If we're calling bar on an object of type A we do need the 2345 // If we're calling bar on an object of type A we do need the
2090 // handler because we may have to call B.noSuchMethod since B 2346 // handler because we may have to call B.noSuchMethod since B
2091 // does not implement bar. 2347 // does not implement bar.
2092 Iterable<ClassElement> holders = 2348 Iterable<ClassElement> holders =
2093 compiler.world.locateNoSuchMethodHolders(selector); 2349 compiler.world.locateNoSuchMethodHolders(selector);
2094 if (holders.every(hasMatchingMember)) continue; 2350 if (holders.every(hasMatchingMember)) continue;
2095 String jsName = namer.invocationMirrorInternalName(selector); 2351 String jsName = namer.invocationMirrorInternalName(selector);
2096 if (!addedJsNames.contains(jsName)) { 2352 addedJsNames[jsName] = selector;
2097 jsAst.Expression method = generateMethod(jsName, selector);
2098 defineStub(jsName, method);
2099 addedJsNames.add(jsName);
2100 }
2101 } 2353 }
2102 } 2354 }
2103 2355
2104 compiler.codegenWorld.invokedNames.forEach(addNoSuchMethodHandlers); 2356 compiler.codegenWorld.invokedNames.forEach(addNoSuchMethodHandlers);
2105 compiler.codegenWorld.invokedGetters.forEach(addNoSuchMethodHandlers); 2357 compiler.codegenWorld.invokedGetters.forEach(addNoSuchMethodHandlers);
2106 compiler.codegenWorld.invokedSetters.forEach(addNoSuchMethodHandlers); 2358 compiler.codegenWorld.invokedSetters.forEach(addNoSuchMethodHandlers);
2359
2360 bool veryFewNoSuchMemberHandlers = (addedJsNames.length < 10);
ngeoffray 2013/03/11 09:41:27 Could that be done earlier, to avoid having to pas
ngeoffray 2013/03/11 09:41:27 Please move this 10 into a field in this class and
erikcorry 2013/03/13 08:55:45 I don't understand this question.
erikcorry 2013/03/13 08:55:45 Done.
2361 for (String jsName in addedJsNames.keys.toList()..sort()) {
2362 Selector selector = addedJsNames[jsName];
2363 jsAst.Expression method =
2364 generateMethod(jsName, selector, veryFewNoSuchMemberHandlers);
2365 if (method != null) defineStub(jsName, method);
2366 }
2107 } 2367 }
2108 2368
2109 String buildIsolateSetup(CodeBuffer buffer, 2369 String buildIsolateSetup(CodeBuffer buffer,
2110 Element appMain, 2370 Element appMain,
2111 Element isolateMain) { 2371 Element isolateMain) {
2112 String mainAccess = "${namer.isolateAccess(appMain)}"; 2372 String mainAccess = "${namer.isolateAccess(appMain)}";
2113 String currentIsolate = "${namer.CURRENT_ISOLATE}"; 2373 String currentIsolate = "${namer.CURRENT_ISOLATE}";
2114 // Since we pass the closurized version of the main method to 2374 // Since we pass the closurized version of the main method to
2115 // the isolate method, we must make sure that it exists. 2375 // the isolate method, we must make sure that it exists.
2116 if (!compiler.codegenWorld.staticFunctionsNeedingGetter.contains(appMain)) { 2376 if (!compiler.codegenWorld.staticFunctionsNeedingGetter.contains(appMain)) {
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
2155 addComment('END invoke [main].', buffer); 2415 addComment('END invoke [main].', buffer);
2156 } 2416 }
2157 2417
2158 void emitGetInterceptorMethod(CodeBuffer buffer, 2418 void emitGetInterceptorMethod(CodeBuffer buffer,
2159 String key, 2419 String key,
2160 Collection<ClassElement> classes) { 2420 Collection<ClassElement> classes) {
2161 jsAst.Statement buildReturnInterceptor(ClassElement cls) { 2421 jsAst.Statement buildReturnInterceptor(ClassElement cls) {
2162 return js.return_(js[namer.isolateAccess(cls)]['prototype']); 2422 return js.return_(js[namer.isolateAccess(cls)]['prototype']);
2163 } 2423 }
2164 2424
2165 jsAst.VariableUse receiver = js['receiver'];
2166 /** 2425 /**
2167 * Build a JavaScrit AST node for doing a type check on 2426 * Build a JavaScrit AST node for doing a type check on
2168 * [cls]. [cls] must be an interceptor class. 2427 * [cls]. [cls] must be an interceptor class.
2169 */ 2428 */
2170 jsAst.Statement buildInterceptorCheck(ClassElement cls) { 2429 jsAst.Statement buildInterceptorCheck(ClassElement cls) {
2171 jsAst.Expression condition; 2430 jsAst.Expression condition;
2172 assert(backend.isInterceptorClass(cls)); 2431 assert(backend.isInterceptorClass(cls));
2173 if (cls == backend.jsBoolClass) { 2432 if (cls == backend.jsBoolClass) {
2174 condition = receiver.typeof.equals(js.string('boolean')); 2433 condition = js['(typeof receiver) == "boolean"'];
2175 } else if (cls == backend.jsIntClass || 2434 } else if (cls == backend.jsIntClass ||
2176 cls == backend.jsDoubleClass || 2435 cls == backend.jsDoubleClass ||
2177 cls == backend.jsNumberClass) { 2436 cls == backend.jsNumberClass) {
2178 throw 'internal error'; 2437 throw 'internal error';
2179 } else if (cls == backend.jsArrayClass) { 2438 } else if (cls == backend.jsArrayClass) {
2180 condition = receiver['constructor'].equals('Array'); 2439 condition = js['receiver.constructor == Array'];
2181 } else if (cls == backend.jsStringClass) { 2440 } else if (cls == backend.jsStringClass) {
2182 condition = receiver.typeof.equals(js.string('string')); 2441 condition = js['(typeof receiver) == "string"'];
2183 } else if (cls == backend.jsNullClass) { 2442 } else if (cls == backend.jsNullClass) {
2184 condition = receiver.equals(new jsAst.LiteralNull()); 2443 condition = js['receiver == null'];
2185 } else if (cls == backend.jsFunctionClass) { 2444 } else if (cls == backend.jsFunctionClass) {
2186 condition = receiver.typeof.equals(js.string('function')); 2445 condition = js['(typeof receiver) == "function"'];
2187 } else { 2446 } else {
2188 throw 'internal error'; 2447 throw 'internal error';
2189 } 2448 }
2190 return js.if_(condition, buildReturnInterceptor(cls)); 2449 return js.if_(condition, buildReturnInterceptor(cls));
2191 } 2450 }
2192 2451
2193 bool hasArray = false; 2452 bool hasArray = false;
2194 bool hasBool = false; 2453 bool hasBool = false;
2195 bool hasDouble = false; 2454 bool hasDouble = false;
2196 bool hasFunction = false; 2455 bool hasFunction = false;
(...skipping 25 matching lines...) Expand all
2222 jsAst.Statement whenNumber; 2481 jsAst.Statement whenNumber;
2223 2482
2224 /// Note: there are two number classes in play: Dart's [num], 2483 /// Note: there are two number classes in play: Dart's [num],
2225 /// and JavaScript's Number (typeof receiver == 'number'). This 2484 /// and JavaScript's Number (typeof receiver == 'number'). This
2226 /// is the fallback used when we have determined that receiver 2485 /// is the fallback used when we have determined that receiver
2227 /// is a JavaScript Number. 2486 /// is a JavaScript Number.
2228 jsAst.Return returnNumberClass = buildReturnInterceptor( 2487 jsAst.Return returnNumberClass = buildReturnInterceptor(
2229 hasDouble ? backend.jsDoubleClass : backend.jsNumberClass); 2488 hasDouble ? backend.jsDoubleClass : backend.jsNumberClass);
2230 2489
2231 if (hasInt) { 2490 if (hasInt) {
2232 jsAst.Expression isInt = js['Math']['floor'](receiver).equals(receiver); 2491 jsAst.Expression isInt = js['Math.floor(receiver) == receiver'];
2233 whenNumber = js.block([ 2492 whenNumber = js.block([
2234 js.if_(isInt, buildReturnInterceptor(backend.jsIntClass)), 2493 js.if_(isInt, buildReturnInterceptor(backend.jsIntClass)),
2235 returnNumberClass]); 2494 returnNumberClass]);
2236 } else { 2495 } else {
2237 whenNumber = returnNumberClass; 2496 whenNumber = returnNumberClass;
2238 } 2497 }
2239 block.statements.add( 2498 block.statements.add(
2240 js.if_(receiver.typeof.equals(js.string('number')), 2499 js.if_('(typeof receiver) == "number"',
2241 whenNumber)); 2500 whenNumber));
2242 } 2501 }
2243 2502
2244 if (hasString) { 2503 if (hasString) {
2245 block.statements.add(buildInterceptorCheck(backend.jsStringClass)); 2504 block.statements.add(buildInterceptorCheck(backend.jsStringClass));
2246 } 2505 }
2247 if (hasNull) { 2506 if (hasNull) {
2248 block.statements.add(buildInterceptorCheck(backend.jsNullClass)); 2507 block.statements.add(buildInterceptorCheck(backend.jsNullClass));
2249 } else { 2508 } else {
2250 // Returning "undefined" here will provoke a JavaScript 2509 // Returning "undefined" here will provoke a JavaScript
2251 // TypeError which is later identified as a null-error by 2510 // TypeError which is later identified as a null-error by
2252 // [unwrapException] in js_helper.dart. 2511 // [unwrapException] in js_helper.dart.
2253 block.statements.add(js.if_(receiver.equals(new jsAst.LiteralNull()), 2512 block.statements.add(js.if_('receiver == null',
2254 js.return_(js.undefined()))); 2513 js.return_(js.undefined())));
2255 } 2514 }
2256 if (hasFunction) { 2515 if (hasFunction) {
2257 block.statements.add(buildInterceptorCheck(backend.jsFunctionClass)); 2516 block.statements.add(buildInterceptorCheck(backend.jsFunctionClass));
2258 } 2517 }
2259 if (hasBool) { 2518 if (hasBool) {
2260 block.statements.add(buildInterceptorCheck(backend.jsBoolClass)); 2519 block.statements.add(buildInterceptorCheck(backend.jsBoolClass));
2261 } 2520 }
2262 // TODO(ahe): It might be faster to check for Array before 2521 // TODO(ahe): It might be faster to check for Array before
2263 // function and bool. 2522 // function and bool.
2264 if (hasArray) { 2523 if (hasArray) {
2265 block.statements.add(buildInterceptorCheck(backend.jsArrayClass)); 2524 block.statements.add(buildInterceptorCheck(backend.jsArrayClass));
2266 } 2525 }
2267 block.statements.add(js.return_(receiver)); 2526 block.statements.add(js.return_(js['receiver']));
2268 2527
2269 buffer.add(jsAst.prettyPrint( 2528 buffer.add(jsAst.prettyPrint(
2270 js[isolateProperties][key].assign(js.fun(['receiver'], block)), 2529 js[isolateProperties][key].assign(js.fun(['receiver'], block)),
2271 compiler)); 2530 compiler));
2272 buffer.add(N); 2531 buffer.add(N);
2273 } 2532 }
2274 2533
2275 /** 2534 /**
2276 * Emit all versions of the [:getInterceptor:] method. 2535 * Emit all versions of the [:getInterceptor:] method.
2277 */ 2536 */
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after
2367 if (selector.isOperator()) { 2626 if (selector.isOperator()) {
2368 String name = selector.name.stringValue; 2627 String name = selector.name.stringValue;
2369 if (name == '==') { 2628 if (name == '==') {
2370 // Unfolds to: 2629 // Unfolds to:
2371 // [: if (receiver == null) return a0 == null; 2630 // [: if (receiver == null) return a0 == null;
2372 // if (typeof receiver != 'object') { 2631 // if (typeof receiver != 'object') {
2373 // return a0 != null && receiver === a0; 2632 // return a0 != null && receiver === a0;
2374 // } 2633 // }
2375 // :]. 2634 // :].
2376 List<jsAst.Statement> body = <jsAst.Statement>[]; 2635 List<jsAst.Statement> body = <jsAst.Statement>[];
2377 body.add(js.if_(js['receiver'].equals(new jsAst.LiteralNull()), 2636 body.add(js.if_('receiver == null',
2378 js.return_(js['a0'].equals(new jsAst.LiteralNull())))); 2637 js.return_(js['a0'].equals(new jsAst.LiteralNull()))));
2379 body.add(js.if_( 2638 body.add(js.if_(
2380 isNotObject('receiver'), 2639 isNotObject('receiver'),
2381 js.return_(js['a0'].equals(new jsAst.LiteralNull()).not.binary( 2640 js.return_(js['a0'].equals(new jsAst.LiteralNull()).not.binary(
2382 '&&', js['receiver'].strictEquals(js['a0']))))); 2641 '&&', js['receiver'].strictEquals(js['a0'])))));
2383 return new jsAst.Block(body); 2642 return new jsAst.Block(body);
2384 } 2643 }
2385 if (!classes.contains(backend.jsIntClass) 2644 if (!classes.contains(backend.jsIntClass)
2386 && !classes.contains(backend.jsNumberClass) 2645 && !classes.contains(backend.jsNumberClass)
2387 && !classes.contains(backend.jsDoubleClass)) { 2646 && !classes.contains(backend.jsDoubleClass)) {
(...skipping 364 matching lines...) Expand 10 before | Expand all | Expand 10 after
2752 """; 3011 """;
2753 const String HOOKS_API_USAGE = """ 3012 const String HOOKS_API_USAGE = """
2754 // The code supports the following hooks: 3013 // The code supports the following hooks:
2755 // dartPrint(message) - if this function is defined it is called 3014 // dartPrint(message) - if this function is defined it is called
2756 // instead of the Dart [print] method. 3015 // instead of the Dart [print] method.
2757 // dartMainRunner(main) - if this function is defined, the Dart [main] 3016 // dartMainRunner(main) - if this function is defined, the Dart [main]
2758 // method will not be invoked directly. 3017 // method will not be invoked directly.
2759 // Instead, a closure that will invoke [main] is 3018 // Instead, a closure that will invoke [main] is
2760 // passed to [dartMainRunner]. 3019 // passed to [dartMainRunner].
2761 """; 3020 """;
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698