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

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

Issue 2934623003: fix #29753, use ES5 constructors for ddc (Closed)
Patch Set: rebase Created 3 years, 6 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/test/browser/language_tests.js » ('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 781 matching lines...) Expand 10 before | Expand all | Expand 10 after
792 // TODO(jmesserly): if the type fails to resolve, should we generate code 792 // TODO(jmesserly): if the type fails to resolve, should we generate code
793 // that throws instead? 793 // that throws instead?
794 assert(options.unsafeForceCompile || options.replCompile); 794 assert(options.unsafeForceCompile || options.replCompile);
795 return _callHelper('dynamic'); 795 return _callHelper('dynamic');
796 } 796 }
797 return _emitType(node.type); 797 return _emitType(node.type);
798 } 798 }
799 799
800 @override 800 @override
801 JS.Statement visitClassTypeAlias(ClassTypeAlias node) { 801 JS.Statement visitClassTypeAlias(ClassTypeAlias node) {
802 ClassElement element = node.element; 802 ClassElement classElem = node.element;
803 var supertype = element.supertype; 803 var supertype = classElem.supertype;
804 804
805 // Forward all generative constructors from the base class. 805 var typeFormals = classElem.typeParameters;
806 var methods = <JS.Method>[]; 806 var isGeneric = typeFormals.isNotEmpty;
807 if (!supertype.isObject) { 807
808 for (var ctor in element.constructors) { 808 // Special case where supertype is Object, and we mixin a single class.
809 var parentCtor = supertype.lookUpConstructor(ctor.name, ctor.library); 809 // The resulting 'class' is a mixable class in this case.
810 // TODO(jmesserly): this avoids spread args for perf. Revisit. 810 bool isMixinAlias = supertype.isObject && classElem.mixins.length == 1;
811 var jsParams = <JS.Identifier>[]; 811
812 for (var p in ctor.parameters) { 812 var classExpr = isMixinAlias
813 if (p.parameterKind != ParameterKind.NAMED) { 813 ? _emitClassHeritage(classElem)
814 jsParams.add(new JS.Identifier(p.name)); 814 : _emitClassExpression(classElem, []);
815 } else { 815 var className = isGeneric
816 jsParams.add(new JS.TemporaryId('namedArgs')); 816 ? new JS.Identifier(classElem.name)
817 break; 817 : _emitTopLevelName(classElem);
818 } 818 var block = <JS.Statement>[];
819 } 819
820 var fun = js.call('function(#) { super.#(#); }', 820 if (isGeneric) {
821 [jsParams, _constructorName(parentCtor), jsParams]) as JS.Fun; 821 if (isMixinAlias) {
822 methods.add(new JS.Method(_constructorName(ctor), fun)); 822 block.add(js.statement('const # = #;', [className, classExpr]));
823 } else {
824 block.add(new JS.ClassDeclaration(classExpr));
823 } 825 }
826 } else {
827 block.add(js.statement('# = #;', [className, classExpr]));
824 } 828 }
825 829
826 var typeFormals = element.typeParameters; 830 if (!isMixinAlias) _defineConstructors(classElem, className, [], block, []);
827 var isGeneric = typeFormals.isNotEmpty; 831
828 var className = isGeneric ? element.name : _emitTopLevelName(element); 832 if (classElem.interfaces.isNotEmpty) {
829 JS.Statement declareInterfaces(JS.Statement decl) { 833 block.add(js.statement('#[#.implements] = () => #;', [
830 if (element.interfaces.isNotEmpty) { 834 className,
831 var body = [decl]..add(js.statement('#[#.implements] = () => #;', [ 835 _runtimeModule,
832 className, 836 new JS.ArrayInitializer(classElem.interfaces.map(_emitType).toList())
833 _runtimeModule, 837 ]));
834 new JS.ArrayInitializer(
835 new List<JS.Expression>.from(element.interfaces.map(_emitType)))
836 ]));
837 decl = _statement(body);
838 }
839 return decl;
840 } 838 }
841 839
842 if (supertype.isObject && element.mixins.length == 1) { 840 if (isGeneric) {
843 // Special case where supertype is Object, and we mixin a single class. 841 return _defineClassTypeArguments(
844 // The resulting 'class' is a mixable class in this case. 842 classElem, typeFormals, _statement(block));
845 var classExpr = _emitClassHeritage(element);
846 if (isGeneric) {
847 var classStmt = js.statement('const # = #;', [className, classExpr]);
848 return _defineClassTypeArguments(
849 element, typeFormals, declareInterfaces(classStmt));
850 } else {
851 var classStmt = js.statement('# = #;', [className, classExpr]);
852 return declareInterfaces(classStmt);
853 }
854 } 843 }
855 844 return _statement(block);
856 var classExpr = _emitClassExpression(element, methods);
857 if (isGeneric) {
858 var classStmt = new JS.ClassDeclaration(classExpr);
859 return _defineClassTypeArguments(
860 element, typeFormals, declareInterfaces(classStmt));
861 } else {
862 var classStmt = js.statement('# = #;', [className, classExpr]);
863 return declareInterfaces(classStmt);
864 }
865 } 845 }
866 846
867 JS.Statement _emitJsType(Element e) { 847 JS.Statement _emitJsType(Element e) {
868 var jsTypeName = getAnnotationName(e, isJSAnnotation); 848 var jsTypeName = getAnnotationName(e, isJSAnnotation);
869 if (jsTypeName == null || jsTypeName == e.name) return null; 849 if (jsTypeName == null || jsTypeName == e.name) return null;
870 850
871 // We export the JS type as if it was a Dart type. For example this allows 851 // We export the JS type as if it was a Dart type. For example this allows
872 // `dom.InputElement` to actually be HTMLInputElement. 852 // `dom.InputElement` to actually be HTMLInputElement.
873 // TODO(jmesserly): if we had the JS name on the Element, we could just 853 // TODO(jmesserly): if we had the JS name on the Element, we could just
874 // generate it correctly when we refer to it. 854 // generate it correctly when we refer to it.
(...skipping 10 matching lines...) Expand all
885 // If this is a JavaScript type, emit it now and then exit. 865 // If this is a JavaScript type, emit it now and then exit.
886 var jsTypeDef = _emitJsType(classElem); 866 var jsTypeDef = _emitJsType(classElem);
887 if (jsTypeDef != null) return jsTypeDef; 867 if (jsTypeDef != null) return jsTypeDef;
888 868
889 var ctors = <ConstructorDeclaration>[]; 869 var ctors = <ConstructorDeclaration>[];
890 var allFields = <FieldDeclaration>[]; 870 var allFields = <FieldDeclaration>[];
891 var fields = <FieldDeclaration>[]; 871 var fields = <FieldDeclaration>[];
892 var staticFields = <FieldDeclaration>[]; 872 var staticFields = <FieldDeclaration>[];
893 var methods = <MethodDeclaration>[]; 873 var methods = <MethodDeclaration>[];
894 874
895 // True if a "call" method or getter exists directly on this class.
896 // If so, we need to install a Function prototype.
897 bool isCallable = false;
898 for (var member in node.members) { 875 for (var member in node.members) {
899 if (member is ConstructorDeclaration) { 876 if (member is ConstructorDeclaration) {
900 ctors.add(member); 877 ctors.add(member);
901 } else if (member is FieldDeclaration) { 878 } else if (member is FieldDeclaration) {
902 allFields.add(member); 879 allFields.add(member);
903 (member.isStatic ? staticFields : fields).add(member); 880 (member.isStatic ? staticFields : fields).add(member);
904 } else if (member is MethodDeclaration) { 881 } else if (member is MethodDeclaration) {
905 methods.add(member); 882 methods.add(member);
906 if (member.name.name == 'call' && !member.isSetter) {
907 //
908 // Make sure "call" has a statically known function type:
909 //
910 // - if it's a method, then it does because all methods do,
911 // - if it's a getter, check the return type.
912 //
913 // Other cases like a getter returning dynamic/Object/Function will be
914 // handled at runtime by the dynamic call mechanism. So we only
915 // concern ourselves with statically known function types.
916 //
917 // For the same reason, we can ignore "noSuchMethod".
918 // call-implemented-by-nSM will be dispatched by dcall at runtime.
919 //
920 isCallable = !member.isGetter || member.returnType is FunctionType;
921 }
922 } 883 }
923 } 884 }
924 885
925 // True if a "call" method or getter exists directly or indirectly on this
926 // class. If so, we need special constructor handling.
927 bool isCallableTransitive =
928 classElem.lookUpMethod('call', currentLibrary) != null;
929 if (!isCallableTransitive) {
930 var callGetter = classElem.lookUpGetter('call', currentLibrary);
931 isCallableTransitive =
932 callGetter != null && callGetter.returnType is FunctionType;
933 }
934
935 JS.Expression className; 886 JS.Expression className;
936 if (classElem.typeParameters.isNotEmpty) { 887 if (classElem.typeParameters.isNotEmpty) {
937 // Generic classes will be defined inside a function that closes over the 888 // Generic classes will be defined inside a function that closes over the
938 // type parameter. So we can use their local variable name directly. 889 // type parameter. So we can use their local variable name directly.
939 className = new JS.Identifier(classElem.name); 890 className = new JS.Identifier(classElem.name);
940 } else { 891 } else {
941 className = _emitTopLevelName(classElem); 892 className = _emitTopLevelName(classElem);
942 } 893 }
943 894
944 var savedClassProperties = _classProperties; 895 var savedClassProperties = _classProperties;
945 _classProperties = 896 _classProperties =
946 new ClassPropertyModel.build(_extensionTypes, virtualFields, classElem); 897 new ClassPropertyModel.build(_extensionTypes, virtualFields, classElem);
947 898
948 var classExpr = _emitClassExpression( 899 var classExpr = _emitClassExpression(classElem, _emitClassMethods(node),
949 classElem, _emitClassMethods(node, ctors, fields),
950 fields: allFields); 900 fields: allFields);
951 901
952 var body = <JS.Statement>[]; 902 var body = <JS.Statement>[];
953 _initExtensionSymbols(classElem, methods, fields, body); 903 _initExtensionSymbols(classElem, methods, fields, body);
954 _emitSuperHelperSymbols(_superHelperSymbols, body); 904 _emitSuperHelperSymbols(_superHelperSymbols, body);
955 905
956 // Emit the class, e.g. `core.Object = class Object { ... }` 906 // Emit the class, e.g. `core.Object = class Object { ... }`
957 _defineClass(classElem, className, classExpr, isCallable, body); 907 _defineClass(classElem, className, classExpr, body);
908 _defineConstructors(classElem, className, fields, body, ctors);
958 909
959 // Emit things that come after the ES6 `class ... { ... }`. 910 // Emit things that come after the ES6 `class ... { ... }`.
960 var jsPeerNames = _getJSPeerNames(classElem); 911 var jsPeerNames = _getJSPeerNames(classElem);
961 JS.Statement deferredBaseClass = 912 JS.Statement deferredBaseClass =
962 _setBaseClass(classElem, className, jsPeerNames, body); 913 _setBaseClass(classElem, className, jsPeerNames, body);
963 914
964 _emitClassTypeTests(classElem, className, body); 915 _emitClassTypeTests(classElem, className, body);
965 916
966 _defineNamedConstructors(ctors, body, className, isCallableTransitive);
967 _emitVirtualFieldSymbols(classElem, body); 917 _emitVirtualFieldSymbols(classElem, body);
968 _emitClassSignature(methods, allFields, classElem, ctors, className, body); 918 _emitClassSignature(methods, allFields, classElem, ctors, className, body);
969 _defineExtensionMembers(className, body); 919 _defineExtensionMembers(className, body);
970 _emitClassMetadata(node.metadata, className, body); 920 _emitClassMetadata(node.metadata, className, body);
971 921
972 JS.Statement classDef = _statement(body); 922 JS.Statement classDef = _statement(body);
973 923
974 var typeFormals = classElem.typeParameters; 924 var typeFormals = classElem.typeParameters;
975 if (typeFormals.isNotEmpty) { 925 if (typeFormals.isNotEmpty) {
976 classDef = _defineClassTypeArguments( 926 classDef = _defineClassTypeArguments(
977 classElem, typeFormals, classDef, className, deferredBaseClass); 927 classElem, typeFormals, classDef, className, deferredBaseClass);
978 } 928 }
979 929
980 body = <JS.Statement>[classDef]; 930 body = <JS.Statement>[classDef];
981 _emitStaticFields(staticFields, classElem, body); 931 _emitStaticFields(staticFields, classElem, body);
982 for (var peer in jsPeerNames) { 932 for (var peer in jsPeerNames) {
983 _registerExtensionType(classElem, peer, body); 933 _registerExtensionType(classElem, peer, body);
984 } 934 }
985 935
986 _classProperties = savedClassProperties; 936 _classProperties = savedClassProperties;
987 return _statement(body); 937 return _statement(body);
988 } 938 }
989 939
990 /// Emits code to support a class with a "call" method and an unnamed
991 /// constructor.
992 ///
993 /// This ensures instances created by the unnamed constructor are functions.
994 /// Named constructors are handled elsewhere, see [_defineNamedConstructors].
995 JS.Expression _emitCallableClass(
996 JS.ClassExpression classExpr, ConstructorElement unnamedCtor) {
997 var ctor = new JS.NamedFunction(
998 classExpr.name, _emitCallableClassConstructor(unnamedCtor));
999
1000 // Name the constructor function the same as the class.
1001 return _callHelper('callableClass(#, #)', [ctor, classExpr]);
1002 }
1003
1004 /// Emits a constructor that ensures instances of this class are callable as
1005 /// functions in JavaScript.
1006 JS.Fun _emitCallableClassConstructor(ConstructorElement ctor) {
1007 return js.call(
1008 r'''function (...args) {
1009 function call(...args) {
1010 return call.call.apply(call, args);
1011 }
1012 call.__proto__ = this.__proto__;
1013 call.#.apply(call, args);
1014 return call;
1015 }''',
1016 [_constructorName(ctor)]);
1017 }
1018
1019 void _emitClassTypeTests(ClassElement classElem, JS.Expression className, 940 void _emitClassTypeTests(ClassElement classElem, JS.Expression className,
1020 List<JS.Statement> body) { 941 List<JS.Statement> body) {
1021 if (classElem == objectClass) { 942 if (classElem == objectClass) {
1022 // We rely on ES6 static inheritance. All types that are represented by 943 // We rely on ES6 static inheritance. All types that are represented by
1023 // class constructor functions will see these definitions, with [this] 944 // class constructor functions will see these definitions, with [this]
1024 // being bound to the class constructor. 945 // being bound to the class constructor.
1025 946
1026 // The 'instanceof' checks don't work for primitive types (which have fast 947 // The 'instanceof' checks don't work for primitive types (which have fast
1027 // definitions below) and don't work for native types. In those cases we 948 // definitions below) and don't work for native types. In those cases we
1028 // fall through to the general purpose checking code. 949 // fall through to the general purpose checking code.
(...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after
1222 1143
1223 void _emitVirtualFieldSymbols( 1144 void _emitVirtualFieldSymbols(
1224 ClassElement classElement, List<JS.Statement> body) { 1145 ClassElement classElement, List<JS.Statement> body) {
1225 _classProperties.virtualFields.forEach((field, virtualField) { 1146 _classProperties.virtualFields.forEach((field, virtualField) {
1226 body.add(js.statement('const # = Symbol(#);', 1147 body.add(js.statement('const # = Symbol(#);',
1227 [virtualField, js.string('${classElement.name}.${field.name}')])); 1148 [virtualField, js.string('${classElement.name}.${field.name}')]));
1228 }); 1149 });
1229 } 1150 }
1230 1151
1231 void _defineClass(ClassElement classElem, JS.Expression className, 1152 void _defineClass(ClassElement classElem, JS.Expression className,
1232 JS.ClassExpression classExpr, bool isCallable, List<JS.Statement> body) { 1153 JS.ClassExpression classExpr, List<JS.Statement> body) {
1233 JS.Expression callableClass;
1234 if (isCallable && classElem.unnamedConstructor != null) {
1235 callableClass =
1236 _emitCallableClass(classExpr, classElem.unnamedConstructor);
1237 }
1238
1239 if (classElem.typeParameters.isNotEmpty) { 1154 if (classElem.typeParameters.isNotEmpty) {
1240 if (callableClass != null) { 1155 body.add(new JS.ClassDeclaration(classExpr));
1241 body.add(js.statement('const # = #;', [classExpr.name, callableClass]));
1242 } else {
1243 body.add(new JS.ClassDeclaration(classExpr));
1244 }
1245 } else { 1156 } else {
1246 body.add(js.statement('# = #;', [className, callableClass ?? classExpr])); 1157 body.add(js.statement('# = #;', [className, classExpr]));
1247 } 1158 }
1248 } 1159 }
1249 1160
1250 List<JS.Identifier> _emitTypeFormals(List<TypeParameterElement> typeFormals) { 1161 List<JS.Identifier> _emitTypeFormals(List<TypeParameterElement> typeFormals) {
1251 return typeFormals 1162 return typeFormals
1252 .map((t) => new JS.Identifier(t.name)) 1163 .map((t) => new JS.Identifier(t.name))
1253 .toList(growable: false); 1164 .toList(growable: false);
1254 } 1165 }
1255 1166
1256 /// Emits a field declaration for TypeScript & Closure's ES6_TYPED 1167 /// Emits a field declaration for TypeScript & Closure's ES6_TYPED
(...skipping 14 matching lines...) Expand all
1271 } 1182 }
1272 1183
1273 @override 1184 @override
1274 JS.Statement visitEnumDeclaration(EnumDeclaration node) { 1185 JS.Statement visitEnumDeclaration(EnumDeclaration node) {
1275 var element = resolutionMap.elementDeclaredByEnumDeclaration(node); 1186 var element = resolutionMap.elementDeclaredByEnumDeclaration(node);
1276 var type = element.type; 1187 var type = element.type;
1277 1188
1278 // Generate a class per section 13 of the spec. 1189 // Generate a class per section 13 of the spec.
1279 // TODO(vsm): Generate any accompanying metadata 1190 // TODO(vsm): Generate any accompanying metadata
1280 1191
1281 // Create constructor and initialize index 1192 var fields = element.fields.where((f) => f.type == type).toList();
1282 var constructor = new JS.Method(_propertyName('new'),
1283 js.call('function(index) { this.index = index; }') as JS.Fun);
1284 var fields = new List<FieldElement>.from(
1285 element.fields.where((f) => f.type == type));
1286 1193
1287 // Create toString() method 1194 // Create toString() method
1288 var nameProperties = new List<JS.Property>(fields.length); 1195 var nameProperties = new List<JS.Property>(fields.length);
1289 for (var i = 0; i < fields.length; ++i) { 1196 for (var i = 0; i < fields.length; ++i) {
1290 nameProperties[i] = new JS.Property( 1197 nameProperties[i] = new JS.Property(
1291 js.number(i), js.string('${type.name}.${fields[i].name}')); 1198 js.number(i), js.string('${type.name}.${fields[i].name}'));
1292 } 1199 }
1293 var nameMap = new JS.ObjectInitializer(nameProperties, multiline: true); 1200 var nameMap = new JS.ObjectInitializer(nameProperties, multiline: true);
1294 var toStringF = new JS.Method(js.string('toString'), 1201 var toStringF = new JS.Method(js.string('toString'),
1295 js.call('function() { return #[this.index]; }', nameMap) as JS.Fun); 1202 js.call('function() { return #[this.index]; }', nameMap) as JS.Fun);
1296 1203
1297 // Create enum class 1204 // Create enum class
1298 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), 1205 var classExpr = new JS.ClassExpression(
1299 _emitClassHeritage(element), [constructor, toStringF]); 1206 new JS.Identifier(type.name), _emitClassHeritage(element), [toStringF]);
1300 var id = _emitTopLevelName(element); 1207 var id = _emitTopLevelName(element);
1301 1208
1302 // Emit metadata for synthetic enum index member. 1209 // Emit metadata for synthetic enum index member.
1303 // TODO(jacobr): make field readonly when that is supported. 1210 // TODO(jacobr): make field readonly when that is supported.
1304 var tInstanceFields = <JS.Property>[ 1211 var tInstanceFields = <JS.Property>[
1305 new JS.Property( 1212 new JS.Property(
1306 _emitMemberName('index'), _emitFieldSignature(types.intType)) 1213 _emitMemberName('index'), _emitFieldSignature(types.intType))
1307 ]; 1214 ];
1308 var sigFields = <JS.Property>[]; 1215 var sigFields = <JS.Property>[];
1309 _buildSignatureField(sigFields, 'fields', tInstanceFields); 1216 _buildSignatureField(sigFields, 'fields', tInstanceFields);
1310 var sig = new JS.ObjectInitializer(sigFields); 1217 var sig = new JS.ObjectInitializer(sigFields);
1311 1218
1312 var result = [ 1219 var result = [
1313 js.statement('# = #', [id, classExpr]), 1220 js.statement('# = #', [id, classExpr]),
1221 js.statement(
1222 '(#.new = function(x) { this.index = x; }).prototype = #.prototype;',
1223 [id, id]),
1314 _callHelperStatement('setSignature(#, #);', [id, sig]) 1224 _callHelperStatement('setSignature(#, #);', [id, sig])
1315 ]; 1225 ];
1316 1226
1317 // defineEnumValues internally depends on dart.constList which uses 1227 // defineEnumValues internally depends on dart.constList which uses
1318 // _interceptors.JSArray. 1228 // _interceptors.JSArray.
1319 _declareBeforeUse(_jsArray); 1229 _declareBeforeUse(_jsArray);
1320 1230
1321 // Create static fields for each enum value, and the "values" getter 1231 // Create static fields for each enum value, and the "values" getter
1322 result.add(_callHelperStatement('defineEnumValues(#, #);', [ 1232 result.add(_callHelperStatement('defineEnumValues(#, #);', [
1323 id, 1233 id,
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after
1444 [value], js.statement('{ this.# = #; }', [name, value])); 1354 [value], js.statement('{ this.# = #; }', [name, value]));
1445 method = new JS.Method(_declareMemberName(field.setter), fn, 1355 method = new JS.Method(_declareMemberName(field.setter), fn,
1446 isSetter: true); 1356 isSetter: true);
1447 jsMethods.add(method); 1357 jsMethods.add(method);
1448 } 1358 }
1449 } 1359 }
1450 } 1360 }
1451 return jsMethods; 1361 return jsMethods;
1452 } 1362 }
1453 1363
1454 List<JS.Method> _emitClassMethods(ClassDeclaration node, 1364 List<JS.Method> _emitClassMethods(ClassDeclaration node) {
1455 List<ConstructorDeclaration> ctors, List<FieldDeclaration> fields) {
1456 var element = resolutionMap.elementDeclaredByClassDeclaration(node); 1365 var element = resolutionMap.elementDeclaredByClassDeclaration(node);
1457 var type = element.type; 1366 var type = element.type;
1458 var isObject = type.isObject;
1459 var virtualFields = _classProperties.virtualFields; 1367 var virtualFields = _classProperties.virtualFields;
1460 1368
1461 // Iff no constructor is specified for a class C, it implicitly has a
1462 // default constructor `C() : super() {}`, unless C is class Object.
1463 var jsMethods = <JS.Method>[]; 1369 var jsMethods = <JS.Method>[];
1464 if (isObject) { 1370 bool hasJsPeer = findAnnotation(element, isJsPeerInterface) != null;
1465 // Implements Dart constructor behavior. 1371 bool hasIterator = false;
1466 // 1372
1467 // Because of ES6 constructor restrictions (`this` is not available until 1373 if (type.isObject) {
1468 // `super` is called), we cannot emit an actual ES6 `constructor` on our 1374 // Dart does not use ES6 constructors.
1469 // classes and preserve the Dart initialization order. 1375 // Add an error to catch any invalid usage.
1470 //
1471 // Instead we use the same trick as named constructors, and do them as
1472 // instance methods that perform initialization.
1473 //
1474 // Therefore, dart:core Object gets the one real `constructor` and
1475 // immediately bounces to the `new() { ... }` initializer, letting us
1476 // bypass the ES6 restrictions.
1477 //
1478 // TODO(jmesserly): we'll need to rethink this.
1479 // See https://github.com/dart-lang/sdk/issues/28322.
1480 // This level of indirection will hurt performance.
1481 jsMethods.add(new JS.Method( 1376 jsMethods.add(new JS.Method(
1482 _propertyName('constructor'), 1377 _propertyName('constructor'),
1483 js.call('function(...args) { return this.new.apply(this, args); }') 1378 js.call(
1484 as JS.Fun)); 1379 r'''function() {
1485 } else if (ctors.isEmpty) { 1380 throw Error("use `new " + #.typeName(#.getReifiedType(this)) +
1486 jsMethods.add(_emitImplicitConstructor(node, fields, virtualFields)); 1381 ".new(...)` to create a Dart object");
1382 }''',
1383 [_runtimeModule, _runtimeModule])));
1487 } 1384 }
1488
1489 bool hasJsPeer = findAnnotation(element, isJsPeerInterface) != null;
1490
1491 bool hasIterator = false;
1492 for (var m in node.members) { 1385 for (var m in node.members) {
1493 if (m is ConstructorDeclaration) { 1386 if (m is ConstructorDeclaration) {
1494 jsMethods 1387 if (m.factoryKeyword != null && !_externalOrNative(m)) {
1495 .add(_emitConstructor(m, type, fields, virtualFields, isObject)); 1388 jsMethods.add(_emitFactoryConstructor(m));
1389 }
1496 } else if (m is MethodDeclaration) { 1390 } else if (m is MethodDeclaration) {
1497 jsMethods.add(_emitMethodDeclaration(type, m)); 1391 jsMethods.add(_emitMethodDeclaration(type, m));
1498 1392
1499 if (m.element is PropertyAccessorElement) { 1393 if (m.element is PropertyAccessorElement) {
1500 jsMethods.add(_emitSuperAccessorWrapper(m, type)); 1394 jsMethods.add(_emitSuperAccessorWrapper(m, type));
1501 } 1395 }
1502 1396
1503 if (!hasJsPeer && m.isGetter && m.name.name == 'iterator') { 1397 if (!hasJsPeer && m.isGetter && m.name.name == 'iterator') {
1504 hasIterator = true; 1398 hasIterator = true;
1505 jsMethods.add(_emitIterable(type)); 1399 jsMethods.add(_emitIterable(type));
(...skipping 27 matching lines...) Expand all
1533 jsMethods.add(_emitIterable(type)); 1427 jsMethods.add(_emitIterable(type));
1534 } 1428 }
1535 1429
1536 // Add all of the super helper methods 1430 // Add all of the super helper methods
1537 jsMethods.addAll(_superHelpers); 1431 jsMethods.addAll(_superHelpers);
1538 _superHelpers.clear(); 1432 _superHelpers.clear();
1539 1433
1540 return jsMethods.where((m) => m != null).toList(growable: false); 1434 return jsMethods.where((m) => m != null).toList(growable: false);
1541 } 1435 }
1542 1436
1437 /// Emits a Dart factory constructor to a JS static method.
1438 JS.Method _emitFactoryConstructor(ConstructorDeclaration node) {
1439 var element = node.element;
1440 var returnType = emitTypeRef(element.returnType);
1441 var name = _constructorName(element);
1442 JS.Fun fun;
1443
1444 var redirect = node.redirectedConstructor;
1445 if (redirect != null) {
1446 // Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz;
1447
1448 var newKeyword = redirect.staticElement.isFactory ? '' : 'new';
1449 // Pass along all arguments verbatim, and let the callee handle them.
1450 // TODO(jmesserly): we'll need something different once we have
1451 // rest/spread support, but this should work for now.
1452 var params =
1453 _emitFormalParameterList(node.parameters, destructure: false);
1454
1455 fun = new JS.Fun(
1456 params,
1457 js.statement(
1458 '{ return $newKeyword #(#); }', [_visit(redirect), params]),
1459 returnType: returnType);
1460 } else {
1461 // Normal factory constructor
1462 var body = <JS.Statement>[];
1463 var init = _emitArgumentInitializers(node, constructor: true);
1464 if (init != null) body.add(init);
1465 body.add(_visit(node.body));
1466
1467 var params = _emitFormalParameterList(node.parameters);
1468 fun = new JS.Fun(params, new JS.Block(body), returnType: returnType);
1469 }
1470
1471 return annotate(new JS.Method(name, fun, isStatic: true), node, element);
1472 }
1473
1543 /// Given a class C that implements method M from interface I, but does not 1474 /// Given a class C that implements method M from interface I, but does not
1544 /// declare M, this will generate an implementation that forwards to 1475 /// declare M, this will generate an implementation that forwards to
1545 /// noSuchMethod. 1476 /// noSuchMethod.
1546 /// 1477 ///
1547 /// For example: 1478 /// For example:
1548 /// 1479 ///
1549 /// class Cat { 1480 /// class Cat {
1550 /// bool eatFood(String food) => true; 1481 /// bool eatFood(String food) => true;
1551 /// } 1482 /// }
1552 /// class MockCat implements Cat { 1483 /// class MockCat implements Cat {
1553 /// noSuchMethod(Invocation invocation) => 3; 1484 /// noSuchMethod(Invocation invocation) => 3;
1554 /// } 1485 /// }
1555 /// 1486 ///
1556 /// It will generate an `eatFood` that looks like: 1487 /// It will generate an `eatFood` that looks like:
1557 /// 1488 ///
1558 /// eatFood(...args) { 1489 /// eatFood(...args) {
1559 /// return core.bool.as(this.noSuchMethod( 1490 /// return core.bool.as(this.noSuchMethod(
1560 /// new dart.InvocationImpl('eatFood', args))); 1491 /// new dart.InvocationImpl.new('eatFood', args)));
1561 /// } 1492 /// }
1562 JS.Method _implementMockMember(ExecutableElement method, InterfaceType type) { 1493 JS.Method _implementMockMember(ExecutableElement method, InterfaceType type) {
1563 var invocationProps = <JS.Property>[]; 1494 var invocationProps = <JS.Property>[];
1564 addProperty(String name, JS.Expression value) { 1495 addProperty(String name, JS.Expression value) {
1565 invocationProps.add(new JS.Property(js.string(name), value)); 1496 invocationProps.add(new JS.Property(js.string(name), value));
1566 } 1497 }
1567 1498
1568 var args = new JS.TemporaryId('args'); 1499 var args = new JS.TemporaryId('args');
1569 var fnArgs = <JS.Parameter>[]; 1500 var fnArgs = <JS.Parameter>[];
1570 JS.Expression positionalArgs; 1501 JS.Expression positionalArgs;
(...skipping 14 matching lines...) Expand all
1585 1516
1586 positionalArgs = new JS.ArrayInitializer([]); 1517 positionalArgs = new JS.ArrayInitializer([]);
1587 } else if (property.isSetter) { 1518 } else if (property.isSetter) {
1588 addProperty('isSetter', js.boolean(true)); 1519 addProperty('isSetter', js.boolean(true));
1589 1520
1590 fnArgs.add(args); 1521 fnArgs.add(args);
1591 positionalArgs = new JS.ArrayInitializer([args]); 1522 positionalArgs = new JS.ArrayInitializer([args]);
1592 } 1523 }
1593 } 1524 }
1594 1525
1595 var fnBody = js.call('this.noSuchMethod(new #.InvocationImpl(#, #, #))', [ 1526 var fnBody =
1527 js.call('this.noSuchMethod(new #.InvocationImpl.new(#, #, #))', [
1596 _runtimeModule, 1528 _runtimeModule,
1597 _declareMemberName(method, useDisplayName: true), 1529 _declareMemberName(method, useDisplayName: true),
1598 positionalArgs, 1530 positionalArgs,
1599 new JS.ObjectInitializer(invocationProps) 1531 new JS.ObjectInitializer(invocationProps)
1600 ]); 1532 ]);
1601 1533
1602 if (!method.returnType.isDynamic) { 1534 if (!method.returnType.isDynamic) {
1603 fnBody = js.call('#._check(#)', [_emitType(method.returnType), fnBody]); 1535 fnBody = js.call('#._check(#)', [_emitType(method.returnType), fnBody]);
1604 } 1536 }
1605 1537
(...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after
1775 newBaseClass = _callHelper('mixin(#)', [mixins]); 1707 newBaseClass = _callHelper('mixin(#)', [mixins]);
1776 } 1708 }
1777 var deferredBaseClass = _callHelperStatement( 1709 var deferredBaseClass = _callHelperStatement(
1778 'setBaseClass(#, #);', [className, newBaseClass]); 1710 'setBaseClass(#, #);', [className, newBaseClass]);
1779 if (typeFormals.isNotEmpty) return deferredBaseClass; 1711 if (typeFormals.isNotEmpty) return deferredBaseClass;
1780 body.add(deferredBaseClass); 1712 body.add(deferredBaseClass);
1781 } 1713 }
1782 return null; 1714 return null;
1783 } 1715 }
1784 1716
1785 void _defineNamedConstructors(List<ConstructorDeclaration> ctors, 1717 /// Defines all constructors for this class as ES5 constructors.
1786 List<JS.Statement> body, JS.Expression className, bool isCallable) { 1718 void _defineConstructors(
1787 var code = isCallable 1719 ClassElement classElem,
1788 ? 'defineNamedConstructorCallable(#, #, #);' 1720 JS.Expression className,
1789 : 'defineNamedConstructor(#, #)'; 1721 List<FieldDeclaration> fields,
1722 List<JS.Statement> body,
1723 List<ConstructorDeclaration> ctors) {
1724 // See if we have a "call" with a statically known function type:
1725 //
1726 // - if it's a method, then it does because all methods do,
1727 // - if it's a getter, check the return type.
1728 //
1729 // Other cases like a getter returning dynamic/Object/Function will be
1730 // handled at runtime by the dynamic call mechanism. So we only
1731 // concern ourselves with statically known function types.
1732 //
1733 // For the same reason, we can ignore "noSuchMethod".
1734 // call-implemented-by-nSM will be dispatched by dcall at runtime.
1735 bool isCallable = classElem.lookUpMethod('call', null) != null ||
1736 classElem.lookUpGetter('call', null)?.returnType is FunctionType;
1790 1737
1791 for (ConstructorDeclaration member in ctors) { 1738 void addConstructor(ConstructorElement element, JS.Expression jsCtor) {
1792 if (member.name != null && member.factoryKeyword == null) { 1739 var ctorName = _constructorName(element);
1793 var args = [className, _constructorName(member.element)]; 1740 if (JS.invalidStaticFieldName(element.name)) {
1794 if (isCallable) { 1741 jsCtor =
1795 args.add(_emitCallableClassConstructor(member.element)); 1742 _callHelper('defineValue(#, #, #)', [className, ctorName, jsCtor]);
1796 } 1743 } else {
1744 jsCtor = js.call('#.# = #', [className, ctorName, jsCtor]);
1745 }
1746 body.add(js.statement('#.prototype = #.prototype;', [jsCtor, className]));
1747 }
1797 1748
1798 body.add(_callHelperStatement(code, args)); 1749 if (classElem.isMixinApplication) {
1750 var supertype = classElem.supertype;
1751 for (var ctor in classElem.constructors) {
1752 List<JS.Identifier> jsParams = _emitParametersForElement(ctor);
1753 var superCtor = supertype.lookUpConstructor(ctor.name, ctor.library);
1754 var superCall =
1755 _superConstructorCall(classElem, className, superCtor, jsParams);
1756 addConstructor(
1757 ctor,
1758 _finishConstructorFunction(
1759 jsParams,
1760 new JS.Block(superCall != null ? [superCall] : []),
1761 isCallable));
1799 } 1762 }
1763 return;
1764 }
1765
1766 // Iff no constructor is specified for a class C, it implicitly has a
1767 // default constructor `C() : super() {}`, unless C is class Object.
1768 if (ctors.isEmpty) {
1769 var superCall = _superConstructorCall(classElem, className);
1770 List<JS.Statement> body = [_initializeFields(fields)];
1771 if (superCall != null) body.add(superCall);
1772
1773 addConstructor(classElem.unnamedConstructor,
1774 _finishConstructorFunction([], new JS.Block(body), isCallable));
1775 return;
1776 }
1777
1778 for (var ctor in ctors) {
1779 var element = ctor.element;
1780 if (element.isFactory || _externalOrNative(ctor)) continue;
1781
1782 addConstructor(
1783 element, _emitConstructor(ctor, fields, isCallable, className));
1800 } 1784 }
1801 } 1785 }
1802 1786
1803 /// Emits static fields for a class, and initialize them eagerly if possible, 1787 /// Emits static fields for a class, and initialize them eagerly if possible,
1804 /// otherwise define them as lazy properties. 1788 /// otherwise define them as lazy properties.
1805 void _emitStaticFields(List<FieldDeclaration> staticFields, 1789 void _emitStaticFields(List<FieldDeclaration> staticFields,
1806 ClassElement classElem, List<JS.Statement> body) { 1790 ClassElement classElem, List<JS.Statement> body) {
1807 var lazyStatics = staticFields.expand((f) => f.fields.variables).toList(); 1791 var lazyStatics = staticFields.expand((f) => f.fields.variables).toList();
1808 if (lazyStatics.isNotEmpty) { 1792 if (lazyStatics.isNotEmpty) {
1809 body.add(_emitLazyFields(classElem, lazyStatics)); 1793 body.add(_emitLazyFields(classElem, lazyStatics));
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after
1968 var fieldSig = _emitFieldSignature(element.type, 1952 var fieldSig = _emitFieldSignature(element.type,
1969 metadata: node.metadata, isFinal: element.isFinal); 1953 metadata: node.metadata, isFinal: element.isFinal);
1970 fieldList.add(new JS.Property(memberName, fieldSig)); 1954 fieldList.add(new JS.Property(memberName, fieldSig));
1971 } 1955 }
1972 } 1956 }
1973 } 1957 }
1974 1958
1975 var tCtors = <JS.Property>[]; 1959 var tCtors = <JS.Property>[];
1976 if (options.emitMetadata) { 1960 if (options.emitMetadata) {
1977 for (ConstructorDeclaration node in ctors) { 1961 for (ConstructorDeclaration node in ctors) {
1978 var memberName = _constructorName(node.element); 1962 var element = node.element;
1979 var element = 1963 var memberName = _constructorName(element);
1980 resolutionMap.elementDeclaredByConstructorDeclaration(node);
1981 var type = _emitAnnotatedFunctionType(element.type, node.metadata, 1964 var type = _emitAnnotatedFunctionType(element.type, node.metadata,
1982 parameters: node.parameters.parameters, 1965 parameters: node.parameters.parameters,
1983 nameType: options.hoistSignatureTypes, 1966 nameType: options.hoistSignatureTypes,
1984 hoistType: options.hoistSignatureTypes, 1967 hoistType: options.hoistSignatureTypes,
1985 definite: true); 1968 definite: true);
1986 var property = new JS.Property(memberName, type); 1969 var property = new JS.Property(memberName, type);
1987 tCtors.add(property); 1970 tCtors.add(property);
1988 } 1971 }
1989 } 1972 }
1990 var sigFields = <JS.Property>[]; 1973 var sigFields = <JS.Property>[];
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
2045 } 2028 }
2046 } 2029 }
2047 } 2030 }
2048 if (dartxNames.isNotEmpty) { 2031 if (dartxNames.isNotEmpty) {
2049 body.add(_callHelperStatement('defineExtensionNames(#)', 2032 body.add(_callHelperStatement('defineExtensionNames(#)',
2050 [new JS.ArrayInitializer(dartxNames, multiline: true)])); 2033 [new JS.ArrayInitializer(dartxNames, multiline: true)]));
2051 } 2034 }
2052 } 2035 }
2053 } 2036 }
2054 2037
2055 /// Generates the implicit default constructor for class C of the form 2038 JS.Expression _emitConstructor(ConstructorDeclaration node,
2056 /// `C() : super() {}`. 2039 List<FieldDeclaration> fields, bool isCallable, JS.Expression className) {
2057 JS.Method _emitImplicitConstructor( 2040 var params = _emitFormalParameterList(node.parameters);
2058 ClassDeclaration node,
2059 List<FieldDeclaration> fields,
2060 Map<FieldElement, JS.TemporaryId> virtualFields) {
2061 // If we don't have a method body, skip this.
2062 var superCall = _superConstructorCall(node.element);
2063 if (fields.isEmpty && superCall == null) return null;
2064 2041
2065 var initFields = _initializeFields(node, fields, virtualFields); 2042 var savedFunction = _currentFunction;
2066 List<JS.Statement> body = [initFields]; 2043 _currentFunction = node.body;
2067 if (superCall != null) { 2044
2068 body.add(superCall); 2045 var savedSuperAllowed = _superAllowed;
2069 } 2046 _superAllowed = false;
2070 var name = _constructorName(resolutionMap 2047 var body = _emitConstructorBody(node, fields, className);
2071 .elementDeclaredByClassDeclaration(node) 2048 _superAllowed = savedSuperAllowed;
2072 .unnamedConstructor); 2049 _currentFunction = savedFunction;
2073 return annotate( 2050
2074 new JS.Method(name, js.call('function() { #; }', [body]) as JS.Fun), 2051 return _finishConstructorFunction(params, body, isCallable);
2075 node,
2076 node.element);
2077 } 2052 }
2078 2053
2079 JS.Method _emitConstructor( 2054 JS.Expression _finishConstructorFunction(
2080 ConstructorDeclaration node, 2055 List<JS.Parameter> params, JS.Block body, isCallable) {
2081 InterfaceType type, 2056 // We consider a class callable if it inherits from anything with a `call`
2082 List<FieldDeclaration> fields, 2057 // method. As a result, we can know the callable JS function was created
2083 Map<FieldElement, JS.TemporaryId> virtualFields, 2058 // at the first constructor that was hit.
2084 bool isObject) { 2059 if (!isCallable) return new JS.Fun(params, body);
2085 if (_externalOrNative(node)) return null; 2060 return js.call(
2086 2061 r'''function callableClass(#) {
2087 var name = _constructorName(node.element); 2062 if (typeof this !== "function") {
2088 var returnType = emitTypeRef(resolutionMap 2063 function self(...args) {
2089 .elementDeclaredByConstructorDeclaration(node) 2064 return self.call.apply(self, args);
2090 .enclosingElement 2065 }
2091 .type); 2066 self.__proto__ = this.__proto__;
2092 2067 callableClass.call(self, #);
2093 // Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz; 2068 return self;
2094 var redirect = node.redirectedConstructor; 2069 }
2095 if (redirect != null) { 2070 #
2096 var newKeyword = 2071 }''',
2097 resolutionMap.staticElementForConstructorReference(redirect).isFactory 2072 [params, params, body]);
2098 ? ''
2099 : 'new';
2100 // Pass along all arguments verbatim, and let the callee handle them.
2101 // TODO(jmesserly): we'll need something different once we have
2102 // rest/spread support, but this should work for now.
2103 var params =
2104 _emitFormalParameterList(node.parameters, destructure: false);
2105
2106 var fun = new JS.Fun(
2107 params,
2108 js.statement(
2109 '{ return $newKeyword #(#); }', [_visit(redirect), params]),
2110 returnType: returnType);
2111 return annotate(
2112 new JS.Method(name, fun, isStatic: true), node, node.element);
2113 }
2114
2115 var params = _emitFormalParameterList(node.parameters);
2116
2117 // Factory constructors are essentially static methods.
2118 if (node.factoryKeyword != null) {
2119 var body = <JS.Statement>[];
2120 var init = _emitArgumentInitializers(node, constructor: true);
2121 if (init != null) body.add(init);
2122 body.add(_visit(node.body));
2123 var fun = new JS.Fun(params, new JS.Block(body), returnType: returnType);
2124 return annotate(
2125 new JS.Method(name, fun, isStatic: true), node, node.element);
2126 }
2127
2128 // Code generation for Object's constructor.
2129 var savedFunction = _currentFunction;
2130 _currentFunction = node.body;
2131 var body = _emitConstructorBody(node, fields, virtualFields);
2132 _currentFunction = savedFunction;
2133
2134 // We generate constructors as initializer methods in the class;
2135 // this allows use of `super` for instance methods/properties.
2136 // It also avoids V8 restrictions on `super` in default constructors.
2137 return annotate(
2138 new JS.Method(name, new JS.Fun(params, body, returnType: returnType)),
2139 node,
2140 node.element);
2141 } 2073 }
2142 2074
2143 JS.Expression _constructorName(ConstructorElement ctor) { 2075 JS.Expression _constructorName(ConstructorElement ctor) {
2144 var name = ctor.name; 2076 var name = ctor.name;
2145 if (name == '') { 2077 if (name == '') {
2146 // Default constructors (factory or not) use `new` as their name. 2078 // Default constructors (factory or not) use `new` as their name.
2147 return _propertyName('new'); 2079 return _propertyName('new');
2148 } 2080 }
2149 return _emitMemberName(name, isStatic: true); 2081 return _emitMemberName(name, isStatic: true);
2150 } 2082 }
2151 2083
2152 JS.Block _emitConstructorBody( 2084 JS.Block _emitConstructorBody(ConstructorDeclaration node,
2153 ConstructorDeclaration node, 2085 List<FieldDeclaration> fields, JS.Expression className) {
2154 List<FieldDeclaration> fields,
2155 Map<FieldElement, JS.TemporaryId> virtualFields) {
2156 var body = <JS.Statement>[]; 2086 var body = <JS.Statement>[];
2157 ClassDeclaration cls = node.parent; 2087 ClassDeclaration cls = node.parent;
2158 2088
2159 // Generate optional/named argument value assignment. These can not have 2089 // Generate optional/named argument value assignment. These can not have
2160 // side effects, and may be used by the constructor's initializers, so it's 2090 // side effects, and may be used by the constructor's initializers, so it's
2161 // nice to do them first. 2091 // nice to do them first.
2162 // Also for const constructors we need to ensure default values are 2092 // Also for const constructors we need to ensure default values are
2163 // available for use by top-level constant initializers. 2093 // available for use by top-level constant initializers.
2164 var init = _emitArgumentInitializers(node, constructor: true); 2094 var init = _emitArgumentInitializers(node, constructor: true);
2165 if (init != null) body.add(init); 2095 if (init != null) body.add(init);
2166 2096
2167 // Redirecting constructors: these are not allowed to have initializers, 2097 // Redirecting constructors: these are not allowed to have initializers,
2168 // and the redirecting ctor invocation runs before field initializers. 2098 // and the redirecting ctor invocation runs before field initializers.
2169 var redirectCall = node.initializers.firstWhere( 2099 var redirectCall = node.initializers.firstWhere(
2170 (i) => i is RedirectingConstructorInvocation, 2100 (i) => i is RedirectingConstructorInvocation,
2171 orElse: () => null); 2101 orElse: () => null);
2172 2102
2173 if (redirectCall != null) { 2103 if (redirectCall != null) {
2174 body.add(_visit(redirectCall)); 2104 body.add(_emitRedirectingConstructor(redirectCall, className));
2175 return new JS.Block(body); 2105 return new JS.Block(body);
2176 } 2106 }
2177 2107
2178 // Generate field initializers. 2108 // Generate field initializers.
2179 // These are expanded into each non-redirecting constructor. 2109 // These are expanded into each non-redirecting constructor.
2180 // In the future we may want to create an initializer function if we have 2110 // In the future we may want to create an initializer function if we have
2181 // multiple constructors, but it needs to be balanced against readability. 2111 // multiple constructors, but it needs to be balanced against readability.
2182 body.add(_initializeFields(cls, fields, virtualFields, node)); 2112 body.add(_initializeFields(fields, node));
2183 2113
2184 var superCall = node.initializers.firstWhere( 2114 var superCall = node.initializers.firstWhere(
2185 (i) => i is SuperConstructorInvocation, 2115 (i) => i is SuperConstructorInvocation,
2186 orElse: () => null) as SuperConstructorInvocation; 2116 orElse: () => null) as SuperConstructorInvocation;
2187 2117
2188 // If no superinitializer is provided, an implicit superinitializer of the 2118 // If no superinitializer is provided, an implicit superinitializer of the
2189 // form `super()` is added at the end of the initializer list, unless the 2119 // form `super()` is added at the end of the initializer list, unless the
2190 // enclosing class is class Object. 2120 // enclosing class is class Object.
2191 var jsSuper = _superConstructorCall(cls.element, superCall); 2121 var superCallArgs =
2192 if (jsSuper != null) body.add(jsSuper); 2122 superCall != null ? _emitArgumentList(superCall.argumentList) : null;
2123 var jsSuper = _superConstructorCall(
2124 cls.element, className, superCall?.staticElement, superCallArgs);
2125 if (jsSuper != null) body.add(annotate(jsSuper, superCall));
2193 2126
2194 body.add(_visit(node.body)); 2127 body.add(_visit(node.body));
2195 return new JS.Block(body)..sourceInformation = node; 2128 return new JS.Block(body)..sourceInformation = node;
2196 } 2129 }
2197 2130
2198 @override 2131 JS.Statement _emitRedirectingConstructor(
2199 JS.Statement visitRedirectingConstructorInvocation( 2132 RedirectingConstructorInvocation node, JS.Expression className) {
2200 RedirectingConstructorInvocation node) { 2133 var ctor = node.staticElement;
2201 var ctor = resolutionMap.staticElementForConstructorReference(node);
2202 var cls = ctor.enclosingElement;
2203 // We can't dispatch to the constructor with `this.new` as that might hit a 2134 // We can't dispatch to the constructor with `this.new` as that might hit a
2204 // derived class constructor with the same name. 2135 // derived class constructor with the same name.
2205 return js.statement('#.prototype.#.call(this, #);', [ 2136 return js.statement('#.#.call(this, #);', [
2206 new JS.Identifier(cls.name), 2137 className,
2207 _constructorName(ctor), 2138 _constructorName(ctor),
2208 _emitArgumentList(node.argumentList) 2139 _emitArgumentList(node.argumentList)
2209 ]); 2140 ]);
2210 } 2141 }
2211 2142
2212 JS.Statement _superConstructorCall(ClassElement element, 2143 JS.Statement _superConstructorCall(
2213 [SuperConstructorInvocation node]) { 2144 ClassElement element, JS.Expression className,
2214 if (element.supertype == null) { 2145 [ConstructorElement superCtor, List<JS.Expression> args]) {
2146 // Get the supertype's unnamed constructor.
2147 superCtor ??= element.supertype?.element?.unnamedConstructor;
2148 if (superCtor == null) {
2215 assert(element.type.isObject || options.unsafeForceCompile); 2149 assert(element.type.isObject || options.unsafeForceCompile);
2216 return null; 2150 return null;
2217 } 2151 }
2218 2152
2219 ConstructorElement superCtor; 2153 // We can skip the super call if it's empty. Typically this happens for
2220 if (node != null) { 2154 // things that extend Object.
2221 superCtor = node.staticElement;
2222 } else {
2223 // Get the supertype's unnamed constructor.
2224 superCtor = element.supertype.element.unnamedConstructor;
2225 }
2226
2227 if (superCtor == null) {
2228 // This will only happen if the code has errors:
2229 // we're trying to generate an implicit constructor for a type where
2230 // we don't have a default constructor in the supertype.
2231 assert(options.unsafeForceCompile);
2232 return null;
2233 }
2234
2235 if (superCtor.name == '' && !_hasUnnamedSuperConstructor(element)) { 2155 if (superCtor.name == '' && !_hasUnnamedSuperConstructor(element)) {
2236 return null; 2156 return null;
2237 } 2157 }
2238 2158
2239 var name = _constructorName(superCtor); 2159 var name = _constructorName(superCtor);
2240 var args = node != null ? _emitArgumentList(node.argumentList) : []; 2160 return js.statement(
2241 return annotate(js.statement('super.#(#);', [name, args]), node); 2161 '#.__proto__.#.call(this, #);', [className, name, args ?? []]);
2242 } 2162 }
2243 2163
2244 bool _hasUnnamedSuperConstructor(ClassElement e) { 2164 bool _hasUnnamedSuperConstructor(ClassElement e) {
2245 var supertype = e.supertype; 2165 var supertype = e.supertype;
2246 if (supertype == null) return false; 2166 if (supertype == null) return false;
2247 if (_hasUnnamedConstructor(supertype.element)) return true; 2167 if (_hasUnnamedConstructor(supertype.element)) return true;
2248 for (var mixin in e.mixins) { 2168 for (var mixin in e.mixins) {
2249 if (_hasUnnamedConstructor(mixin.element)) return true; 2169 if (_hasUnnamedConstructor(mixin.element)) return true;
2250 } 2170 }
2251 return false; 2171 return false;
2252 } 2172 }
2253 2173
2254 bool _hasUnnamedConstructor(ClassElement e) { 2174 bool _hasUnnamedConstructor(ClassElement e) {
2255 if (e.type.isObject) return false; 2175 if (e.type.isObject) return false;
2256 if (!e.unnamedConstructor.isSynthetic) return true; 2176 if (!e.unnamedConstructor.isSynthetic) return true;
2257 if (e.fields.any((f) => !f.isStatic && !f.isSynthetic)) return true; 2177 if (e.fields.any((f) => !f.isStatic && !f.isSynthetic)) return true;
2258 return _hasUnnamedSuperConstructor(e); 2178 return _hasUnnamedSuperConstructor(e);
2259 } 2179 }
2260 2180
2261 /// Initialize fields. They follow the sequence: 2181 /// Initialize fields. They follow the sequence:
2262 /// 2182 ///
2263 /// 1. field declaration initializer if non-const, 2183 /// 1. field declaration initializer if non-const,
2264 /// 2. field initializing parameters, 2184 /// 2. field initializing parameters,
2265 /// 3. constructor field initializers, 2185 /// 3. constructor field initializers,
2266 /// 4. initialize fields not covered in 1-3 2186 /// 4. initialize fields not covered in 1-3
2267 JS.Statement _initializeFields( 2187 JS.Statement _initializeFields(List<FieldDeclaration> fieldDecls,
2268 ClassDeclaration cls,
2269 List<FieldDeclaration> fieldDecls,
2270 Map<FieldElement, JS.TemporaryId> virtualFields,
2271 [ConstructorDeclaration ctor]) { 2188 [ConstructorDeclaration ctor]) {
2272 // Run field initializers if they can have side-effects. 2189 // Run field initializers if they can have side-effects.
2273 var fields = new Map<FieldElement, JS.Expression>(); 2190 var fields = new Map<FieldElement, JS.Expression>();
2274 var unsetFields = new Map<FieldElement, VariableDeclaration>(); 2191 var unsetFields = new Map<FieldElement, VariableDeclaration>();
2275 for (var declaration in fieldDecls) { 2192 for (var declaration in fieldDecls) {
2276 for (var fieldNode in declaration.fields.variables) { 2193 for (var fieldNode in declaration.fields.variables) {
2277 var element = fieldNode.element; 2194 var element = fieldNode.element;
2278 if (_constants.isFieldInitConstant(fieldNode)) { 2195 if (_constants.isFieldInitConstant(fieldNode)) {
2279 unsetFields[element as FieldElement] = fieldNode; 2196 unsetFields[element as FieldElement] = fieldNode;
2280 } else { 2197 } else {
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
2313 if (fieldNode.initializer != null) { 2230 if (fieldNode.initializer != null) {
2314 value = _visit(fieldNode.initializer); 2231 value = _visit(fieldNode.initializer);
2315 } else { 2232 } else {
2316 value = new JS.LiteralNull(); 2233 value = new JS.LiteralNull();
2317 } 2234 }
2318 fields[element] = value; 2235 fields[element] = value;
2319 }); 2236 });
2320 2237
2321 var body = <JS.Statement>[]; 2238 var body = <JS.Statement>[];
2322 fields.forEach((FieldElement e, JS.Expression initialValue) { 2239 fields.forEach((FieldElement e, JS.Expression initialValue) {
2323 JS.Expression access = virtualFields[e] ?? _declareMemberName(e.getter); 2240 JS.Expression access =
2241 _classProperties.virtualFields[e] ?? _declareMemberName(e.getter);
2324 body.add(js.statement('this.# = #;', [access, initialValue])); 2242 body.add(js.statement('this.# = #;', [access, initialValue]));
2325 }); 2243 });
2326 2244
2327 return _statement(body); 2245 return _statement(body);
2328 } 2246 }
2329 2247
2330 FormalParameterList _parametersOf(node) { 2248 FormalParameterList _parametersOf(node) {
2331 // TODO(jmesserly): clean this up. If we can model ES6 spread/rest args, we 2249 // TODO(jmesserly): clean this up. If we can model ES6 spread/rest args, we
2332 // could handle argument initializers more consistently in a separate 2250 // could handle argument initializers more consistently in a separate
2333 // lowering pass. 2251 // lowering pass.
(...skipping 461 matching lines...) Expand 10 before | Expand all | Expand 10 after
2795 } 2713 }
2796 return _callHelper('gbind(#, #)', [simpleId, typeArgs]); 2714 return _callHelper('gbind(#, #)', [simpleId, typeArgs]);
2797 } 2715 }
2798 2716
2799 /// Emits a simple identifier, handling implicit `this` as well as 2717 /// Emits a simple identifier, handling implicit `this` as well as
2800 /// going through the qualified library name if necessary, but *not* handling 2718 /// going through the qualified library name if necessary, but *not* handling
2801 /// inferred generic function instantiation. 2719 /// inferred generic function instantiation.
2802 JS.Expression _emitSimpleIdentifier(SimpleIdentifier node) { 2720 JS.Expression _emitSimpleIdentifier(SimpleIdentifier node) {
2803 var accessor = resolutionMap.staticElementForIdentifier(node); 2721 var accessor = resolutionMap.staticElementForIdentifier(node);
2804 if (accessor == null) { 2722 if (accessor == null) {
2805 return _callHelper('throw("compile error: unresolved identifier: " + #)', 2723 return _callHelper(
2724 'throw(Error("compile error: unresolved identifier: " + #))',
2806 js.escapedString(node.name ?? '<null>')); 2725 js.escapedString(node.name ?? '<null>'));
2807 } 2726 }
2808 2727
2809 // Get the original declaring element. If we had a property accessor, this 2728 // Get the original declaring element. If we had a property accessor, this
2810 // indirects back to a (possibly synthetic) field. 2729 // indirects back to a (possibly synthetic) field.
2811 var element = accessor; 2730 var element = accessor;
2812 if (accessor is PropertyAccessorElement) element = accessor.variable; 2731 if (accessor is PropertyAccessorElement) element = accessor.variable;
2813 2732
2814 // type literal 2733 // type literal
2815 if (element is TypeDefiningElement) { 2734 if (element is TypeDefiningElement) {
(...skipping 984 matching lines...) Expand 10 before | Expand all | Expand 10 after
3800 return args; 3719 return args;
3801 } 3720 }
3802 3721
3803 @override 3722 @override
3804 JS.Property visitNamedExpression(NamedExpression node) { 3723 JS.Property visitNamedExpression(NamedExpression node) {
3805 assert(node.parent is ArgumentList); 3724 assert(node.parent is ArgumentList);
3806 return new JS.Property( 3725 return new JS.Property(
3807 _propertyName(node.name.label.name), _visit(node.expression)); 3726 _propertyName(node.name.label.name), _visit(node.expression));
3808 } 3727 }
3809 3728
3729 List<JS.Parameter> _emitParametersForElement(ExecutableElement member) {
3730 var jsParams = <JS.Identifier>[];
3731 for (var p in member.parameters) {
3732 if (p.parameterKind != ParameterKind.NAMED) {
3733 jsParams.add(new JS.Identifier(p.name));
3734 } else {
3735 jsParams.add(new JS.TemporaryId('namedArgs'));
3736 break;
3737 }
3738 }
3739 return jsParams;
3740 }
3741
3810 List<JS.Parameter> _emitFormalParameterList(FormalParameterList node, 3742 List<JS.Parameter> _emitFormalParameterList(FormalParameterList node,
3811 {bool destructure: true}) { 3743 {bool destructure: true}) {
3812 if (node == null) return []; 3744 if (node == null) return [];
3813 3745
3814 destructure = destructure && options.destructureNamedParams; 3746 destructure = destructure && options.destructureNamedParams;
3815 3747
3816 var result = <JS.Parameter>[]; 3748 var result = <JS.Parameter>[];
3817 var namedVars = <JS.DestructuredVariable>[]; 3749 var namedVars = <JS.DestructuredVariable>[];
3818 var hasNamedArgsConflictingWithObjectProperties = false; 3750 var hasNamedArgsConflictingWithObjectProperties = false;
3819 var needsOpts = false; 3751 var needsOpts = false;
(...skipping 238 matching lines...) Expand 10 before | Expand all | Expand 10 after
4058 if (parent is ClassElement) { 3990 if (parent is ClassElement) {
4059 return getter 3991 return getter
4060 ? parent.getGetter(element.name) 3992 ? parent.getGetter(element.name)
4061 : parent.getSetter(element.name); 3993 : parent.getSetter(element.name);
4062 } 3994 }
4063 return null; 3995 return null;
4064 } 3996 }
4065 3997
4066 JS.Expression _emitConstructorName( 3998 JS.Expression _emitConstructorName(
4067 ConstructorElement element, DartType type, SimpleIdentifier name) { 3999 ConstructorElement element, DartType type, SimpleIdentifier name) {
4068 var classElem = element.enclosingElement; 4000 return _emitJSInterop(type.element) ??
4069 var interop = _emitJSInterop(classElem); 4001 new JS.PropertyAccess(
4070 if (interop != null) return interop; 4002 _emitConstructorAccess(type), _constructorName(element));
4071 var typeName = _emitConstructorAccess(type);
4072 if (name != null || element.isFactory) {
4073 var namedCtor = _constructorName(element);
4074 return new JS.PropertyAccess(typeName, namedCtor);
4075 }
4076 return typeName;
4077 } 4003 }
4078 4004
4079 @override 4005 @override
4080 visitConstructorName(ConstructorName node) { 4006 visitConstructorName(ConstructorName node) {
4081 return _emitConstructorName(node.staticElement, node.type.type, node.name); 4007 return _emitConstructorName(node.staticElement, node.type.type, node.name);
4082 } 4008 }
4083 4009
4084 JS.Expression _emitInstanceCreationExpression( 4010 JS.Expression _emitInstanceCreationExpression(
4085 ConstructorElement element, 4011 ConstructorElement element,
4086 DartType type, 4012 DartType type,
4087 SimpleIdentifier name, 4013 SimpleIdentifier name,
4088 ArgumentList argumentList, 4014 ArgumentList argumentList,
4089 bool isConst) { 4015 bool isConst) {
4090 JS.Expression emitNew() { 4016 JS.Expression emitNew() {
4091 JS.Expression ctor; 4017 JS.Expression ctor;
4092 bool isFactory = false; 4018 bool isFactory = false;
4093 bool isNative = false; 4019 bool isNative = false;
4094 if (element == null) { 4020 if (element == null) {
4095 ctor = _callHelper( 4021 ctor = _callHelper(
4096 'throw("compile error: unresolved constructor: " + # + "." + #)', [ 4022 'throw(Error("compile error: unresolved constructor: " '
4097 js.escapedString(type?.name ?? '<null>'), 4023 '+ # + "." + #))',
4098 js.escapedString(name?.name ?? '<unnamed>') 4024 [
4099 ]); 4025 js.escapedString(type?.name ?? '<null>'),
4026 js.escapedString(name?.name ?? '<unnamed>')
4027 ]);
4100 } else { 4028 } else {
4101 ctor = _emitConstructorName(element, type, name); 4029 ctor = _emitConstructorName(element, type, name);
4102 isFactory = element.isFactory; 4030 isFactory = element.isFactory;
4103 var classElem = element.enclosingElement; 4031 var classElem = element.enclosingElement;
4104 isNative = _isJSNative(classElem); 4032 isNative = _isJSNative(classElem);
4105 } 4033 }
4106 var args = _emitArgumentList(argumentList); 4034 var args = _emitArgumentList(argumentList);
4107 // Native factory constructors are JS constructors - use new here. 4035 // Native factory constructors are JS constructors - use new here.
4108 return isFactory && !isNative 4036 return isFactory && !isNative
4109 ? new JS.Call(ctor, args) 4037 ? new JS.Call(ctor, args)
(...skipping 1180 matching lines...) Expand 10 before | Expand all | Expand 10 after
5290 visitNullLiteral(NullLiteral node) => new JS.LiteralNull(); 5218 visitNullLiteral(NullLiteral node) => new JS.LiteralNull();
5291 5219
5292 @override 5220 @override
5293 visitSymbolLiteral(SymbolLiteral node) { 5221 visitSymbolLiteral(SymbolLiteral node) {
5294 JS.Expression emitSymbol() { 5222 JS.Expression emitSymbol() {
5295 // TODO(vsm): Handle qualified symbols correctly. 5223 // TODO(vsm): Handle qualified symbols correctly.
5296 var last = node.components.last.toString(); 5224 var last = node.components.last.toString();
5297 var name = js.string(node.components.join('.'), "'"); 5225 var name = js.string(node.components.join('.'), "'");
5298 if (last.startsWith('_')) { 5226 if (last.startsWith('_')) {
5299 var nativeSymbol = _emitPrivateNameSymbol(currentLibrary, last); 5227 var nativeSymbol = _emitPrivateNameSymbol(currentLibrary, last);
5300 return js.call('new #(#, #)', [ 5228 return js.call('new #.new(#, #)', [
5301 _emitConstructorAccess(privateSymbolClass.type), 5229 _emitConstructorAccess(privateSymbolClass.type),
5302 name, 5230 name,
5303 nativeSymbol 5231 nativeSymbol
5304 ]); 5232 ]);
5305 } else { 5233 } else {
5306 return js 5234 return js
5307 .call('#.new(#)', [_emitConstructorAccess(types.symbolType), name]); 5235 .call('#.new(#)', [_emitConstructorAccess(types.symbolType), name]);
5308 } 5236 }
5309 } 5237 }
5310 5238
(...skipping 484 matching lines...) Expand 10 before | Expand all | Expand 10 after
5795 visitConfiguration(node) => _unreachable(node); 5723 visitConfiguration(node) => _unreachable(node);
5796 5724
5797 /// Unusued, see [_emitConstructor]. 5725 /// Unusued, see [_emitConstructor].
5798 @override 5726 @override
5799 visitConstructorDeclaration(node) => _unreachable(node); 5727 visitConstructorDeclaration(node) => _unreachable(node);
5800 5728
5801 /// Unusued, see [_emitFieldInitializers]. 5729 /// Unusued, see [_emitFieldInitializers].
5802 @override 5730 @override
5803 visitConstructorFieldInitializer(node) => _unreachable(node); 5731 visitConstructorFieldInitializer(node) => _unreachable(node);
5804 5732
5733 /// Unusued, see [_emitRedirectingConstructor].
5734 @override
5735 visitRedirectingConstructorInvocation(node) => _unreachable(node);
5736
5805 /// Unusued. Handled in [visitForEachStatement]. 5737 /// Unusued. Handled in [visitForEachStatement].
5806 @override 5738 @override
5807 visitDeclaredIdentifier(node) => _unreachable(node); 5739 visitDeclaredIdentifier(node) => _unreachable(node);
5808 5740
5809 /// Unused, handled by imports/exports. 5741 /// Unused, handled by imports/exports.
5810 @override 5742 @override
5811 visitDottedName(node) => _unreachable(node); 5743 visitDottedName(node) => _unreachable(node);
5812 5744
5813 /// Unused, handled by [visitEnumDeclaration]. 5745 /// Unused, handled by [visitEnumDeclaration].
5814 @override 5746 @override
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after
5979 if (targetIdentifier.staticElement is! PrefixElement) return false; 5911 if (targetIdentifier.staticElement is! PrefixElement) return false;
5980 var prefix = targetIdentifier.staticElement as PrefixElement; 5912 var prefix = targetIdentifier.staticElement as PrefixElement;
5981 5913
5982 // The library the prefix is referring to must come from a deferred import. 5914 // The library the prefix is referring to must come from a deferred import.
5983 var containingLibrary = resolutionMap 5915 var containingLibrary = resolutionMap
5984 .elementDeclaredByCompilationUnit(target.root as CompilationUnit) 5916 .elementDeclaredByCompilationUnit(target.root as CompilationUnit)
5985 .library; 5917 .library;
5986 var imports = containingLibrary.getImportsWithPrefix(prefix); 5918 var imports = containingLibrary.getImportsWithPrefix(prefix);
5987 return imports.length == 1 && imports[0].isDeferred; 5919 return imports.length == 1 && imports[0].isDeferred;
5988 } 5920 }
OLDNEW
« no previous file with comments | « pkg/dev_compiler/lib/sdk/ddc_sdk.sum ('k') | pkg/dev_compiler/test/browser/language_tests.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698