OLD | NEW |
---|---|
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 import 'dart:collection' show HashMap, HashSet; | 5 import 'dart:collection' show HashMap, HashSet; |
6 import 'dart:math' show min, max; | 6 import 'dart:math' show min, max; |
7 | 7 |
8 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 8 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
9 import 'package:analyzer/dart/ast/ast.dart'; | 9 import 'package:analyzer/dart/ast/ast.dart'; |
10 import 'package:analyzer/dart/ast/token.dart' show Token, TokenType; | 10 import 'package:analyzer/dart/ast/token.dart' show Token, TokenType; |
(...skipping 683 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
694 if (findAnnotation(classElem, isPublicJSAnnotation) != null) return null; | 694 if (findAnnotation(classElem, isPublicJSAnnotation) != null) return null; |
695 | 695 |
696 // If this is a JavaScript type, emit it now and then exit. | 696 // If this is a JavaScript type, emit it now and then exit. |
697 var jsTypeDef = _emitJsType(classElem); | 697 var jsTypeDef = _emitJsType(classElem); |
698 if (jsTypeDef != null) return jsTypeDef; | 698 if (jsTypeDef != null) return jsTypeDef; |
699 | 699 |
700 var ctors = <ConstructorDeclaration>[]; | 700 var ctors = <ConstructorDeclaration>[]; |
701 var fields = <FieldDeclaration>[]; | 701 var fields = <FieldDeclaration>[]; |
702 var staticFields = <FieldDeclaration>[]; | 702 var staticFields = <FieldDeclaration>[]; |
703 var methods = <MethodDeclaration>[]; | 703 var methods = <MethodDeclaration>[]; |
704 | |
705 // The "call" method or getter, if one exists. This can also be | |
706 // "noSuchMethod" method, because nSM could implement "call". | |
707 ExecutableElement callMethod; | |
704 for (var member in node.members) { | 708 for (var member in node.members) { |
705 if (member is ConstructorDeclaration) { | 709 if (member is ConstructorDeclaration) { |
706 ctors.add(member); | 710 ctors.add(member); |
707 } else if (member is FieldDeclaration) { | 711 } else if (member is FieldDeclaration) { |
708 (member.isStatic ? staticFields : fields).add(member); | 712 (member.isStatic ? staticFields : fields).add(member); |
709 } else if (member is MethodDeclaration) { | 713 } else if (member is MethodDeclaration) { |
710 methods.add(member); | 714 methods.add(member); |
715 var name = member.name.name; | |
716 if (name == 'call' && !member.isSetter) { | |
717 callMethod = member.element; | |
718 } else if (name == 'noSuchMethod' && | |
719 !member.isGetter && | |
720 !member.isSetter && | |
721 // Exclude SDK because we know they don't use nSM to implement call. | |
722 !classElem.library.source.isInSystemLibrary) { | |
723 callMethod ??= member.element; | |
724 } | |
711 } | 725 } |
712 } | 726 } |
713 | 727 |
714 JS.Expression className; | 728 JS.Expression className; |
715 if (classElem.typeParameters.isNotEmpty) { | 729 if (classElem.typeParameters.isNotEmpty) { |
716 // Generic classes will be defined inside a function that closes over the | 730 // Generic classes will be defined inside a function that closes over the |
717 // type parameter. So we can use their local variable name directly. | 731 // type parameter. So we can use their local variable name directly. |
718 className = new JS.Identifier(classElem.name); | 732 className = new JS.Identifier(classElem.name); |
719 } else { | 733 } else { |
720 className = _emitTopLevelName(classElem); | 734 className = _emitTopLevelName(classElem); |
(...skipping 10 matching lines...) Expand all Loading... | |
731 var classExpr = _emitClassExpression(classElem, | 745 var classExpr = _emitClassExpression(classElem, |
732 _emitClassMethods(node, ctors, fields, superclasses, virtualFields), | 746 _emitClassMethods(node, ctors, fields, superclasses, virtualFields), |
733 fields: allFields); | 747 fields: allFields); |
734 | 748 |
735 var body = <JS.Statement>[]; | 749 var body = <JS.Statement>[]; |
736 var extensions = _extensionsToImplement(classElem); | 750 var extensions = _extensionsToImplement(classElem); |
737 _initExtensionSymbols(classElem, methods, fields, body); | 751 _initExtensionSymbols(classElem, methods, fields, body); |
738 _emitSuperHelperSymbols(_superHelperSymbols, body); | 752 _emitSuperHelperSymbols(_superHelperSymbols, body); |
739 | 753 |
740 // Emit the class, e.g. `core.Object = class Object { ... }` | 754 // Emit the class, e.g. `core.Object = class Object { ... }` |
741 _defineClass(classElem, className, classExpr, body); | 755 _defineClass(classElem, className, classExpr, callMethod, body); |
742 | 756 |
743 // Emit things that come after the ES6 `class ... { ... }`. | 757 // Emit things that come after the ES6 `class ... { ... }`. |
744 var jsPeerName = _getJSPeerName(classElem); | 758 var jsPeerName = _getJSPeerName(classElem); |
745 _setBaseClass(classElem, className, jsPeerName, body); | 759 _setBaseClass(classElem, className, jsPeerName, body); |
746 | 760 |
747 _emitClassTypeTests(classElem, className, body); | 761 _emitClassTypeTests(classElem, className, body); |
748 | 762 |
749 _defineNamedConstructors(ctors, body, className); | 763 _defineNamedConstructors(ctors, body, className, callMethod); |
750 _emitVirtualFieldSymbols(virtualFieldSymbols, body); | 764 _emitVirtualFieldSymbols(virtualFieldSymbols, body); |
751 _emitClassSignature(methods, classElem, ctors, extensions, className, body); | 765 _emitClassSignature(methods, classElem, ctors, extensions, className, body); |
752 _defineExtensionMembers(extensions, className, body); | 766 _defineExtensionMembers(extensions, className, body); |
753 _emitClassMetadata(node.metadata, className, body); | 767 _emitClassMetadata(node.metadata, className, body); |
754 | 768 |
755 JS.Statement classDef = _statement(body); | 769 JS.Statement classDef = _statement(body); |
756 var typeFormals = classElem.typeParameters; | 770 var typeFormals = classElem.typeParameters; |
757 if (typeFormals.isNotEmpty) { | 771 if (typeFormals.isNotEmpty) { |
758 classDef = _defineClassTypeArguments(classElem, typeFormals, classDef); | 772 classDef = _defineClassTypeArguments(classElem, typeFormals, classDef); |
759 } | 773 } |
760 | 774 |
761 body = <JS.Statement>[classDef]; | 775 body = <JS.Statement>[classDef]; |
762 _emitStaticFields(staticFields, staticFieldOverrides, classElem, body); | 776 _emitStaticFields(staticFields, staticFieldOverrides, classElem, body); |
763 _registerExtensionType(classElem, jsPeerName, body); | 777 _registerExtensionType(classElem, jsPeerName, body); |
764 return _statement(body); | 778 return _statement(body); |
765 } | 779 } |
766 | 780 |
781 /// Emits code to support a class with a "call" method and an unnamed | |
782 /// constructor. | |
783 /// | |
784 /// This ensures instances created by the unnamed constructor are functions. | |
785 /// Named constructors are handled elsewhere, see [_defineNamedConstructors]. | |
786 JS.Expression _emitCallableClass(JS.ClassExpression classExpr, | |
787 ConstructorElement unnamedCtor, ExecutableElement callMethod) { | |
788 var ctor = new JS.NamedFunction( | |
789 classExpr.name, _emitCallableClassConstructor(unnamedCtor, callMethod)); | |
790 | |
791 // Name the constructor function the same as the class. | |
792 return js.call('dart.callableClass(#, #)', [ctor, classExpr]); | |
793 } | |
794 | |
795 JS.Fun _emitCallableClassConstructor( | |
796 ConstructorElement ctor, ExecutableElement callMethod) { | |
797 bool dynamicCall = false; | |
798 if (callMethod is PropertyAccessorElement) { | |
799 assert(callMethod.isGetter); | |
800 dynamicCall = callMethod.returnType is! FunctionType; | |
vsm
2016/06/15 19:52:26
Do we need a dcall here if one of the param types
Jennifer Messerly
2016/06/15 20:16:51
I don't think so. Those dcalls will be handled by
| |
801 } else if (callMethod.name == 'noSuchMethod') { | |
802 dynamicCall = true; | |
803 } | |
804 | |
805 var callCall = | |
806 dynamicCall ? 'dart.dcall(self, args)' : 'self.call.apply(self, args)'; | |
807 return js.call( | |
808 r'''function (...args) { | |
809 const self = this; | |
810 function call(...args) { | |
811 return #; | |
812 } | |
813 call.__proto__ = this.__proto__; | |
814 call.#.apply(call, args); | |
815 return call; | |
816 }''', | |
817 [js.call(callCall), _constructorName(ctor)]); | |
818 } | |
819 | |
767 void _emitClassTypeTests(ClassElement classElem, JS.Expression className, | 820 void _emitClassTypeTests(ClassElement classElem, JS.Expression className, |
768 List<JS.Statement> body) { | 821 List<JS.Statement> body) { |
769 if (classElem == objectClass) { | 822 if (classElem == objectClass) { |
770 // We rely on ES6 static inheritance. All types that are represented by | 823 // We rely on ES6 static inheritance. All types that are represented by |
771 // class constructor functions will see these definitions, with [this] | 824 // class constructor functions will see these definitions, with [this] |
772 // being bound to the class constructor. | 825 // being bound to the class constructor. |
773 | 826 |
774 // The 'instanceof' checks don't work for primitive types (which have fast | 827 // The 'instanceof' checks don't work for primitive types (which have fast |
775 // definitions below) and don't work for native types. In those cases we | 828 // definitions below) and don't work for native types. In those cases we |
776 // fall through to the general purpose checking code. | 829 // fall through to the general purpose checking code. |
(...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
971 virtualFields[field.element] = virtualField; | 1024 virtualFields[field.element] = virtualField; |
972 virtualFieldSymbols.add(js.statement( | 1025 virtualFieldSymbols.add(js.statement( |
973 'const # = Symbol(#.name + "." + #.toString());', | 1026 'const # = Symbol(#.name + "." + #.toString());', |
974 [virtualField, className, fieldName])); | 1027 [virtualField, className, fieldName])); |
975 } | 1028 } |
976 } | 1029 } |
977 } | 1030 } |
978 } | 1031 } |
979 } | 1032 } |
980 | 1033 |
981 void _defineClass(ClassElement classElem, JS.Expression className, | 1034 void _defineClass( |
982 JS.ClassExpression classExpr, List<JS.Statement> body) { | 1035 ClassElement classElem, |
1036 JS.Expression className, | |
1037 JS.ClassExpression classExpr, | |
1038 ExecutableElement callMethod, | |
1039 List<JS.Statement> body) { | |
1040 JS.Expression callableClass; | |
1041 if (callMethod != null && classElem.unnamedConstructor != null) { | |
1042 callableClass = _emitCallableClass( | |
1043 classExpr, classElem.unnamedConstructor, callMethod); | |
1044 } | |
1045 | |
983 if (classElem.typeParameters.isNotEmpty) { | 1046 if (classElem.typeParameters.isNotEmpty) { |
984 body.add(new JS.ClassDeclaration(classExpr)); | 1047 if (callableClass != null) { |
1048 body.add(js.statement('const # = #;', [classExpr.name, callableClass])); | |
1049 } else { | |
1050 body.add(new JS.ClassDeclaration(classExpr)); | |
1051 } | |
985 } else { | 1052 } else { |
986 body.add(js.statement('# = #;', [className, classExpr])); | 1053 body.add(js.statement('# = #;', [className, callableClass ?? classExpr])); |
987 } | 1054 } |
988 } | 1055 } |
989 | 1056 |
990 void _emitVirtualFieldSymbols( | 1057 void _emitVirtualFieldSymbols( |
991 List<JS.Statement> virtualFields, List<JS.Statement> body) { | 1058 List<JS.Statement> virtualFields, List<JS.Statement> body) { |
992 body.addAll(virtualFields); | 1059 body.addAll(virtualFields); |
993 } | 1060 } |
994 | 1061 |
995 List<JS.Identifier> _emitTypeFormals(List<TypeParameterElement> typeFormals) { | 1062 List<JS.Identifier> _emitTypeFormals(List<TypeParameterElement> typeFormals) { |
996 return typeFormals | 1063 return typeFormals |
(...skipping 396 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1393 body.add(js.statement( | 1460 body.add(js.statement( |
1394 'dart.setExtensionBaseClass(#, #);', [className, newBaseClass])); | 1461 'dart.setExtensionBaseClass(#, #);', [className, newBaseClass])); |
1395 } else if (_hasDeferredSupertype.contains(classElem)) { | 1462 } else if (_hasDeferredSupertype.contains(classElem)) { |
1396 var newBaseClass = _emitType(classElem.type.superclass, | 1463 var newBaseClass = _emitType(classElem.type.superclass, |
1397 nameType: false, subClass: classElem, className: className); | 1464 nameType: false, subClass: classElem, className: className); |
1398 body.add( | 1465 body.add( |
1399 js.statement('dart.setBaseClass(#, #);', [className, newBaseClass])); | 1466 js.statement('dart.setBaseClass(#, #);', [className, newBaseClass])); |
1400 } | 1467 } |
1401 } | 1468 } |
1402 | 1469 |
1403 void _defineNamedConstructors(List<ConstructorDeclaration> ctors, | 1470 void _defineNamedConstructors( |
1404 List<JS.Statement> body, JS.Expression className) { | 1471 List<ConstructorDeclaration> ctors, |
1472 List<JS.Statement> body, | |
1473 JS.Expression className, | |
1474 ExecutableElement callMethod) { | |
1475 var code = callMethod != null | |
1476 ? 'dart.defineNamedConstructorCallable(#, #, #);' | |
1477 : 'dart.defineNamedConstructor(#, #)'; | |
1478 | |
1405 for (ConstructorDeclaration member in ctors) { | 1479 for (ConstructorDeclaration member in ctors) { |
1406 if (member.name != null && member.factoryKeyword == null) { | 1480 if (member.name != null && member.factoryKeyword == null) { |
1407 body.add(js.statement('dart.defineNamedConstructor(#, #);', | 1481 var args = [className, _constructorName(member.element)]; |
1408 [className, _constructorName(member.element)])); | 1482 if (callMethod != null) { |
1483 args.add(_emitCallableClassConstructor(member.element, callMethod)); | |
1484 } | |
1485 | |
1486 body.add(js.statement(code, args)); | |
1409 } | 1487 } |
1410 } | 1488 } |
1411 } | 1489 } |
1412 | 1490 |
1413 /// Emits static fields for a class, and initialize them eagerly if possible, | 1491 /// Emits static fields for a class, and initialize them eagerly if possible, |
1414 /// otherwise define them as lazy properties. | 1492 /// otherwise define them as lazy properties. |
1415 void _emitStaticFields( | 1493 void _emitStaticFields( |
1416 List<FieldDeclaration> staticFields, | 1494 List<FieldDeclaration> staticFields, |
1417 Set<FieldElement> staticFieldOverrides, | 1495 Set<FieldElement> staticFieldOverrides, |
1418 ClassElement classElem, | 1496 ClassElement classElem, |
(...skipping 3714 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
5133 } | 5211 } |
5134 | 5212 |
5135 bool isLibraryPrefix(Expression node) => | 5213 bool isLibraryPrefix(Expression node) => |
5136 node is SimpleIdentifier && node.staticElement is PrefixElement; | 5214 node is SimpleIdentifier && node.staticElement is PrefixElement; |
5137 | 5215 |
5138 LibraryElement _getLibrary(AnalysisContext c, String uri) => | 5216 LibraryElement _getLibrary(AnalysisContext c, String uri) => |
5139 c.computeLibraryElement(c.sourceFactory.forUri(uri)); | 5217 c.computeLibraryElement(c.sourceFactory.forUri(uri)); |
5140 | 5218 |
5141 bool _isDartRuntime(LibraryElement l) => | 5219 bool _isDartRuntime(LibraryElement l) => |
5142 l.isInSdk && l.source.uri.toString() == 'dart:_runtime'; | 5220 l.isInSdk && l.source.uri.toString() == 'dart:_runtime'; |
OLD | NEW |