OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 """; |
OLD | NEW |