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

Side by Side Diff: lib/src/codegen/js_codegen.dart

Issue 1138793002: Tag closures with their types (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Rebase Created 5 years, 7 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 // 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 library dev_compiler.src.codegen.js_codegen; 5 library dev_compiler.src.codegen.js_codegen;
6 6
7 import 'dart:collection' show HashSet, HashMap; 7 import 'dart:collection' show HashSet, HashMap;
8 8
9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator;
10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; 10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator;
(...skipping 317 matching lines...) Expand 10 before | Expand all | Expand 10 after
328 JS.Statement visitClassDeclaration(ClassDeclaration node) { 328 JS.Statement visitClassDeclaration(ClassDeclaration node) {
329 // If we've already emitted this class, skip it. 329 // If we've already emitted this class, skip it.
330 var classElem = node.element; 330 var classElem = node.element;
331 var type = classElem.type; 331 var type = classElem.type;
332 var jsName = getAnnotationValue(node, _isJsNameAnnotation); 332 var jsName = getAnnotationValue(node, _isJsNameAnnotation);
333 333
334 if (jsName != null) return _emitJsType(node.name.name, jsName); 334 if (jsName != null) return _emitJsType(node.name.name, jsName);
335 335
336 var ctors = <ConstructorDeclaration>[]; 336 var ctors = <ConstructorDeclaration>[];
337 var fields = <FieldDeclaration>[]; 337 var fields = <FieldDeclaration>[];
338 var methods = <MethodDeclaration>[];
338 for (var member in node.members) { 339 for (var member in node.members) {
339 if (member is ConstructorDeclaration) { 340 if (member is ConstructorDeclaration) {
340 ctors.add(member); 341 ctors.add(member);
341 } else if (member is FieldDeclaration && !member.isStatic) { 342 } else if (member is FieldDeclaration && !member.isStatic) {
342 fields.add(member); 343 fields.add(member);
344 } else if (member is MethodDeclaration) {
345 methods.add(member);
343 } 346 }
344 } 347 }
345 348
346 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), 349 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name),
347 _classHeritage(classElem), _emitClassMethods(node, ctors, fields)); 350 _classHeritage(classElem), _emitClassMethods(node, ctors, fields));
348 351
349 String jsPeerName; 352 String jsPeerName;
350 var jsPeer = getAnnotationValue(node, _isJsPeerInterface); 353 var jsPeer = getAnnotationValue(node, _isJsPeerInterface);
351 if (jsPeer != null) { 354 if (jsPeer != null) {
352 jsPeerName = getConstantField(jsPeer, 'name', types.stringType); 355 jsPeerName = getConstantField(jsPeer, 'name', types.stringType);
353 } 356 }
354 357
355 var body = 358 var body = _finishClassMembers(
356 _finishClassMembers(classElem, classExpr, ctors, fields, jsPeerName); 359 classElem, classExpr, ctors, fields, methods, jsPeerName);
357 360
358 var result = _finishClassDef(type, body); 361 var result = _finishClassDef(type, body);
359 362
360 if (jsPeerName != null) { 363 if (jsPeerName != null) {
361 // This class isn't allowed to be lazy, because we need to set up 364 // This class isn't allowed to be lazy, because we need to set up
362 // the native JS type eagerly at this point. 365 // the native JS type eagerly at this point.
363 // If we wanted to support laziness, we could defer the hookup until 366 // If we wanted to support laziness, we could defer the hookup until
364 // the end of the Dart library cycle load. 367 // the end of the Dart library cycle load.
365 assert(_loader.isLoaded(classElem)); 368 assert(_loader.isLoaded(classElem));
366 369
(...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after
529 return new JS.Method(js.call('$_SYMBOL.iterator'), js.call( 532 return new JS.Method(js.call('$_SYMBOL.iterator'), js.call(
530 'function() { return new dart.JsIterator(this.#); }', 533 'function() { return new dart.JsIterator(this.#); }',
531 [_emitMemberName('iterator', type: t)])); 534 [_emitMemberName('iterator', type: t)]));
532 } 535 }
533 536
534 /// Emit class members that need to come after the class declaration, such 537 /// Emit class members that need to come after the class declaration, such
535 /// as static fields. See [_emitClassMethods] for things that are emitted 538 /// as static fields. See [_emitClassMethods] for things that are emitted
536 /// inside the ES6 `class { ... }` node. 539 /// inside the ES6 `class { ... }` node.
537 JS.Statement _finishClassMembers(ClassElement classElem, 540 JS.Statement _finishClassMembers(ClassElement classElem,
538 JS.ClassExpression cls, List<ConstructorDeclaration> ctors, 541 JS.ClassExpression cls, List<ConstructorDeclaration> ctors,
539 List<FieldDeclaration> fields, String jsPeerName) { 542 List<FieldDeclaration> fields, List<MethodDeclaration> methods,
543 String jsPeerName) {
540 var name = classElem.name; 544 var name = classElem.name;
541 var body = <JS.Statement>[]; 545 var body = <JS.Statement>[];
542 body.add(new JS.ClassDeclaration(cls)); 546 body.add(new JS.ClassDeclaration(cls));
543 547
544 // TODO(jmesserly): we should really just extend native Array. 548 // TODO(jmesserly): we should really just extend native Array.
545 if (jsPeerName != null && classElem.typeParameters.isNotEmpty) { 549 if (jsPeerName != null && classElem.typeParameters.isNotEmpty) {
546 body.add(js.statement('dart.setBaseClass(#, dart.global.#);', [ 550 body.add(js.statement('dart.setBaseClass(#, dart.global.#);', [
547 classElem.name, 551 classElem.name,
548 _propertyName(jsPeerName) 552 _propertyName(jsPeerName)
549 ])); 553 ]));
(...skipping 21 matching lines...) Expand all
571 // Instance fields, if they override getter/setter pairs 575 // Instance fields, if they override getter/setter pairs
572 for (FieldDeclaration member in fields) { 576 for (FieldDeclaration member in fields) {
573 for (VariableDeclaration fieldDecl in member.fields.variables) { 577 for (VariableDeclaration fieldDecl in member.fields.variables) {
574 var field = fieldDecl.element; 578 var field = fieldDecl.element;
575 if (_fieldsNeedingStorage.contains(field)) { 579 if (_fieldsNeedingStorage.contains(field)) {
576 body.add(_overrideField(field)); 580 body.add(_overrideField(field));
577 } 581 }
578 } 582 }
579 } 583 }
580 584
585 {
vsm 2015/05/18 17:35:10 Add comment to this block ... e.g, // Emit signatu
Leaf 2015/05/19 00:02:21 Done.
586 var tStatics = [];
587 var tMethods = [];
588 var sNames = [];
589 var cType = classElem.type;
590 for (MethodDeclaration node in methods) {
591 if (!(node.isSetter || node.isGetter || node.isAbstract)) {
592 var name = node.name.name;
593 var element = node.element;
594 var unary = node.parameters.parameters.isEmpty;
595 var memberName = _emitMemberName(name,
596 type: cType, unary: unary, isStatic: node.isStatic);
597 var property =
598 new JS.Property(memberName, _emitTypeName(element.type));
599 if (node.isStatic) {
600 tStatics.add(property);
601 sNames.add(memberName);
602 } else tMethods.add(property);
603 }
604 }
605 build(name, elements) {
606 var o =
607 new JS.ObjectInitializer(elements, vertical: elements.length > 1);
608 var e = js.call('() => #', o);
609 var p = new JS.Property(_propertyName(name), e);
610 return p;
611 }
612 var sigFields = [];
613 if (!tMethods.isEmpty) sigFields.add(build('methods', tMethods));
614 if (!tStatics.isEmpty) {
615 assert(!sNames.isEmpty);
616 var aNames = new JS.Property(
617 _propertyName('names'), new JS.ArrayInitializer(sNames));
618 sigFields.add(build('statics', tStatics));
619 sigFields.add(aNames);
620 }
621
622 var sig = new JS.ObjectInitializer(sigFields);
623 var classExpr = new JS.Identifier(name);
624 body.add(js.statement('dart.setSignature(#, #);', [classExpr, sig]));
625 }
626
581 return _statement(body); 627 return _statement(body);
582 } 628 }
583 629
584 JS.Statement _overrideField(FieldElement e) { 630 JS.Statement _overrideField(FieldElement e) {
585 var cls = e.enclosingElement; 631 var cls = e.enclosingElement;
586 return js.statement('dart.virtualField(#, #)', [ 632 return js.statement('dart.virtualField(#, #)', [
587 cls.name, 633 cls.name,
588 _emitMemberName(e.name, type: cls.type) 634 _emitMemberName(e.name, type: cls.type)
589 ]); 635 ]);
590 } 636 }
(...skipping 317 matching lines...) Expand 10 before | Expand all | Expand 10 after
908 if (node.isGetter || node.isSetter) { 954 if (node.isGetter || node.isSetter) {
909 // Add these later so we can use getter/setter syntax. 955 // Add these later so we can use getter/setter syntax.
910 _properties.add(node); 956 _properties.add(node);
911 return null; 957 return null;
912 } 958 }
913 959
914 var body = <JS.Statement>[]; 960 var body = <JS.Statement>[];
915 _flushLibraryProperties(body); 961 _flushLibraryProperties(body);
916 962
917 var name = node.name.name; 963 var name = node.name.name;
918 body.add(js.comment('Function $name: ${node.element.type}'));
919 964
920 body.add(new JS.FunctionDeclaration( 965 var id = new JS.Identifier(name);
921 new JS.Identifier(name), _visit(node.functionExpression))); 966 body.add(new JS.FunctionDeclaration(id, _visit(node.functionExpression)));
967 body.add(_emitFunctionTagged(id, node.element.type, topLevel: true)
968 .toStatement());
922 969
923 if (isPublic(name)) _addExport(name); 970 if (isPublic(name)) _addExport(name);
924 return _statement(body); 971 return _statement(body);
925 } 972 }
926 973
927 JS.Method _emitTopLevelProperty(FunctionDeclaration node) { 974 JS.Method _emitTopLevelProperty(FunctionDeclaration node) {
928 var name = node.name.name; 975 var name = node.name.name;
929 return new JS.Method(_propertyName(name), _visit(node.functionExpression), 976 return new JS.Method(_propertyName(name), _visit(node.functionExpression),
930 isGetter: node.isGetter, isSetter: node.isSetter); 977 isGetter: node.isGetter, isSetter: node.isSetter);
931 } 978 }
932 979
980 bool _executesAtTopLevel(AstNode node) {
981 var ancestor = node.getAncestor((n) => n is FunctionBody ||
982 (n is FieldDeclaration && n.staticKeyword == null) ||
983 (n is ConstructorDeclaration && n.constKeyword == null));
984 return ancestor == null;
985 }
986
987 bool _typeIsLoaded(DartType type) {
988 if (type is FunctionType && (type.name == '' || type.name == null)) {
989 return (_typeIsLoaded(type.returnType) &&
990 type.optionalParameterTypes.every(_typeIsLoaded) &&
991 type.namedParameterTypes.values.every(_typeIsLoaded) &&
992 type.normalParameterTypes.every(_typeIsLoaded));
993 }
994 if (type.isDynamic || type.isVoid || type.isBottom) return true;
995 return _loader.isLoaded(type.element);
996 }
997
998 JS.Expression _emitFunctionTagged(JS.Expression clos, DartType type,
999 {topLevel: false}) {
1000 var name = type.name;
1001 var lazy = topLevel && !_typeIsLoaded(type);
Jennifer Messerly 2015/05/19 18:19:01 Definitely not something that needs to be changed
Leaf 2015/05/19 22:31:12 Acknowledged.
1002
1003 if (type is FunctionType && (name == '' || name == null)) {
1004 if (type.returnType.isDynamic &&
1005 type.optionalParameterTypes.isEmpty &&
1006 type.namedParameterTypes.isEmpty &&
1007 type.normalParameterTypes.every((t) => t.isDynamic)) {
1008 return js.call('dart.fn(#)', [clos]);
1009 }
1010 if (lazy) {
1011 return js.call('dart.fn(#, () => #)', [clos, _emitTypeName(type)]);
1012 }
1013 return js.call('dart.fn(#, #)', [clos, _emitFunctionTypeParts(type)]);
1014 }
1015 throw 'Function has non function type: $type';
1016 }
1017
933 @override 1018 @override
934 JS.Expression visitFunctionExpression(FunctionExpression node) { 1019 JS.Expression visitFunctionExpression(FunctionExpression node) {
935 var params = _visit(node.parameters); 1020 var params = _visit(node.parameters);
936 if (params == null) params = []; 1021 if (params == null) params = [];
937 1022
938 var parent = node.parent; 1023 var parent = node.parent;
939 if (parent is FunctionDeclaration && 1024 if (parent is FunctionDeclaration &&
940 parent.parent is! FunctionDeclarationStatement) { 1025 parent.parent is! FunctionDeclarationStatement) {
941 return new JS.Fun(params, _visit(node.body)); 1026 return new JS.Fun(params, _visit(node.body));
942 } else { 1027 } else {
943 String code; 1028 String code;
944 AstNode body; 1029 AstNode body;
945 var nodeBody = node.body; 1030 var nodeBody = node.body;
946 if (nodeBody is ExpressionFunctionBody) { 1031 if (nodeBody is ExpressionFunctionBody) {
947 code = '(#) => #'; 1032 code = '(#) => #';
948 body = nodeBody.expression; 1033 body = nodeBody.expression;
949 } else { 1034 } else {
950 code = '(#) => { #; }'; 1035 code = '(#) => { #; }';
951 body = nodeBody; 1036 body = nodeBody;
952 } 1037 }
953 return js.call(code, [params, _visit(body)]); 1038 var clos = js.call(code, [params, _visit(body)]);
1039 if (parent.parent is! FunctionDeclarationStatement) {
Jennifer Messerly 2015/05/19 18:19:01 maybe pull out `parent.parent is! FunctionDeclarat
Leaf 2015/05/19 22:31:13 Done.
1040 var type = getStaticType(node);
1041 return _emitFunctionTagged(clos, type,
1042 topLevel: _executesAtTopLevel(node));
1043 }
1044 return clos;
954 } 1045 }
955 } 1046 }
956 1047
957 @override 1048 @override
958 JS.Statement visitFunctionDeclarationStatement( 1049 JS.Statement visitFunctionDeclarationStatement(
959 FunctionDeclarationStatement node) { 1050 FunctionDeclarationStatement node) {
960 var func = node.functionDeclaration; 1051 var func = node.functionDeclaration;
961 if (func.isGetter || func.isSetter) { 1052 if (func.isGetter || func.isSetter) {
962 return js.comment('Unimplemented function get/set statement: $node'); 1053 return js.comment('Unimplemented function get/set statement: $node');
963 } 1054 }
964 1055
965 // Use an => function to bind this. 1056 // Use an => function to bind this.
966 // Technically we only need to do this if the function actually closes over 1057 // Technically we only need to do this if the function actually closes over
967 // `this`, but it seems harmless enough to just do it always. 1058 // `this`, but it seems harmless enough to just do it always.
968 var name = new JS.Identifier(func.name.name); 1059 var name = new JS.Identifier(func.name.name);
969 return new JS.Block([ 1060 return new JS.Block([
970 js.comment("// Function ${func.name.name}: ${func.element.type}\n"), 1061 js.statement('let # = #;', [name, _visit(func.functionExpression)]),
971 js.statement('let # = #;', [name, _visit(func.functionExpression)]) 1062 _emitFunctionTagged(name, func.element.type).toStatement()
972 ]); 1063 ]);
973 } 1064 }
974 1065
975 /// Writes a simple identifier. This can handle implicit `this` as well as 1066 /// Writes a simple identifier. This can handle implicit `this` as well as
976 /// going through the qualified library name if necessary. 1067 /// going through the qualified library name if necessary.
977 @override 1068 @override
978 JS.Expression visitSimpleIdentifier(SimpleIdentifier node) { 1069 JS.Expression visitSimpleIdentifier(SimpleIdentifier node) {
979 var accessor = node.staticElement; 1070 var accessor = node.staticElement;
980 if (accessor == null) { 1071 if (accessor == null) {
981 return js.commentExpression( 1072 return js.commentExpression(
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
1052 JS.ObjectInitializer _emitTypeProperties(Map<String, DartType> types) { 1143 JS.ObjectInitializer _emitTypeProperties(Map<String, DartType> types) {
1053 var properties = <JS.Property>[]; 1144 var properties = <JS.Property>[];
1054 types.forEach((name, type) { 1145 types.forEach((name, type) {
1055 var key = new JS.LiteralString(name); 1146 var key = new JS.LiteralString(name);
1056 var value = _emitTypeName(type); 1147 var value = _emitTypeName(type);
1057 properties.add(new JS.Property(key, value)); 1148 properties.add(new JS.Property(key, value));
1058 }); 1149 });
1059 return new JS.ObjectInitializer(properties); 1150 return new JS.ObjectInitializer(properties);
1060 } 1151 }
1061 1152
1153 List<JS.Expression> _emitFunctionTypeParts(FunctionType type) {
1154 var returnType = type.returnType;
1155 var parameterTypes = type.normalParameterTypes;
1156 var optionalTypes = type.optionalParameterTypes;
1157 var namedTypes = type.namedParameterTypes;
1158 var rt = _emitTypeName(returnType);
1159 var ra = _emitTypeNames(parameterTypes);
1160 if (!namedTypes.isEmpty) {
1161 assert(optionalTypes.isEmpty);
1162 var na = _emitTypeProperties(namedTypes);
1163 return [rt, ra, na];
1164 }
1165 if (!optionalTypes.isEmpty) {
1166 assert(namedTypes.isEmpty);
1167 var oa = _emitTypeNames(optionalTypes);
1168 return [rt, ra, oa];
1169 }
1170 return [rt, ra];
1171 }
1172
1062 /// Emits a Dart [type] into code. 1173 /// Emits a Dart [type] into code.
1063 /// 1174 ///
1064 /// If [lowerTypedef] is set, a typedef will be expanded as if it were a 1175 /// If [lowerTypedef] is set, a typedef will be expanded as if it were a
1065 /// function type. Similarly if [lowerGeneric] is set, the `List$()` form 1176 /// function type. Similarly if [lowerGeneric] is set, the `List$()` form
1066 /// will be used instead of `List`. These flags are used when generating 1177 /// will be used instead of `List`. These flags are used when generating
1067 /// the definitions for typedefs and generic types, respectively. 1178 /// the definitions for typedefs and generic types, respectively.
1068 JS.Expression _emitTypeName(DartType type, 1179 JS.Expression _emitTypeName(DartType type,
1069 {bool lowerTypedef: false, bool lowerGeneric: false}) { 1180 {bool lowerTypedef: false, bool lowerGeneric: false}) {
1070 1181
1071 // The void and dynamic types are not defined in core. 1182 // The void and dynamic types are not defined in core.
1072 if (type.isVoid) { 1183 if (type.isVoid) {
1073 return js.call('dart.void'); 1184 return js.call('dart.void');
1074 } else if (type.isDynamic) { 1185 } else if (type.isDynamic) {
1075 return js.call('dart.dynamic'); 1186 return js.call('dart.dynamic');
1187 } else if (type.isBottom) {
1188 return js.call('dart.bottom');
1076 } 1189 }
1077 1190
1078 _loader.declareBeforeUse(type.element); 1191 _loader.declareBeforeUse(type.element);
1079 1192
1080 // TODO(jmesserly): like constants, should we hoist function types out of 1193 // TODO(jmesserly): like constants, should we hoist function types out of
1081 // methods? Similar issue with generic types. For all of these, we may want 1194 // methods? Similar issue with generic types. For all of these, we may want
1082 // to canonicalize them too, at least when inside the same library. 1195 // to canonicalize them too, at least when inside the same library.
1083 var name = type.name; 1196 var name = type.name;
1084 var element = type.element; 1197 var element = type.element;
1085 if (name == '' || lowerTypedef) { 1198 if (name == '' || name == null || lowerTypedef) {
1086 var fnType = type as FunctionType; 1199 var parts = _emitFunctionTypeParts(type as FunctionType);
1087 var returnType = fnType.returnType; 1200 return js.call('dart.functionType(#)', [parts]);
1088 var parameterTypes = fnType.normalParameterTypes;
1089 var optionalTypes = fnType.optionalParameterTypes;
1090 var namedTypes = fnType.namedParameterTypes;
1091 if (namedTypes.isEmpty) {
1092 if (optionalTypes.isEmpty) {
1093 return js.call('dart.functionType(#, #)', [
1094 _emitTypeName(returnType),
1095 _emitTypeNames(parameterTypes)
1096 ]);
1097 } else {
1098 return js.call('dart.functionType(#, #, #)', [
1099 _emitTypeName(returnType),
1100 _emitTypeNames(parameterTypes),
1101 _emitTypeNames(optionalTypes)
1102 ]);
1103 }
1104 } else {
1105 assert(optionalTypes.isEmpty);
1106 return js.call('dart.functionType(#, #, #)', [
1107 _emitTypeName(returnType),
1108 _emitTypeNames(parameterTypes),
1109 _emitTypeProperties(namedTypes)
1110 ]);
1111 }
1112 } 1201 }
1113 1202
1114 if (type is TypeParameterType) { 1203 if (type is TypeParameterType) {
1115 return new JS.Identifier(name); 1204 return new JS.Identifier(name);
1116 } 1205 }
1117 1206
1118 if (type is ParameterizedType) { 1207 if (type is ParameterizedType) {
1119 var args = type.typeArguments; 1208 var args = type.typeArguments;
1120 var isCurrentClass = 1209 var isCurrentClass =
1121 args.isNotEmpty && _loader.isCurrentElement(type.element); 1210 args.isNotEmpty && _loader.isCurrentElement(type.element);
(...skipping 1346 matching lines...) Expand 10 before | Expand all | Expand 10 after
2468 2557
2469 /// A special kind of element created by the compiler, signifying a temporary 2558 /// A special kind of element created by the compiler, signifying a temporary
2470 /// variable. These objects use instance equality, and should be shared 2559 /// variable. These objects use instance equality, and should be shared
2471 /// everywhere in the tree where they are treated as the same variable. 2560 /// everywhere in the tree where they are treated as the same variable.
2472 class TemporaryVariableElement extends LocalVariableElementImpl { 2561 class TemporaryVariableElement extends LocalVariableElementImpl {
2473 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); 2562 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name);
2474 2563
2475 int get hashCode => identityHashCode(this); 2564 int get hashCode => identityHashCode(this);
2476 bool operator ==(Object other) => identical(this, other); 2565 bool operator ==(Object other) => identical(this, other);
2477 } 2566 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698