| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 import '../common.dart'; | 5 import '../common.dart'; |
| 6 import '../common/backend_api.dart' show ForeignResolver; | 6 import '../common/backend_api.dart' show ForeignResolver; |
| 7 import '../common/resolution.dart' show ParsingContext, Resolution; | 7 import '../common/resolution.dart' show ParsingContext, Resolution; |
| 8 import '../compiler.dart' show Compiler; | 8 import '../compiler.dart' show Compiler; |
| 9 import '../constants/expressions.dart'; |
| 9 import '../constants/values.dart'; | 10 import '../constants/values.dart'; |
| 10 import '../core_types.dart' show CoreTypes; | 11 import '../core_types.dart' show CoreTypes; |
| 11 import '../dart_types.dart'; | 12 import '../dart_types.dart'; |
| 12 import '../elements/elements.dart'; | 13 import '../elements/elements.dart'; |
| 13 import '../js/js.dart' as js; | 14 import '../js/js.dart' as js; |
| 14 import '../js_backend/js_backend.dart'; | 15 import '../js_backend/js_backend.dart'; |
| 15 import '../tree/tree.dart'; | 16 import '../tree/tree.dart'; |
| 16 import '../universe/side_effects.dart' show SideEffects; | 17 import '../universe/side_effects.dart' show SideEffects; |
| 17 import '../util/util.dart'; | 18 import '../util/util.dart'; |
| 18 import 'enqueue.dart'; | 19 import 'enqueue.dart'; |
| 19 import 'js.dart'; | 20 import 'js.dart'; |
| 20 | 21 |
| 22 typedef dynamic /*DartType|SpecialType*/ TypeLookup(String typeString); |
| 23 |
| 21 /// This class is a temporary work-around until we get a more powerful DartType. | 24 /// This class is a temporary work-around until we get a more powerful DartType. |
| 22 class SpecialType { | 25 class SpecialType { |
| 23 final String name; | 26 final String name; |
| 24 const SpecialType._(this.name); | 27 const SpecialType._(this.name); |
| 25 | 28 |
| 26 /// The type Object, but no subtypes: | 29 /// The type Object, but no subtypes: |
| 27 static const JsObject = const SpecialType._('=Object'); | 30 static const JsObject = const SpecialType._('=Object'); |
| 28 | 31 |
| 29 int get hashCode => name.hashCode; | 32 int get hashCode => name.hashCode; |
| 30 | 33 |
| (...skipping 225 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 256 /// [nullType] define the types for `Object` and `Null`, respectively. The | 259 /// [nullType] define the types for `Object` and `Null`, respectively. The |
| 257 /// latter is used for the type strings of the form '' and 'var'. | 260 /// latter is used for the type strings of the form '' and 'var'. |
| 258 /// [validTags] can be used to restrict which tags are accepted. | 261 /// [validTags] can be used to restrict which tags are accepted. |
| 259 static void processSpecString( | 262 static void processSpecString( |
| 260 DiagnosticReporter reporter, Spannable spannable, String specString, | 263 DiagnosticReporter reporter, Spannable spannable, String specString, |
| 261 {Iterable<String> validTags, | 264 {Iterable<String> validTags, |
| 262 void setSideEffects(SideEffects newEffects), | 265 void setSideEffects(SideEffects newEffects), |
| 263 void setThrows(NativeThrowBehavior throwKind), | 266 void setThrows(NativeThrowBehavior throwKind), |
| 264 void setIsAllocation(bool isAllocation), | 267 void setIsAllocation(bool isAllocation), |
| 265 void setUseGvn(bool useGvn), | 268 void setUseGvn(bool useGvn), |
| 266 dynamic resolveType(String typeString), | 269 TypeLookup lookupType, |
| 267 List typesReturned, | 270 List typesReturned, |
| 268 List typesInstantiated, | 271 List typesInstantiated, |
| 269 objectType, | 272 objectType, |
| 270 nullType}) { | 273 nullType}) { |
| 271 bool seenError = false; | 274 bool seenError = false; |
| 272 | 275 |
| 273 void reportError(String message) { | 276 void reportError(String message) { |
| 274 seenError = true; | 277 seenError = true; |
| 275 reporter.reportErrorMessage( | 278 reporter.reportErrorMessage( |
| 276 spannable, MessageKind.GENERIC, {'text': message}); | 279 spannable, MessageKind.GENERIC, {'text': message}); |
| (...skipping 22 matching lines...) Expand all Loading... |
| 299 } | 302 } |
| 300 return; | 303 return; |
| 301 } | 304 } |
| 302 if (typesString == '' || typesString == 'var') { | 305 if (typesString == '' || typesString == 'var') { |
| 303 if (onVar != null) { | 306 if (onVar != null) { |
| 304 onVar(); | 307 onVar(); |
| 305 } | 308 } |
| 306 return; | 309 return; |
| 307 } | 310 } |
| 308 for (final typeString in typesString.split('|')) { | 311 for (final typeString in typesString.split('|')) { |
| 309 onType(resolveType(typeString.trim())); | 312 onType(_parseType(typeString.trim(), spannable, reporter, lookupType)); |
| 310 } | 313 } |
| 311 } | 314 } |
| 312 | 315 |
| 313 if (!specString.contains(';') && !specString.contains(':')) { | 316 if (!specString.contains(';') && !specString.contains(':')) { |
| 314 // Form (1), types or pseudo-types like 'void' and 'var'. | 317 // Form (1), types or pseudo-types like 'void' and 'var'. |
| 315 resolveTypesString(specString.trim(), onVar: () { | 318 resolveTypesString(specString.trim(), onVar: () { |
| 316 typesReturned.add(objectType); | 319 typesReturned.add(objectType); |
| 317 typesReturned.add(nullType); | 320 typesReturned.add(nullType); |
| 318 }, onType: (type) { | 321 }, onType: (type) { |
| 319 typesInstantiated.add(type); | 322 typesInstantiated.add(type); |
| (...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 479 break; | 482 break; |
| 480 default: | 483 default: |
| 481 reportError("Unrecognized side-effect flag: '$dependency'."); | 484 reportError("Unrecognized side-effect flag: '$dependency'."); |
| 482 } | 485 } |
| 483 } | 486 } |
| 484 } | 487 } |
| 485 | 488 |
| 486 return sideEffects; | 489 return sideEffects; |
| 487 } | 490 } |
| 488 | 491 |
| 489 static NativeBehavior ofJsCall(Send jsCall, DiagnosticReporter reporter, | 492 /// Returns a [TypeLookup] that uses [resolver] to perform lookup and [node] |
| 493 /// as position for errors. |
| 494 static TypeLookup _typeLookup(Node node, ForeignResolver resolver) { |
| 495 return (String name) => resolver.resolveTypeFromString(node, name); |
| 496 } |
| 497 |
| 498 /// Compute the [NativeBehavior] for a [Send] node calling the 'JS' function. |
| 499 static NativeBehavior ofJsCallSend(Send jsCall, DiagnosticReporter reporter, |
| 490 ParsingContext parsing, CoreTypes coreTypes, ForeignResolver resolver) { | 500 ParsingContext parsing, CoreTypes coreTypes, ForeignResolver resolver) { |
| 501 var argNodes = jsCall.arguments; |
| 502 if (argNodes.isEmpty || argNodes.tail.isEmpty) { |
| 503 reporter.reportErrorMessage(jsCall, MessageKind.WRONG_ARGUMENT_FOR_JS); |
| 504 return new NativeBehavior(); |
| 505 } |
| 506 |
| 507 var specArgument = argNodes.head; |
| 508 if (specArgument is! StringNode || specArgument.isInterpolation) { |
| 509 reporter.reportErrorMessage( |
| 510 specArgument, MessageKind.WRONG_ARGUMENT_FOR_JS_FIRST); |
| 511 return new NativeBehavior(); |
| 512 } |
| 513 |
| 514 var codeArgument = argNodes.tail.head; |
| 515 if (codeArgument is! StringNode || codeArgument.isInterpolation) { |
| 516 reporter.reportErrorMessage( |
| 517 codeArgument, MessageKind.WRONG_ARGUMENT_FOR_JS_SECOND); |
| 518 return new NativeBehavior(); |
| 519 } |
| 520 |
| 521 String specString = specArgument.dartString.slowToString(); |
| 522 String codeString = codeArgument.dartString.slowToString(); |
| 523 |
| 524 return ofJsCall(specString, codeString, _typeLookup(specArgument, resolver), |
| 525 specArgument, reporter, coreTypes); |
| 526 } |
| 527 |
| 528 /// Compute the [NativeBehavior] for a call to the 'JS' function with the |
| 529 /// given [specString] and [codeString] (first and second arguments). |
| 530 static NativeBehavior ofJsCall( |
| 531 String specString, |
| 532 String codeString, |
| 533 TypeLookup lookupType, |
| 534 Spannable spannable, |
| 535 DiagnosticReporter reporter, |
| 536 CoreTypes coreTypes) { |
| 491 // The first argument of a JS-call is a string encoding various attributes | 537 // The first argument of a JS-call is a string encoding various attributes |
| 492 // of the code. | 538 // of the code. |
| 493 // | 539 // |
| 494 // 'Type1|Type2'. A union type. | 540 // 'Type1|Type2'. A union type. |
| 495 // '=Object'. A JavaScript Object, no subtype. | 541 // '=Object'. A JavaScript Object, no subtype. |
| 496 | 542 |
| 497 NativeBehavior behavior = new NativeBehavior(); | 543 NativeBehavior behavior = new NativeBehavior(); |
| 498 | 544 |
| 499 var argNodes = jsCall.arguments; | 545 behavior.codeTemplateText = codeString; |
| 500 if (argNodes.isEmpty || argNodes.tail.isEmpty) { | |
| 501 reporter.reportErrorMessage(jsCall, MessageKind.GENERIC, | |
| 502 {'text': "JS expression takes two or more arguments."}); | |
| 503 return behavior; | |
| 504 } | |
| 505 | |
| 506 var specArgument = argNodes.head; | |
| 507 if (specArgument is! StringNode || specArgument.isInterpolation) { | |
| 508 reporter.reportErrorMessage(specArgument, MessageKind.GENERIC, | |
| 509 {'text': "JS first argument must be a string literal."}); | |
| 510 return behavior; | |
| 511 } | |
| 512 | |
| 513 var codeArgument = argNodes.tail.head; | |
| 514 if (codeArgument is! StringNode || codeArgument.isInterpolation) { | |
| 515 reporter.reportErrorMessage(codeArgument, MessageKind.GENERIC, | |
| 516 {'text': "JS second argument must be a string literal."}); | |
| 517 return behavior; | |
| 518 } | |
| 519 | |
| 520 behavior.codeTemplateText = codeArgument.dartString.slowToString(); | |
| 521 behavior.codeTemplate = js.js.parseForeignJS(behavior.codeTemplateText); | 546 behavior.codeTemplate = js.js.parseForeignJS(behavior.codeTemplateText); |
| 522 | 547 |
| 523 String specString = specArgument.dartString.slowToString(); | |
| 524 | |
| 525 dynamic resolveType(String typeString) { | |
| 526 return _parseType( | |
| 527 typeString, | |
| 528 parsing, | |
| 529 (name) => resolver.resolveTypeFromString(specArgument, name), | |
| 530 specArgument); | |
| 531 } | |
| 532 | |
| 533 bool sideEffectsAreEncodedInSpecString = false; | 548 bool sideEffectsAreEncodedInSpecString = false; |
| 534 | 549 |
| 535 void setSideEffects(SideEffects newEffects) { | 550 void setSideEffects(SideEffects newEffects) { |
| 536 sideEffectsAreEncodedInSpecString = true; | 551 sideEffectsAreEncodedInSpecString = true; |
| 537 behavior.sideEffects.setTo(newEffects); | 552 behavior.sideEffects.setTo(newEffects); |
| 538 } | 553 } |
| 539 | 554 |
| 540 bool throwBehaviorFromSpecString = false; | 555 bool throwBehaviorFromSpecString = false; |
| 541 void setThrows(NativeThrowBehavior throwBehavior) { | 556 void setThrows(NativeThrowBehavior throwBehavior) { |
| 542 throwBehaviorFromSpecString = true; | 557 throwBehaviorFromSpecString = true; |
| 543 behavior.throwBehavior = throwBehavior; | 558 behavior.throwBehavior = throwBehavior; |
| 544 } | 559 } |
| 545 | 560 |
| 546 void setIsAllocation(bool isAllocation) { | 561 void setIsAllocation(bool isAllocation) { |
| 547 behavior.isAllocation = isAllocation; | 562 behavior.isAllocation = isAllocation; |
| 548 } | 563 } |
| 549 | 564 |
| 550 void setUseGvn(bool useGvn) { | 565 void setUseGvn(bool useGvn) { |
| 551 behavior.useGvn = useGvn; | 566 behavior.useGvn = useGvn; |
| 552 } | 567 } |
| 553 | 568 |
| 554 processSpecString(reporter, specArgument, specString, | 569 processSpecString(reporter, spannable, specString, |
| 555 setSideEffects: setSideEffects, | 570 setSideEffects: setSideEffects, |
| 556 setThrows: setThrows, | 571 setThrows: setThrows, |
| 557 setIsAllocation: setIsAllocation, | 572 setIsAllocation: setIsAllocation, |
| 558 setUseGvn: setUseGvn, | 573 setUseGvn: setUseGvn, |
| 559 resolveType: resolveType, | 574 lookupType: lookupType, |
| 560 typesReturned: behavior.typesReturned, | 575 typesReturned: behavior.typesReturned, |
| 561 typesInstantiated: behavior.typesInstantiated, | 576 typesInstantiated: behavior.typesInstantiated, |
| 562 objectType: coreTypes.objectType, | 577 objectType: coreTypes.objectType, |
| 563 nullType: coreTypes.nullType); | 578 nullType: coreTypes.nullType); |
| 564 | 579 |
| 565 if (!sideEffectsAreEncodedInSpecString) { | 580 if (!sideEffectsAreEncodedInSpecString) { |
| 566 new SideEffectsVisitor(behavior.sideEffects) | 581 new SideEffectsVisitor(behavior.sideEffects) |
| 567 .visit(behavior.codeTemplate.ast); | 582 .visit(behavior.codeTemplate.ast); |
| 568 } | 583 } |
| 569 if (!throwBehaviorFromSpecString) { | 584 if (!throwBehaviorFromSpecString) { |
| 570 behavior.throwBehavior = | 585 behavior.throwBehavior = |
| 571 new ThrowBehaviorVisitor().analyze(behavior.codeTemplate.ast); | 586 new ThrowBehaviorVisitor().analyze(behavior.codeTemplate.ast); |
| 572 } | 587 } |
| 573 | 588 |
| 574 return behavior; | 589 return behavior; |
| 575 } | 590 } |
| 576 | 591 |
| 577 static void _fillNativeBehaviorOfBuiltinOrEmbeddedGlobal( | 592 static void _fillNativeBehaviorOfBuiltinOrEmbeddedGlobal( |
| 578 NativeBehavior behavior, | 593 NativeBehavior behavior, |
| 579 Send jsBuiltinOrEmbeddedGlobalCall, | 594 Spannable spannable, |
| 595 String specString, |
| 596 TypeLookup lookupType, |
| 580 DiagnosticReporter reporter, | 597 DiagnosticReporter reporter, |
| 581 ParsingContext parsing, | |
| 582 CoreTypes coreTypes, | 598 CoreTypes coreTypes, |
| 583 ForeignResolver resolver, | 599 {List<String> validTags}) { |
| 584 {bool isBuiltin, | 600 void setSideEffects(SideEffects newEffects) { |
| 585 List<String> validTags}) { | 601 behavior.sideEffects.setTo(newEffects); |
| 602 } |
| 603 |
| 604 processSpecString(reporter, spannable, specString, |
| 605 validTags: validTags, |
| 606 lookupType: lookupType, |
| 607 setSideEffects: setSideEffects, |
| 608 typesReturned: behavior.typesReturned, |
| 609 typesInstantiated: behavior.typesInstantiated, |
| 610 objectType: coreTypes.objectType, |
| 611 nullType: coreTypes.nullType); |
| 612 } |
| 613 |
| 614 static NativeBehavior ofJsBuiltinCallSend( |
| 615 Send jsBuiltinCall, |
| 616 DiagnosticReporter reporter, |
| 617 CoreTypes coreTypes, |
| 618 ForeignResolver resolver) { |
| 619 NativeBehavior behavior = new NativeBehavior(); |
| 620 behavior.sideEffects.setTo(new SideEffects()); |
| 621 |
| 586 // The first argument of a JS-embedded global call is a string encoding | 622 // The first argument of a JS-embedded global call is a string encoding |
| 587 // the type of the code. | 623 // the type of the code. |
| 588 // | 624 // |
| 589 // 'Type1|Type2'. A union type. | 625 // 'Type1|Type2'. A union type. |
| 590 // '=Object'. A JavaScript Object, no subtype. | 626 // '=Object'. A JavaScript Object, no subtype. |
| 591 | 627 |
| 592 String builtinOrGlobal = isBuiltin ? "builtin" : "embedded global"; | 628 Link<Node> argNodes = jsBuiltinCall.arguments; |
| 593 | |
| 594 Link<Node> argNodes = jsBuiltinOrEmbeddedGlobalCall.arguments; | |
| 595 if (argNodes.isEmpty) { | 629 if (argNodes.isEmpty) { |
| 596 reporter.internalError(jsBuiltinOrEmbeddedGlobalCall, | 630 reporter.internalError( |
| 597 "JS $builtinOrGlobal expression has no type."); | 631 jsBuiltinCall, "JS builtin expression has no type."); |
| 598 } | 632 } |
| 599 | 633 |
| 600 // We don't check the given name. That needs to be done at a later point. | 634 // We don't check the given name. That needs to be done at a later point. |
| 601 // This is, because we want to allow non-literals (like references to | 635 // This is, because we want to allow non-literals (like references to |
| 602 // enums) as names. | 636 // enums) as names. |
| 603 if (argNodes.tail.isEmpty) { | 637 if (argNodes.tail.isEmpty) { |
| 604 reporter.internalError(jsBuiltinOrEmbeddedGlobalCall, | 638 reporter.internalError(jsBuiltinCall, "JS builtin is missing name."); |
| 605 'JS $builtinOrGlobal is missing name.'); | |
| 606 } | |
| 607 | |
| 608 if (!isBuiltin) { | |
| 609 if (!argNodes.tail.tail.isEmpty) { | |
| 610 reporter.internalError(argNodes.tail.tail.head, | |
| 611 'JS embedded global has more than 2 arguments'); | |
| 612 } | |
| 613 } | 639 } |
| 614 | 640 |
| 615 LiteralString specLiteral = argNodes.head.asLiteralString(); | 641 LiteralString specLiteral = argNodes.head.asLiteralString(); |
| 616 if (specLiteral == null) { | 642 if (specLiteral == null) { |
| 617 // TODO(sra): We could accept a type identifier? e.g. JS(bool, '1<2'). It | 643 // TODO(sra): We could accept a type identifier? e.g. JS(bool, '1<2'). It |
| 618 // is not very satisfactory because it does not work for void, dynamic. | 644 // is not very satisfactory because it does not work for void, dynamic. |
| 619 reporter.internalError(argNodes.head, "Unexpected first argument."); | 645 reporter.internalError(argNodes.head, "Unexpected first argument."); |
| 620 } | 646 } |
| 621 | 647 |
| 622 String specString = specLiteral.dartString.slowToString(); | 648 String specString = specLiteral.dartString.slowToString(); |
| 623 | 649 |
| 624 dynamic resolveType(String typeString) { | 650 return ofJsBuiltinCall(specString, _typeLookup(jsBuiltinCall, resolver), |
| 625 return _parseType( | 651 jsBuiltinCall, reporter, coreTypes); |
| 626 typeString, | |
| 627 parsing, | |
| 628 (name) => resolver.resolveTypeFromString(specLiteral, name), | |
| 629 jsBuiltinOrEmbeddedGlobalCall); | |
| 630 } | |
| 631 | |
| 632 void setSideEffects(SideEffects newEffects) { | |
| 633 behavior.sideEffects.setTo(newEffects); | |
| 634 } | |
| 635 | |
| 636 processSpecString(reporter, jsBuiltinOrEmbeddedGlobalCall, specString, | |
| 637 validTags: validTags, | |
| 638 resolveType: resolveType, | |
| 639 setSideEffects: setSideEffects, | |
| 640 typesReturned: behavior.typesReturned, | |
| 641 typesInstantiated: behavior.typesInstantiated, | |
| 642 objectType: coreTypes.objectType, | |
| 643 nullType: coreTypes.nullType); | |
| 644 } | 652 } |
| 645 | 653 |
| 646 static NativeBehavior ofJsBuiltinCall( | 654 static NativeBehavior ofJsBuiltinCall( |
| 647 Send jsBuiltinCall, | 655 String specString, |
| 656 TypeLookup lookupType, |
| 657 Spannable spannable, |
| 648 DiagnosticReporter reporter, | 658 DiagnosticReporter reporter, |
| 649 ParsingContext parsing, | 659 CoreTypes coreTypes) { |
| 660 NativeBehavior behavior = new NativeBehavior(); |
| 661 behavior.sideEffects.setTo(new SideEffects()); |
| 662 _fillNativeBehaviorOfBuiltinOrEmbeddedGlobal( |
| 663 behavior, spannable, specString, lookupType, reporter, coreTypes); |
| 664 return behavior; |
| 665 } |
| 666 |
| 667 static NativeBehavior ofJsEmbeddedGlobalCallSend( |
| 668 Send jsEmbeddedGlobalCall, |
| 669 DiagnosticReporter reporter, |
| 650 CoreTypes coreTypes, | 670 CoreTypes coreTypes, |
| 651 ForeignResolver resolver) { | 671 ForeignResolver resolver) { |
| 652 NativeBehavior behavior = new NativeBehavior(); | 672 NativeBehavior behavior = new NativeBehavior(); |
| 653 behavior.sideEffects.setTo(new SideEffects()); | |
| 654 | |
| 655 _fillNativeBehaviorOfBuiltinOrEmbeddedGlobal( | |
| 656 behavior, jsBuiltinCall, reporter, parsing, coreTypes, resolver, | |
| 657 isBuiltin: true); | |
| 658 | |
| 659 return behavior; | |
| 660 } | |
| 661 | |
| 662 static NativeBehavior ofJsEmbeddedGlobalCall( | |
| 663 Send jsEmbeddedGlobalCall, | |
| 664 DiagnosticReporter reporter, | |
| 665 ParsingContext parsing, | |
| 666 CoreTypes coreTypes, | |
| 667 ForeignResolver resolver) { | |
| 668 NativeBehavior behavior = new NativeBehavior(); | |
| 669 // TODO(sra): Allow the use site to override these defaults. | 673 // TODO(sra): Allow the use site to override these defaults. |
| 670 // Embedded globals are usually pre-computed data structures or JavaScript | 674 // Embedded globals are usually pre-computed data structures or JavaScript |
| 671 // functions that never change. | 675 // functions that never change. |
| 672 behavior.sideEffects.setTo(new SideEffects.empty()); | 676 behavior.sideEffects.setTo(new SideEffects.empty()); |
| 673 behavior.throwBehavior = NativeThrowBehavior.NEVER; | 677 behavior.throwBehavior = NativeThrowBehavior.NEVER; |
| 674 | 678 |
| 679 // The first argument of a JS-embedded global call is a string encoding |
| 680 // the type of the code. |
| 681 // |
| 682 // 'Type1|Type2'. A union type. |
| 683 // '=Object'. A JavaScript Object, no subtype. |
| 684 |
| 685 Link<Node> argNodes = jsEmbeddedGlobalCall.arguments; |
| 686 if (argNodes.isEmpty) { |
| 687 reporter.internalError( |
| 688 jsEmbeddedGlobalCall, "JS embedded global expression has no type."); |
| 689 } |
| 690 |
| 691 // We don't check the given name. That needs to be done at a later point. |
| 692 // This is, because we want to allow non-literals (like references to |
| 693 // enums) as names. |
| 694 if (argNodes.tail.isEmpty) { |
| 695 reporter.internalError( |
| 696 jsEmbeddedGlobalCall, "JS embedded global is missing name."); |
| 697 } |
| 698 |
| 699 if (!argNodes.tail.tail.isEmpty) { |
| 700 reporter.internalError(argNodes.tail.tail.head, |
| 701 'JS embedded global has more than 2 arguments'); |
| 702 } |
| 703 |
| 704 LiteralString specLiteral = argNodes.head.asLiteralString(); |
| 705 if (specLiteral == null) { |
| 706 // TODO(sra): We could accept a type identifier? e.g. JS(bool, '1<2'). It |
| 707 // is not very satisfactory because it does not work for void, dynamic. |
| 708 reporter.internalError(argNodes.head, "Unexpected first argument."); |
| 709 } |
| 710 |
| 711 String specString = specLiteral.dartString.slowToString(); |
| 712 |
| 713 return ofJsEmbeddedGlobalCall( |
| 714 specString, |
| 715 _typeLookup(jsEmbeddedGlobalCall, resolver), |
| 716 jsEmbeddedGlobalCall, |
| 717 reporter, |
| 718 coreTypes); |
| 719 } |
| 720 |
| 721 static NativeBehavior ofJsEmbeddedGlobalCall( |
| 722 String specString, |
| 723 TypeLookup lookupType, |
| 724 Spannable spannable, |
| 725 DiagnosticReporter reporter, |
| 726 CoreTypes coreTypes) { |
| 727 NativeBehavior behavior = new NativeBehavior(); |
| 728 // TODO(sra): Allow the use site to override these defaults. |
| 729 // Embedded globals are usually pre-computed data structures or JavaScript |
| 730 // functions that never change. |
| 731 behavior.sideEffects.setTo(new SideEffects.empty()); |
| 732 behavior.throwBehavior = NativeThrowBehavior.NEVER; |
| 675 _fillNativeBehaviorOfBuiltinOrEmbeddedGlobal( | 733 _fillNativeBehaviorOfBuiltinOrEmbeddedGlobal( |
| 676 behavior, jsEmbeddedGlobalCall, reporter, parsing, coreTypes, resolver, | 734 behavior, spannable, specString, lookupType, reporter, coreTypes, |
| 677 isBuiltin: false, validTags: const ['returns', 'creates']); | 735 validTags: ['returns', 'creates']); |
| 678 | |
| 679 return behavior; | 736 return behavior; |
| 680 } | 737 } |
| 681 | 738 |
| 682 static NativeBehavior ofMethod(FunctionElement method, Compiler compiler) { | 739 static NativeBehavior ofMethodElement( |
| 683 FunctionType type = method.computeType(compiler.resolution); | 740 FunctionElement element, Compiler compiler) { |
| 741 FunctionType type = element.computeType(compiler.resolution); |
| 742 List<ConstantExpression> metadata = <ConstantExpression>[]; |
| 743 for (MetadataAnnotation annotation in element.implementation.metadata) { |
| 744 annotation.ensureResolved(compiler.resolution); |
| 745 metadata.add(annotation.constant); |
| 746 } |
| 747 |
| 748 DartType lookup(String name) { |
| 749 Element e = element.buildScope().lookup(name); |
| 750 if (e == null) return null; |
| 751 if (e is! ClassElement) return null; |
| 752 ClassElement cls = e; |
| 753 cls.ensureResolved(compiler.resolution); |
| 754 return cls.thisType; |
| 755 } |
| 756 |
| 757 return ofMethod(element, type, metadata, lookup, compiler, |
| 758 isJsInterop: compiler.backend.isJsInterop(element)); |
| 759 } |
| 760 |
| 761 static NativeBehavior ofMethod( |
| 762 Spannable spannable, |
| 763 FunctionType type, |
| 764 List<ConstantExpression> metadata, |
| 765 TypeLookup lookupType, |
| 766 Compiler compiler, |
| 767 {bool isJsInterop}) { |
| 684 var behavior = new NativeBehavior(); | 768 var behavior = new NativeBehavior(); |
| 685 var returnType = type.returnType; | 769 var returnType = type.returnType; |
| 686 bool isInterop = compiler.backend.isJsInterop(method); | |
| 687 // Note: For dart:html and other internal libraries we maintain, we can | 770 // Note: For dart:html and other internal libraries we maintain, we can |
| 688 // trust the return type and use it to limit what we enqueue. We have to | 771 // trust the return type and use it to limit what we enqueue. We have to |
| 689 // be more conservative about JS interop types and assume they can return | 772 // be more conservative about JS interop types and assume they can return |
| 690 // anything (unless the user provides the experimental flag to trust the | 773 // anything (unless the user provides the experimental flag to trust the |
| 691 // type of js-interop APIs). We do restrict the allocation effects and say | 774 // type of js-interop APIs). We do restrict the allocation effects and say |
| 692 // that interop calls create only interop types (which may be unsound if | 775 // that interop calls create only interop types (which may be unsound if |
| 693 // an interop call returns a DOM type and declares a dynamic return type, | 776 // an interop call returns a DOM type and declares a dynamic return type, |
| 694 // but otherwise we would include a lot of code by default). | 777 // but otherwise we would include a lot of code by default). |
| 695 // TODO(sigmund,sra): consider doing something better for numeric types. | 778 // TODO(sigmund,sra): consider doing something better for numeric types. |
| 696 behavior.typesReturned.add( | 779 behavior.typesReturned.add( |
| 697 !isInterop || compiler.options.trustJSInteropTypeAnnotations | 780 !isJsInterop || compiler.options.trustJSInteropTypeAnnotations |
| 698 ? returnType | 781 ? returnType |
| 699 : const DynamicType()); | 782 : const DynamicType()); |
| 700 if (!type.returnType.isVoid) { | 783 if (!type.returnType.isVoid) { |
| 701 // Declared types are nullable. | 784 // Declared types are nullable. |
| 702 behavior.typesReturned.add(compiler.coreTypes.nullType); | 785 behavior.typesReturned.add(compiler.coreTypes.nullType); |
| 703 } | 786 } |
| 704 behavior._capture(type, compiler.resolution, | 787 behavior._capture(type, compiler.resolution, |
| 705 isInterop: isInterop, compiler: compiler); | 788 isInterop: isJsInterop, compiler: compiler); |
| 706 | 789 |
| 707 for (DartType type in type.optionalParameterTypes) { | 790 for (DartType type in type.optionalParameterTypes) { |
| 708 behavior._escape(type, compiler.resolution); | 791 behavior._escape(type, compiler.resolution); |
| 709 } | 792 } |
| 710 for (DartType type in type.namedParameterTypes) { | 793 for (DartType type in type.namedParameterTypes) { |
| 711 behavior._escape(type, compiler.resolution); | 794 behavior._escape(type, compiler.resolution); |
| 712 } | 795 } |
| 713 | 796 |
| 714 behavior._overrideWithAnnotations(method, compiler); | 797 behavior._overrideWithAnnotations( |
| 798 spannable, metadata, lookupType, compiler); |
| 715 return behavior; | 799 return behavior; |
| 716 } | 800 } |
| 717 | 801 |
| 718 static NativeBehavior ofFieldLoad(MemberElement field, Compiler compiler) { | 802 static NativeBehavior ofFieldElementLoad( |
| 803 MemberElement element, Compiler compiler) { |
| 719 Resolution resolution = compiler.resolution; | 804 Resolution resolution = compiler.resolution; |
| 720 DartType type = field.computeType(resolution); | 805 DartType type = element.computeType(resolution); |
| 806 List<ConstantExpression> metadata = <ConstantExpression>[]; |
| 807 for (MetadataAnnotation annotation in element.implementation.metadata) { |
| 808 annotation.ensureResolved(compiler.resolution); |
| 809 metadata.add(annotation.constant); |
| 810 } |
| 811 |
| 812 DartType lookup(String name) { |
| 813 Element e = element.buildScope().lookup(name); |
| 814 if (e == null) return null; |
| 815 if (e is! ClassElement) return null; |
| 816 ClassElement cls = e; |
| 817 cls.ensureResolved(compiler.resolution); |
| 818 return cls.thisType; |
| 819 } |
| 820 |
| 821 return ofFieldLoad(element, type, metadata, lookup, compiler, |
| 822 isJsInterop: compiler.backend.isJsInterop(element)); |
| 823 } |
| 824 |
| 825 static NativeBehavior ofFieldLoad( |
| 826 Spannable spannable, |
| 827 DartType type, |
| 828 List<ConstantExpression> metadata, |
| 829 TypeLookup lookupType, |
| 830 Compiler compiler, |
| 831 {bool isJsInterop}) { |
| 832 Resolution resolution = compiler.resolution; |
| 721 var behavior = new NativeBehavior(); | 833 var behavior = new NativeBehavior(); |
| 722 bool isInterop = compiler.backend.isJsInterop(field); | |
| 723 // TODO(sigmund,sra): consider doing something better for numeric types. | 834 // TODO(sigmund,sra): consider doing something better for numeric types. |
| 724 behavior.typesReturned.add( | 835 behavior.typesReturned.add( |
| 725 !isInterop || compiler.options.trustJSInteropTypeAnnotations | 836 !isJsInterop || compiler.options.trustJSInteropTypeAnnotations |
| 726 ? type | 837 ? type |
| 727 : const DynamicType()); | 838 : const DynamicType()); |
| 728 // Declared types are nullable. | 839 // Declared types are nullable. |
| 729 behavior.typesReturned.add(resolution.coreTypes.nullType); | 840 behavior.typesReturned.add(resolution.coreTypes.nullType); |
| 730 behavior._capture(type, resolution, | 841 behavior._capture(type, resolution, |
| 731 isInterop: isInterop, compiler: compiler); | 842 isInterop: isJsInterop, compiler: compiler); |
| 732 behavior._overrideWithAnnotations(field, compiler); | 843 behavior._overrideWithAnnotations( |
| 844 spannable, metadata, lookupType, compiler); |
| 733 return behavior; | 845 return behavior; |
| 734 } | 846 } |
| 735 | 847 |
| 736 static NativeBehavior ofFieldStore(MemberElement field, Compiler compiler) { | 848 static NativeBehavior ofFieldElementStore( |
| 737 Resolution resolution = compiler.resolution; | 849 MemberElement field, Resolution resolution) { |
| 738 DartType type = field.computeType(resolution); | 850 DartType type = field.computeType(resolution); |
| 851 return ofFieldStore(type, resolution); |
| 852 } |
| 853 |
| 854 static NativeBehavior ofFieldStore(DartType type, Resolution resolution) { |
| 739 var behavior = new NativeBehavior(); | 855 var behavior = new NativeBehavior(); |
| 740 behavior._escape(type, resolution); | 856 behavior._escape(type, resolution); |
| 741 // We don't override the default behaviour - the annotations apply to | 857 // We don't override the default behaviour - the annotations apply to |
| 742 // loading the field. | 858 // loading the field. |
| 743 return behavior; | 859 return behavior; |
| 744 } | 860 } |
| 745 | 861 |
| 746 void _overrideWithAnnotations(Element element, Compiler compiler) { | 862 void _overrideWithAnnotations( |
| 747 if (element.implementation.metadata.isEmpty) return; | 863 Spannable spannable, |
| 748 | 864 Iterable<ConstantExpression> metadata, |
| 749 DartType lookup(String name) { | 865 TypeLookup lookupType, |
| 750 Element e = element.buildScope().lookup(name); | 866 Compiler compiler) { |
| 751 if (e == null) return null; | 867 if (metadata.isEmpty) return; |
| 752 if (e is! ClassElement) return null; | |
| 753 ClassElement cls = e; | |
| 754 cls.ensureResolved(compiler.resolution); | |
| 755 return cls.thisType; | |
| 756 } | |
| 757 | 868 |
| 758 NativeEnqueuer enqueuer = compiler.enqueuer.resolution.nativeEnqueuer; | 869 NativeEnqueuer enqueuer = compiler.enqueuer.resolution.nativeEnqueuer; |
| 759 var creates = | 870 var creates = _collect(spannable, metadata, compiler, |
| 760 _collect(element, compiler, enqueuer.annotationCreatesClass, lookup); | 871 enqueuer.annotationCreatesClass, lookupType); |
| 761 var returns = | 872 var returns = _collect(spannable, metadata, compiler, |
| 762 _collect(element, compiler, enqueuer.annotationReturnsClass, lookup); | 873 enqueuer.annotationReturnsClass, lookupType); |
| 763 | 874 |
| 764 if (creates != null) { | 875 if (creates != null) { |
| 765 typesInstantiated | 876 typesInstantiated |
| 766 ..clear() | 877 ..clear() |
| 767 ..addAll(creates); | 878 ..addAll(creates); |
| 768 } | 879 } |
| 769 if (returns != null) { | 880 if (returns != null) { |
| 770 typesReturned | 881 typesReturned |
| 771 ..clear() | 882 ..clear() |
| 772 ..addAll(returns); | 883 ..addAll(returns); |
| 773 } | 884 } |
| 774 } | 885 } |
| 775 | 886 |
| 776 /** | 887 /** |
| 777 * Returns a list of type constraints from the annotations of | 888 * Returns a list of type constraints from the annotations of |
| 778 * [annotationClass]. | 889 * [annotationClass]. |
| 779 * Returns `null` if no constraints. | 890 * Returns `null` if no constraints. |
| 780 */ | 891 */ |
| 781 static _collect(Element element, Compiler compiler, Element annotationClass, | 892 static _collect(Spannable spannable, Iterable<ConstantExpression> metadata, |
| 782 lookup(str)) { | 893 Compiler compiler, Element annotationClass, TypeLookup lookupType) { |
| 783 DiagnosticReporter reporter = compiler.reporter; | 894 DiagnosticReporter reporter = compiler.reporter; |
| 784 var types = null; | 895 var types = null; |
| 785 for (MetadataAnnotation annotation in element.implementation.metadata) { | 896 for (ConstantExpression constant in metadata) { |
| 786 annotation.ensureResolved(compiler.resolution); | 897 ConstantValue value = compiler.constants.getConstantValue(constant); |
| 787 ConstantValue value = | |
| 788 compiler.constants.getConstantValue(annotation.constant); | |
| 789 if (!value.isConstructedObject) continue; | 898 if (!value.isConstructedObject) continue; |
| 790 ConstructedConstantValue constructedObject = value; | 899 ConstructedConstantValue constructedObject = value; |
| 791 if (constructedObject.type.element != annotationClass) continue; | 900 if (constructedObject.type.element != annotationClass) continue; |
| 792 | 901 |
| 793 Iterable<ConstantValue> fields = constructedObject.fields.values; | 902 Iterable<ConstantValue> fields = constructedObject.fields.values; |
| 794 // TODO(sra): Better validation of the constant. | 903 // TODO(sra): Better validation of the constant. |
| 795 if (fields.length != 1 || !fields.single.isString) { | 904 if (fields.length != 1 || !fields.single.isString) { |
| 796 reporter.internalError( | 905 reporter.internalError(spannable, |
| 797 annotation, 'Annotations needs one string: ${annotation.node}'); | 906 'Annotations needs one string: ${constant.toStructuredText()}'); |
| 798 } | 907 } |
| 799 StringConstantValue specStringConstant = fields.single; | 908 StringConstantValue specStringConstant = fields.single; |
| 800 String specString = specStringConstant.toDartString().slowToString(); | 909 String specString = specStringConstant.toDartString().slowToString(); |
| 801 for (final typeString in specString.split('|')) { | 910 for (final typeString in specString.split('|')) { |
| 802 var type = | 911 var type = _parseType(typeString, spannable, reporter, lookupType); |
| 803 _parseType(typeString, compiler.parsingContext, lookup, annotation); | |
| 804 if (types == null) types = []; | 912 if (types == null) types = []; |
| 805 types.add(type); | 913 types.add(type); |
| 806 } | 914 } |
| 807 } | 915 } |
| 808 return types; | 916 return types; |
| 809 } | 917 } |
| 810 | 918 |
| 811 /// Models the behavior of having intances of [type] escape from Dart code | 919 /// Models the behavior of having intances of [type] escape from Dart code |
| 812 /// into native code. | 920 /// into native code. |
| 813 void _escape(DartType type, Resolution resolution) { | 921 void _escape(DartType type, Resolution resolution) { |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 867 } else { | 975 } else { |
| 868 // Otherwise, when the declared type is a Dart type, we do not | 976 // Otherwise, when the declared type is a Dart type, we do not |
| 869 // register an allocation because we assume it cannot be instantiated | 977 // register an allocation because we assume it cannot be instantiated |
| 870 // from within the JS-interop code. It must have escaped from another | 978 // from within the JS-interop code. It must have escaped from another |
| 871 // API. | 979 // API. |
| 872 } | 980 } |
| 873 } | 981 } |
| 874 } | 982 } |
| 875 } | 983 } |
| 876 | 984 |
| 877 static dynamic _parseType(String typeString, ParsingContext parsing, | 985 static dynamic /*DartType|SpecialType*/ _parseType(String typeString, |
| 878 lookup(name), locationNodeOrElement) { | 986 Spannable spannable, DiagnosticReporter reporter, TypeLookup lookupType) { |
| 879 DiagnosticReporter reporter = parsing.reporter; | |
| 880 if (typeString == '=Object') return SpecialType.JsObject; | 987 if (typeString == '=Object') return SpecialType.JsObject; |
| 881 if (typeString == 'dynamic') { | 988 if (typeString == 'dynamic') { |
| 882 return const DynamicType(); | 989 return const DynamicType(); |
| 883 } | 990 } |
| 884 var type = lookup(typeString); | 991 var type = lookupType(typeString); |
| 885 if (type != null) return type; | 992 if (type != null) return type; |
| 886 | 993 |
| 887 int index = typeString.indexOf('<'); | 994 int index = typeString.indexOf('<'); |
| 888 if (index < 1) { | 995 if (index < 1) { |
| 889 reporter.reportErrorMessage(_errorNode(locationNodeOrElement, parsing), | 996 reporter.reportErrorMessage(spannable, MessageKind.GENERIC, |
| 890 MessageKind.GENERIC, {'text': "Type '$typeString' not found."}); | 997 {'text': "Type '$typeString' not found."}); |
| 891 return const DynamicType(); | 998 return const DynamicType(); |
| 892 } | 999 } |
| 893 type = lookup(typeString.substring(0, index)); | 1000 type = lookupType(typeString.substring(0, index)); |
| 894 if (type != null) { | 1001 if (type != null) { |
| 895 // TODO(sra): Parse type parameters. | 1002 // TODO(sra): Parse type parameters. |
| 896 return type; | 1003 return type; |
| 897 } | 1004 } |
| 898 reporter.reportErrorMessage(_errorNode(locationNodeOrElement, parsing), | 1005 reporter.reportErrorMessage(spannable, MessageKind.GENERIC, |
| 899 MessageKind.GENERIC, {'text': "Type '$typeString' not found."}); | 1006 {'text': "Type '$typeString' not found."}); |
| 900 return const DynamicType(); | 1007 return const DynamicType(); |
| 901 } | 1008 } |
| 902 | |
| 903 static _errorNode(locationNodeOrElement, ParsingContext parsing) { | |
| 904 if (locationNodeOrElement is Node) return locationNodeOrElement; | |
| 905 return locationNodeOrElement.parseNode(parsing); | |
| 906 } | |
| 907 } | 1009 } |
| OLD | NEW |