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

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: 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
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 364 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698