OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 | 2 |
3 // for details. All rights reserved. Use of this source code is governed by a | 3 // for details. All rights reserved. Use of this source code is governed by a |
4 // BSD-style license that can be found in the LICENSE file. | 4 // BSD-style license that can be found in the LICENSE file. |
5 | 5 |
6 import 'dart:collection' show HashMap, HashSet; | 6 import 'dart:collection' show HashMap, HashSet; |
7 import 'dart:math' show min, max; | 7 import 'dart:math' show min, max; |
8 | 8 |
9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
10 import 'package:analyzer/dart/ast/ast.dart'; | 10 import 'package:analyzer/dart/ast/ast.dart'; |
(...skipping 373 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
384 var properties = <JS.Property>[]; | 384 var properties = <JS.Property>[]; |
385 _libraries.forEach((library, value) { | 385 _libraries.forEach((library, value) { |
386 // TODO(jacobr): we could specify a short library name instead of the | 386 // TODO(jacobr): we could specify a short library name instead of the |
387 // full library uri if we wanted to save space. | 387 // full library uri if we wanted to save space. |
388 properties.add(new JS.Property( | 388 properties.add(new JS.Property( |
389 js.string(jsLibraryDebuggerName(_libraryRoot, library)), value)); | 389 js.string(jsLibraryDebuggerName(_libraryRoot, library)), value)); |
390 }); | 390 }); |
391 return new JS.ObjectInitializer(properties, multiline: true); | 391 return new JS.ObjectInitializer(properties, multiline: true); |
392 } | 392 } |
393 | 393 |
394 List<String> _getJSName(Element e) { | 394 /// If [e] is a property accessor element, this returns the |
395 if (e.library == null || | 395 /// (possibly synthetic) field that corresponds to it, otherwise returns [e]. |
396 findAnnotation(e.library, isPublicJSAnnotation) == null) { | 396 Element _getNonAccessorElement(Element e) => |
397 return null; | 397 e is PropertyAccessorElement ? e.variable : e; |
398 } | |
399 | 398 |
| 399 /// Returns the name of [e] but removes trailing `=` from setter names. |
| 400 // TODO(jmesserly): it would be nice if Analyzer had something like this. |
| 401 // `Element.displayName` is close, but it also normalizes operator names in |
| 402 // a way we don't want. |
| 403 String _getElementName(Element e) => _getNonAccessorElement(e).name; |
| 404 |
| 405 bool _isExternal(Element e) => |
| 406 e is ExecutableElement && e.isExternal || |
| 407 e is PropertyInducingElement && |
| 408 (e.getter.isExternal || e.setter.isExternal); |
| 409 |
| 410 bool _isJSElement(Element e) => |
| 411 e?.library != null && |
| 412 _isJSNative(e.library) && |
| 413 (_isExternal(e) || e is ClassElement && _isJSNative(e)); |
| 414 |
| 415 String _getJSNameWithoutGlobal(Element e) { |
| 416 if (!_isJSElement(e)) return null; |
400 var libraryJSName = getAnnotationName(e.library, isPublicJSAnnotation); | 417 var libraryJSName = getAnnotationName(e.library, isPublicJSAnnotation); |
401 var libraryPrefix = <String>[]; | 418 var jsName = |
402 if (libraryJSName != null && libraryJSName.isNotEmpty) { | 419 getAnnotationName(e, isPublicJSAnnotation) ?? _getElementName(e); |
403 libraryPrefix.addAll(libraryJSName.split('.')); | 420 return libraryJSName != null ? '$libraryJSName.$jsName' : jsName; |
404 } | |
405 | |
406 var original = e; | |
407 var variableElement = e; | |
408 if (original is PropertyAccessorElement) { | |
409 variableElement = original.variable; | |
410 if (original.isSynthetic) e = variableElement; | |
411 } | |
412 | |
413 String elementJSName; | |
414 if (findAnnotation(e, isPublicJSAnnotation) != null) { | |
415 elementJSName = getAnnotationName(e, isPublicJSAnnotation) ?? ''; | |
416 } | |
417 | |
418 if (variableElement is TopLevelVariableElement) { | |
419 elementJSName = _jsInteropStaticMemberName(original); | |
420 } | |
421 if (elementJSName == null) return null; | |
422 | |
423 var elementJSParts = <String>[]; | |
424 if (elementJSName.isNotEmpty) { | |
425 elementJSParts.addAll(elementJSName.split('.')); | |
426 } else { | |
427 elementJSParts.add(e.name); | |
428 } | |
429 | |
430 return libraryPrefix..addAll(elementJSParts); | |
431 } | 421 } |
432 | 422 |
433 JS.Expression _emitJSInterop(Element e) { | 423 JS.Expression _emitJSInterop(Element e) { |
434 var jsName = _getJSName(e); | 424 var jsName = _getJSNameWithoutGlobal(e); |
435 if (jsName == null) return null; | 425 return jsName != null ? _emitJSInteropForGlobal(jsName) : null; |
436 var fullName = ['global']..addAll(jsName); | 426 } |
437 JS.Expression access = _runtimeModule; | 427 |
438 for (var part in fullName) { | 428 JS.Expression _emitJSInteropForGlobal(String name) { |
439 access = new JS.PropertyAccess(access, js.string(part)); | 429 var access = _callHelper('global'); |
| 430 for (var part in name.split('.')) { |
| 431 access = new JS.PropertyAccess(access, js.escapedString(part, "'")); |
440 } | 432 } |
441 return access; | 433 return access; |
442 } | 434 } |
443 | 435 |
444 String _jsInteropStaticMemberName(Element e) { | 436 JS.Expression _emitJSInteropStaticMemberName(Element e) { |
445 if (e?.library == null || | 437 if (!_isJSElement(e)) return null; |
446 findAnnotation(e.library, isPublicJSAnnotation) == null) { | 438 var name = getAnnotationName(e, isPublicJSAnnotation); |
447 return null; | 439 if (name != null) { |
| 440 if (name.contains('.')) { |
| 441 throw new UnsupportedError( |
| 442 'static members do not support "." in their names. ' |
| 443 'See https://github.com/dart-lang/sdk/issues/27926'); |
| 444 } |
| 445 } else { |
| 446 name = _getElementName(e); |
448 } | 447 } |
449 if (e is ExecutableElement && e.isExternal) { | 448 return js.escapedString(name, "'"); |
450 return getAnnotationName(e, isPublicJSAnnotation) ?? | |
451 (e is PropertyAccessorElement ? e.variable : e).name; | |
452 } | |
453 if (e is PropertyInducingElement && e.getter.isExternal) { | |
454 return getAnnotationName(e, isPublicJSAnnotation) ?? e.name; | |
455 } | |
456 return null; | |
457 } | |
458 | |
459 String _emitJSInteropStaticMemberName(Element e) { | |
460 var name = _jsInteropStaticMemberName(e); | |
461 // We do not support statics names with JS annotations containing dots. | |
462 // See https://github.com/dart-lang/sdk/issues/27926 | |
463 if (name != null && name.contains('.')) { | |
464 throw new UnsupportedError( | |
465 'We do not support JS annotations containing dots on static members. ' | |
466 'See https://github.com/dart-lang/sdk/issues/27926'); | |
467 } | |
468 return name; | |
469 } | 449 } |
470 | 450 |
471 /// Flattens blocks in [items] to a single list. | 451 /// Flattens blocks in [items] to a single list. |
472 /// | 452 /// |
473 /// This will not flatten blocks that are marked as being scopes. | 453 /// This will not flatten blocks that are marked as being scopes. |
474 void _copyAndFlattenBlocks( | 454 void _copyAndFlattenBlocks( |
475 List<JS.ModuleItem> result, Iterable<JS.ModuleItem> items) { | 455 List<JS.ModuleItem> result, Iterable<JS.ModuleItem> items) { |
476 for (var item in items) { | 456 for (var item in items) { |
477 if (item is JS.Block && !item.isScope) { | 457 if (item is JS.Block && !item.isScope) { |
478 _copyAndFlattenBlocks(result, item.statements); | 458 _copyAndFlattenBlocks(result, item.statements); |
(...skipping 371 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
850 ])); | 830 ])); |
851 } | 831 } |
852 | 832 |
853 if (isGeneric) { | 833 if (isGeneric) { |
854 return _defineClassTypeArguments( | 834 return _defineClassTypeArguments( |
855 classElem, typeFormals, _statement(block)); | 835 classElem, typeFormals, _statement(block)); |
856 } | 836 } |
857 return _statement(block); | 837 return _statement(block); |
858 } | 838 } |
859 | 839 |
860 JS.Statement _emitJsType(Element e) { | 840 JS.Statement _emitJSType(Element e) { |
861 var jsTypeName = getAnnotationName(e, isJSAnnotation); | 841 var jsTypeName = getAnnotationName(e, isJSAnnotation); |
862 if (jsTypeName == null || jsTypeName == e.name) return null; | 842 if (jsTypeName == null || jsTypeName == e.name) return null; |
863 | 843 |
864 // We export the JS type as if it was a Dart type. For example this allows | 844 // We export the JS type as if it was a Dart type. For example this allows |
865 // `dom.InputElement` to actually be HTMLInputElement. | 845 // `dom.InputElement` to actually be HTMLInputElement. |
866 // TODO(jmesserly): if we had the JS name on the Element, we could just | 846 // TODO(jmesserly): if we had the JS name on the Element, we could just |
867 // generate it correctly when we refer to it. | 847 // generate it correctly when we refer to it. |
868 return js.statement('# = #;', [_emitTopLevelName(e), jsTypeName]); | 848 return js.statement('# = #;', [_emitTopLevelName(e), jsTypeName]); |
869 } | 849 } |
870 | 850 |
871 @override | 851 @override |
872 JS.Statement visitClassDeclaration(ClassDeclaration node) { | 852 JS.Statement visitClassDeclaration(ClassDeclaration node) { |
873 var classElem = resolutionMap.elementDeclaredByClassDeclaration(node); | 853 var classElem = resolutionMap.elementDeclaredByClassDeclaration(node); |
874 | 854 |
875 // If this class is annotated with `@JS`, then there is nothing to emit. | 855 // If this class is annotated with `@JS`, then there is nothing to emit. |
876 if (findAnnotation(classElem, isPublicJSAnnotation) != null) return null; | 856 if (findAnnotation(classElem, isPublicJSAnnotation) != null) return null; |
877 | 857 |
878 // If this is a JavaScript type, emit it now and then exit. | 858 // If this is a JavaScript type, emit it now and then exit. |
879 var jsTypeDef = _emitJsType(classElem); | 859 var jsTypeDef = _emitJSType(classElem); |
880 if (jsTypeDef != null) return jsTypeDef; | 860 if (jsTypeDef != null) return jsTypeDef; |
881 | 861 |
882 var ctors = <ConstructorDeclaration>[]; | 862 var ctors = <ConstructorDeclaration>[]; |
883 var allFields = <FieldDeclaration>[]; | 863 var allFields = <FieldDeclaration>[]; |
884 var fields = <FieldDeclaration>[]; | 864 var fields = <FieldDeclaration>[]; |
885 var staticFields = <FieldDeclaration>[]; | 865 var staticFields = <FieldDeclaration>[]; |
886 var methods = <MethodDeclaration>[]; | 866 var methods = <MethodDeclaration>[]; |
887 | 867 |
888 for (var member in node.members) { | 868 for (var member in node.members) { |
889 if (member is ConstructorDeclaration) { | 869 if (member is ConstructorDeclaration) { |
(...skipping 802 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1692 _runtimeModule, | 1672 _runtimeModule, |
1693 _propertyName(jsPeerName), | 1673 _propertyName(jsPeerName), |
1694 _emitTopLevelName(classElem) | 1674 _emitTopLevelName(classElem) |
1695 ])); | 1675 ])); |
1696 } | 1676 } |
1697 } | 1677 } |
1698 | 1678 |
1699 JS.Statement _setBaseClass(ClassElement classElem, JS.Expression className, | 1679 JS.Statement _setBaseClass(ClassElement classElem, JS.Expression className, |
1700 List<String> jsPeerNames, List<JS.Statement> body) { | 1680 List<String> jsPeerNames, List<JS.Statement> body) { |
1701 var typeFormals = classElem.typeParameters; | 1681 var typeFormals = classElem.typeParameters; |
1702 if (jsPeerNames.isNotEmpty && typeFormals.isNotEmpty) { | 1682 if (jsPeerNames.length == 1 && typeFormals.isNotEmpty) { |
1703 for (var peer in jsPeerNames) { | 1683 var newBaseClass = _callHelper('global.#', jsPeerNames[0]); |
1704 // TODO(jmesserly): we should just extend Array in the first place | 1684 body.add(_callHelperStatement( |
1705 var newBaseClass = _callHelper('global.#', [peer]); | 1685 'setExtensionBaseClass(#, #);', [className, newBaseClass])); |
1706 body.add(_callHelperStatement( | |
1707 'setExtensionBaseClass(#, #);', [className, newBaseClass])); | |
1708 } | |
1709 } else if (_hasDeferredSupertype.contains(classElem)) { | 1686 } else if (_hasDeferredSupertype.contains(classElem)) { |
1710 // TODO(vsm): consider just threading the deferred supertype through | 1687 // TODO(vsm): consider just threading the deferred supertype through |
1711 // instead of recording classElem in a set on the class and recomputing | 1688 // instead of recording classElem in a set on the class and recomputing |
1712 var newBaseClass = _emitType(classElem.type.superclass, | 1689 var newBaseClass = _emitType(classElem.type.superclass, |
1713 nameType: false, subClass: classElem, className: className); | 1690 nameType: false, subClass: classElem, className: className); |
1714 if (classElem.type.mixins.isNotEmpty) { | 1691 if (classElem.type.mixins.isNotEmpty) { |
1715 var mixins = classElem.type.mixins | 1692 var mixins = classElem.type.mixins |
1716 .map((t) => _emitType(t, nameType: false)) | 1693 .map((t) => _emitType(t, nameType: false)) |
1717 .toList(); | 1694 .toList(); |
1718 mixins.insert(0, newBaseClass); | 1695 mixins.insert(0, newBaseClass); |
(...skipping 1034 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2753 } | 2730 } |
2754 return _callHelper('gbind(#, #)', [simpleId, typeArgs]); | 2731 return _callHelper('gbind(#, #)', [simpleId, typeArgs]); |
2755 } | 2732 } |
2756 | 2733 |
2757 /// Emits a simple identifier, handling implicit `this` as well as | 2734 /// Emits a simple identifier, handling implicit `this` as well as |
2758 /// going through the qualified library name if necessary, but *not* handling | 2735 /// going through the qualified library name if necessary, but *not* handling |
2759 /// inferred generic function instantiation. | 2736 /// inferred generic function instantiation. |
2760 JS.Expression _emitSimpleIdentifier(SimpleIdentifier node) { | 2737 JS.Expression _emitSimpleIdentifier(SimpleIdentifier node) { |
2761 var accessor = resolutionMap.staticElementForIdentifier(node); | 2738 var accessor = resolutionMap.staticElementForIdentifier(node); |
2762 if (accessor == null) { | 2739 if (accessor == null) { |
2763 return _callHelper( | 2740 return _throwUnsafe('unresolved identifier: ' + (node.name ?? '<null>')); |
2764 'throw(Error("compile error: unresolved identifier: " + #))', | |
2765 js.escapedString(node.name ?? '<null>')); | |
2766 } | 2741 } |
2767 | 2742 |
2768 // Get the original declaring element. If we had a property accessor, this | 2743 // Get the original declaring element. If we had a property accessor, this |
2769 // indirects back to a (possibly synthetic) field. | 2744 // indirects back to a (possibly synthetic) field. |
2770 var element = accessor; | 2745 var element = accessor; |
2771 if (accessor is PropertyAccessorElement) element = accessor.variable; | 2746 if (accessor is PropertyAccessorElement) element = accessor.variable; |
2772 | 2747 |
2773 // type literal | 2748 // type literal |
2774 if (element is TypeDefiningElement) { | 2749 if (element is TypeDefiningElement) { |
2775 _declareBeforeUse(element); | 2750 _declareBeforeUse(element); |
(...skipping 24 matching lines...) Expand all Loading... |
2800 var member = _emitMemberName(name, | 2775 var member = _emitMemberName(name, |
2801 isStatic: isStatic, type: type, element: accessor); | 2776 isStatic: isStatic, type: type, element: accessor); |
2802 | 2777 |
2803 if (isStatic) { | 2778 if (isStatic) { |
2804 var dynType = _emitStaticAccess(type); | 2779 var dynType = _emitStaticAccess(type); |
2805 return new JS.PropertyAccess(dynType, member); | 2780 return new JS.PropertyAccess(dynType, member); |
2806 } | 2781 } |
2807 | 2782 |
2808 // For instance members, we add implicit-this. | 2783 // For instance members, we add implicit-this. |
2809 // For method tear-offs, we ensure it's a bound method. | 2784 // For method tear-offs, we ensure it's a bound method. |
2810 var tearOff = element is MethodElement && !inInvocationContext(node); | 2785 if (element is MethodElement && |
2811 if (tearOff) { | 2786 !inInvocationContext(node) && |
2812 // To be safe always use the symbolized name when binding on a native | 2787 !_isJSNative(element.enclosingElement)) { |
2813 // class as bind assumes the name will match the name class sigatures | 2788 return _callHelper('bind(this, #)', member); |
2814 // which is symbolized for native classes. | |
2815 var safeName = _emitMemberName(name, | |
2816 isStatic: isStatic, | |
2817 type: type, | |
2818 element: accessor, | |
2819 alwaysSymbolizeNative: true); | |
2820 return _callHelper('bind(this, #)', safeName); | |
2821 } | 2789 } |
2822 return js.call('this.#', member); | 2790 return js.call('this.#', member); |
2823 } | 2791 } |
2824 | 2792 |
2825 if (element is ParameterElement) { | 2793 if (element is ParameterElement) { |
2826 return _emitParameter(element); | 2794 return _emitParameter(element); |
2827 } | 2795 } |
2828 | 2796 |
2829 // If this is one of our compiler's temporary variables, return its JS form. | 2797 // If this is one of our compiler's temporary variables, return its JS form. |
2830 if (element is TemporaryVariableElement) { | 2798 if (element is TemporaryVariableElement) { |
(...skipping 242 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3073 return _callHelper('dynamic'); | 3041 return _callHelper('dynamic'); |
3074 } else if (type.isBottom) { | 3042 } else if (type.isBottom) { |
3075 return _callHelper('bottom'); | 3043 return _callHelper('bottom'); |
3076 } | 3044 } |
3077 | 3045 |
3078 var element = type.element; | 3046 var element = type.element; |
3079 if (element is TypeDefiningElement) { | 3047 if (element is TypeDefiningElement) { |
3080 _declareBeforeUse(element); | 3048 _declareBeforeUse(element); |
3081 } | 3049 } |
3082 | 3050 |
3083 var interop = _emitJSInterop(element); | |
3084 // Type parameters don't matter as JS interop types cannot be reified. | 3051 // Type parameters don't matter as JS interop types cannot be reified. |
3085 // We have to use lazy JS types because until we have proper module | 3052 // We have to use lazy JS types because until we have proper module |
3086 // loading for JS libraries bundled with Dart libraries, we will sometimes | 3053 // loading for JS libraries bundled with Dart libraries, we will sometimes |
3087 // need to load Dart libraries before the corresponding JS libraries are | 3054 // need to load Dart libraries before the corresponding JS libraries are |
3088 // actually loaded. | 3055 // actually loaded. |
3089 // Given a JS type such as: | 3056 // Given a JS type such as: |
3090 // @JS('google.maps.Location') | 3057 // @JS('google.maps.Location') |
3091 // class Location { ... } | 3058 // class Location { ... } |
3092 // We can't emit a reference to MyType because the JS library that defines | 3059 // We can't emit a reference to MyType because the JS library that defines |
3093 // it may be loaded after our code. So for now, we use a special lazy type | 3060 // it may be loaded after our code. So for now, we use a special lazy type |
3094 // object to represent MyType. | 3061 // object to represent MyType. |
3095 // Anonymous JS types do not have a corresponding concrete JS type so we | 3062 // Anonymous JS types do not have a corresponding concrete JS type so we |
3096 // have to use a helper to define them. | 3063 // have to use a helper to define them. |
3097 if (interop != null) { | 3064 if (_isObjectLiteral(element)) { |
3098 if (_isObjectLiteral(element)) { | 3065 return _callHelper('anonymousJSType(#)', js.escapedString(element.name)); |
3099 return _callHelper( | 3066 } |
3100 'lazyAnonymousJSType(#)', js.string(element.displayName)); | 3067 var jsName = _getJSNameWithoutGlobal(element); |
3101 } else { | 3068 if (jsName != null) { |
3102 return _callHelper('lazyJSType(() => #, #)', | 3069 return _callHelper('lazyJSType(() => #, #)', |
3103 [interop, js.string(_getJSName(element).join('.'))]); | 3070 [_emitJSInteropForGlobal(jsName), js.escapedString(jsName)]); |
3104 } | |
3105 } | 3071 } |
3106 | 3072 |
3107 // TODO(jmesserly): like constants, should we hoist function types out of | 3073 // TODO(jmesserly): like constants, should we hoist function types out of |
3108 // methods? Similar issue with generic types. For all of these, we may want | 3074 // methods? Similar issue with generic types. For all of these, we may want |
3109 // to canonicalize them too, at least when inside the same library. | 3075 // to canonicalize them too, at least when inside the same library. |
3110 var name = type.name; | 3076 var name = type.name; |
3111 if (name == '' || name == null || lowerTypedef) { | 3077 if (name == '' || name == null || lowerTypedef) { |
3112 // TODO(jmesserly): should we change how typedefs work? They currently | 3078 // TODO(jmesserly): should we change how typedefs work? They currently |
3113 // go through use similar logic as generic classes. This makes them | 3079 // go through use similar logic as generic classes. This makes them |
3114 // different from universal function types. | 3080 // different from universal function types. |
(...skipping 14 matching lines...) Expand all Loading... |
3129 if (args.any((a) => !a.isDynamic)) { | 3095 if (args.any((a) => !a.isDynamic)) { |
3130 jsArgs = args.map((x) => _emitType(x, | 3096 jsArgs = args.map((x) => _emitType(x, |
3131 nameType: nameType, | 3097 nameType: nameType, |
3132 hoistType: hoistType, | 3098 hoistType: hoistType, |
3133 subClass: subClass, | 3099 subClass: subClass, |
3134 className: className)); | 3100 className: className)); |
3135 } else if (lowerGeneric || element == subClass) { | 3101 } else if (lowerGeneric || element == subClass) { |
3136 jsArgs = []; | 3102 jsArgs = []; |
3137 } | 3103 } |
3138 if (jsArgs != null) { | 3104 if (jsArgs != null) { |
3139 var genericName = _emitTopLevelName(element, suffix: '\$'); | 3105 var genericName = _emitTopLevelNameNoInterop(element, suffix: '\$'); |
3140 var typeRep = js.call('#(#)', [genericName, jsArgs]); | 3106 var typeRep = js.call('#(#)', [genericName, jsArgs]); |
3141 return nameType | 3107 return nameType |
3142 ? _typeTable.nameType(type, typeRep, hoistType: hoistType) | 3108 ? _typeTable.nameType(type, typeRep, hoistType: hoistType) |
3143 : typeRep; | 3109 : typeRep; |
3144 } | 3110 } |
3145 } | 3111 } |
3146 | 3112 |
3147 return _emitTopLevelNameNoInterop(element); | 3113 return _emitTopLevelNameNoInterop(element); |
3148 } | 3114 } |
3149 | 3115 |
3150 JS.PropertyAccess _emitTopLevelName(Element e, {String suffix: ''}) { | 3116 JS.PropertyAccess _emitTopLevelName(Element e, {String suffix: ''}) { |
3151 var interop = _emitJSInterop(e); | 3117 return _emitJSInterop(e) ?? _emitTopLevelNameNoInterop(e, suffix: suffix); |
3152 if (interop != null) return interop; | |
3153 return _emitTopLevelNameNoInterop(e, suffix: suffix); | |
3154 } | 3118 } |
3155 | 3119 |
3156 JS.PropertyAccess _emitTopLevelNameNoInterop(Element e, {String suffix: ''}) { | 3120 JS.PropertyAccess _emitTopLevelNameNoInterop(Element e, {String suffix: ''}) { |
3157 String name = getJSExportName(e) + suffix; | 3121 var name = getJSExportName(e) ?? _getElementName(e); |
3158 return new JS.PropertyAccess( | 3122 return new JS.PropertyAccess( |
3159 emitLibraryName(e.library), _propertyName(name)); | 3123 emitLibraryName(e.library), _propertyName(name + suffix)); |
3160 } | 3124 } |
3161 | 3125 |
3162 @override | 3126 @override |
3163 JS.Expression visitAssignmentExpression(AssignmentExpression node) { | 3127 JS.Expression visitAssignmentExpression(AssignmentExpression node) { |
3164 var left = node.leftHandSide; | 3128 var left = node.leftHandSide; |
3165 var right = node.rightHandSide; | 3129 var right = node.rightHandSide; |
3166 if (node.operator.type == TokenType.EQ) return _emitSet(left, right); | 3130 if (node.operator.type == TokenType.EQ) return _emitSet(left, right); |
3167 var op = node.operator.lexeme; | 3131 var op = node.operator.lexeme; |
3168 assert(op.endsWith('=')); | 3132 assert(op.endsWith('=')); |
3169 op = op.substring(0, op.length - 1); // remove trailing '=' | 3133 op = op.substring(0, op.length - 1); // remove trailing '=' |
(...skipping 277 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3447 if (!_superAllowed && jsTarget is JS.Super) { | 3411 if (!_superAllowed && jsTarget is JS.Super) { |
3448 return _getSuperHelper(member, jsName) | 3412 return _getSuperHelper(member, jsName) |
3449 ..sourceInformation = jsTarget.sourceInformation; | 3413 ..sourceInformation = jsTarget.sourceInformation; |
3450 } | 3414 } |
3451 return new JS.PropertyAccess(jsTarget, jsName); | 3415 return new JS.PropertyAccess(jsTarget, jsName); |
3452 } | 3416 } |
3453 | 3417 |
3454 JS.Expression _getSuperHelper(Element member, JS.Expression jsName) { | 3418 JS.Expression _getSuperHelper(Element member, JS.Expression jsName) { |
3455 var jsMethod = _superHelpers.putIfAbsent(member.name, () { | 3419 var jsMethod = _superHelpers.putIfAbsent(member.name, () { |
3456 if (member is PropertyAccessorElement) { | 3420 if (member is PropertyAccessorElement) { |
3457 var field = member.variable as FieldElement; | |
3458 var name = field.name; | |
3459 var isSetter = member.isSetter; | 3421 var isSetter = member.isSetter; |
3460 var fn = js.call( | 3422 var fn = js.call( |
3461 isSetter | 3423 isSetter |
3462 ? 'function(x) { super[#] = x; }' | 3424 ? 'function(x) { super[#] = x; }' |
3463 : 'function() { return super[#]; }', | 3425 : 'function() { return super[#]; }', |
3464 [jsName]); | 3426 [jsName]); |
3465 return new JS.Method(new JS.TemporaryId(name), fn, | 3427 return new JS.Method(new JS.TemporaryId(member.variable.name), fn, |
3466 isGetter: !isSetter, isSetter: isSetter); | 3428 isGetter: !isSetter, isSetter: isSetter); |
3467 } else { | 3429 } else { |
3468 var method = member as MethodElement; | 3430 var method = member as MethodElement; |
3469 var name = method.name; | 3431 var name = method.name; |
3470 // For generic methods, we can simply pass along the type arguments, | 3432 // For generic methods, we can simply pass along the type arguments, |
3471 // and let the resulting closure accept the actual arguments. | 3433 // and let the resulting closure accept the actual arguments. |
3472 List<JS.Identifier> params; | 3434 List<JS.Identifier> params; |
3473 if (method.typeParameters.isNotEmpty) { | 3435 if (method.typeParameters.isNotEmpty) { |
3474 params = _emitTypeFormals(method.typeParameters); | 3436 params = _emitTypeFormals(method.typeParameters); |
3475 } else { | 3437 } else { |
(...skipping 15 matching lines...) Expand all Loading... |
3491 } | 3453 } |
3492 | 3454 |
3493 JS.Expression _emitMethodCall(Expression target, MethodInvocation node) { | 3455 JS.Expression _emitMethodCall(Expression target, MethodInvocation node) { |
3494 var args = _emitArgumentList(node.argumentList); | 3456 var args = _emitArgumentList(node.argumentList); |
3495 var typeArgs = _emitInvokeTypeArguments(node); | 3457 var typeArgs = _emitInvokeTypeArguments(node); |
3496 | 3458 |
3497 var type = getStaticType(target); | 3459 var type = getStaticType(target); |
3498 var element = node.methodName.staticElement; | 3460 var element = node.methodName.staticElement; |
3499 bool isStatic = element is ExecutableElement && element.isStatic; | 3461 bool isStatic = element is ExecutableElement && element.isStatic; |
3500 var name = node.methodName.name; | 3462 var name = node.methodName.name; |
3501 var memberName = | 3463 var jsName = |
3502 _emitMemberName(name, type: type, isStatic: isStatic, element: element); | 3464 _emitMemberName(name, type: type, isStatic: isStatic, element: element); |
3503 | 3465 |
3504 JS.Expression jsTarget = _emitTarget(target, element, isStatic); | 3466 JS.Expression jsTarget = _emitTarget(target, element, isStatic); |
3505 if (isDynamicInvoke(target) || isDynamicInvoke(node.methodName)) { | 3467 if (isDynamicInvoke(target) || isDynamicInvoke(node.methodName)) { |
3506 if (typeArgs != null) { | 3468 if (typeArgs != null) { |
3507 return _callHelper('#(#, #, #, #)', [ | 3469 return _callHelper('#(#, #, #, #)', [ |
3508 _emitDynamicOperationName('dgsend'), | 3470 _emitDynamicOperationName('dgsend'), |
3509 jsTarget, | 3471 jsTarget, |
3510 new JS.ArrayInitializer(typeArgs), | 3472 new JS.ArrayInitializer(typeArgs), |
3511 memberName, | 3473 jsName, |
3512 args | 3474 args |
3513 ]); | 3475 ]); |
3514 } else { | 3476 } else { |
3515 return _callHelper('#(#, #, #)', | 3477 return _callHelper('#(#, #, #)', |
3516 [_emitDynamicOperationName('dsend'), jsTarget, memberName, args]); | 3478 [_emitDynamicOperationName('dsend'), jsTarget, jsName, args]); |
3517 } | 3479 } |
3518 } | 3480 } |
3519 if (_isObjectMemberCall(target, name)) { | 3481 if (_isObjectMemberCall(target, name)) { |
3520 assert(typeArgs == null); // Object methods don't take type args. | 3482 assert(typeArgs == null); // Object methods don't take type args. |
3521 return _callHelper('#(#, #)', [name, jsTarget, args]); | 3483 return _callHelper('#(#, #)', [name, jsTarget, args]); |
3522 } | 3484 } |
3523 jsTarget = _emitTargetAccess(jsTarget, memberName, element); | 3485 jsTarget = _emitTargetAccess(jsTarget, jsName, element); |
3524 if (typeArgs != null) jsTarget = new JS.Call(jsTarget, typeArgs); | 3486 if (typeArgs != null) jsTarget = new JS.Call(jsTarget, typeArgs); |
3525 | 3487 |
3526 return new JS.Call(jsTarget, args); | 3488 return new JS.Call(jsTarget, args); |
3527 } | 3489 } |
3528 | 3490 |
3529 JS.Expression _emitDynamicInvoke( | 3491 JS.Expression _emitDynamicInvoke( |
3530 InvocationExpression node, JS.Expression fn, List<JS.Expression> args) { | 3492 InvocationExpression node, JS.Expression fn, List<JS.Expression> args) { |
3531 var typeArgs = _emitInvokeTypeArguments(node); | 3493 var typeArgs = _emitInvokeTypeArguments(node); |
3532 if (typeArgs != null) { | 3494 if (typeArgs != null) { |
3533 return _callHelper( | 3495 return _callHelper( |
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3670 return f.typeArguments.skip(g.typeArguments.length); | 3632 return f.typeArguments.skip(g.typeArguments.length); |
3671 } | 3633 } |
3672 | 3634 |
3673 /// Emits code for the `JS(...)` macro. | 3635 /// Emits code for the `JS(...)` macro. |
3674 _emitForeignJS(MethodInvocation node) { | 3636 _emitForeignJS(MethodInvocation node) { |
3675 var e = node.methodName.staticElement; | 3637 var e = node.methodName.staticElement; |
3676 if (isInlineJS(e)) { | 3638 if (isInlineJS(e)) { |
3677 var args = node.argumentList.arguments; | 3639 var args = node.argumentList.arguments; |
3678 // arg[0] is static return type, used in `RestrictedStaticTypeAnalyzer` | 3640 // arg[0] is static return type, used in `RestrictedStaticTypeAnalyzer` |
3679 var code = args[1]; | 3641 var code = args[1]; |
3680 List<AstNode> templateArgs; | 3642 List<Expression> templateArgs; |
3681 String source; | 3643 String source; |
3682 if (code is StringInterpolation) { | 3644 if (code is StringInterpolation) { |
3683 if (args.length > 2) { | 3645 if (args.length > 2) { |
3684 throw new ArgumentError( | 3646 throw new ArgumentError( |
3685 "Can't mix template args and string interpolation in JS calls."); | 3647 "Can't mix template args and string interpolation in JS calls."); |
3686 } | 3648 } |
3687 templateArgs = <Expression>[]; | 3649 templateArgs = <Expression>[]; |
3688 source = code.elements.map((element) { | 3650 source = code.elements.map((element) { |
3689 if (element is InterpolationExpression) { | 3651 if (element is InterpolationExpression) { |
3690 templateArgs.add(element.expression); | 3652 templateArgs.add(element.expression); |
(...skipping 22 matching lines...) Expand all Loading... |
3713 _extensionTypes.isNativeClass(containingClass.element)) { | 3675 _extensionTypes.isNativeClass(containingClass.element)) { |
3714 var constructorName = source.substring(4, source.indexOf('(')); | 3676 var constructorName = source.substring(4, source.indexOf('(')); |
3715 var className = containingClass.name.name; | 3677 var className = containingClass.name.name; |
3716 if (className == constructorName) { | 3678 if (className == constructorName) { |
3717 source = | 3679 source = |
3718 source.replaceFirst('new $className(', 'new self.$className('); | 3680 source.replaceFirst('new $className(', 'new self.$className('); |
3719 } | 3681 } |
3720 } | 3682 } |
3721 } | 3683 } |
3722 | 3684 |
| 3685 JS.Expression visitTemplateArg(Expression arg) { |
| 3686 if (arg is InvocationExpression) { |
| 3687 var e = arg is MethodInvocation |
| 3688 ? arg.methodName.staticElement |
| 3689 : (arg as FunctionExpressionInvocation).staticElement; |
| 3690 if (e?.name == 'getGenericClass' && |
| 3691 e.library.name == 'dart._runtime' && |
| 3692 arg.argumentList.arguments.length == 1) { |
| 3693 var typeArg = arg.argumentList.arguments[0]; |
| 3694 if (typeArg is SimpleIdentifier) { |
| 3695 var typeElem = typeArg.staticElement; |
| 3696 if (typeElem is TypeDefiningElement && |
| 3697 typeElem.type is ParameterizedType) { |
| 3698 return _emitTopLevelNameNoInterop(typeElem, suffix: '\$'); |
| 3699 } |
| 3700 } |
| 3701 } |
| 3702 } |
| 3703 return _visit(arg); |
| 3704 } |
| 3705 |
3723 // TODO(rnystrom): The JS() calls are almost never nested, and probably | 3706 // TODO(rnystrom): The JS() calls are almost never nested, and probably |
3724 // really shouldn't be, but there are at least a couple of calls in the | 3707 // really shouldn't be, but there are at least a couple of calls in the |
3725 // HTML library where an argument to JS() is itself a JS() call. If those | 3708 // HTML library where an argument to JS() is itself a JS() call. If those |
3726 // go away, this can just assert(!_isInForeignJS). | 3709 // go away, this can just assert(!_isInForeignJS). |
3727 // Inside JS(), type names evaluate to the raw runtime type, not the | 3710 // Inside JS(), type names evaluate to the raw runtime type, not the |
3728 // wrapped Type object. | 3711 // wrapped Type object. |
3729 var wasInForeignJS = _isInForeignJS; | 3712 var wasInForeignJS = _isInForeignJS; |
3730 _isInForeignJS = true; | 3713 _isInForeignJS = true; |
| 3714 var jsArgs = templateArgs.map(visitTemplateArg).toList(); |
| 3715 _isInForeignJS = wasInForeignJS; |
3731 | 3716 |
3732 var template = js.parseForeignJS(source); | 3717 var result = js.parseForeignJS(source).instantiate(jsArgs); |
3733 var result = template.instantiate(_visitList(templateArgs)); | |
3734 | |
3735 _isInForeignJS = wasInForeignJS; | |
3736 | 3718 |
3737 // `throw` is emitted as a statement by `parseForeignJS`. | 3719 // `throw` is emitted as a statement by `parseForeignJS`. |
3738 assert(result is JS.Expression || node.parent is ExpressionStatement); | 3720 assert(result is JS.Expression || node.parent is ExpressionStatement); |
3739 return result; | 3721 return result; |
3740 } | 3722 } |
3741 return null; | 3723 return null; |
3742 } | 3724 } |
3743 | 3725 |
3744 @override | 3726 @override |
3745 JS.Expression visitFunctionExpressionInvocation( | 3727 JS.Expression visitFunctionExpressionInvocation( |
(...skipping 307 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4053 ConstructorElement element, | 4035 ConstructorElement element, |
4054 DartType type, | 4036 DartType type, |
4055 SimpleIdentifier name, | 4037 SimpleIdentifier name, |
4056 ArgumentList argumentList, | 4038 ArgumentList argumentList, |
4057 bool isConst) { | 4039 bool isConst) { |
4058 JS.Expression emitNew() { | 4040 JS.Expression emitNew() { |
4059 JS.Expression ctor; | 4041 JS.Expression ctor; |
4060 bool isFactory = false; | 4042 bool isFactory = false; |
4061 bool isNative = false; | 4043 bool isNative = false; |
4062 if (element == null) { | 4044 if (element == null) { |
4063 ctor = _callHelper( | 4045 ctor = _throwUnsafe('unresolved constructor: ${type?.name ?? '<null>'}' |
4064 'throw(Error("compile error: unresolved constructor: " ' | 4046 '.${name?.name ?? '<unnamed>'}'); |
4065 '+ # + "." + #))', | |
4066 [ | |
4067 js.escapedString(type?.name ?? '<null>'), | |
4068 js.escapedString(name?.name ?? '<unnamed>') | |
4069 ]); | |
4070 } else { | 4047 } else { |
4071 ctor = _emitConstructorName(element, type, name); | 4048 ctor = _emitConstructorName(element, type, name); |
4072 isFactory = element.isFactory; | 4049 isFactory = element.isFactory; |
4073 var classElem = element.enclosingElement; | 4050 var classElem = element.enclosingElement; |
4074 isNative = _isJSNative(classElem); | 4051 isNative = _isJSNative(classElem); |
4075 } | 4052 } |
4076 var args = _emitArgumentList(argumentList); | 4053 var args = _emitArgumentList(argumentList); |
4077 // Native factory constructors are JS constructors - use new here. | 4054 // Native factory constructors are JS constructors - use new here. |
4078 return isFactory && !isNative | 4055 return isFactory && !isNative |
4079 ? new JS.Call(ctor, args) | 4056 ? new JS.Call(ctor, args) |
4080 : new JS.New(ctor, args); | 4057 : new JS.New(ctor, args); |
4081 } | 4058 } |
4082 | 4059 |
4083 if (element != null && _isObjectLiteral(element.enclosingElement)) { | 4060 if (element != null && _isObjectLiteral(element.enclosingElement)) { |
4084 return _emitObjectLiteral(argumentList); | 4061 return _emitObjectLiteral(argumentList); |
4085 } | 4062 } |
4086 if (isConst) return _emitConst(emitNew); | 4063 if (isConst) return _emitConst(emitNew); |
4087 return emitNew(); | 4064 return emitNew(); |
4088 } | 4065 } |
4089 | 4066 |
4090 bool _isObjectLiteral(ClassElement classElem) { | 4067 bool _isObjectLiteral(Element classElem) { |
4091 return findAnnotation(classElem, isPublicJSAnnotation) != null && | 4068 return _isJSNative(classElem) && |
4092 findAnnotation(classElem, isJSAnonymousAnnotation) != null; | 4069 findAnnotation(classElem, isJSAnonymousAnnotation) != null; |
4093 } | 4070 } |
4094 | 4071 |
4095 bool _isJSNative(ClassElement classElem) => | 4072 bool _isJSNative(Element e) => |
4096 findAnnotation(classElem, isPublicJSAnnotation) != null; | 4073 findAnnotation(e, isPublicJSAnnotation) != null; |
4097 | 4074 |
4098 JS.Expression _emitObjectLiteral(ArgumentList argumentList) { | 4075 JS.Expression _emitObjectLiteral(ArgumentList argumentList) { |
4099 var args = _emitArgumentList(argumentList); | 4076 var args = _emitArgumentList(argumentList); |
4100 if (args.isEmpty) { | 4077 if (args.isEmpty) { |
4101 return js.call('{}'); | 4078 return js.call('{}'); |
4102 } | 4079 } |
4103 assert(args.single is JS.ObjectInitializer); | 4080 assert(args.single is JS.ObjectInitializer); |
4104 return args.single; | 4081 return args.single; |
4105 } | 4082 } |
4106 | 4083 |
(...skipping 266 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4373 } | 4350 } |
4374 return false; | 4351 return false; |
4375 } | 4352 } |
4376 | 4353 |
4377 int _asIntInRange(Expression expr, int low, int high) { | 4354 int _asIntInRange(Expression expr, int low, int high) { |
4378 expr = expr.unParenthesized; | 4355 expr = expr.unParenthesized; |
4379 if (expr is IntegerLiteral) { | 4356 if (expr is IntegerLiteral) { |
4380 if (expr.value >= low && expr.value <= high) return expr.value; | 4357 if (expr.value >= low && expr.value <= high) return expr.value; |
4381 return null; | 4358 return null; |
4382 } | 4359 } |
4383 int finishIdentifier(SimpleIdentifier identifier) { | 4360 |
4384 Element staticElement = identifier.staticElement; | 4361 Identifier id; |
4385 if (staticElement is PropertyAccessorElement && staticElement.isGetter) { | 4362 if (expr is SimpleIdentifier) { |
4386 PropertyInducingElement variable = staticElement.variable; | 4363 id = expr; |
4387 int value = variable?.computeConstantValue()?.toIntValue(); | 4364 } else if (expr is PrefixedIdentifier && !expr.isDeferred) { |
4388 if (value != null && value >= low && value <= high) return value; | 4365 id = expr.identifier; |
4389 } | 4366 } else { |
4390 return null; | 4367 return null; |
4391 } | 4368 } |
4392 | 4369 var element = id.staticElement; |
4393 if (expr is SimpleIdentifier) { | 4370 if (element is PropertyAccessorElement && element.isGetter) { |
4394 return finishIdentifier(expr); | 4371 var variable = element.variable; |
4395 } else if (expr is PrefixedIdentifier && !expr.isDeferred) { | 4372 int value = variable?.computeConstantValue()?.toIntValue(); |
4396 return finishIdentifier(expr.identifier); | 4373 if (value != null && value >= low && value <= high) return value; |
4397 } | 4374 } |
4398 return null; | 4375 return null; |
4399 } | 4376 } |
4400 | 4377 |
4401 bool _isDefinitelyNonNegative(Expression expr) { | 4378 bool _isDefinitelyNonNegative(Expression expr) { |
4402 expr = expr.unParenthesized; | 4379 expr = expr.unParenthesized; |
4403 if (expr is IntegerLiteral) { | 4380 if (expr is IntegerLiteral) { |
4404 return expr.value >= 0; | 4381 return expr.value >= 0; |
4405 } | 4382 } |
4406 if (_nodeIsBitwiseOperation(expr)) return true; | 4383 if (_nodeIsBitwiseOperation(expr)) return true; |
(...skipping 425 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4832 // TODO(jmesserly): handle explicitly passed type args. | 4809 // TODO(jmesserly): handle explicitly passed type args. |
4833 if (type == null) return null; | 4810 if (type == null) return null; |
4834 return _emitFunctionTypeArguments(type, instantiated); | 4811 return _emitFunctionTypeArguments(type, instantiated); |
4835 } | 4812 } |
4836 | 4813 |
4837 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. | 4814 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. |
4838 JS.Expression _emitAccess( | 4815 JS.Expression _emitAccess( |
4839 Expression target, SimpleIdentifier memberId, DartType resultType) { | 4816 Expression target, SimpleIdentifier memberId, DartType resultType) { |
4840 var accessor = memberId.staticElement; | 4817 var accessor = memberId.staticElement; |
4841 // If `member` is a getter/setter, get the corresponding | 4818 // If `member` is a getter/setter, get the corresponding |
4842 var field = | 4819 var field = _getNonAccessorElement(accessor); |
4843 accessor is PropertyAccessorElement ? accessor.variable : accessor; | |
4844 String memberName = memberId.name; | 4820 String memberName = memberId.name; |
4845 var typeArgs = _getTypeArgs(accessor, resultType); | 4821 var typeArgs = _getTypeArgs(accessor, resultType); |
4846 | 4822 |
4847 bool isStatic = field is ClassMemberElement && field.isStatic; | 4823 bool isStatic = field is ClassMemberElement && field.isStatic; |
4848 var name = _emitMemberName(memberName, | 4824 var jsName = _emitMemberName(memberName, |
4849 type: getStaticType(target), isStatic: isStatic, element: accessor); | 4825 type: getStaticType(target), isStatic: isStatic, element: accessor); |
4850 if (isDynamicInvoke(target)) { | 4826 if (isDynamicInvoke(target)) { |
4851 return _callHelper('#(#, #)', | 4827 return _callHelper('#(#, #)', |
4852 [_emitDynamicOperationName('dload'), _visit(target), name]); | 4828 [_emitDynamicOperationName('dload'), _visit(target), jsName]); |
4853 } | 4829 } |
4854 | 4830 |
4855 var jsTarget = _emitTarget(target, accessor, isStatic); | 4831 var jsTarget = _emitTarget(target, accessor, isStatic); |
4856 | 4832 |
4857 var isSuper = jsTarget is JS.Super; | 4833 var isSuper = jsTarget is JS.Super; |
4858 if (isSuper && | 4834 if (isSuper && |
4859 accessor.isSynthetic && | 4835 accessor.isSynthetic && |
4860 field is FieldElementImpl && | 4836 field is FieldElementImpl && |
4861 !virtualFields.isVirtual(field)) { | 4837 !virtualFields.isVirtual(field)) { |
4862 // If super.x is a sealed field, then x is an instance property since | 4838 // If super.x is a sealed field, then x is an instance property since |
4863 // subclasses cannot override x. | 4839 // subclasses cannot override x. |
4864 jsTarget = annotate(new JS.This(), target); | 4840 jsTarget = annotate(new JS.This(), target); |
4865 } | 4841 } |
4866 | 4842 |
4867 JS.Expression result; | 4843 JS.Expression result; |
4868 if (accessor is MethodElement && !isStatic) { | 4844 if (_isObjectMemberCall(target, memberName)) { |
4869 // Tear-off methods: explicitly bind it. | 4845 if (_isObjectMethod(memberName)) { |
4870 // To be safe always use the symbolized name when binding on a native | 4846 result = _callHelper('bind(#, #)', [jsTarget, jsName]); |
4871 // class as bind assumes the name will match the name class signatures | 4847 } else { |
4872 // which is symbolized for native classes. | 4848 result = _callHelper('#(#)', [memberName, jsTarget]); |
4873 var safeName = _emitMemberName(memberName, | 4849 } |
4874 type: getStaticType(target), | 4850 } else if (accessor is MethodElement && |
4875 isStatic: isStatic, | 4851 !isStatic && |
4876 element: accessor, | 4852 !_isJSNative(accessor.enclosingElement)) { |
4877 alwaysSymbolizeNative: true); | |
4878 if (isSuper) { | 4853 if (isSuper) { |
4879 result = _callHelper('bind(this, #, #)', | 4854 result = _callHelper('bind(this, #, #)', |
4880 [safeName, _emitTargetAccess(jsTarget, name, accessor)]); | 4855 [jsName, _emitTargetAccess(jsTarget, jsName, accessor)]); |
4881 } else if (_isObjectMemberCall(target, memberName)) { | |
4882 var fn = js.call( | |
4883 memberName == 'noSuchMethod' | |
4884 ? 'function(i) { return #.#(this, i); }' | |
4885 : 'function() { return #.#(this); }', | |
4886 [_runtimeModule, memberName]); | |
4887 result = _callHelper( | |
4888 'bind(#, #, #)', [jsTarget, _propertyName(memberName), fn]); | |
4889 } else { | 4856 } else { |
4890 result = _callHelper('bind(#, #)', [jsTarget, safeName]); | 4857 result = _callHelper('bind(#, #)', [jsTarget, jsName]); |
4891 } | 4858 } |
4892 } else if (_isObjectMemberCall(target, memberName)) { | |
4893 result = _callHelper('#(#)', [memberName, jsTarget]); | |
4894 } else { | 4859 } else { |
4895 result = _emitTargetAccess(jsTarget, name, accessor); | 4860 result = _emitTargetAccess(jsTarget, jsName, accessor); |
4896 } | 4861 } |
4897 if (typeArgs == null) { | 4862 return typeArgs == null |
4898 return result; | 4863 ? result |
4899 } | 4864 : _callHelper('gbind(#, #)', [result, typeArgs]); |
4900 return _callHelper('gbind(#, #)', [result, typeArgs]); | |
4901 } | 4865 } |
4902 | 4866 |
4903 JS.LiteralString _emitDynamicOperationName(String name) => | 4867 JS.LiteralString _emitDynamicOperationName(String name) => |
4904 js.string(options.replCompile ? '${name}Repl' : name); | 4868 js.string(options.replCompile ? '${name}Repl' : name); |
4905 | 4869 |
4906 /// Emits a generic send, like an operator method. | 4870 /// Emits a generic send, like an operator method. |
4907 /// | 4871 /// |
4908 /// **Please note** this function does not support method invocation syntax | 4872 /// **Please note** this function does not support method invocation syntax |
4909 /// `obj.name(args)` because that could be a getter followed by a call. | 4873 /// `obj.name(args)` because that could be a getter followed by a call. |
4910 /// See [visitMethodInvocation]. | 4874 /// See [visitMethodInvocation]. |
(...skipping 492 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
5403 JS.Expression result = _visit(node); | 5367 JS.Expression result = _visit(node); |
5404 if (isNullable(node)) result = _callHelper('test(#)', result); | 5368 if (isNullable(node)) result = _callHelper('test(#)', result); |
5405 return result; | 5369 return result; |
5406 } | 5370 } |
5407 | 5371 |
5408 /// Like [_emitMemberName], but for declaration sites. | 5372 /// Like [_emitMemberName], but for declaration sites. |
5409 /// | 5373 /// |
5410 /// Unlike call sites, we always have an element available, so we can use it | 5374 /// Unlike call sites, we always have an element available, so we can use it |
5411 /// directly rather than computing the relevant options for [_emitMemberName]. | 5375 /// directly rather than computing the relevant options for [_emitMemberName]. |
5412 JS.Expression _declareMemberName(ExecutableElement e, {bool useExtension}) { | 5376 JS.Expression _declareMemberName(ExecutableElement e, {bool useExtension}) { |
5413 var name = (e is PropertyAccessorElement) ? e.variable.name : e.name; | 5377 return _emitMemberName(_getElementName(e), |
5414 return _emitMemberName(name, | |
5415 isStatic: e.isStatic, | 5378 isStatic: e.isStatic, |
5416 useExtension: | 5379 useExtension: |
5417 useExtension ?? _extensionTypes.isNativeClass(e.enclosingElement)); | 5380 useExtension ?? _extensionTypes.isNativeClass(e.enclosingElement), |
| 5381 element: e); |
5418 } | 5382 } |
5419 | 5383 |
5420 /// This handles member renaming for private names and operators. | 5384 /// This handles member renaming for private names and operators. |
5421 /// | 5385 /// |
5422 /// Private names are generated using ES6 symbols: | 5386 /// Private names are generated using ES6 symbols: |
5423 /// | 5387 /// |
5424 /// // At the top of the module: | 5388 /// // At the top of the module: |
5425 /// let _x = Symbol('_x'); | 5389 /// let _x = Symbol('_x'); |
5426 /// let _y = Symbol('_y'); | 5390 /// let _y = Symbol('_y'); |
5427 /// ... | 5391 /// ... |
(...skipping 26 matching lines...) Expand all Loading... |
5454 /// | 5418 /// |
5455 /// Unary minus looks like: `x._negate()`. | 5419 /// Unary minus looks like: `x._negate()`. |
5456 /// | 5420 /// |
5457 /// Equality is a bit special, it is generated via the Dart `equals` runtime | 5421 /// Equality is a bit special, it is generated via the Dart `equals` runtime |
5458 /// helper, that checks for null. The user defined method is called '=='. | 5422 /// helper, that checks for null. The user defined method is called '=='. |
5459 /// | 5423 /// |
5460 JS.Expression _emitMemberName(String name, | 5424 JS.Expression _emitMemberName(String name, |
5461 {DartType type, | 5425 {DartType type, |
5462 bool isStatic: false, | 5426 bool isStatic: false, |
5463 bool useExtension, | 5427 bool useExtension, |
5464 bool alwaysSymbolizeNative: false, | |
5465 Element element}) { | 5428 Element element}) { |
5466 // Static members skip the rename steps and may require JS interop renames. | 5429 // Static members skip the rename steps and may require JS interop renames. |
5467 if (isStatic) { | 5430 if (isStatic) { |
5468 return _propertyName(_emitJSInteropStaticMemberName(element) ?? name); | 5431 return _emitJSInteropStaticMemberName(element) ?? _propertyName(name); |
| 5432 } |
| 5433 |
| 5434 // We allow some (illegal in Dart) member names to be used in our private |
| 5435 // SDK code. These renames need to be included at every declaration, |
| 5436 // including overrides in subclasses. |
| 5437 if (element != null) { |
| 5438 var runtimeName = getJSExportName(element); |
| 5439 if (runtimeName != null) return _propertyName(runtimeName); |
5469 } | 5440 } |
5470 | 5441 |
5471 if (name.startsWith('_')) { | 5442 if (name.startsWith('_')) { |
5472 return _emitPrivateNameSymbol(currentLibrary, name); | 5443 return _emitPrivateNameSymbol(currentLibrary, name); |
5473 } | 5444 } |
5474 | 5445 |
5475 // When generating synthetic names, we use _ as the prefix, since Dart names | 5446 // When generating synthetic names, we use _ as the prefix, since Dart names |
5476 // won't have this (eliminated above), nor will static names reach here. | 5447 // won't have this (eliminated above), nor will static names reach here. |
5477 switch (name) { | 5448 switch (name) { |
5478 case '[]': | 5449 case '[]': |
5479 name = '_get'; | 5450 name = '_get'; |
5480 break; | 5451 break; |
5481 case '[]=': | 5452 case '[]=': |
5482 name = '_set'; | 5453 name = '_set'; |
5483 break; | 5454 break; |
5484 case 'unary-': | 5455 case 'unary-': |
5485 name = '_negate'; | 5456 name = '_negate'; |
5486 break; | 5457 break; |
5487 case 'constructor': | 5458 case 'constructor': |
5488 case 'prototype': | 5459 case 'prototype': |
5489 name = '_$name'; | 5460 name = '_$name'; |
5490 break; | 5461 break; |
5491 } | 5462 } |
5492 | 5463 |
5493 var result = _propertyName(name); | 5464 var result = _propertyName(name); |
5494 | 5465 |
5495 if (useExtension == null) { | 5466 useExtension ??= _isSymbolizedMember(type, name); |
5496 // Dart "extension" methods. Used for JS Array, Boolean, Number, String. | |
5497 var baseType = type; | |
5498 while (baseType is TypeParameterType) { | |
5499 baseType = (baseType.element as TypeParameterElement).bound; | |
5500 } | |
5501 useExtension = baseType is InterfaceType && | |
5502 _isSymbolizedMember(baseType, name, alwaysSymbolizeNative); | |
5503 } | |
5504 | 5467 |
5505 return useExtension | 5468 return useExtension |
5506 ? js.call('#.#', [_extensionSymbolsModule, result]) | 5469 ? js.call('#.#', [_extensionSymbolsModule, result]) |
5507 : result; | 5470 : result; |
5508 } | 5471 } |
5509 | 5472 |
5510 var _forwardingCache = new HashMap<Element, Map<String, ExecutableElement>>(); | 5473 var _forwardingCache = new HashMap<Element, Map<String, ExecutableElement>>(); |
5511 | 5474 |
5512 Element _lookupForwardedMember(ClassElement element, String name) { | 5475 Element _lookupForwardedMember(ClassElement element, String name) { |
5513 // We only care about public methods. | 5476 // We only care about public methods. |
(...skipping 16 matching lines...) Expand all Loading... |
5530 return member; | 5493 return member; |
5531 } | 5494 } |
5532 | 5495 |
5533 /// Don't symbolize native members that just forward to the underlying | 5496 /// Don't symbolize native members that just forward to the underlying |
5534 /// native member. We limit this to non-renamed members as the receiver | 5497 /// native member. We limit this to non-renamed members as the receiver |
5535 /// may be a mock type. | 5498 /// may be a mock type. |
5536 /// | 5499 /// |
5537 /// Note, this is an underlying assumption here that, if another native type | 5500 /// Note, this is an underlying assumption here that, if another native type |
5538 /// subtypes this one, it also forwards this member to its underlying native | 5501 /// subtypes this one, it also forwards this member to its underlying native |
5539 /// one without renaming. | 5502 /// one without renaming. |
5540 bool _isSymbolizedMember( | 5503 bool _isSymbolizedMember(DartType type, String name) { |
5541 InterfaceType type, String name, bool alwaysSymbolizeNative) { | |
5542 // Object members are handled separately. | 5504 // Object members are handled separately. |
5543 if (isObjectMember(name)) { | 5505 if (isObjectMember(name)) { |
5544 return false; | 5506 return false; |
5545 } | 5507 } |
5546 | 5508 |
5547 var element = type.element; | 5509 while (type is TypeParameterType) { |
5548 if (_extensionTypes.isNativeClass(element)) { | 5510 type = (type as TypeParameterType).bound; |
5549 var member = _lookupForwardedMember(element, name); | 5511 } |
| 5512 if (type is InterfaceType) { |
| 5513 var element = type.element; |
| 5514 if (_extensionTypes.isNativeClass(element)) { |
| 5515 var member = _lookupForwardedMember(element, name); |
5550 | 5516 |
5551 // Fields on a native class are implicitly native. | 5517 // Fields on a native class are implicitly native. |
5552 // Methods/getters/setters are marked external/native. | 5518 // Methods/getters/setters are marked external/native. |
5553 if (member is FieldElement || | 5519 if (member is FieldElement || |
5554 member is ExecutableElement && member.isExternal) { | 5520 member is ExecutableElement && member.isExternal) { |
5555 var jsName = getAnnotationName(member, isJsName); | 5521 var jsName = getAnnotationName(member, isJsName); |
5556 return alwaysSymbolizeNative || (jsName != null && jsName != name); | 5522 return jsName != null && jsName != name; |
5557 } else { | 5523 } else { |
5558 // Non-external members must be symbolized. | 5524 // Non-external members must be symbolized. |
5559 return true; | 5525 return true; |
| 5526 } |
5560 } | 5527 } |
| 5528 // If the receiver *may* be a native type (i.e., an interface allowed to |
| 5529 // be implemented by a native class), conservatively symbolize - we don't |
| 5530 // know whether it'll be implemented via forwarding. |
| 5531 // TODO(vsm): Consider CHA here to be less conservative. |
| 5532 return _extensionTypes.isNativeInterface(element); |
5561 } | 5533 } |
5562 // If the receiver *may* be a native type (i.e., an interface allowed to | 5534 return false; |
5563 // be implemented by a native class), conservatively symbolize - we don't | |
5564 // whether it'll be implemented via forwarding. | |
5565 // TODO(vsm): Consider CHA here to be less conservative. | |
5566 return _extensionTypes.isNativeInterface(element); | |
5567 } | 5535 } |
5568 | 5536 |
5569 JS.TemporaryId _emitPrivateNameSymbol(LibraryElement library, String name) { | 5537 JS.TemporaryId _emitPrivateNameSymbol(LibraryElement library, String name) { |
5570 return _privateNames | 5538 return _privateNames |
5571 .putIfAbsent(library, () => new HashMap()) | 5539 .putIfAbsent(library, () => new HashMap()) |
5572 .putIfAbsent(name, () { | 5540 .putIfAbsent(name, () { |
5573 var id = new JS.TemporaryId(name); | 5541 var id = new JS.TemporaryId(name); |
5574 _moduleItems.add( | 5542 _moduleItems.add( |
5575 js.statement('const # = Symbol(#);', [id, js.string(id.name, "'")])); | 5543 js.statement('const # = Symbol(#);', [id, js.string(id.name, "'")])); |
5576 return id; | 5544 return id; |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
5610 switch (name) { | 5578 switch (name) { |
5611 case 'hashCode': | 5579 case 'hashCode': |
5612 case 'toString': | 5580 case 'toString': |
5613 case 'noSuchMethod': | 5581 case 'noSuchMethod': |
5614 case 'runtimeType': | 5582 case 'runtimeType': |
5615 return true; | 5583 return true; |
5616 } | 5584 } |
5617 return false; | 5585 return false; |
5618 } | 5586 } |
5619 | 5587 |
| 5588 bool _isObjectMethod(String name) => |
| 5589 name == 'toString' || name == 'noSuchMethod'; |
| 5590 |
5620 // TODO(leafp): Various analyzer pieces computed similar things. | 5591 // TODO(leafp): Various analyzer pieces computed similar things. |
5621 // Share this logic somewhere? | 5592 // Share this logic somewhere? |
5622 DartType _getExpectedReturnType(ExecutableElement element) { | 5593 DartType _getExpectedReturnType(ExecutableElement element) { |
5623 FunctionType functionType = element.type; | 5594 FunctionType functionType = element.type; |
5624 if (functionType == null) { | 5595 if (functionType == null) { |
5625 return DynamicTypeImpl.instance; | 5596 return DynamicTypeImpl.instance; |
5626 } | 5597 } |
5627 var type = functionType.returnType; | 5598 var type = functionType.returnType; |
5628 | 5599 |
5629 InterfaceType expectedType = null; | 5600 InterfaceType expectedType = null; |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
5668 | 5639 |
5669 JS.Statement _callHelperStatement(String code, args) { | 5640 JS.Statement _callHelperStatement(String code, args) { |
5670 if (args is List) { | 5641 if (args is List) { |
5671 args.insert(0, _runtimeModule); | 5642 args.insert(0, _runtimeModule); |
5672 } else { | 5643 } else { |
5673 args = [_runtimeModule, args]; | 5644 args = [_runtimeModule, args]; |
5674 } | 5645 } |
5675 return js.statement('#.$code', args); | 5646 return js.statement('#.$code', args); |
5676 } | 5647 } |
5677 | 5648 |
| 5649 JS.Expression _throwUnsafe(String message) => _callHelper( |
| 5650 'throw(Error(#))', js.escapedString("compile error: $message")); |
| 5651 |
5678 _unreachable(AstNode node) { | 5652 _unreachable(AstNode node) { |
5679 throw new UnsupportedError( | 5653 throw new UnsupportedError( |
5680 'tried to generate an unreachable node: `$node`'); | 5654 'tried to generate an unreachable node: `$node`'); |
5681 } | 5655 } |
5682 | 5656 |
5683 /// Unused, see methods for emitting declarations. | 5657 /// Unused, see methods for emitting declarations. |
5684 @override | 5658 @override |
5685 visitAnnotation(node) => _unreachable(node); | 5659 visitAnnotation(node) => _unreachable(node); |
5686 | 5660 |
5687 /// Unused, see [_emitArgumentList]. | 5661 /// Unused, see [_emitArgumentList]. |
(...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
5895 if (targetIdentifier.staticElement is! PrefixElement) return false; | 5869 if (targetIdentifier.staticElement is! PrefixElement) return false; |
5896 var prefix = targetIdentifier.staticElement as PrefixElement; | 5870 var prefix = targetIdentifier.staticElement as PrefixElement; |
5897 | 5871 |
5898 // The library the prefix is referring to must come from a deferred import. | 5872 // The library the prefix is referring to must come from a deferred import. |
5899 var containingLibrary = resolutionMap | 5873 var containingLibrary = resolutionMap |
5900 .elementDeclaredByCompilationUnit(target.root as CompilationUnit) | 5874 .elementDeclaredByCompilationUnit(target.root as CompilationUnit) |
5901 .library; | 5875 .library; |
5902 var imports = containingLibrary.getImportsWithPrefix(prefix); | 5876 var imports = containingLibrary.getImportsWithPrefix(prefix); |
5903 return imports.length == 1 && imports[0].isDeferred; | 5877 return imports.length == 1 && imports[0].isDeferred; |
5904 } | 5878 } |
OLD | NEW |