Chromium Code Reviews| 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 364 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 375 var properties = <JS.Property>[]; | 375 var properties = <JS.Property>[]; |
| 376 _libraries.forEach((library, value) { | 376 _libraries.forEach((library, value) { |
| 377 // TODO(jacobr): we could specify a short library name instead of the | 377 // TODO(jacobr): we could specify a short library name instead of the |
| 378 // full library uri if we wanted to save space. | 378 // full library uri if we wanted to save space. |
| 379 properties.add(new JS.Property( | 379 properties.add(new JS.Property( |
| 380 js.string(jsLibraryDebuggerName(_libraryRoot, library)), value)); | 380 js.string(jsLibraryDebuggerName(_libraryRoot, library)), value)); |
| 381 }); | 381 }); |
| 382 return new JS.ObjectInitializer(properties, multiline: true); | 382 return new JS.ObjectInitializer(properties, multiline: true); |
| 383 } | 383 } |
| 384 | 384 |
| 385 List<String> _getJSName(Element e) { | 385 String _getJSNameWithoutGlobal(Element e) { |
|
vsm
2017/06/30 18:08:47
This will still support something like:
@JS('foo.
Jennifer Messerly
2017/07/05 21:17:16
exactly, it just skips the redundant step. I belie
| |
| 386 if (e.library == null || | 386 if (e.library == null || !_isJSNative(e.library)) return null; |
| 387 findAnnotation(e.library, isPublicJSAnnotation) == null) { | 387 bool isJS = |
| 388 return null; | 388 e is PropertyAccessorElement && e.variable is TopLevelVariableElement |
| 389 } | 389 ? e.isExternal |
| 390 : _isJSNative(e); | |
|
vsm
2017/06/30 18:08:47
Maybe this should e.isExternal || _isJSNative(e) ?
Jennifer Messerly
2017/07/05 21:17:16
That's what I was wondering too. This is exactly t
| |
| 391 if (!isJS) return null; | |
| 390 | 392 |
| 391 var libraryJSName = getAnnotationName(e.library, isPublicJSAnnotation); | 393 var libraryJSName = getAnnotationName(e.library, isPublicJSAnnotation); |
| 392 var libraryPrefix = <String>[]; | 394 var jsName = getAnnotationName(e, isPublicJSAnnotation); |
| 393 if (libraryJSName != null && libraryJSName.isNotEmpty) { | 395 jsName ??= e is PropertyAccessorElement ? e.variable.name : e.name; |
| 394 libraryPrefix.addAll(libraryJSName.split('.')); | 396 return libraryJSName != null ? '$libraryJSName.$jsName' : jsName; |
| 395 } | |
| 396 | |
| 397 var original = e; | |
| 398 var variableElement = e; | |
| 399 if (original is PropertyAccessorElement) { | |
| 400 variableElement = original.variable; | |
| 401 if (original.isSynthetic) e = variableElement; | |
| 402 } | |
| 403 | |
| 404 String elementJSName; | |
| 405 if (findAnnotation(e, isPublicJSAnnotation) != null) { | |
| 406 elementJSName = getAnnotationName(e, isPublicJSAnnotation) ?? ''; | |
| 407 } | |
| 408 | |
| 409 if (variableElement is TopLevelVariableElement) { | |
| 410 elementJSName = _jsInteropStaticMemberName(original); | |
| 411 } | |
| 412 if (elementJSName == null) return null; | |
| 413 | |
| 414 var elementJSParts = <String>[]; | |
| 415 if (elementJSName.isNotEmpty) { | |
| 416 elementJSParts.addAll(elementJSName.split('.')); | |
| 417 } else { | |
| 418 elementJSParts.add(e.name); | |
| 419 } | |
| 420 | |
| 421 return libraryPrefix..addAll(elementJSParts); | |
| 422 } | 397 } |
| 423 | 398 |
| 424 JS.Expression _emitJSInterop(Element e) { | 399 JS.Expression _emitJSInterop(Element e) { |
| 425 var jsName = _getJSName(e); | 400 var jsName = _getJSNameWithoutGlobal(e); |
| 426 if (jsName == null) return null; | 401 return jsName != null ? _emitJSInteropForJSName(jsName) : null; |
| 427 var fullName = ['global']..addAll(jsName); | 402 } |
| 428 JS.Expression access = _runtimeModule; | 403 |
| 429 for (var part in fullName) { | 404 JS.Expression _emitJSInteropForJSName(String name) { |
| 430 access = new JS.PropertyAccess(access, js.string(part)); | 405 var access = _callHelper('global'); |
| 406 for (var part in name.split('.')) { | |
| 407 access = new JS.PropertyAccess(access, js.escapedString(part, "'")); | |
| 431 } | 408 } |
| 432 return access; | 409 return access; |
| 433 } | 410 } |
| 434 | 411 |
| 435 String _jsInteropStaticMemberName(Element e) { | 412 JS.Expression _emitJSInteropStaticMemberName(Element e) { |
| 436 if (e?.library == null || | 413 if (e?.library == null || !_isJSNative(e.library)) return null; |
| 437 findAnnotation(e.library, isPublicJSAnnotation) == null) { | 414 bool isJS = e is ExecutableElement && e.isExternal || |
| 438 return null; | 415 e is PropertyInducingElement && e.getter.isExternal; |
| 439 } | 416 if (!isJS) return null; |
| 440 if (e is ExecutableElement && e.isExternal) { | 417 var name = getAnnotationName(e, isPublicJSAnnotation); |
| 441 return getAnnotationName(e, isPublicJSAnnotation) ?? | |
| 442 (e is PropertyAccessorElement ? e.variable : e).name; | |
| 443 } | |
| 444 if (e is PropertyInducingElement && e.getter.isExternal) { | |
| 445 return getAnnotationName(e, isPublicJSAnnotation) ?? e.name; | |
| 446 } | |
| 447 return null; | |
| 448 } | |
| 449 | |
| 450 String _emitJSInteropStaticMemberName(Element e) { | |
| 451 var name = _jsInteropStaticMemberName(e); | |
| 452 // We do not support statics names with JS annotations containing dots. | 418 // We do not support statics names with JS annotations containing dots. |
| 453 // See https://github.com/dart-lang/sdk/issues/27926 | 419 // See https://github.com/dart-lang/sdk/issues/27926 |
| 454 if (name != null && name.contains('.')) { | 420 if (name != null) { |
| 455 throw new UnsupportedError( | 421 if (name.contains('.')) { |
| 456 'We do not support JS annotations containing dots on static members. ' | 422 throw new UnsupportedError( |
| 457 'See https://github.com/dart-lang/sdk/issues/27926'); | 423 'We do not support JS annotations containing dots on static members. ' |
| 424 'See https://github.com/dart-lang/sdk/issues/27926'); | |
| 425 } | |
| 426 } else { | |
| 427 name = e is PropertyAccessorElement ? e.variable.name : e.name; | |
| 458 } | 428 } |
| 459 return name; | 429 return js.escapedString(name, "'"); |
| 460 } | 430 } |
| 461 | 431 |
| 462 /// Flattens blocks in [items] to a single list. | 432 /// Flattens blocks in [items] to a single list. |
| 463 /// | 433 /// |
| 464 /// This will not flatten blocks that are marked as being scopes. | 434 /// This will not flatten blocks that are marked as being scopes. |
| 465 void _copyAndFlattenBlocks( | 435 void _copyAndFlattenBlocks( |
| 466 List<JS.ModuleItem> result, Iterable<JS.ModuleItem> items) { | 436 List<JS.ModuleItem> result, Iterable<JS.ModuleItem> items) { |
| 467 for (var item in items) { | 437 for (var item in items) { |
| 468 if (item is JS.Block && !item.isScope) { | 438 if (item is JS.Block && !item.isScope) { |
| 469 _copyAndFlattenBlocks(result, item.statements); | 439 _copyAndFlattenBlocks(result, item.statements); |
| (...skipping 372 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 842 ])); | 812 ])); |
| 843 } | 813 } |
| 844 | 814 |
| 845 if (isGeneric) { | 815 if (isGeneric) { |
| 846 return _defineClassTypeArguments( | 816 return _defineClassTypeArguments( |
| 847 classElem, typeFormals, _statement(block)); | 817 classElem, typeFormals, _statement(block)); |
| 848 } | 818 } |
| 849 return _statement(block); | 819 return _statement(block); |
| 850 } | 820 } |
| 851 | 821 |
| 852 JS.Statement _emitJsType(Element e) { | 822 JS.Statement _emitJSType(Element e) { |
| 853 var jsTypeName = getAnnotationName(e, isJSAnnotation); | 823 var jsTypeName = getAnnotationName(e, isJSAnnotation); |
| 854 if (jsTypeName == null || jsTypeName == e.name) return null; | 824 if (jsTypeName == null || jsTypeName == e.name) return null; |
| 855 | 825 |
| 856 // We export the JS type as if it was a Dart type. For example this allows | 826 // We export the JS type as if it was a Dart type. For example this allows |
| 857 // `dom.InputElement` to actually be HTMLInputElement. | 827 // `dom.InputElement` to actually be HTMLInputElement. |
| 858 // TODO(jmesserly): if we had the JS name on the Element, we could just | 828 // TODO(jmesserly): if we had the JS name on the Element, we could just |
| 859 // generate it correctly when we refer to it. | 829 // generate it correctly when we refer to it. |
| 860 return js.statement('# = #;', [_emitTopLevelName(e), jsTypeName]); | 830 return js.statement('# = #;', [_emitTopLevelName(e), jsTypeName]); |
| 861 } | 831 } |
| 862 | 832 |
| 863 @override | 833 @override |
| 864 JS.Statement visitClassDeclaration(ClassDeclaration node) { | 834 JS.Statement visitClassDeclaration(ClassDeclaration node) { |
| 865 var classElem = resolutionMap.elementDeclaredByClassDeclaration(node); | 835 var classElem = resolutionMap.elementDeclaredByClassDeclaration(node); |
| 866 | 836 |
| 867 // If this class is annotated with `@JS`, then there is nothing to emit. | 837 // If this class is annotated with `@JS`, then there is nothing to emit. |
| 868 if (findAnnotation(classElem, isPublicJSAnnotation) != null) return null; | 838 if (findAnnotation(classElem, isPublicJSAnnotation) != null) return null; |
| 869 | 839 |
| 870 // If this is a JavaScript type, emit it now and then exit. | 840 // If this is a JavaScript type, emit it now and then exit. |
| 871 var jsTypeDef = _emitJsType(classElem); | 841 var jsTypeDef = _emitJSType(classElem); |
| 872 if (jsTypeDef != null) return jsTypeDef; | 842 if (jsTypeDef != null) return jsTypeDef; |
| 873 | 843 |
| 874 var ctors = <ConstructorDeclaration>[]; | 844 var ctors = <ConstructorDeclaration>[]; |
| 875 var allFields = <FieldDeclaration>[]; | 845 var allFields = <FieldDeclaration>[]; |
| 876 var fields = <FieldDeclaration>[]; | 846 var fields = <FieldDeclaration>[]; |
| 877 var staticFields = <FieldDeclaration>[]; | 847 var staticFields = <FieldDeclaration>[]; |
| 878 var methods = <MethodDeclaration>[]; | 848 var methods = <MethodDeclaration>[]; |
| 879 | 849 |
| 880 for (var member in node.members) { | 850 for (var member in node.members) { |
| 881 if (member is ConstructorDeclaration) { | 851 if (member is ConstructorDeclaration) { |
| (...skipping 802 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1684 _runtimeModule, | 1654 _runtimeModule, |
| 1685 _propertyName(jsPeerName), | 1655 _propertyName(jsPeerName), |
| 1686 _emitTopLevelName(classElem) | 1656 _emitTopLevelName(classElem) |
| 1687 ])); | 1657 ])); |
| 1688 } | 1658 } |
| 1689 } | 1659 } |
| 1690 | 1660 |
| 1691 JS.Statement _setBaseClass(ClassElement classElem, JS.Expression className, | 1661 JS.Statement _setBaseClass(ClassElement classElem, JS.Expression className, |
| 1692 List<String> jsPeerNames, List<JS.Statement> body) { | 1662 List<String> jsPeerNames, List<JS.Statement> body) { |
| 1693 var typeFormals = classElem.typeParameters; | 1663 var typeFormals = classElem.typeParameters; |
| 1694 if (jsPeerNames.isNotEmpty && typeFormals.isNotEmpty) { | 1664 if (jsPeerNames.length == 1 && typeFormals.isNotEmpty) { |
| 1695 for (var peer in jsPeerNames) { | 1665 var newBaseClass = _callHelper('global.#', jsPeerNames[0]); |
| 1696 // TODO(jmesserly): we should just extend Array in the first place | 1666 body.add(_callHelperStatement( |
| 1697 var newBaseClass = _callHelper('global.#', [peer]); | 1667 'setExtensionBaseClass(#, #);', [className, newBaseClass])); |
|
vsm
2017/06/30 18:08:47
Ha - I was just trying to figure why JSArray's pro
Jennifer Messerly
2017/07/05 21:17:16
yup! it's pretty crazy ... hope it's a tad better
| |
| 1698 body.add(_callHelperStatement( | |
| 1699 'setExtensionBaseClass(#, #);', [className, newBaseClass])); | |
| 1700 } | |
| 1701 } else if (_hasDeferredSupertype.contains(classElem)) { | 1668 } else if (_hasDeferredSupertype.contains(classElem)) { |
| 1702 // TODO(vsm): consider just threading the deferred supertype through | 1669 // TODO(vsm): consider just threading the deferred supertype through |
| 1703 // instead of recording classElem in a set on the class and recomputing | 1670 // instead of recording classElem in a set on the class and recomputing |
| 1704 var newBaseClass = _emitType(classElem.type.superclass, | 1671 var newBaseClass = _emitType(classElem.type.superclass, |
| 1705 nameType: false, subClass: classElem, className: className); | 1672 nameType: false, subClass: classElem, className: className); |
| 1706 if (classElem.type.mixins.isNotEmpty) { | 1673 if (classElem.type.mixins.isNotEmpty) { |
| 1707 var mixins = classElem.type.mixins | 1674 var mixins = classElem.type.mixins |
| 1708 .map((t) => _emitType(t, nameType: false)) | 1675 .map((t) => _emitType(t, nameType: false)) |
| 1709 .toList(); | 1676 .toList(); |
| 1710 mixins.insert(0, newBaseClass); | 1677 mixins.insert(0, newBaseClass); |
| (...skipping 1034 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2745 } | 2712 } |
| 2746 return _callHelper('gbind(#, #)', [simpleId, typeArgs]); | 2713 return _callHelper('gbind(#, #)', [simpleId, typeArgs]); |
| 2747 } | 2714 } |
| 2748 | 2715 |
| 2749 /// Emits a simple identifier, handling implicit `this` as well as | 2716 /// Emits a simple identifier, handling implicit `this` as well as |
| 2750 /// going through the qualified library name if necessary, but *not* handling | 2717 /// going through the qualified library name if necessary, but *not* handling |
| 2751 /// inferred generic function instantiation. | 2718 /// inferred generic function instantiation. |
| 2752 JS.Expression _emitSimpleIdentifier(SimpleIdentifier node) { | 2719 JS.Expression _emitSimpleIdentifier(SimpleIdentifier node) { |
| 2753 var accessor = resolutionMap.staticElementForIdentifier(node); | 2720 var accessor = resolutionMap.staticElementForIdentifier(node); |
| 2754 if (accessor == null) { | 2721 if (accessor == null) { |
| 2755 return _callHelper( | 2722 return _throwUnsafe('unresolved identifier: ' + (node.name ?? '<null>')); |
| 2756 'throw(Error("compile error: unresolved identifier: " + #))', | |
| 2757 js.escapedString(node.name ?? '<null>')); | |
| 2758 } | 2723 } |
| 2759 | 2724 |
| 2760 // Get the original declaring element. If we had a property accessor, this | 2725 // Get the original declaring element. If we had a property accessor, this |
| 2761 // indirects back to a (possibly synthetic) field. | 2726 // indirects back to a (possibly synthetic) field. |
| 2762 var element = accessor; | 2727 var element = accessor; |
| 2763 if (accessor is PropertyAccessorElement) element = accessor.variable; | 2728 if (accessor is PropertyAccessorElement) element = accessor.variable; |
| 2764 | 2729 |
| 2765 // type literal | 2730 // type literal |
| 2766 if (element is TypeDefiningElement) { | 2731 if (element is TypeDefiningElement) { |
| 2767 _declareBeforeUse(element); | 2732 _declareBeforeUse(element); |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 2792 var member = _emitMemberName(name, | 2757 var member = _emitMemberName(name, |
| 2793 isStatic: isStatic, type: type, element: accessor); | 2758 isStatic: isStatic, type: type, element: accessor); |
| 2794 | 2759 |
| 2795 if (isStatic) { | 2760 if (isStatic) { |
| 2796 var dynType = _emitStaticAccess(type); | 2761 var dynType = _emitStaticAccess(type); |
| 2797 return new JS.PropertyAccess(dynType, member); | 2762 return new JS.PropertyAccess(dynType, member); |
| 2798 } | 2763 } |
| 2799 | 2764 |
| 2800 // For instance members, we add implicit-this. | 2765 // For instance members, we add implicit-this. |
| 2801 // For method tear-offs, we ensure it's a bound method. | 2766 // For method tear-offs, we ensure it's a bound method. |
| 2802 var tearOff = element is MethodElement && !inInvocationContext(node); | 2767 if (element is MethodElement && |
| 2803 if (tearOff) { | 2768 !inInvocationContext(node) && |
| 2804 // To be safe always use the symbolized name when binding on a native | 2769 !_isJSNative(element.enclosingElement)) { |
| 2805 // class as bind assumes the name will match the name class sigatures | 2770 return _callHelper('bind(this, #)', member); |
| 2806 // which is symbolized for native classes. | |
| 2807 var safeName = _emitMemberName(name, | |
| 2808 isStatic: isStatic, | |
| 2809 type: type, | |
| 2810 element: accessor, | |
| 2811 alwaysSymbolizeNative: true); | |
| 2812 return _callHelper('bind(this, #)', safeName); | |
| 2813 } | 2771 } |
| 2814 return js.call('this.#', member); | 2772 return js.call('this.#', member); |
| 2815 } | 2773 } |
| 2816 | 2774 |
| 2817 if (element is ParameterElement) { | 2775 if (element is ParameterElement) { |
| 2818 return _emitParameter(element); | 2776 return _emitParameter(element); |
| 2819 } | 2777 } |
| 2820 | 2778 |
| 2821 // If this is one of our compiler's temporary variables, return its JS form. | 2779 // If this is one of our compiler's temporary variables, return its JS form. |
| 2822 if (element is TemporaryVariableElement) { | 2780 if (element is TemporaryVariableElement) { |
| (...skipping 242 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3065 return _callHelper('dynamic'); | 3023 return _callHelper('dynamic'); |
| 3066 } else if (type.isBottom) { | 3024 } else if (type.isBottom) { |
| 3067 return _callHelper('bottom'); | 3025 return _callHelper('bottom'); |
| 3068 } | 3026 } |
| 3069 | 3027 |
| 3070 var element = type.element; | 3028 var element = type.element; |
| 3071 if (element is TypeDefiningElement) { | 3029 if (element is TypeDefiningElement) { |
| 3072 _declareBeforeUse(element); | 3030 _declareBeforeUse(element); |
| 3073 } | 3031 } |
| 3074 | 3032 |
| 3075 var interop = _emitJSInterop(element); | |
| 3076 // Type parameters don't matter as JS interop types cannot be reified. | 3033 // Type parameters don't matter as JS interop types cannot be reified. |
| 3077 // We have to use lazy JS types because until we have proper module | 3034 // We have to use lazy JS types because until we have proper module |
| 3078 // loading for JS libraries bundled with Dart libraries, we will sometimes | 3035 // loading for JS libraries bundled with Dart libraries, we will sometimes |
| 3079 // need to load Dart libraries before the corresponding JS libraries are | 3036 // need to load Dart libraries before the corresponding JS libraries are |
| 3080 // actually loaded. | 3037 // actually loaded. |
| 3081 // Given a JS type such as: | 3038 // Given a JS type such as: |
| 3082 // @JS('google.maps.Location') | 3039 // @JS('google.maps.Location') |
| 3083 // class Location { ... } | 3040 // class Location { ... } |
| 3084 // We can't emit a reference to MyType because the JS library that defines | 3041 // We can't emit a reference to MyType because the JS library that defines |
| 3085 // it may be loaded after our code. So for now, we use a special lazy type | 3042 // it may be loaded after our code. So for now, we use a special lazy type |
| 3086 // object to represent MyType. | 3043 // object to represent MyType. |
| 3087 // Anonymous JS types do not have a corresponding concrete JS type so we | 3044 // Anonymous JS types do not have a corresponding concrete JS type so we |
| 3088 // have to use a helper to define them. | 3045 // have to use a helper to define them. |
| 3089 if (interop != null) { | 3046 if (_isObjectLiteral(element)) { |
| 3090 if (_isObjectLiteral(element)) { | 3047 return _callHelper('anonymousJSType(#)', js.escapedString(element.name)); |
| 3091 return _callHelper( | 3048 } |
| 3092 'lazyAnonymousJSType(#)', js.string(element.displayName)); | 3049 var jsName = _getJSNameWithoutGlobal(element); |
| 3093 } else { | 3050 if (jsName != null) { |
| 3094 return _callHelper('lazyJSType(() => #, #)', | 3051 return _callHelper('lazyJSType(() => #, #)', |
| 3095 [interop, js.string(_getJSName(element).join('.'))]); | 3052 [_emitJSInteropForJSName(jsName), js.escapedString(jsName)]); |
| 3096 } | |
| 3097 } | 3053 } |
| 3098 | 3054 |
| 3099 // TODO(jmesserly): like constants, should we hoist function types out of | 3055 // TODO(jmesserly): like constants, should we hoist function types out of |
| 3100 // methods? Similar issue with generic types. For all of these, we may want | 3056 // methods? Similar issue with generic types. For all of these, we may want |
| 3101 // to canonicalize them too, at least when inside the same library. | 3057 // to canonicalize them too, at least when inside the same library. |
| 3102 var name = type.name; | 3058 var name = type.name; |
| 3103 if (name == '' || name == null || lowerTypedef) { | 3059 if (name == '' || name == null || lowerTypedef) { |
| 3104 // TODO(jmesserly): should we change how typedefs work? They currently | 3060 // TODO(jmesserly): should we change how typedefs work? They currently |
| 3105 // go through use similar logic as generic classes. This makes them | 3061 // go through use similar logic as generic classes. This makes them |
| 3106 // different from universal function types. | 3062 // different from universal function types. |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 3121 if (args.any((a) => !a.isDynamic)) { | 3077 if (args.any((a) => !a.isDynamic)) { |
| 3122 jsArgs = args.map((x) => _emitType(x, | 3078 jsArgs = args.map((x) => _emitType(x, |
| 3123 nameType: nameType, | 3079 nameType: nameType, |
| 3124 hoistType: hoistType, | 3080 hoistType: hoistType, |
| 3125 subClass: subClass, | 3081 subClass: subClass, |
| 3126 className: className)); | 3082 className: className)); |
| 3127 } else if (lowerGeneric || element == subClass) { | 3083 } else if (lowerGeneric || element == subClass) { |
| 3128 jsArgs = []; | 3084 jsArgs = []; |
| 3129 } | 3085 } |
| 3130 if (jsArgs != null) { | 3086 if (jsArgs != null) { |
| 3131 var genericName = _emitTopLevelName(element, suffix: '\$'); | 3087 var genericName = _emitTopLevelNameNoInterop(element, suffix: '\$'); |
| 3132 var typeRep = js.call('#(#)', [genericName, jsArgs]); | 3088 var typeRep = js.call('#(#)', [genericName, jsArgs]); |
| 3133 return nameType | 3089 return nameType |
| 3134 ? _typeTable.nameType(type, typeRep, hoistType: hoistType) | 3090 ? _typeTable.nameType(type, typeRep, hoistType: hoistType) |
| 3135 : typeRep; | 3091 : typeRep; |
| 3136 } | 3092 } |
| 3137 } | 3093 } |
| 3138 | 3094 |
| 3139 return _emitTopLevelNameNoInterop(element); | 3095 return _emitTopLevelNameNoInterop(element); |
| 3140 } | 3096 } |
| 3141 | 3097 |
| 3142 JS.PropertyAccess _emitTopLevelName(Element e, {String suffix: ''}) { | 3098 JS.PropertyAccess _emitTopLevelName(Element e, {String suffix: ''}) { |
| 3143 var interop = _emitJSInterop(e); | 3099 return _emitJSInterop(e) ?? _emitTopLevelNameNoInterop(e, suffix: suffix); |
| 3144 if (interop != null) return interop; | |
| 3145 return _emitTopLevelNameNoInterop(e, suffix: suffix); | |
| 3146 } | 3100 } |
| 3147 | 3101 |
| 3148 JS.PropertyAccess _emitTopLevelNameNoInterop(Element e, {String suffix: ''}) { | 3102 JS.PropertyAccess _emitTopLevelNameNoInterop(Element e, {String suffix: ''}) { |
| 3149 String name = getJSExportName(e) + suffix; | 3103 var name = getJSExportName(e) ?? |
| 3104 (e is PropertyAccessorElement ? e.variable.name : e.name); | |
|
vsm
2017/06/30 18:08:47
Consider factoring out - this guarded expr seems f
Jennifer Messerly
2017/07/05 21:17:16
Done.
| |
| 3150 return new JS.PropertyAccess( | 3105 return new JS.PropertyAccess( |
| 3151 emitLibraryName(e.library), _propertyName(name)); | 3106 emitLibraryName(e.library), _propertyName(name + suffix)); |
| 3152 } | 3107 } |
| 3153 | 3108 |
| 3154 @override | 3109 @override |
| 3155 JS.Expression visitAssignmentExpression(AssignmentExpression node) { | 3110 JS.Expression visitAssignmentExpression(AssignmentExpression node) { |
| 3156 var left = node.leftHandSide; | 3111 var left = node.leftHandSide; |
| 3157 var right = node.rightHandSide; | 3112 var right = node.rightHandSide; |
| 3158 if (node.operator.type == TokenType.EQ) return _emitSet(left, right); | 3113 if (node.operator.type == TokenType.EQ) return _emitSet(left, right); |
| 3159 var op = node.operator.lexeme; | 3114 var op = node.operator.lexeme; |
| 3160 assert(op.endsWith('=')); | 3115 assert(op.endsWith('=')); |
| 3161 op = op.substring(0, op.length - 1); // remove trailing '=' | 3116 op = op.substring(0, op.length - 1); // remove trailing '=' |
| (...skipping 277 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3439 if (!_superAllowed && jsTarget is JS.Super) { | 3394 if (!_superAllowed && jsTarget is JS.Super) { |
| 3440 return _getSuperHelper(member, jsName) | 3395 return _getSuperHelper(member, jsName) |
| 3441 ..sourceInformation = jsTarget.sourceInformation; | 3396 ..sourceInformation = jsTarget.sourceInformation; |
| 3442 } | 3397 } |
| 3443 return new JS.PropertyAccess(jsTarget, jsName); | 3398 return new JS.PropertyAccess(jsTarget, jsName); |
| 3444 } | 3399 } |
| 3445 | 3400 |
| 3446 JS.Expression _getSuperHelper(Element member, JS.Expression jsName) { | 3401 JS.Expression _getSuperHelper(Element member, JS.Expression jsName) { |
| 3447 var jsMethod = _superHelpers.putIfAbsent(member.name, () { | 3402 var jsMethod = _superHelpers.putIfAbsent(member.name, () { |
| 3448 if (member is PropertyAccessorElement) { | 3403 if (member is PropertyAccessorElement) { |
| 3449 var field = member.variable as FieldElement; | |
| 3450 var name = field.name; | |
| 3451 var isSetter = member.isSetter; | 3404 var isSetter = member.isSetter; |
| 3452 var fn = js.call( | 3405 var fn = js.call( |
| 3453 isSetter | 3406 isSetter |
| 3454 ? 'function(x) { super[#] = x; }' | 3407 ? 'function(x) { super[#] = x; }' |
| 3455 : 'function() { return super[#]; }', | 3408 : 'function() { return super[#]; }', |
| 3456 [jsName]); | 3409 [jsName]); |
| 3457 return new JS.Method(new JS.TemporaryId(name), fn, | 3410 return new JS.Method(new JS.TemporaryId(member.variable.name), fn, |
| 3458 isGetter: !isSetter, isSetter: isSetter); | 3411 isGetter: !isSetter, isSetter: isSetter); |
| 3459 } else { | 3412 } else { |
| 3460 var method = member as MethodElement; | 3413 var method = member as MethodElement; |
| 3461 var name = method.name; | 3414 var name = method.name; |
| 3462 // For generic methods, we can simply pass along the type arguments, | 3415 // For generic methods, we can simply pass along the type arguments, |
| 3463 // and let the resulting closure accept the actual arguments. | 3416 // and let the resulting closure accept the actual arguments. |
| 3464 List<JS.Identifier> params; | 3417 List<JS.Identifier> params; |
| 3465 if (method.typeParameters.isNotEmpty) { | 3418 if (method.typeParameters.isNotEmpty) { |
| 3466 params = _emitTypeFormals(method.typeParameters); | 3419 params = _emitTypeFormals(method.typeParameters); |
| 3467 } else { | 3420 } else { |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 3483 } | 3436 } |
| 3484 | 3437 |
| 3485 JS.Expression _emitMethodCall(Expression target, MethodInvocation node) { | 3438 JS.Expression _emitMethodCall(Expression target, MethodInvocation node) { |
| 3486 var args = _emitArgumentList(node.argumentList); | 3439 var args = _emitArgumentList(node.argumentList); |
| 3487 var typeArgs = _emitInvokeTypeArguments(node); | 3440 var typeArgs = _emitInvokeTypeArguments(node); |
| 3488 | 3441 |
| 3489 var type = getStaticType(target); | 3442 var type = getStaticType(target); |
| 3490 var element = node.methodName.staticElement; | 3443 var element = node.methodName.staticElement; |
| 3491 bool isStatic = element is ExecutableElement && element.isStatic; | 3444 bool isStatic = element is ExecutableElement && element.isStatic; |
| 3492 var name = node.methodName.name; | 3445 var name = node.methodName.name; |
| 3493 var memberName = | 3446 var jsName = |
| 3494 _emitMemberName(name, type: type, isStatic: isStatic, element: element); | 3447 _emitMemberName(name, type: type, isStatic: isStatic, element: element); |
| 3495 | 3448 |
| 3496 JS.Expression jsTarget = _emitTarget(target, element, isStatic); | 3449 JS.Expression jsTarget = _emitTarget(target, element, isStatic); |
| 3497 if (isDynamicInvoke(target) || isDynamicInvoke(node.methodName)) { | 3450 if (isDynamicInvoke(target) || isDynamicInvoke(node.methodName)) { |
| 3498 if (typeArgs != null) { | 3451 if (typeArgs != null) { |
| 3499 return _callHelper('#(#, #, #, #)', [ | 3452 return _callHelper('#(#, #, #, #)', [ |
| 3500 _emitDynamicOperationName('dgsend'), | 3453 _emitDynamicOperationName('dgsend'), |
| 3501 jsTarget, | 3454 jsTarget, |
| 3502 new JS.ArrayInitializer(typeArgs), | 3455 new JS.ArrayInitializer(typeArgs), |
| 3503 memberName, | 3456 jsName, |
| 3504 args | 3457 args |
| 3505 ]); | 3458 ]); |
| 3506 } else { | 3459 } else { |
| 3507 return _callHelper('#(#, #, #)', | 3460 return _callHelper('#(#, #, #)', |
| 3508 [_emitDynamicOperationName('dsend'), jsTarget, memberName, args]); | 3461 [_emitDynamicOperationName('dsend'), jsTarget, jsName, args]); |
| 3509 } | 3462 } |
| 3510 } | 3463 } |
| 3511 if (_isObjectMemberCall(target, name)) { | 3464 if (_isObjectMemberCall(target, name)) { |
| 3512 assert(typeArgs == null); // Object methods don't take type args. | 3465 assert(typeArgs == null); // Object methods don't take type args. |
| 3513 return _callHelper('#(#, #)', [name, jsTarget, args]); | 3466 return _callHelper('#(#, #)', [name, jsTarget, args]); |
| 3514 } | 3467 } |
| 3515 jsTarget = _emitTargetAccess(jsTarget, memberName, element); | 3468 jsTarget = _emitTargetAccess(jsTarget, jsName, element); |
| 3516 if (typeArgs != null) jsTarget = new JS.Call(jsTarget, typeArgs); | 3469 if (typeArgs != null) jsTarget = new JS.Call(jsTarget, typeArgs); |
| 3517 | 3470 |
| 3518 return new JS.Call(jsTarget, args); | 3471 return new JS.Call(jsTarget, args); |
| 3519 } | 3472 } |
| 3520 | 3473 |
| 3521 JS.Expression _emitDynamicInvoke( | 3474 JS.Expression _emitDynamicInvoke( |
| 3522 InvocationExpression node, JS.Expression fn, List<JS.Expression> args) { | 3475 InvocationExpression node, JS.Expression fn, List<JS.Expression> args) { |
| 3523 var typeArgs = _emitInvokeTypeArguments(node); | 3476 var typeArgs = _emitInvokeTypeArguments(node); |
| 3524 if (typeArgs != null) { | 3477 if (typeArgs != null) { |
| 3525 return _callHelper( | 3478 return _callHelper( |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3609 return f.typeArguments.skip(g.typeArguments.length); | 3562 return f.typeArguments.skip(g.typeArguments.length); |
| 3610 } | 3563 } |
| 3611 | 3564 |
| 3612 /// Emits code for the `JS(...)` macro. | 3565 /// Emits code for the `JS(...)` macro. |
| 3613 _emitForeignJS(MethodInvocation node) { | 3566 _emitForeignJS(MethodInvocation node) { |
| 3614 var e = node.methodName.staticElement; | 3567 var e = node.methodName.staticElement; |
| 3615 if (isInlineJS(e)) { | 3568 if (isInlineJS(e)) { |
| 3616 var args = node.argumentList.arguments; | 3569 var args = node.argumentList.arguments; |
| 3617 // arg[0] is static return type, used in `RestrictedStaticTypeAnalyzer` | 3570 // arg[0] is static return type, used in `RestrictedStaticTypeAnalyzer` |
| 3618 var code = args[1]; | 3571 var code = args[1]; |
| 3619 List<AstNode> templateArgs; | 3572 List<Expression> templateArgs; |
| 3620 String source; | 3573 String source; |
| 3621 if (code is StringInterpolation) { | 3574 if (code is StringInterpolation) { |
| 3622 if (args.length > 2) { | 3575 if (args.length > 2) { |
| 3623 throw new ArgumentError( | 3576 throw new ArgumentError( |
| 3624 "Can't mix template args and string interpolation in JS calls."); | 3577 "Can't mix template args and string interpolation in JS calls."); |
| 3625 } | 3578 } |
| 3626 templateArgs = <Expression>[]; | 3579 templateArgs = <Expression>[]; |
| 3627 source = code.elements.map((element) { | 3580 source = code.elements.map((element) { |
| 3628 if (element is InterpolationExpression) { | 3581 if (element is InterpolationExpression) { |
| 3629 templateArgs.add(element.expression); | 3582 templateArgs.add(element.expression); |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 3652 _extensionTypes.isNativeClass(containingClass.element)) { | 3605 _extensionTypes.isNativeClass(containingClass.element)) { |
| 3653 var constructorName = source.substring(4, source.indexOf('(')); | 3606 var constructorName = source.substring(4, source.indexOf('(')); |
| 3654 var className = containingClass.name.name; | 3607 var className = containingClass.name.name; |
| 3655 if (className == constructorName) { | 3608 if (className == constructorName) { |
| 3656 source = | 3609 source = |
| 3657 source.replaceFirst('new $className(', 'new self.$className('); | 3610 source.replaceFirst('new $className(', 'new self.$className('); |
| 3658 } | 3611 } |
| 3659 } | 3612 } |
| 3660 } | 3613 } |
| 3661 | 3614 |
| 3615 JS.Expression visitTemplateArg(Expression arg) { | |
| 3616 if (arg is InvocationExpression) { | |
| 3617 var e = arg is MethodInvocation | |
| 3618 ? arg.methodName.staticElement | |
| 3619 : (arg as FunctionExpressionInvocation).staticElement; | |
| 3620 if (e?.name == 'getGenericClass' && | |
| 3621 e.library.name == 'dart._runtime' && | |
| 3622 arg.argumentList.arguments.length == 1) { | |
| 3623 var typeArg = arg.argumentList.arguments[0]; | |
| 3624 if (typeArg is SimpleIdentifier) { | |
| 3625 var typeElem = typeArg.staticElement; | |
| 3626 if (typeElem is TypeDefiningElement && | |
| 3627 typeElem.type is ParameterizedType) { | |
| 3628 return _emitTopLevelNameNoInterop(typeElem, suffix: '\$'); | |
| 3629 } | |
| 3630 } | |
| 3631 } | |
| 3632 } | |
| 3633 return _visit(arg); | |
| 3634 } | |
| 3635 | |
| 3662 // TODO(rnystrom): The JS() calls are almost never nested, and probably | 3636 // TODO(rnystrom): The JS() calls are almost never nested, and probably |
| 3663 // really shouldn't be, but there are at least a couple of calls in the | 3637 // really shouldn't be, but there are at least a couple of calls in the |
| 3664 // HTML library where an argument to JS() is itself a JS() call. If those | 3638 // HTML library where an argument to JS() is itself a JS() call. If those |
| 3665 // go away, this can just assert(!_isInForeignJS). | 3639 // go away, this can just assert(!_isInForeignJS). |
| 3666 // Inside JS(), type names evaluate to the raw runtime type, not the | 3640 // Inside JS(), type names evaluate to the raw runtime type, not the |
| 3667 // wrapped Type object. | 3641 // wrapped Type object. |
| 3668 var wasInForeignJS = _isInForeignJS; | 3642 var wasInForeignJS = _isInForeignJS; |
| 3669 _isInForeignJS = true; | 3643 _isInForeignJS = true; |
| 3644 var jsArgs = templateArgs.map(visitTemplateArg).toList(); | |
| 3645 _isInForeignJS = wasInForeignJS; | |
| 3670 | 3646 |
| 3671 var template = js.parseForeignJS(source); | 3647 var result = js.parseForeignJS(source).instantiate(jsArgs); |
| 3672 var result = template.instantiate(_visitList(templateArgs)); | |
| 3673 | |
| 3674 _isInForeignJS = wasInForeignJS; | |
| 3675 | 3648 |
| 3676 // `throw` is emitted as a statement by `parseForeignJS`. | 3649 // `throw` is emitted as a statement by `parseForeignJS`. |
| 3677 assert(result is JS.Expression || node.parent is ExpressionStatement); | 3650 assert(result is JS.Expression || node.parent is ExpressionStatement); |
| 3678 return result; | 3651 return result; |
| 3679 } | 3652 } |
| 3680 return null; | 3653 return null; |
| 3681 } | 3654 } |
| 3682 | 3655 |
| 3683 @override | 3656 @override |
| 3684 JS.Expression visitFunctionExpressionInvocation( | 3657 JS.Expression visitFunctionExpressionInvocation( |
| (...skipping 307 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3992 ConstructorElement element, | 3965 ConstructorElement element, |
| 3993 DartType type, | 3966 DartType type, |
| 3994 SimpleIdentifier name, | 3967 SimpleIdentifier name, |
| 3995 ArgumentList argumentList, | 3968 ArgumentList argumentList, |
| 3996 bool isConst) { | 3969 bool isConst) { |
| 3997 JS.Expression emitNew() { | 3970 JS.Expression emitNew() { |
| 3998 JS.Expression ctor; | 3971 JS.Expression ctor; |
| 3999 bool isFactory = false; | 3972 bool isFactory = false; |
| 4000 bool isNative = false; | 3973 bool isNative = false; |
| 4001 if (element == null) { | 3974 if (element == null) { |
| 4002 ctor = _callHelper( | 3975 ctor = _throwUnsafe('unresolved constructor: ${type?.name ?? '<null>'}' |
| 4003 'throw(Error("compile error: unresolved constructor: " ' | 3976 '.${name?.name ?? '<unnamed>'}'); |
| 4004 '+ # + "." + #))', | |
| 4005 [ | |
| 4006 js.escapedString(type?.name ?? '<null>'), | |
| 4007 js.escapedString(name?.name ?? '<unnamed>') | |
| 4008 ]); | |
| 4009 } else { | 3977 } else { |
| 4010 ctor = _emitConstructorName(element, type, name); | 3978 ctor = _emitConstructorName(element, type, name); |
| 4011 isFactory = element.isFactory; | 3979 isFactory = element.isFactory; |
| 4012 var classElem = element.enclosingElement; | 3980 var classElem = element.enclosingElement; |
| 4013 isNative = _isJSNative(classElem); | 3981 isNative = _isJSNative(classElem); |
| 4014 } | 3982 } |
| 4015 var args = _emitArgumentList(argumentList); | 3983 var args = _emitArgumentList(argumentList); |
| 4016 // Native factory constructors are JS constructors - use new here. | 3984 // Native factory constructors are JS constructors - use new here. |
| 4017 return isFactory && !isNative | 3985 return isFactory && !isNative |
| 4018 ? new JS.Call(ctor, args) | 3986 ? new JS.Call(ctor, args) |
| 4019 : new JS.New(ctor, args); | 3987 : new JS.New(ctor, args); |
| 4020 } | 3988 } |
| 4021 | 3989 |
| 4022 if (element != null && _isObjectLiteral(element.enclosingElement)) { | 3990 if (element != null && _isObjectLiteral(element.enclosingElement)) { |
| 4023 return _emitObjectLiteral(argumentList); | 3991 return _emitObjectLiteral(argumentList); |
| 4024 } | 3992 } |
| 4025 if (isConst) return _emitConst(emitNew); | 3993 if (isConst) return _emitConst(emitNew); |
| 4026 return emitNew(); | 3994 return emitNew(); |
| 4027 } | 3995 } |
| 4028 | 3996 |
| 4029 bool _isObjectLiteral(ClassElement classElem) { | 3997 bool _isObjectLiteral(Element classElem) { |
| 4030 return findAnnotation(classElem, isPublicJSAnnotation) != null && | 3998 return _isJSNative(classElem) && |
| 4031 findAnnotation(classElem, isJSAnonymousAnnotation) != null; | 3999 findAnnotation(classElem, isJSAnonymousAnnotation) != null; |
| 4032 } | 4000 } |
| 4033 | 4001 |
| 4034 bool _isJSNative(ClassElement classElem) => | 4002 bool _isJSNative(Element e) => |
| 4035 findAnnotation(classElem, isPublicJSAnnotation) != null; | 4003 findAnnotation(e, isPublicJSAnnotation) != null; |
| 4036 | 4004 |
| 4037 JS.Expression _emitObjectLiteral(ArgumentList argumentList) { | 4005 JS.Expression _emitObjectLiteral(ArgumentList argumentList) { |
| 4038 var args = _emitArgumentList(argumentList); | 4006 var args = _emitArgumentList(argumentList); |
| 4039 if (args.isEmpty) { | 4007 if (args.isEmpty) { |
| 4040 return js.call('{}'); | 4008 return js.call('{}'); |
| 4041 } | 4009 } |
| 4042 assert(args.single is JS.ObjectInitializer); | 4010 assert(args.single is JS.ObjectInitializer); |
| 4043 return args.single; | 4011 return args.single; |
| 4044 } | 4012 } |
| 4045 | 4013 |
| (...skipping 237 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 4283 } | 4251 } |
| 4284 return false; | 4252 return false; |
| 4285 } | 4253 } |
| 4286 | 4254 |
| 4287 int _asIntInRange(Expression expr, int low, int high) { | 4255 int _asIntInRange(Expression expr, int low, int high) { |
| 4288 expr = expr.unParenthesized; | 4256 expr = expr.unParenthesized; |
| 4289 if (expr is IntegerLiteral) { | 4257 if (expr is IntegerLiteral) { |
| 4290 if (expr.value >= low && expr.value <= high) return expr.value; | 4258 if (expr.value >= low && expr.value <= high) return expr.value; |
| 4291 return null; | 4259 return null; |
| 4292 } | 4260 } |
| 4293 int finishIdentifier(SimpleIdentifier identifier) { | 4261 |
| 4294 Element staticElement = identifier.staticElement; | 4262 Identifier id; |
| 4295 if (staticElement is PropertyAccessorElement && staticElement.isGetter) { | 4263 if (expr is SimpleIdentifier) { |
| 4296 PropertyInducingElement variable = staticElement.variable; | 4264 id = expr; |
| 4297 int value = variable?.computeConstantValue()?.toIntValue(); | 4265 } else if (expr is PrefixedIdentifier && !expr.isDeferred) { |
| 4298 if (value != null && value >= low && value <= high) return value; | 4266 id = expr.identifier; |
| 4299 } | 4267 } else { |
| 4300 return null; | 4268 return null; |
| 4301 } | 4269 } |
| 4302 | 4270 var elmement = id.staticElement; |
|
vsm
2017/06/30 18:08:47
elmement -> element
Jennifer Messerly
2017/07/05 21:17:16
Done.
| |
| 4303 if (expr is SimpleIdentifier) { | 4271 if (elmement is PropertyAccessorElement && elmement.isGetter) { |
| 4304 return finishIdentifier(expr); | 4272 var variable = elmement.variable; |
| 4305 } else if (expr is PrefixedIdentifier && !expr.isDeferred) { | 4273 int value = variable?.computeConstantValue()?.toIntValue(); |
| 4306 return finishIdentifier(expr.identifier); | 4274 if (value != null && value >= low && value <= high) return value; |
| 4307 } | 4275 } |
| 4308 return null; | 4276 return null; |
| 4309 } | 4277 } |
| 4310 | 4278 |
| 4311 bool _isDefinitelyNonNegative(Expression expr) { | 4279 bool _isDefinitelyNonNegative(Expression expr) { |
| 4312 expr = expr.unParenthesized; | 4280 expr = expr.unParenthesized; |
| 4313 if (expr is IntegerLiteral) { | 4281 if (expr is IntegerLiteral) { |
| 4314 return expr.value >= 0; | 4282 return expr.value >= 0; |
| 4315 } | 4283 } |
| 4316 if (_nodeIsBitwiseOperation(expr)) return true; | 4284 if (_nodeIsBitwiseOperation(expr)) return true; |
| (...skipping 447 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 4764 JS.Expression _emitAccess( | 4732 JS.Expression _emitAccess( |
| 4765 Expression target, SimpleIdentifier memberId, DartType resultType) { | 4733 Expression target, SimpleIdentifier memberId, DartType resultType) { |
| 4766 var accessor = memberId.staticElement; | 4734 var accessor = memberId.staticElement; |
| 4767 // If `member` is a getter/setter, get the corresponding | 4735 // If `member` is a getter/setter, get the corresponding |
| 4768 var field = | 4736 var field = |
| 4769 accessor is PropertyAccessorElement ? accessor.variable : accessor; | 4737 accessor is PropertyAccessorElement ? accessor.variable : accessor; |
| 4770 String memberName = memberId.name; | 4738 String memberName = memberId.name; |
| 4771 var typeArgs = _getTypeArgs(accessor, resultType); | 4739 var typeArgs = _getTypeArgs(accessor, resultType); |
| 4772 | 4740 |
| 4773 bool isStatic = field is ClassMemberElement && field.isStatic; | 4741 bool isStatic = field is ClassMemberElement && field.isStatic; |
| 4774 var name = _emitMemberName(memberName, | 4742 var jsName = _emitMemberName(memberName, |
| 4775 type: getStaticType(target), isStatic: isStatic, element: accessor); | 4743 type: getStaticType(target), isStatic: isStatic, element: accessor); |
| 4776 if (isDynamicInvoke(target)) { | 4744 if (isDynamicInvoke(target)) { |
| 4777 return _callHelper('#(#, #)', | 4745 return _callHelper('#(#, #)', |
| 4778 [_emitDynamicOperationName('dload'), _visit(target), name]); | 4746 [_emitDynamicOperationName('dload'), _visit(target), jsName]); |
| 4779 } | 4747 } |
| 4780 | 4748 |
| 4781 var jsTarget = _emitTarget(target, accessor, isStatic); | 4749 var jsTarget = _emitTarget(target, accessor, isStatic); |
| 4782 | 4750 |
| 4783 var isSuper = jsTarget is JS.Super; | 4751 var isSuper = jsTarget is JS.Super; |
| 4784 if (isSuper && | 4752 if (isSuper && |
| 4785 accessor.isSynthetic && | 4753 accessor.isSynthetic && |
| 4786 field is FieldElementImpl && | 4754 field is FieldElementImpl && |
| 4787 !virtualFields.isVirtual(field)) { | 4755 !virtualFields.isVirtual(field)) { |
| 4788 // If super.x is a sealed field, then x is an instance property since | 4756 // If super.x is a sealed field, then x is an instance property since |
| 4789 // subclasses cannot override x. | 4757 // subclasses cannot override x. |
| 4790 jsTarget = annotate(new JS.This(), target); | 4758 jsTarget = annotate(new JS.This(), target); |
| 4791 } | 4759 } |
| 4792 | 4760 |
| 4793 JS.Expression result; | 4761 JS.Expression result; |
| 4794 if (accessor is MethodElement && !isStatic) { | 4762 if (_isObjectMemberCall(target, memberName)) { |
| 4795 // Tear-off methods: explicitly bind it. | 4763 if (_isObjectMethod(memberName)) { |
| 4796 // To be safe always use the symbolized name when binding on a native | 4764 result = _callHelper('bind(#, #)', [jsTarget, jsName]); |
| 4797 // class as bind assumes the name will match the name class signatures | 4765 } else { |
| 4798 // which is symbolized for native classes. | 4766 result = _callHelper('#(#)', [memberName, jsTarget]); |
| 4799 var safeName = _emitMemberName(memberName, | 4767 } |
| 4800 type: getStaticType(target), | 4768 } else if (accessor is MethodElement && |
| 4801 isStatic: isStatic, | 4769 !isStatic && |
| 4802 element: accessor, | 4770 !_isJSNative(accessor.enclosingElement)) { |
| 4803 alwaysSymbolizeNative: true); | |
| 4804 if (isSuper) { | 4771 if (isSuper) { |
| 4805 result = _callHelper('bind(this, #, #)', | 4772 result = _callHelper('bind(this, #, #)', |
| 4806 [safeName, _emitTargetAccess(jsTarget, name, accessor)]); | 4773 [jsName, _emitTargetAccess(jsTarget, jsName, accessor)]); |
| 4807 } else if (_isObjectMemberCall(target, memberName)) { | |
| 4808 var fn = js.call( | |
| 4809 memberName == 'noSuchMethod' | |
| 4810 ? 'function(i) { return #.#(this, i); }' | |
| 4811 : 'function() { return #.#(this); }', | |
| 4812 [_runtimeModule, memberName]); | |
| 4813 result = _callHelper( | |
| 4814 'bind(#, #, #)', [jsTarget, _propertyName(memberName), fn]); | |
| 4815 } else { | 4774 } else { |
| 4816 result = _callHelper('bind(#, #)', [jsTarget, safeName]); | 4775 result = _callHelper('bind(#, #)', [jsTarget, jsName]); |
| 4817 } | 4776 } |
| 4818 } else if (_isObjectMemberCall(target, memberName)) { | |
| 4819 result = _callHelper('#(#)', [memberName, jsTarget]); | |
| 4820 } else { | 4777 } else { |
| 4821 result = _emitTargetAccess(jsTarget, name, accessor); | 4778 result = _emitTargetAccess(jsTarget, jsName, accessor); |
| 4822 } | 4779 } |
| 4823 if (typeArgs == null) { | 4780 return typeArgs == null |
| 4824 return result; | 4781 ? result |
| 4825 } | 4782 : _callHelper('gbind(#, #)', [result, typeArgs]); |
| 4826 return _callHelper('gbind(#, #)', [result, typeArgs]); | |
| 4827 } | 4783 } |
| 4828 | 4784 |
| 4829 JS.LiteralString _emitDynamicOperationName(String name) => | 4785 JS.LiteralString _emitDynamicOperationName(String name) => |
| 4830 js.string(options.replCompile ? '${name}Repl' : name); | 4786 js.string(options.replCompile ? '${name}Repl' : name); |
| 4831 | 4787 |
| 4832 /// Emits a generic send, like an operator method. | 4788 /// Emits a generic send, like an operator method. |
| 4833 /// | 4789 /// |
| 4834 /// **Please note** this function does not support method invocation syntax | 4790 /// **Please note** this function does not support method invocation syntax |
| 4835 /// `obj.name(args)` because that could be a getter followed by a call. | 4791 /// `obj.name(args)` because that could be a getter followed by a call. |
| 4836 /// See [visitMethodInvocation]. | 4792 /// See [visitMethodInvocation]. |
| (...skipping 490 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 5327 JS.Expression result = _visit(node); | 5283 JS.Expression result = _visit(node); |
| 5328 if (isNullable(node)) result = _callHelper('test(#)', result); | 5284 if (isNullable(node)) result = _callHelper('test(#)', result); |
| 5329 return result; | 5285 return result; |
| 5330 } | 5286 } |
| 5331 | 5287 |
| 5332 /// Like [_emitMemberName], but for declaration sites. | 5288 /// Like [_emitMemberName], but for declaration sites. |
| 5333 /// | 5289 /// |
| 5334 /// Unlike call sites, we always have an element available, so we can use it | 5290 /// Unlike call sites, we always have an element available, so we can use it |
| 5335 /// directly rather than computing the relevant options for [_emitMemberName]. | 5291 /// directly rather than computing the relevant options for [_emitMemberName]. |
| 5336 JS.Expression _declareMemberName(ExecutableElement e, {bool useExtension}) { | 5292 JS.Expression _declareMemberName(ExecutableElement e, {bool useExtension}) { |
| 5337 var name = (e is PropertyAccessorElement) ? e.variable.name : e.name; | 5293 var name = e is PropertyAccessorElement ? e.variable.name : e.name; |
| 5338 return _emitMemberName(name, | 5294 return _emitMemberName(name, |
| 5339 isStatic: e.isStatic, | 5295 isStatic: e.isStatic, |
| 5340 useExtension: | 5296 useExtension: |
| 5341 useExtension ?? _extensionTypes.isNativeClass(e.enclosingElement)); | 5297 useExtension ?? _extensionTypes.isNativeClass(e.enclosingElement), |
| 5298 element: e); | |
| 5342 } | 5299 } |
| 5343 | 5300 |
| 5344 /// This handles member renaming for private names and operators. | 5301 /// This handles member renaming for private names and operators. |
| 5345 /// | 5302 /// |
| 5346 /// Private names are generated using ES6 symbols: | 5303 /// Private names are generated using ES6 symbols: |
| 5347 /// | 5304 /// |
| 5348 /// // At the top of the module: | 5305 /// // At the top of the module: |
| 5349 /// let _x = Symbol('_x'); | 5306 /// let _x = Symbol('_x'); |
| 5350 /// let _y = Symbol('_y'); | 5307 /// let _y = Symbol('_y'); |
| 5351 /// ... | 5308 /// ... |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 5378 /// | 5335 /// |
| 5379 /// Unary minus looks like: `x._negate()`. | 5336 /// Unary minus looks like: `x._negate()`. |
| 5380 /// | 5337 /// |
| 5381 /// Equality is a bit special, it is generated via the Dart `equals` runtime | 5338 /// Equality is a bit special, it is generated via the Dart `equals` runtime |
| 5382 /// helper, that checks for null. The user defined method is called '=='. | 5339 /// helper, that checks for null. The user defined method is called '=='. |
| 5383 /// | 5340 /// |
| 5384 JS.Expression _emitMemberName(String name, | 5341 JS.Expression _emitMemberName(String name, |
| 5385 {DartType type, | 5342 {DartType type, |
| 5386 bool isStatic: false, | 5343 bool isStatic: false, |
| 5387 bool useExtension, | 5344 bool useExtension, |
| 5388 bool alwaysSymbolizeNative: false, | |
| 5389 Element element}) { | 5345 Element element}) { |
| 5390 // Static members skip the rename steps and may require JS interop renames. | 5346 // Static members skip the rename steps and may require JS interop renames. |
| 5391 if (isStatic) { | 5347 if (isStatic) { |
| 5392 return _propertyName(_emitJSInteropStaticMemberName(element) ?? name); | 5348 return _emitJSInteropStaticMemberName(element) ?? _propertyName(name); |
| 5349 } | |
| 5350 | |
| 5351 // We allow some (illegal in Dart) member names to be used in our private | |
| 5352 // SDK code. These renames need to be included at every declaration, | |
| 5353 // including overrides in subclasses. | |
| 5354 if (element != null) { | |
| 5355 var runtimeName = getJSExportName(element); | |
| 5356 if (runtimeName != null) return _propertyName(runtimeName); | |
| 5393 } | 5357 } |
| 5394 | 5358 |
| 5395 if (name.startsWith('_')) { | 5359 if (name.startsWith('_')) { |
| 5396 return _emitPrivateNameSymbol(currentLibrary, name); | 5360 return _emitPrivateNameSymbol(currentLibrary, name); |
| 5397 } | 5361 } |
| 5398 | 5362 |
| 5399 // When generating synthetic names, we use _ as the prefix, since Dart names | 5363 // When generating synthetic names, we use _ as the prefix, since Dart names |
| 5400 // won't have this (eliminated above), nor will static names reach here. | 5364 // won't have this (eliminated above), nor will static names reach here. |
| 5401 switch (name) { | 5365 switch (name) { |
| 5402 case '[]': | 5366 case '[]': |
| 5403 name = '_get'; | 5367 name = '_get'; |
| 5404 break; | 5368 break; |
| 5405 case '[]=': | 5369 case '[]=': |
| 5406 name = '_set'; | 5370 name = '_set'; |
| 5407 break; | 5371 break; |
| 5408 case 'unary-': | 5372 case 'unary-': |
| 5409 name = '_negate'; | 5373 name = '_negate'; |
| 5410 break; | 5374 break; |
| 5411 case 'constructor': | 5375 case 'constructor': |
| 5412 case 'prototype': | 5376 case 'prototype': |
| 5413 name = '_$name'; | 5377 name = '_$name'; |
| 5414 break; | 5378 break; |
| 5415 } | 5379 } |
| 5416 | 5380 |
| 5417 var result = _propertyName(name); | 5381 var result = _propertyName(name); |
| 5418 | 5382 |
| 5419 if (useExtension == null) { | 5383 useExtension ??= _isSymbolizedMember(type, name); |
| 5420 // Dart "extension" methods. Used for JS Array, Boolean, Number, String. | |
| 5421 var baseType = type; | |
| 5422 while (baseType is TypeParameterType) { | |
| 5423 baseType = (baseType.element as TypeParameterElement).bound; | |
| 5424 } | |
| 5425 useExtension = baseType is InterfaceType && | |
| 5426 _isSymbolizedMember(baseType, name, alwaysSymbolizeNative); | |
| 5427 } | |
| 5428 | 5384 |
| 5429 return useExtension | 5385 return useExtension |
| 5430 ? js.call('#.#', [_extensionSymbolsModule, result]) | 5386 ? js.call('#.#', [_extensionSymbolsModule, result]) |
| 5431 : result; | 5387 : result; |
| 5432 } | 5388 } |
| 5433 | 5389 |
| 5434 var _forwardingCache = new HashMap<Element, Map<String, ExecutableElement>>(); | 5390 var _forwardingCache = new HashMap<Element, Map<String, ExecutableElement>>(); |
| 5435 | 5391 |
| 5436 Element _lookupForwardedMember(ClassElement element, String name) { | 5392 Element _lookupForwardedMember(ClassElement element, String name) { |
| 5437 // We only care about public methods. | 5393 // We only care about public methods. |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 5454 return member; | 5410 return member; |
| 5455 } | 5411 } |
| 5456 | 5412 |
| 5457 /// Don't symbolize native members that just forward to the underlying | 5413 /// Don't symbolize native members that just forward to the underlying |
| 5458 /// native member. We limit this to non-renamed members as the receiver | 5414 /// native member. We limit this to non-renamed members as the receiver |
| 5459 /// may be a mock type. | 5415 /// may be a mock type. |
| 5460 /// | 5416 /// |
| 5461 /// Note, this is an underlying assumption here that, if another native type | 5417 /// Note, this is an underlying assumption here that, if another native type |
| 5462 /// subtypes this one, it also forwards this member to its underlying native | 5418 /// subtypes this one, it also forwards this member to its underlying native |
| 5463 /// one without renaming. | 5419 /// one without renaming. |
| 5464 bool _isSymbolizedMember( | 5420 bool _isSymbolizedMember(DartType type, String name) { |
| 5465 InterfaceType type, String name, bool alwaysSymbolizeNative) { | |
| 5466 // Object members are handled separately. | 5421 // Object members are handled separately. |
| 5467 if (isObjectMember(name)) { | 5422 if (isObjectMember(name)) { |
| 5468 return false; | 5423 return false; |
| 5469 } | 5424 } |
| 5470 | 5425 |
| 5471 var element = type.element; | 5426 while (type is TypeParameterType) { |
| 5472 if (_extensionTypes.isNativeClass(element)) { | 5427 type = (type as TypeParameterType).bound; |
| 5473 var member = _lookupForwardedMember(element, name); | 5428 } |
| 5429 if (type is InterfaceType) { | |
| 5430 var element = type.element; | |
| 5431 if (_extensionTypes.isNativeClass(element)) { | |
| 5432 var member = _lookupForwardedMember(element, name); | |
| 5474 | 5433 |
| 5475 // Fields on a native class are implicitly native. | 5434 // Fields on a native class are implicitly native. |
| 5476 // Methods/getters/setters are marked external/native. | 5435 // Methods/getters/setters are marked external/native. |
| 5477 if (member is FieldElement || | 5436 if (member is FieldElement || |
| 5478 member is ExecutableElement && member.isExternal) { | 5437 member is ExecutableElement && member.isExternal) { |
| 5479 var jsName = getAnnotationName(member, isJsName); | 5438 var jsName = getAnnotationName(member, isJsName); |
| 5480 return alwaysSymbolizeNative || (jsName != null && jsName != name); | 5439 return jsName != null && jsName != name; |
| 5481 } else { | 5440 } else { |
| 5482 // Non-external members must be symbolized. | 5441 // Non-external members must be symbolized. |
| 5483 return true; | 5442 return true; |
| 5443 } | |
| 5484 } | 5444 } |
| 5445 // If the receiver *may* be a native type (i.e., an interface allowed to | |
| 5446 // be implemented by a native class), conservatively symbolize - we don't | |
| 5447 // whether it'll be implemented via forwarding. | |
|
vsm
2017/06/30 18:08:48
"whether" -> "know whether"
Jennifer Messerly
2017/07/05 21:17:16
oh funny, we must've missed this in the original C
| |
| 5448 // TODO(vsm): Consider CHA here to be less conservative. | |
| 5449 return _extensionTypes.isNativeInterface(element); | |
| 5485 } | 5450 } |
| 5486 // If the receiver *may* be a native type (i.e., an interface allowed to | 5451 return false; |
| 5487 // be implemented by a native class), conservatively symbolize - we don't | |
| 5488 // whether it'll be implemented via forwarding. | |
| 5489 // TODO(vsm): Consider CHA here to be less conservative. | |
| 5490 return _extensionTypes.isNativeInterface(element); | |
| 5491 } | 5452 } |
| 5492 | 5453 |
| 5493 JS.TemporaryId _emitPrivateNameSymbol(LibraryElement library, String name) { | 5454 JS.TemporaryId _emitPrivateNameSymbol(LibraryElement library, String name) { |
| 5494 return _privateNames | 5455 return _privateNames |
| 5495 .putIfAbsent(library, () => new HashMap()) | 5456 .putIfAbsent(library, () => new HashMap()) |
| 5496 .putIfAbsent(name, () { | 5457 .putIfAbsent(name, () { |
| 5497 var id = new JS.TemporaryId(name); | 5458 var id = new JS.TemporaryId(name); |
| 5498 _moduleItems.add( | 5459 _moduleItems.add( |
| 5499 js.statement('const # = Symbol(#);', [id, js.string(id.name, "'")])); | 5460 js.statement('const # = Symbol(#);', [id, js.string(id.name, "'")])); |
| 5500 return id; | 5461 return id; |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 5544 switch (name) { | 5505 switch (name) { |
| 5545 case 'hashCode': | 5506 case 'hashCode': |
| 5546 case 'toString': | 5507 case 'toString': |
| 5547 case 'noSuchMethod': | 5508 case 'noSuchMethod': |
| 5548 case 'runtimeType': | 5509 case 'runtimeType': |
| 5549 return true; | 5510 return true; |
| 5550 } | 5511 } |
| 5551 return false; | 5512 return false; |
| 5552 } | 5513 } |
| 5553 | 5514 |
| 5515 bool _isObjectMethod(String name) => | |
| 5516 name == 'toString' || name == 'noSuchMethod'; | |
| 5517 | |
| 5554 // TODO(leafp): Various analyzer pieces computed similar things. | 5518 // TODO(leafp): Various analyzer pieces computed similar things. |
| 5555 // Share this logic somewhere? | 5519 // Share this logic somewhere? |
| 5556 DartType _getExpectedReturnType(ExecutableElement element) { | 5520 DartType _getExpectedReturnType(ExecutableElement element) { |
| 5557 FunctionType functionType = element.type; | 5521 FunctionType functionType = element.type; |
| 5558 if (functionType == null) { | 5522 if (functionType == null) { |
| 5559 return DynamicTypeImpl.instance; | 5523 return DynamicTypeImpl.instance; |
| 5560 } | 5524 } |
| 5561 var type = functionType.returnType; | 5525 var type = functionType.returnType; |
| 5562 | 5526 |
| 5563 InterfaceType expectedType = null; | 5527 InterfaceType expectedType = null; |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 5602 | 5566 |
| 5603 JS.Statement _callHelperStatement(String code, args) { | 5567 JS.Statement _callHelperStatement(String code, args) { |
| 5604 if (args is List) { | 5568 if (args is List) { |
| 5605 args.insert(0, _runtimeModule); | 5569 args.insert(0, _runtimeModule); |
| 5606 } else { | 5570 } else { |
| 5607 args = [_runtimeModule, args]; | 5571 args = [_runtimeModule, args]; |
| 5608 } | 5572 } |
| 5609 return js.statement('#.$code', args); | 5573 return js.statement('#.$code', args); |
| 5610 } | 5574 } |
| 5611 | 5575 |
| 5576 JS.Expression _throwUnsafe(String message) => _callHelper( | |
| 5577 'throw(Error(#))', js.escapedString("compile error: $message")); | |
| 5578 | |
| 5612 _unreachable(AstNode node) { | 5579 _unreachable(AstNode node) { |
| 5613 throw new UnsupportedError( | 5580 throw new UnsupportedError( |
| 5614 'tried to generate an unreachable node: `$node`'); | 5581 'tried to generate an unreachable node: `$node`'); |
| 5615 } | 5582 } |
| 5616 | 5583 |
| 5617 /// Unused, see methods for emitting declarations. | 5584 /// Unused, see methods for emitting declarations. |
| 5618 @override | 5585 @override |
| 5619 visitAnnotation(node) => _unreachable(node); | 5586 visitAnnotation(node) => _unreachable(node); |
| 5620 | 5587 |
| 5621 /// Unused, see [_emitArgumentList]. | 5588 /// Unused, see [_emitArgumentList]. |
| (...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 5829 if (targetIdentifier.staticElement is! PrefixElement) return false; | 5796 if (targetIdentifier.staticElement is! PrefixElement) return false; |
| 5830 var prefix = targetIdentifier.staticElement as PrefixElement; | 5797 var prefix = targetIdentifier.staticElement as PrefixElement; |
| 5831 | 5798 |
| 5832 // The library the prefix is referring to must come from a deferred import. | 5799 // The library the prefix is referring to must come from a deferred import. |
| 5833 var containingLibrary = resolutionMap | 5800 var containingLibrary = resolutionMap |
| 5834 .elementDeclaredByCompilationUnit(target.root as CompilationUnit) | 5801 .elementDeclaredByCompilationUnit(target.root as CompilationUnit) |
| 5835 .library; | 5802 .library; |
| 5836 var imports = containingLibrary.getImportsWithPrefix(prefix); | 5803 var imports = containingLibrary.getImportsWithPrefix(prefix); |
| 5837 return imports.length == 1 && imports[0].isDeferred; | 5804 return imports.length == 1 && imports[0].isDeferred; |
| 5838 } | 5805 } |
| OLD | NEW |