Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(383)

Side by Side Diff: pkg/dev_compiler/lib/src/compiler/code_generator.dart

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

Powered by Google App Engine
This is Rietveld 408576698