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 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 """; |
OLD | NEW |