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 |