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

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: Reordered some stuff due to code review feedback 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 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
62 CodeBuffer mainBuffer; 62 CodeBuffer mainBuffer;
63 final CodeBuffer deferredBuffer = new CodeBuffer(); 63 final CodeBuffer deferredBuffer = new CodeBuffer();
64 /** Shorter access to [isolatePropertiesName]. Both here in the code, as 64 /** Shorter access to [isolatePropertiesName]. Both here in the code, as
65 well as in the generated code. */ 65 well as in the generated code. */
66 String isolateProperties; 66 String isolateProperties;
67 String classesCollector; 67 String classesCollector;
68 final Set<ClassElement> neededClasses = new Set<ClassElement>(); 68 final Set<ClassElement> neededClasses = new Set<ClassElement>();
69 final List<ClassElement> regularClasses = <ClassElement>[]; 69 final List<ClassElement> regularClasses = <ClassElement>[];
70 final List<ClassElement> deferredClasses = <ClassElement>[]; 70 final List<ClassElement> deferredClasses = <ClassElement>[];
71 final List<ClassElement> nativeClasses = <ClassElement>[]; 71 final List<ClassElement> nativeClasses = <ClassElement>[];
72 final List<Selector> trivialNsmHandlers = <Selector>[];
72 73
73 // TODO(ngeoffray): remove this field. 74 // TODO(ngeoffray): remove this field.
74 Set<ClassElement> instantiatedClasses; 75 Set<ClassElement> instantiatedClasses;
75 76
76 final List<jsAst.Expression> boundClosures = <jsAst.Expression>[]; 77 final List<jsAst.Expression> boundClosures = <jsAst.Expression>[];
77 78
78 JavaScriptBackend get backend => compiler.backend; 79 JavaScriptBackend get backend => compiler.backend;
79 80
80 String get _ => compiler.enableMinification ? "" : " "; 81 String get _ => compiler.enableMinification ? "" : " ";
81 String get n => compiler.enableMinification ? "" : "\n"; 82 String get n => compiler.enableMinification ? "" : "\n";
(...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after
270 js['var constructor'], 271 js['var constructor'],
271 272
272 // if (typeof fields == "function") { 273 // if (typeof fields == "function") {
273 js.if_(js['typeof fields == "function"'], [ 274 js.if_(js['typeof fields == "function"'], [
274 js['constructor = fields'] 275 js['constructor = fields']
275 ], /* else */ [ 276 ], /* else */ [
276 js['var str = "function " + cls + "("'], 277 js['var str = "function " + cls + "("'],
277 js['var body = ""'], 278 js['var body = ""'],
278 279
279 // for (var i = 0; i < fields.length; i++) { 280 // for (var i = 0; i < fields.length; i++) {
280 js.for_(js['var i = 0'], js['i < fields.length'], js['i++'], [ 281 js.for_('var i = 0', 'i < fields.length', 'i++', [
281 // if (i != 0) str += ", "; 282 // if (i != 0) str += ", ";
282 js.if_(js['i != 0'], js['str += ", "']), 283 js.if_('i != 0', js['str += ", "']),
283 284
284 js['var field = fields[i]'], 285 js['var field = fields[i]'],
285 js['field = generateAccessor(field, prototype)'], 286 js['field = generateAccessor(field, prototype)'],
286 js['str += field'], 287 js['str += field'],
287 js['body += ("this." + field + " = " + field + ";\\n")'] 288 js['body += ("this." + field + " = " + field + ";\\n")']
288 ]), 289 ]),
289 290
290 js['str += (") {" + body + "}\\nreturn " + cls)'], 291 js['str += (") {" + body + "}\\nreturn " + cls)'],
291 292
292 js['constructor = (new Function(str))()'] 293 js['constructor = (new Function(str))()']
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
325 326
326 js.if_(js['tmp.__proto__'], [ 327 js.if_(js['tmp.__proto__'], [
327 js['tmp.__proto__ = {}'], 328 js['tmp.__proto__ = {}'],
328 js.if_(js[r'typeof tmp.get$f != "undefined"'], 329 js.if_(js[r'typeof tmp.get$f != "undefined"'],
329 js['$supportsProtoName = true']) 330 js['$supportsProtoName = true'])
330 331
331 ]) 332 ])
332 ]; 333 ];
333 } 334 }
334 335
336 const MAX_MINIFIED_LENGTH_FOR_DIFF_ENCODING = 4;
337
338 // If we need fewer than this many noSuchMethod handlers we can save space by
ngeoffray 2013/03/13 15:03:27 indentation off by one
erikcorry 2013/03/14 10:06:57 Fixed
339 // just emitting them in JS, rather than emitting the JS needed to generate
340 // them at run time.
341 const VERY_FEW_NO_SUCH_METHOD_HANDLERS = 10;
342
343 /**
344 * Adds (at runtime) the handlers to the Object class which catch calls to
345 * methods that the object does not have. The handlers create an invocation
346 * mirror object.
347 *
348 * The current version only gives you the minified name when minifying (when
349 * not minifying this method is not called).
350 *
351 * In order to generate the noSuchMethod handlers we only need the minified
352 * name of the method. We test the first character of the minified name to
353 * determine if it is a getter or a setter, and we use the arguments array at
354 * runtime to get the number of arguments and their values. If the method
355 * involves named arguments etc. then we don't handle it here, but emit the
356 * handler method directly on the Object class.
357 *
358 * The minified names are mostly 1-4 character names, which we emit in sorted
359 * order (primary key is length, secondary ordering is lexicographic). This
360 * gives an order like ... dD dI dX da ...
361 *
362 * Gzip is good with repeated text, but it can't diff-encode, so we do that
363 * for it. We encode the minified names in a comma-separated string, but all
364 * the 1-4 character names are encoded before the first comma as a series of
365 * base 26 numbers. The last digit of each number is lower case, the others
366 * are upper case, so 1 is "b" and 26 is "Ba".
367 *
368 * We think of the minified names as base 88 numbers using the ASCII
369 * characters from # to z. The base 26 numbers each encode the delta from
370 * the previous minified name to the next. So if there is a minified name
371 * called Df and the next is Dh, then they are 2971 and 2973 when thought of
372 * as base 88 numbers. The difference is 2, which is "c" in lower-case-
373 * terminated base 26.
374 *
375 * The reason we don't encode long minified names with this method is that
376 * decoding the base 88 numbers would overflow JavaScript's puny integers.
377 *
378 * There are some selectors that have a special calling convention (because
379 * they are called with the receiver as the first argument). They need a
380 * slightly different noSuchMethod handler, so we handle these first.
381 */
382 void addTrivialNsmHandlers(List<jsAst.Node> statements) {
383 if (trivialNsmHandlers.length == 0) return;
384 // Sort by calling convention, JS name length and by JS name.
385 trivialNsmHandlers.sort((a, b) {
386 bool aIsIntercepted = backend.isInterceptedName(a.name);
387 bool bIsIntercepted = backend.isInterceptedName(b.name);
388 if (aIsIntercepted != bIsIntercepted) return aIsIntercepted ? -1 : 1;
389 String aName = namer.invocationMirrorInternalName(a);
390 String bName = namer.invocationMirrorInternalName(b);
391 if (aName.length != bName.length) return aName.length - bName.length;
392 return aName.compareTo(bName);
393 });
394
395 // Find out how many selectors there are with the special calling
396 // convention.
397 int firstNormalSelector = trivialNsmHandlers.length;
398 for (int i = 0; i < trivialNsmHandlers.length; i++) {
399 if (!backend.isInterceptedName(trivialNsmHandlers[i].name)) {
400 firstNormalSelector = i;
401 break;
402 }
403 }
404
405 // Get the short names (JS names, perhaps minified).
406 Iterable<String> shorts = trivialNsmHandlers.map((selector) =>
407 namer.invocationMirrorInternalName(selector));
408 final diffShorts = <String>[];
409 var diffEncoding = new StringBuffer();
410
411 // Treat string as a number in base 88 with digits in ASCII order from # to
412 // z. The short name sorting is based on length, and uses ASCII order for
413 // equal length strings so this means that names are ascending. The hash
414 // character, #, is never given as input, but we need it because it's the
415 // implicit leading zero (otherwise we could not code names with leading
416 // dollar signs).
417 int fromBase88(String x) {
418 int answer = 0;
419 for (int i = 0; i < x.length; i++) {
420 int c = x.codeUnitAt(i);
421 // No support for Unicode minified identifiers in JS.
422 assert(c >= $$ && c <= $z);
423 answer *= 88;
424 answer += c - $HASH;
425 }
426 return answer;
427 }
428
429 // Big endian encoding, A = 0, B = 1, etc. terminate with lower case.
ngeoffray 2013/03/13 15:03:27 terminate -> terminates
erikcorry 2013/03/14 10:06:57 This was an imperative verb, not a singular one.
430 String toBase26(int x) {
431 int c = x;
432 var encodingChars = <int>[];
433 encodingChars.add($a + (c % 26));
434 while (true) {
435 c ~/= 26;
436 if (c == 0) break;
437 encodingChars.add($A + (c % 26));
438 }
439 return new String.fromCharCodes(encodingChars.reversed.toList());
440 }
441
442 bool minify = compiler.enableMinification;
ngeoffray 2013/03/13 15:03:27 Isn't that always true in this method?
erikcorry 2013/03/14 10:06:57 No, but we only diff encode if we are minifying.
443 bool useDiffEncoding = minify && shorts.length > 30;
444
445 int previous = 0;
446 int nameCounter = 0;
447 for (String short in shorts) {
448 // Emit period that resets the diff base to zero when we switch to normal
449 // calling convention (this avoids the need to code negative diffs).
450 if (useDiffEncoding && nameCounter == firstNormalSelector) {
451 diffEncoding.write(".");
452 previous = 0;
453 }
454 if (short.length <= MAX_MINIFIED_LENGTH_FOR_DIFF_ENCODING &&
455 useDiffEncoding) {
456 int base63 = fromBase88(short);
457 int diff = base63 - previous;
458 previous = base63;
459 String base26Diff = toBase26(diff);
460 diffEncoding.write(base26Diff);
461 } else {
462 if (useDiffEncoding || diffEncoding.length != 0) {
463 diffEncoding.write(",");
464 }
465 diffEncoding.write(short);
466 }
467 nameCounter++;
468 }
469
470 // Startup code that loops over the method names and puts handlers on the
471 // Object class to catch noSuchMethod invocations.
472 ClassElement objectClass = compiler.objectClass;
473 String createInvocationMirror = namer.getName(
474 compiler.createInvocationMirrorElement);
475 String noSuchMethodName = namer.publicInstanceMethodNameByArity(
476 Compiler.NO_SUCH_METHOD, Compiler.NO_SUCH_METHOD_ARG_COUNT);
477 var type = 0;
478 if (useDiffEncoding) {
479 statements.addAll([
480 js['var objectClassObject = '
481 ' collectedClasses["${namer.getName(objectClass)}"],'
482 ' shortNames = "$diffEncoding".split(","),'
483 ' nameNumber = 0,'
484 ' diffEncodedString = shortNames[0],'
485 ' calculatedShortNames = [0, 1]'], // 0, 1 are args for splice.
486 js.for_('var i = 0', 'i < diffEncodedString.length', 'i++', [
487 js['var codes = [],'
488 ' diff = 0,'
489 ' digit = diffEncodedString.charCodeAt(i)'],
490 js.if_('digit == ${$PERIOD}', [
491 js['nameNumber = 0'],
492 js['digit = diffEncodedString.charCodeAt(++i)']
493 ]),
494 js.while_('digit <= ${$Z}', [
495 js['diff *= 26'],
496 js['diff += (digit - ${$A})'],
497 js['digit = diffEncodedString.charCodeAt(++i)']
498 ]),
499 js['diff *= 26'],
500 js['diff += (digit - ${$a})'],
501 js['nameNumber += diff'],
502 js.for_('var remaining = nameNumber',
503 'remaining > 0',
504 'remaining = ((remaining / 88) | 0)', [
505 js['codes.unshift(${$HASH} + (remaining % 88))']
506 ]),
507 js['calculatedShortNames.push('
508 ' String.fromCharCode.apply(String, codes))']
509 ]),
510 js['shortNames.splice.apply(shortNames, calculatedShortNames)']
511 ]);
512 } else {
513 // No useDiffEncoding version.
514 Iterable<String> longs = trivialNsmHandlers.map((selector) =>
515 selector.invocationMirrorMemberName);
516 String longNamesConstant = minify ? "" :
517 ',longNames = "${longs.join(",")}".split(",")';
518 statements.add(
519 js['var objectClassObject = '
520 ' collectedClasses["${namer.getName(objectClass)}"],'
521 ' shortNames = "$diffEncoding".split(",")'
522 ' $longNamesConstant']);
523 }
524
525 String sliceOffset = '," + (j < $firstNormalSelector ? 1 : 0)';
526 if (firstNormalSelector == 0) sliceOffset = '"';
527 if (firstNormalSelector == shorts.length) sliceOffset = ', 1"';
528
529 String whatToPatch = nativeEmitter.handleNoSuchMethod ?
530 "Object.prototype" :
531 "objectClassObject";
ngeoffray 2013/03/13 15:03:27 What is objectClassObject?
erikcorry 2013/03/14 10:06:57 It's the JS object corresponding to the Dart Objec
532
533 statements.addAll([
534 js.for_('var j = 0', 'j < shortNames.length', 'j++', [
535 js['var type = 0'],
536 js['var short = shortNames[j]'],
537 js.if_('short[0] == "${namer.getterPrefix[0]}"', js['type = 1']),
538 js.if_('short[0] == "${namer.setterPrefix[0]}"', js['type = 2']),
539 js['$whatToPatch[short] = Function("'
540 'return this.$noSuchMethodName('
541 'this,'
542 '${namer.CURRENT_ISOLATE}.$createInvocationMirror(\'"'
543 ' + ${minify ? "shortNames" : "longNames"}[j]'
544 ' + "\',\'" + short + "\',"'
545 ' + type'
546 ' + ",Array.prototype.slice.call(arguments'
547 '$sliceOffset'
548 ' + "),[]))")']
549 ])
550 ]);
551 }
552
335 jsAst.Fun get finishClassesFunction { 553 jsAst.Fun get finishClassesFunction {
336 // Class descriptions are collected in a JS object. 554 // Class descriptions are collected in a JS object.
337 // 'finishClasses' takes all collected descriptions and sets up 555 // 'finishClasses' takes all collected descriptions and sets up
338 // the prototype. 556 // the prototype.
339 // Once set up, the constructors prototype field satisfy: 557 // Once set up, the constructors prototype field satisfy:
340 // - it contains all (local) members. 558 // - it contains all (local) members.
341 // - its internal prototype (__proto__) points to the superclass' 559 // - its internal prototype (__proto__) points to the superclass'
342 // prototype field. 560 // prototype field.
343 // - the prototype's constructor field points to the JavaScript 561 // - the prototype's constructor field points to the JavaScript
344 // constructor. 562 // constructor.
345 // For engines where we have access to the '__proto__' we can manipulate 563 // 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 564 // the object literal directly. For other engines we have to create a new
347 // object and copy over the members. 565 // object and copy over the members.
348 566
349 // function(collectedClasses, 567 List<jsAst.Node> statements = [
350 // isolateProperties,
351 // existingIsolateProperties) {
352 return js.fun(['collectedClasses', 'isolateProperties',
353 'existingIsolateProperties'], [
354 js['var pendingClasses = {}'], 568 js['var pendingClasses = {}'],
355 569
356 js['var hasOwnProperty = Object.prototype.hasOwnProperty'], 570 js['var hasOwnProperty = Object.prototype.hasOwnProperty'],
357 571
358 // for (var cls in collectedClasses) { 572 // for (var cls in collectedClasses) {
359 js.forIn('cls', 'collectedClasses', [ 573 js.forIn('cls', 'collectedClasses', [
360 // if (hasOwnProperty.call(collectedClasses, cls)) { 574 // if (hasOwnProperty.call(collectedClasses, cls)) {
361 js.if_(js['hasOwnProperty.call(collectedClasses, cls)'], [ 575 js.if_('hasOwnProperty.call(collectedClasses, cls)', [
362 js['var desc = collectedClasses[cls]'], 576 js['var desc = collectedClasses[cls]'],
363 577
364 /* The 'fields' are either a constructor function or a 578 /* The 'fields' are either a constructor function or a
365 * string encoding fields, constructor and superclass. Get 579 * string encoding fields, constructor and superclass. Get
366 * the superclass and the fields in the format 580 * the superclass and the fields in the format
367 * Super;field1,field2 from the null-string property on the 581 * Super;field1,field2 from the null-string property on the
368 * descriptor. 582 * descriptor.
369 */ 583 */
370 // var fields = desc[""], supr; 584 // var fields = desc[""], supr;
371 js['var fields = desc[""], supr'], 585 js['var fields = desc[""], supr'],
372 586
373 js.if_(js['typeof fields == "string"'], [ 587 js.if_('typeof fields == "string"', [
374 js['var s = fields.split(";")'], 588 js['var s = fields.split(";")'],
375 js['supr = s[0]'], 589 js['supr = s[0]'],
376 js['fields = s[1] == "" ? [] : s[1].split(",")'], 590 js['fields = s[1] == "" ? [] : s[1].split(",")'],
377 ], /* else */ [ 591 ], /* else */ [
378 js['supr = desc.super'] 592 js['supr = desc.super']
379 ]), 593 ]),
380 594
381 js['isolateProperties[cls] = defineClass(cls, fields, desc)'], 595 js['isolateProperties[cls] = defineClass(cls, fields, desc)'],
382 596
383 // if (supr) pendingClasses[cls] = supr; 597 // if (supr) pendingClasses[cls] = supr;
384 js.if_(js['supr'], js['pendingClasses[cls] = supr']) 598 js.if_('supr', js['pendingClasses[cls] = supr'])
385 ]) 599 ])
386 ]), 600 ]),
387 601
388 js['var finishedClasses = {}'], 602 js['var finishedClasses = {}'],
389 603
390 // function finishClass(cls) { ... } 604 // function finishClass(cls) { ... }
391 buildFinishClass(), 605 buildFinishClass(),
606 ];
392 607
608 addTrivialNsmHandlers(statements);
609
610 statements.add(
393 // for (var cls in pendingClasses) finishClass(cls); 611 // for (var cls in pendingClasses) finishClass(cls);
394 js.forIn('cls', 'pendingClasses', js['finishClass']('cls')) 612 js.forIn('cls', 'pendingClasses', js['finishClass(cls)'])
395 ]); 613 );
614 // function(collectedClasses,
615 // isolateProperties,
616 // existingIsolateProperties) {
617 return js.fun(['collectedClasses', 'isolateProperties',
618 'existingIsolateProperties'], statements);
396 } 619 }
397 620
398 jsAst.FunctionDeclaration buildFinishClass() { 621 jsAst.FunctionDeclaration buildFinishClass() {
399 // function finishClass(cls) { 622 // function finishClass(cls) {
400 jsAst.Fun fun = js.fun(['cls'], [ 623 jsAst.Fun fun = js.fun(['cls'], [
401 624
402 // TODO(8540): Remove this work around. 625 // TODO(8540): Remove this work around.
403 /* Opera does not support 'getOwnPropertyNames'. Therefore we use 626 /* Opera does not support 'getOwnPropertyNames'. Therefore we use
404 hasOwnProperty instead. */ 627 hasOwnProperty instead. */
405 js['var hasOwnProperty = Object.prototype.hasOwnProperty'], 628 js['var hasOwnProperty = Object.prototype.hasOwnProperty'],
406 629
407 // if (hasOwnProperty.call(finishedClasses, cls)) return; 630 // if (hasOwnProperty.call(finishedClasses, cls)) return;
408 js.if_(js['hasOwnProperty.call(finishedClasses, cls)'], 631 js.if_('hasOwnProperty.call(finishedClasses, cls)',
409 js.return_()), 632 js.return_()),
410 633
411 js['finishedClasses[cls] = true'], 634 js['finishedClasses[cls] = true'],
635
412 js['var superclass = pendingClasses[cls]'], 636 js['var superclass = pendingClasses[cls]'],
413 637
414 /* The superclass is only false (empty string) for Dart's Object class. */ 638 // The superclass is only false (empty string) for Dart's Object class.
415 js.if_(js['!superclass'], js.return_()), 639 // The minifier together with noSuchMethod can put methods on the
640 // Object.prototype object, and they show through here, so we check that
641 // we have a string.
642 js.if_('!superclass || typeof superclass != "string"', js.return_()),
416 js['finishClass(superclass)'], 643 js['finishClass(superclass)'],
417 js['var constructor = isolateProperties[cls]'], 644 js['var constructor = isolateProperties[cls]'],
418 js['var superConstructor = isolateProperties[superclass]'], 645 js['var superConstructor = isolateProperties[superclass]'],
419 646
420 // if (!superConstructor) 647 // if (!superConstructor)
421 // superConstructor = existingIsolateProperties[superclass]; 648 // superConstructor = existingIsolateProperties[superclass];
422 js.if_(js['superConstructor'].not, 649 js.if_(js['superConstructor'].not,
423 js['superConstructor'].assign( 650 js['superConstructor'].assign(
424 js['existingIsolateProperties'][js['superclass']])), 651 js['existingIsolateProperties'][js['superclass']])),
425 652
(...skipping 13 matching lines...) Expand all
439 js['tmp.prototype = superConstructor.prototype'], 666 js['tmp.prototype = superConstructor.prototype'],
440 js['var newPrototype = new tmp()'], 667 js['var newPrototype = new tmp()'],
441 668
442 js['constructor.prototype = newPrototype'], 669 js['constructor.prototype = newPrototype'],
443 js['newPrototype.constructor = constructor'], 670 js['newPrototype.constructor = constructor'],
444 671
445 // for (var member in prototype) { 672 // for (var member in prototype) {
446 js.forIn('member', 'prototype', [ 673 js.forIn('member', 'prototype', [
447 /* Short version of: if (member == '') */ 674 /* Short version of: if (member == '') */
448 // if (!member) continue; 675 // if (!member) continue;
449 js.if_(js['!member'], new jsAst.Continue(null)), 676 js.if_('!member', new jsAst.Continue(null)),
450 677
451 // if (hasOwnProperty.call(prototype, member)) { 678 // if (hasOwnProperty.call(prototype, member)) {
452 js.if_(js['hasOwnProperty.call(prototype, member)'], [ 679 js.if_('hasOwnProperty.call(prototype, member)', [
453 js['newPrototype[member] = prototype[member]'] 680 js['newPrototype[member] = prototype[member]']
454 ]) 681 ])
455 ]) 682 ])
456 683
457 ]) 684 ])
458 ]); 685 ]);
459 686
460 return new jsAst.FunctionDeclaration( 687 return new jsAst.FunctionDeclaration(
461 new jsAst.VariableDeclaration('finishClass'), 688 new jsAst.VariableDeclaration('finishClass'),
462 fun); 689 fun);
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
504 'null'], 731 'null'],
505 732
506 js['var isolatePrototype = oldIsolate.prototype'], 733 js['var isolatePrototype = oldIsolate.prototype'],
507 js['var str = "{\\n"'], 734 js['var str = "{\\n"'],
508 js['str += ' 735 js['str += '
509 '"var properties = $isolate.${namer.isolatePropertiesName};\\n"'], 736 '"var properties = $isolate.${namer.isolatePropertiesName};\\n"'],
510 js['var hasOwnProperty = Object.prototype.hasOwnProperty'], 737 js['var hasOwnProperty = Object.prototype.hasOwnProperty'],
511 738
512 // for (var staticName in isolateProperties) { 739 // for (var staticName in isolateProperties) {
513 js.forIn('staticName', 'isolateProperties', [ 740 js.forIn('staticName', 'isolateProperties', [
514 js.if_(js['hasOwnProperty.call(isolateProperties, staticName)'], [ 741 js.if_('hasOwnProperty.call(isolateProperties, staticName)', [
515 js['str += ("this." + staticName + "= properties." + staticName + ' 742 js['str += ("this." + staticName + "= properties." + staticName + '
516 '";\\n")'] 743 '";\\n")']
517 ]) 744 ])
518 ]), 745 ]),
519 746
520 js['str += "}\\n"'], 747 js['str += "}\\n"'],
521 748
522 js['var newIsolate = new Function(str)'], 749 js['var newIsolate = new Function(str)'],
523 js['newIsolate.prototype = isolatePrototype'], 750 js['newIsolate.prototype = isolatePrototype'],
524 js['isolatePrototype.constructor = newIsolate'], 751 js['isolatePrototype.constructor = newIsolate'],
(...skipping 26 matching lines...) Expand all
551 js['var sentinelUndefined = {}'], 778 js['var sentinelUndefined = {}'],
552 js['var sentinelInProgress = {}'], 779 js['var sentinelInProgress = {}'],
553 js['prototype[fieldName] = sentinelUndefined'], 780 js['prototype[fieldName] = sentinelUndefined'],
554 781
555 // prototype[getterName] = function() { 782 // prototype[getterName] = function() {
556 js['prototype'][js['getterName']].assign(js.fun([], [ 783 js['prototype'][js['getterName']].assign(js.fun([], [
557 js['var result = $isolate[fieldName]'], 784 js['var result = $isolate[fieldName]'],
558 785
559 // try { 786 // try {
560 js.try_([ 787 js.try_([
561 js.if_(js['result === sentinelUndefined'], [ 788 js.if_('result === sentinelUndefined', [
562 js['$isolate[fieldName] = sentinelInProgress'], 789 js['$isolate[fieldName] = sentinelInProgress'],
563 790
564 // try { 791 // try {
565 js.try_([ 792 js.try_([
566 js['result = $isolate[fieldName] = lazyValue()'], 793 js['result = $isolate[fieldName] = lazyValue()'],
567 794
568 ], finallyPart: [ 795 ], finallyPart: [
569 // Use try-finally, not try-catch/throw as it destroys the 796 // Use try-finally, not try-catch/throw as it destroys the
570 // stack trace. 797 // stack trace.
571 798
572 // if (result === sentinelUndefined) { 799 // if (result === sentinelUndefined) {
573 js.if_(js['result === sentinelUndefined'], [ 800 js.if_('result === sentinelUndefined', [
574 // if ($isolate[fieldName] === sentinelInProgress) { 801 // if ($isolate[fieldName] === sentinelInProgress) {
575 js.if_(js['$isolate[fieldName] === sentinelInProgress'], [ 802 js.if_('$isolate[fieldName] === sentinelInProgress', [
576 js['$isolate[fieldName] = null'], 803 js['$isolate[fieldName] = null'],
577 ]) 804 ])
578 ]) 805 ])
579 ]) 806 ])
580 ], /* else */ [ 807 ], /* else */ [
581 js.if_(js['result === sentinelInProgress'], 808 js.if_('result === sentinelInProgress',
582 js['$cyclicThrow(staticName)'] 809 js['$cyclicThrow(staticName)']
583 ) 810 )
584 ]), 811 ]),
585 812
586 // return result; 813 // return result;
587 js.return_('result') 814 js.return_('result')
588 815
589 ], finallyPart: [ 816 ], finallyPart: [
590 js['$isolate[getterName] = getter'] 817 js['$isolate[getterName] = getter']
591 ]) 818 ])
(...skipping 557 matching lines...) Expand 10 before | Expand all | Expand 10 after
1149 visitField, 1376 visitField,
1150 includeBackendMembers: true, 1377 includeBackendMembers: true,
1151 includeSuperMembers: isInstantiated && !classElement.isNative()); 1378 includeSuperMembers: isInstantiated && !classElement.isNative());
1152 } 1379 }
1153 1380
1154 void generateGetter(Element member, String fieldName, String accessorName, 1381 void generateGetter(Element member, String fieldName, String accessorName,
1155 ClassBuilder builder) { 1382 ClassBuilder builder) {
1156 assert(!backend.isInterceptorClass(member)); 1383 assert(!backend.isInterceptorClass(member));
1157 String getterName = namer.getterNameFromAccessorName(accessorName); 1384 String getterName = namer.getterNameFromAccessorName(accessorName);
1158 builder.addProperty(getterName, 1385 builder.addProperty(getterName,
1159 js.fun([], js.return_(js['this'][fieldName]))); 1386 js.fun([], js.return_(js['this.$fieldName'])));
1160 } 1387 }
1161 1388
1162 void generateSetter(Element member, String fieldName, String accessorName, 1389 void generateSetter(Element member, String fieldName, String accessorName,
1163 ClassBuilder builder) { 1390 ClassBuilder builder) {
1164 assert(!backend.isInterceptorClass(member)); 1391 assert(!backend.isInterceptorClass(member));
1165 String setterName = namer.setterNameFromAccessorName(accessorName); 1392 String setterName = namer.setterNameFromAccessorName(accessorName);
1166 List<String> args = backend.isInterceptedMethod(member) 1393 List<String> args = backend.isInterceptedMethod(member)
1167 ? ['receiver', 'v'] 1394 ? ['receiver', 'v']
1168 : ['v']; 1395 : ['v'];
1169 builder.addProperty(setterName, 1396 builder.addProperty(setterName,
(...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after
1339 emitInstanceMembers(classElement, builder); 1566 emitInstanceMembers(classElement, builder);
1340 1567
1341 jsAst.Expression init = 1568 jsAst.Expression init =
1342 js[classesCollector][className].assign(builder.toObjectInitializer()); 1569 js[classesCollector][className].assign(builder.toObjectInitializer());
1343 buffer.write(jsAst.prettyPrint(init, compiler)); 1570 buffer.write(jsAst.prettyPrint(init, compiler));
1344 buffer.write('$N$n'); 1571 buffer.write('$N$n');
1345 } 1572 }
1346 1573
1347 bool get getterAndSetterCanBeImplementedByFieldSpec => true; 1574 bool get getterAndSetterCanBeImplementedByFieldSpec => true;
1348 1575
1576 /// If this is true then we can generate the noSuchMethod handlers at startup
1577 /// time, instead of them being emitted as part of the Object class.
1578 bool get generateTrivialNsmHandlers => true;
1579
1349 int _selectorRank(Selector selector) { 1580 int _selectorRank(Selector selector) {
1350 int arity = selector.argumentCount * 3; 1581 int arity = selector.argumentCount * 3;
1351 if (selector.isGetter()) return arity + 2; 1582 if (selector.isGetter()) return arity + 2;
1352 if (selector.isSetter()) return arity + 1; 1583 if (selector.isSetter()) return arity + 1;
1353 return arity; 1584 return arity;
1354 } 1585 }
1355 1586
1356 int _compareSelectorNames(Selector selector1, Selector selector2) { 1587 int _compareSelectorNames(Selector selector1, Selector selector2) {
1357 String name1 = selector1.name.toString(); 1588 String name1 = selector1.name.toString();
1358 String name2 = selector2.name.toString(); 1589 String name2 = selector2.name.toString();
(...skipping 601 matching lines...) Expand 10 before | Expand all | Expand 10 after
1960 if (selectors != null && !selectors.isEmpty) { 2191 if (selectors != null && !selectors.isEmpty) {
1961 emitCallStubForGetter(member, selectors, builder.addProperty); 2192 emitCallStubForGetter(member, selectors, builder.addProperty);
1962 } 2193 }
1963 } else if (member.isFunction()) { 2194 } else if (member.isFunction()) {
1964 if (compiler.codegenWorld.hasInvokedGetter(member, compiler)) { 2195 if (compiler.codegenWorld.hasInvokedGetter(member, compiler)) {
1965 emitDynamicFunctionGetter(member, builder.addProperty); 2196 emitDynamicFunctionGetter(member, builder.addProperty);
1966 } 2197 }
1967 } 2198 }
1968 } 2199 }
1969 2200
2201 // Identify the noSuchMethod handlers that are so simple that we can
2202 // generate them programatically.
2203 bool isTrivialNsmHandler(
2204 int type, List argNames, Selector selector, String internalName) {
2205 if (!generateTrivialNsmHandlers) return false;
2206 // Check for wrong calling convention.
ngeoffray 2013/03/13 15:03:27 wrong :-)? maybe change it to "interceptor"?
erikcorry 2013/03/14 10:06:57 Done.
2207 if (backend.isInterceptedName(selector.name)) {
2208 // We can handle the strange calling convention used by intercepted names
ngeoffray 2013/03/13 15:03:27 strange -> interceptor
erikcorry 2013/03/14 10:06:57 Removed word 'strange'.
2209 // in the diff encoding, but we don't use that for non-minified code.
2210 if (!compiler.enableMinification) return false;
ngeoffray 2013/03/13 15:03:27 Isn't this code only used by the minifier?
erikcorry 2013/03/14 10:06:57 no
2211 String shortName = namer.invocationMirrorInternalName(selector);
2212 if (shortName.length > MAX_MINIFIED_LENGTH_FOR_DIFF_ENCODING) {
2213 return false;
2214 }
2215 }
2216 // Check for named arguments.
2217 if (argNames.length != 0) return false;
2218 // Check for unexpected name (this doesn't really happen).
2219 if (internalName.startsWith(namer.getterPrefix[0])) return type == 1;
2220 if (internalName.startsWith(namer.setterPrefix[0])) return type == 2;
2221 return type == 0;
2222 }
2223
1970 void emitNoSuchMethodHandlers(DefineStubFunction defineStub) { 2224 void emitNoSuchMethodHandlers(DefineStubFunction defineStub) {
1971 // Do not generate no such method handlers if there is no class. 2225 // Do not generate no such method handlers if there is no class.
1972 if (compiler.codegenWorld.instantiatedClasses.isEmpty) return; 2226 if (compiler.codegenWorld.instantiatedClasses.isEmpty) return;
1973 2227
1974 String noSuchMethodName = namer.publicInstanceMethodNameByArity( 2228 String noSuchMethodName = namer.publicInstanceMethodNameByArity(
1975 Compiler.NO_SUCH_METHOD, Compiler.NO_SUCH_METHOD_ARG_COUNT); 2229 Compiler.NO_SUCH_METHOD, Compiler.NO_SUCH_METHOD_ARG_COUNT);
1976 2230
1977 Element createInvocationMirrorElement = 2231 Element createInvocationMirrorElement =
1978 compiler.findHelper(const SourceString("createInvocationMirror")); 2232 compiler.findHelper(const SourceString("createInvocationMirror"));
1979 String createInvocationMirrorName = 2233 String createInvocationMirrorName =
1980 namer.getName(createInvocationMirrorElement); 2234 namer.getName(createInvocationMirrorElement);
1981 2235
1982 // Keep track of the JavaScript names we've already added so we 2236 // Keep track of the JavaScript names we've already added so we
1983 // do not introduce duplicates (bad for code size). 2237 // do not introduce duplicates (bad for code size).
1984 Set<String> addedJsNames = new Set<String>(); 2238 Map<String, Selector> addedJsNames = new Map<String, Selector>();
1985
1986 jsAst.Expression generateMethod(String jsName, Selector selector) {
1987 // Values match JSInvocationMirror in js-helper library.
1988 int type = selector.invocationMirrorKind;
1989 String methodName = selector.invocationMirrorMemberName;
1990 List<jsAst.Parameter> parameters = <jsAst.Parameter>[];
1991 CodeBuffer args = new CodeBuffer();
1992 for (int i = 0; i < selector.argumentCount; i++) {
1993 parameters.add(new jsAst.Parameter('\$$i'));
1994 }
1995
1996 List<jsAst.Expression> argNames =
1997 selector.getOrderedNamedArguments().map((SourceString name) =>
1998 js.string(name.slowToString())).toList();
1999
2000 String internalName = namer.invocationMirrorInternalName(selector);
2001
2002 String createInvocationMirror = namer.getName(
2003 compiler.createInvocationMirrorElement);
2004
2005 assert(backend.isInterceptedName(Compiler.NO_SUCH_METHOD));
2006 jsAst.Expression expression = js['this.$noSuchMethodName'](
2007 [js['this'],
2008 js[namer.CURRENT_ISOLATE][createInvocationMirror]([
2009 js.string(methodName),
2010 js.string(internalName),
2011 type,
2012 new jsAst.ArrayInitializer.from(
2013 parameters.map((param) => js[param.name]).toList()),
2014 new jsAst.ArrayInitializer.from(argNames)])]);
2015 parameters = backend.isInterceptedName(selector.name)
2016 ? ([new jsAst.Parameter('\$receiver')]..addAll(parameters))
2017 : parameters;
2018 return js.fun(parameters, js.return_(expression));
2019 }
2020 2239
2021 void addNoSuchMethodHandlers(SourceString ignore, Set<Selector> selectors) { 2240 void addNoSuchMethodHandlers(SourceString ignore, Set<Selector> selectors) {
2022 // Cache the object class and type. 2241 // Cache the object class and type.
2023 ClassElement objectClass = compiler.objectClass; 2242 ClassElement objectClass = compiler.objectClass;
2024 DartType objectType = objectClass.computeType(compiler); 2243 DartType objectType = objectClass.computeType(compiler);
2025 2244
2026 for (Selector selector in selectors) { 2245 for (Selector selector in selectors) {
2027 // Introduce a helper function that determines if the given 2246 // Introduce a helper function that determines if the given
2028 // class has a member that matches the current name and 2247 // class has a member that matches the current name and
2029 // selector (grabbed from the scope). 2248 // selector (grabbed from the scope).
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
2088 // the handler either because all objects of type D implement 2307 // the handler either because all objects of type D implement
2089 // bar through inheritance. 2308 // bar through inheritance.
2090 // 2309 //
2091 // If we're calling bar on an object of type A we do need the 2310 // If we're calling bar on an object of type A we do need the
2092 // handler because we may have to call B.noSuchMethod since B 2311 // handler because we may have to call B.noSuchMethod since B
2093 // does not implement bar. 2312 // does not implement bar.
2094 Iterable<ClassElement> holders = 2313 Iterable<ClassElement> holders =
2095 compiler.world.locateNoSuchMethodHolders(selector); 2314 compiler.world.locateNoSuchMethodHolders(selector);
2096 if (holders.every(hasMatchingMember)) continue; 2315 if (holders.every(hasMatchingMember)) continue;
2097 String jsName = namer.invocationMirrorInternalName(selector); 2316 String jsName = namer.invocationMirrorInternalName(selector);
2098 if (!addedJsNames.contains(jsName)) { 2317 addedJsNames[jsName] = selector;
2099 jsAst.Expression method = generateMethod(jsName, selector);
2100 defineStub(jsName, method);
2101 addedJsNames.add(jsName);
2102 }
2103 } 2318 }
2104 } 2319 }
2105 2320
2106 compiler.codegenWorld.invokedNames.forEach(addNoSuchMethodHandlers); 2321 compiler.codegenWorld.invokedNames.forEach(addNoSuchMethodHandlers);
2107 compiler.codegenWorld.invokedGetters.forEach(addNoSuchMethodHandlers); 2322 compiler.codegenWorld.invokedGetters.forEach(addNoSuchMethodHandlers);
2108 compiler.codegenWorld.invokedSetters.forEach(addNoSuchMethodHandlers); 2323 compiler.codegenWorld.invokedSetters.forEach(addNoSuchMethodHandlers);
2324
2325 // Set flag used by generateMethod helper below. If we have very few
2326 // handlers we use defineStub for them all, rather than try to generate them
2327 // at runtime.
2328 bool haveVeryFewNoSuchMemberHandlers =
2329 (addedJsNames.length < VERY_FEW_NO_SUCH_METHOD_HANDLERS);
2330
2331 jsAst.Expression generateMethod(String jsName, Selector selector) {
2332 // Values match JSInvocationMirror in js-helper library.
2333 int type = selector.invocationMirrorKind;
2334 List<jsAst.Parameter> parameters = <jsAst.Parameter>[];
2335 CodeBuffer args = new CodeBuffer();
2336 for (int i = 0; i < selector.argumentCount; i++) {
2337 parameters.add(new jsAst.Parameter('\$$i'));
2338 }
2339
2340 List<jsAst.Expression> argNames =
2341 selector.getOrderedNamedArguments().map((SourceString name) =>
2342 js.string(name.slowToString())).toList();
2343
2344 String methodName = selector.invocationMirrorMemberName;
2345 String internalName = namer.invocationMirrorInternalName(selector);
2346 if (!haveVeryFewNoSuchMemberHandlers &&
2347 isTrivialNsmHandler(type, argNames, selector, internalName)) {
2348 trivialNsmHandlers.add(selector);
2349 return null;
2350 }
2351
2352 String createInvocationMirror = namer.getName(
2353 compiler.createInvocationMirrorElement);
2354
2355 assert(backend.isInterceptedName(Compiler.NO_SUCH_METHOD));
2356 jsAst.Expression expression = js['this.$noSuchMethodName'](
2357 [js['this'],
2358 js[namer.CURRENT_ISOLATE][createInvocationMirror]([
2359 js.string(compiler.enableMinification ?
2360 internalName : methodName),
2361 js.string(internalName),
2362 type,
2363 new jsAst.ArrayInitializer.from(
2364 parameters.map((param) => js[param.name]).toList()),
2365 new jsAst.ArrayInitializer.from(argNames)])]);
2366 parameters = backend.isInterceptedName(selector.name)
2367 ? ([new jsAst.Parameter('\$receiver')]..addAll(parameters))
2368 : parameters;
2369 return js.fun(parameters, js.return_(expression));
2370 }
2371
2372 for (String jsName in addedJsNames.keys.toList()..sort()) {
2373 Selector selector = addedJsNames[jsName];
2374 jsAst.Expression method = generateMethod(jsName, selector);
2375 if (method != null) defineStub(jsName, method);
2376 }
2109 } 2377 }
2110 2378
2111 String buildIsolateSetup(CodeBuffer buffer, 2379 String buildIsolateSetup(CodeBuffer buffer,
2112 Element appMain, 2380 Element appMain,
2113 Element isolateMain) { 2381 Element isolateMain) {
2114 String mainAccess = "${namer.isolateAccess(appMain)}"; 2382 String mainAccess = "${namer.isolateAccess(appMain)}";
2115 String currentIsolate = "${namer.CURRENT_ISOLATE}"; 2383 String currentIsolate = "${namer.CURRENT_ISOLATE}";
2116 // Since we pass the closurized version of the main method to 2384 // Since we pass the closurized version of the main method to
2117 // the isolate method, we must make sure that it exists. 2385 // the isolate method, we must make sure that it exists.
2118 if (!compiler.codegenWorld.staticFunctionsNeedingGetter.contains(appMain)) { 2386 if (!compiler.codegenWorld.staticFunctionsNeedingGetter.contains(appMain)) {
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
2157 addComment('END invoke [main].', buffer); 2425 addComment('END invoke [main].', buffer);
2158 } 2426 }
2159 2427
2160 void emitGetInterceptorMethod(CodeBuffer buffer, 2428 void emitGetInterceptorMethod(CodeBuffer buffer,
2161 String key, 2429 String key,
2162 Collection<ClassElement> classes) { 2430 Collection<ClassElement> classes) {
2163 jsAst.Statement buildReturnInterceptor(ClassElement cls) { 2431 jsAst.Statement buildReturnInterceptor(ClassElement cls) {
2164 return js.return_(js[namer.isolateAccess(cls)]['prototype']); 2432 return js.return_(js[namer.isolateAccess(cls)]['prototype']);
2165 } 2433 }
2166 2434
2167 jsAst.VariableUse receiver = js['receiver'];
2168 /** 2435 /**
2169 * Build a JavaScrit AST node for doing a type check on 2436 * Build a JavaScrit AST node for doing a type check on
2170 * [cls]. [cls] must be an interceptor class. 2437 * [cls]. [cls] must be an interceptor class.
2171 */ 2438 */
2172 jsAst.Statement buildInterceptorCheck(ClassElement cls) { 2439 jsAst.Statement buildInterceptorCheck(ClassElement cls) {
2173 jsAst.Expression condition; 2440 jsAst.Expression condition;
2174 assert(backend.isInterceptorClass(cls)); 2441 assert(backend.isInterceptorClass(cls));
2175 if (cls == backend.jsBoolClass) { 2442 if (cls == backend.jsBoolClass) {
2176 condition = receiver.typeof.equals(js.string('boolean')); 2443 condition = js['(typeof receiver) == "boolean"'];
2177 } else if (cls == backend.jsIntClass || 2444 } else if (cls == backend.jsIntClass ||
2178 cls == backend.jsDoubleClass || 2445 cls == backend.jsDoubleClass ||
2179 cls == backend.jsNumberClass) { 2446 cls == backend.jsNumberClass) {
2180 throw 'internal error'; 2447 throw 'internal error';
2181 } else if (cls == backend.jsArrayClass || 2448 } else if (cls == backend.jsArrayClass ||
2182 cls == backend.jsMutableArrayClass || 2449 cls == backend.jsMutableArrayClass ||
2183 cls == backend.jsFixedArrayClass || 2450 cls == backend.jsFixedArrayClass ||
2184 cls == backend.jsExtendableArrayClass) { 2451 cls == backend.jsExtendableArrayClass) {
2185 condition = receiver['constructor'].equals('Array'); 2452 condition = js['receiver.constructor == Array'];
2186 } else if (cls == backend.jsStringClass) { 2453 } else if (cls == backend.jsStringClass) {
2187 condition = receiver.typeof.equals(js.string('string')); 2454 condition = js['(typeof receiver) == "string"'];
2188 } else if (cls == backend.jsNullClass) { 2455 } else if (cls == backend.jsNullClass) {
2189 condition = receiver.equals(new jsAst.LiteralNull()); 2456 condition = js['receiver == null'];
2190 } else if (cls == backend.jsFunctionClass) { 2457 } else if (cls == backend.jsFunctionClass) {
2191 condition = receiver.typeof.equals(js.string('function')); 2458 condition = js['(typeof receiver) == "function"'];
2192 } else { 2459 } else {
2193 throw 'internal error'; 2460 throw 'internal error';
2194 } 2461 }
2195 return js.if_(condition, buildReturnInterceptor(cls)); 2462 return js.if_(condition, buildReturnInterceptor(cls));
2196 } 2463 }
2197 2464
2198 bool hasArray = false; 2465 bool hasArray = false;
2199 bool hasBool = false; 2466 bool hasBool = false;
2200 bool hasDouble = false; 2467 bool hasDouble = false;
2201 bool hasFunction = false; 2468 bool hasFunction = false;
(...skipping 28 matching lines...) Expand all
2230 jsAst.Statement whenNumber; 2497 jsAst.Statement whenNumber;
2231 2498
2232 /// Note: there are two number classes in play: Dart's [num], 2499 /// Note: there are two number classes in play: Dart's [num],
2233 /// and JavaScript's Number (typeof receiver == 'number'). This 2500 /// and JavaScript's Number (typeof receiver == 'number'). This
2234 /// is the fallback used when we have determined that receiver 2501 /// is the fallback used when we have determined that receiver
2235 /// is a JavaScript Number. 2502 /// is a JavaScript Number.
2236 jsAst.Return returnNumberClass = buildReturnInterceptor( 2503 jsAst.Return returnNumberClass = buildReturnInterceptor(
2237 hasDouble ? backend.jsDoubleClass : backend.jsNumberClass); 2504 hasDouble ? backend.jsDoubleClass : backend.jsNumberClass);
2238 2505
2239 if (hasInt) { 2506 if (hasInt) {
2240 jsAst.Expression isInt = js['Math']['floor'](receiver).equals(receiver); 2507 jsAst.Expression isInt = js['Math.floor(receiver) == receiver'];
2241 whenNumber = js.block([ 2508 whenNumber = js.block([
2242 js.if_(isInt, buildReturnInterceptor(backend.jsIntClass)), 2509 js.if_(isInt, buildReturnInterceptor(backend.jsIntClass)),
2243 returnNumberClass]); 2510 returnNumberClass]);
2244 } else { 2511 } else {
2245 whenNumber = returnNumberClass; 2512 whenNumber = returnNumberClass;
2246 } 2513 }
2247 block.statements.add( 2514 block.statements.add(
2248 js.if_(receiver.typeof.equals(js.string('number')), 2515 js.if_('(typeof receiver) == "number"',
2249 whenNumber)); 2516 whenNumber));
2250 } 2517 }
2251 2518
2252 if (hasString) { 2519 if (hasString) {
2253 block.statements.add(buildInterceptorCheck(backend.jsStringClass)); 2520 block.statements.add(buildInterceptorCheck(backend.jsStringClass));
2254 } 2521 }
2255 if (hasNull) { 2522 if (hasNull) {
2256 block.statements.add(buildInterceptorCheck(backend.jsNullClass)); 2523 block.statements.add(buildInterceptorCheck(backend.jsNullClass));
2257 } else { 2524 } else {
2258 // Returning "undefined" here will provoke a JavaScript 2525 // Returning "undefined" here will provoke a JavaScript
2259 // TypeError which is later identified as a null-error by 2526 // TypeError which is later identified as a null-error by
2260 // [unwrapException] in js_helper.dart. 2527 // [unwrapException] in js_helper.dart.
2261 block.statements.add(js.if_(receiver.equals(new jsAst.LiteralNull()), 2528 block.statements.add(js.if_('receiver == null',
2262 js.return_(js.undefined()))); 2529 js.return_(js.undefined())));
2263 } 2530 }
2264 if (hasFunction) { 2531 if (hasFunction) {
2265 block.statements.add(buildInterceptorCheck(backend.jsFunctionClass)); 2532 block.statements.add(buildInterceptorCheck(backend.jsFunctionClass));
2266 } 2533 }
2267 if (hasBool) { 2534 if (hasBool) {
2268 block.statements.add(buildInterceptorCheck(backend.jsBoolClass)); 2535 block.statements.add(buildInterceptorCheck(backend.jsBoolClass));
2269 } 2536 }
2270 // TODO(ahe): It might be faster to check for Array before 2537 // TODO(ahe): It might be faster to check for Array before
2271 // function and bool. 2538 // function and bool.
2272 if (hasArray) { 2539 if (hasArray) {
2273 block.statements.add(buildInterceptorCheck(backend.jsArrayClass)); 2540 block.statements.add(buildInterceptorCheck(backend.jsArrayClass));
2274 } 2541 }
2275 block.statements.add(js.return_(receiver)); 2542 block.statements.add(js.return_(js['receiver']));
2276 2543
2277 buffer.write(jsAst.prettyPrint( 2544 buffer.write(jsAst.prettyPrint(
2278 js[isolateProperties][key].assign(js.fun(['receiver'], block)), 2545 js[isolateProperties][key].assign(js.fun(['receiver'], block)),
2279 compiler)); 2546 compiler));
2280 buffer.write(N); 2547 buffer.write(N);
2281 } 2548 }
2282 2549
2283 /** 2550 /**
2284 * Emit all versions of the [:getInterceptor:] method. 2551 * Emit all versions of the [:getInterceptor:] method.
2285 */ 2552 */
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after
2375 if (selector.isOperator()) { 2642 if (selector.isOperator()) {
2376 String name = selector.name.stringValue; 2643 String name = selector.name.stringValue;
2377 if (name == '==') { 2644 if (name == '==') {
2378 // Unfolds to: 2645 // Unfolds to:
2379 // [: if (receiver == null) return a0 == null; 2646 // [: if (receiver == null) return a0 == null;
2380 // if (typeof receiver != 'object') { 2647 // if (typeof receiver != 'object') {
2381 // return a0 != null && receiver === a0; 2648 // return a0 != null && receiver === a0;
2382 // } 2649 // }
2383 // :]. 2650 // :].
2384 List<jsAst.Statement> body = <jsAst.Statement>[]; 2651 List<jsAst.Statement> body = <jsAst.Statement>[];
2385 body.add(js.if_(js['receiver'].equals(new jsAst.LiteralNull()), 2652 body.add(js.if_('receiver == null',
2386 js.return_(js['a0'].equals(new jsAst.LiteralNull())))); 2653 js.return_(js['a0'].equals(new jsAst.LiteralNull()))));
2387 body.add(js.if_( 2654 body.add(js.if_(
2388 isNotObject('receiver'), 2655 isNotObject('receiver'),
2389 js.return_(js['a0'].equals(new jsAst.LiteralNull()).not.binary( 2656 js.return_(js['a0'].equals(new jsAst.LiteralNull()).not.binary(
2390 '&&', js['receiver'].strictEquals(js['a0']))))); 2657 '&&', js['receiver'].strictEquals(js['a0'])))));
2391 return new jsAst.Block(body); 2658 return new jsAst.Block(body);
2392 } 2659 }
2393 if (!classes.contains(backend.jsIntClass) 2660 if (!classes.contains(backend.jsIntClass)
2394 && !classes.contains(backend.jsNumberClass) 2661 && !classes.contains(backend.jsNumberClass)
2395 && !classes.contains(backend.jsDoubleClass)) { 2662 && !classes.contains(backend.jsDoubleClass)) {
(...skipping 366 matching lines...) Expand 10 before | Expand all | Expand 10 after
2762 """; 3029 """;
2763 const String HOOKS_API_USAGE = """ 3030 const String HOOKS_API_USAGE = """
2764 // The code supports the following hooks: 3031 // The code supports the following hooks:
2765 // dartPrint(message) - if this function is defined it is called 3032 // dartPrint(message) - if this function is defined it is called
2766 // instead of the Dart [print] method. 3033 // instead of the Dart [print] method.
2767 // dartMainRunner(main) - if this function is defined, the Dart [main] 3034 // dartMainRunner(main) - if this function is defined, the Dart [main]
2768 // method will not be invoked directly. 3035 // method will not be invoked directly.
2769 // Instead, a closure that will invoke [main] is 3036 // Instead, a closure that will invoke [main] is
2770 // passed to [dartMainRunner]. 3037 // passed to [dartMainRunner].
2771 """; 3038 """;
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698