Chromium Code Reviews| 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 |