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

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: More comment fixes 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
« no previous file with comments | « lib/runtime/dart_runtime.js ('k') | lib/src/codegen/reify_coercions.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // 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 // Emit the signature on the class recording the runtime type information
586 {
587 var tStatics = [];
588 var tMethods = [];
589 var sNames = [];
590 var cType = classElem.type;
591 for (MethodDeclaration node in methods) {
592 if (!(node.isSetter || node.isGetter || node.isAbstract)) {
593 var name = node.name.name;
594 var element = node.element;
595 var unary = node.parameters.parameters.isEmpty;
596 var memberName = _emitMemberName(name,
597 type: cType, unary: unary, isStatic: node.isStatic);
598 var property =
599 new JS.Property(memberName, _emitTypeName(element.type));
600 if (node.isStatic) {
601 tStatics.add(property);
602 sNames.add(memberName);
603 } else tMethods.add(property);
604 }
605 }
606 build(name, elements) {
607 var o =
608 new JS.ObjectInitializer(elements, vertical: elements.length > 1);
609 var e = js.call('() => #', o);
610 var p = new JS.Property(_propertyName(name), e);
611 return p;
612 }
613 var sigFields = [];
614 if (!tMethods.isEmpty) sigFields.add(build('methods', tMethods));
615 if (!tStatics.isEmpty) {
616 assert(!sNames.isEmpty);
617 var aNames = new JS.Property(
618 _propertyName('names'), new JS.ArrayInitializer(sNames));
619 sigFields.add(build('statics', tStatics));
620 sigFields.add(aNames);
621 }
622
623 var sig = new JS.ObjectInitializer(sigFields);
624 var classExpr = new JS.Identifier(name);
625 body.add(js.statement('dart.setSignature(#, #);', [classExpr, sig]));
626 }
627
581 return _statement(body); 628 return _statement(body);
582 } 629 }
583 630
584 JS.Statement _overrideField(FieldElement e) { 631 JS.Statement _overrideField(FieldElement e) {
585 var cls = e.enclosingElement; 632 var cls = e.enclosingElement;
586 return js.statement('dart.virtualField(#, #)', [ 633 return js.statement('dart.virtualField(#, #)', [
587 cls.name, 634 cls.name,
588 _emitMemberName(e.name, type: cls.type) 635 _emitMemberName(e.name, type: cls.type)
589 ]); 636 ]);
590 } 637 }
(...skipping 317 matching lines...) Expand 10 before | Expand all | Expand 10 after
908 if (node.isGetter || node.isSetter) { 955 if (node.isGetter || node.isSetter) {
909 // Add these later so we can use getter/setter syntax. 956 // Add these later so we can use getter/setter syntax.
910 _properties.add(node); 957 _properties.add(node);
911 return null; 958 return null;
912 } 959 }
913 960
914 var body = <JS.Statement>[]; 961 var body = <JS.Statement>[];
915 _flushLibraryProperties(body); 962 _flushLibraryProperties(body);
916 963
917 var name = node.name.name; 964 var name = node.name.name;
918 body.add(js.comment('Function $name: ${node.element.type}'));
919 965
920 body.add(new JS.FunctionDeclaration( 966 var id = new JS.Identifier(name);
921 new JS.Identifier(name), _visit(node.functionExpression))); 967 body.add(new JS.FunctionDeclaration(id, _visit(node.functionExpression)));
968 body.add(_emitFunctionTagged(id, node.element.type, topLevel: true)
969 .toStatement());
922 970
923 if (isPublic(name)) _addExport(name); 971 if (isPublic(name)) _addExport(name);
924 return _statement(body); 972 return _statement(body);
925 } 973 }
926 974
927 JS.Method _emitTopLevelProperty(FunctionDeclaration node) { 975 JS.Method _emitTopLevelProperty(FunctionDeclaration node) {
928 var name = node.name.name; 976 var name = node.name.name;
929 return new JS.Method(_propertyName(name), _visit(node.functionExpression), 977 return new JS.Method(_propertyName(name), _visit(node.functionExpression),
930 isGetter: node.isGetter, isSetter: node.isSetter); 978 isGetter: node.isGetter, isSetter: node.isSetter);
931 } 979 }
932 980
981 bool _executesAtTopLevel(AstNode node) {
982 var ancestor = node.getAncestor((n) => n is FunctionBody ||
983 (n is FieldDeclaration && n.staticKeyword == null) ||
984 (n is ConstructorDeclaration && n.constKeyword == null));
985 return ancestor == null;
986 }
987
988 bool _typeIsLoaded(DartType type) {
989 if (type is FunctionType && (type.name == '' || type.name == null)) {
990 return (_typeIsLoaded(type.returnType) &&
991 type.optionalParameterTypes.every(_typeIsLoaded) &&
992 type.namedParameterTypes.values.every(_typeIsLoaded) &&
993 type.normalParameterTypes.every(_typeIsLoaded));
994 }
995 if (type.isDynamic || type.isVoid || type.isBottom) return true;
996 return _loader.isLoaded(type.element);
997 }
998
999 JS.Expression _emitFunctionTagged(JS.Expression clos, DartType type,
1000 {topLevel: false}) {
1001 var name = type.name;
1002 var lazy = topLevel && !_typeIsLoaded(type);
1003
1004 if (type is FunctionType && (name == '' || name == null)) {
1005 if (type.returnType.isDynamic &&
1006 type.optionalParameterTypes.isEmpty &&
1007 type.namedParameterTypes.isEmpty &&
1008 type.normalParameterTypes.every((t) => t.isDynamic)) {
1009 return js.call('dart.fn(#)', [clos]);
1010 }
1011 if (lazy) {
1012 return js.call('dart.fn(#, () => #)', [clos, _emitTypeName(type)]);
1013 }
1014 return js.call('dart.fn(#, #)', [clos, _emitFunctionTypeParts(type)]);
1015 }
1016 throw 'Function has non function type: $type';
1017 }
1018
933 @override 1019 @override
934 JS.Expression visitFunctionExpression(FunctionExpression node) { 1020 JS.Expression visitFunctionExpression(FunctionExpression node) {
935 var params = _visit(node.parameters); 1021 var params = _visit(node.parameters);
936 if (params == null) params = []; 1022 if (params == null) params = [];
937 1023
938 var parent = node.parent; 1024 var parent = node.parent;
939 if (parent is FunctionDeclaration && 1025 var inDecl = parent is FunctionDeclaration;
940 parent.parent is! FunctionDeclarationStatement) { 1026 var inStmt = parent.parent is FunctionDeclarationStatement;
1027 if (inDecl && !inStmt) {
941 return new JS.Fun(params, _visit(node.body)); 1028 return new JS.Fun(params, _visit(node.body));
942 } else { 1029 } else {
943 String code; 1030 String code;
944 AstNode body; 1031 AstNode body;
945 var nodeBody = node.body; 1032 var nodeBody = node.body;
946 if (nodeBody is ExpressionFunctionBody) { 1033 if (nodeBody is ExpressionFunctionBody) {
947 code = '(#) => #'; 1034 code = '(#) => #';
948 body = nodeBody.expression; 1035 body = nodeBody.expression;
949 } else { 1036 } else {
950 code = '(#) => { #; }'; 1037 code = '(#) => { #; }';
951 body = nodeBody; 1038 body = nodeBody;
952 } 1039 }
953 return js.call(code, [params, _visit(body)]); 1040 var clos = js.call(code, [params, _visit(body)]);
1041 if (!inStmt) {
1042 var type = getStaticType(node);
1043 return _emitFunctionTagged(clos, type,
1044 topLevel: _executesAtTopLevel(node));
1045 }
1046 return clos;
954 } 1047 }
955 } 1048 }
956 1049
957 @override 1050 @override
958 JS.Statement visitFunctionDeclarationStatement( 1051 JS.Statement visitFunctionDeclarationStatement(
959 FunctionDeclarationStatement node) { 1052 FunctionDeclarationStatement node) {
960 var func = node.functionDeclaration; 1053 var func = node.functionDeclaration;
961 if (func.isGetter || func.isSetter) { 1054 if (func.isGetter || func.isSetter) {
962 return js.comment('Unimplemented function get/set statement: $node'); 1055 return js.comment('Unimplemented function get/set statement: $node');
963 } 1056 }
964 1057
965 // Use an => function to bind this. 1058 // Use an => function to bind this.
966 // Technically we only need to do this if the function actually closes over 1059 // 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. 1060 // `this`, but it seems harmless enough to just do it always.
968 var name = new JS.Identifier(func.name.name); 1061 var name = new JS.Identifier(func.name.name);
969 return new JS.Block([ 1062 return new JS.Block([
970 js.comment("// Function ${func.name.name}: ${func.element.type}\n"), 1063 js.statement('let # = #;', [name, _visit(func.functionExpression)]),
971 js.statement('let # = #;', [name, _visit(func.functionExpression)]) 1064 _emitFunctionTagged(name, func.element.type).toStatement()
972 ]); 1065 ]);
973 } 1066 }
974 1067
975 /// Writes a simple identifier. This can handle implicit `this` as well as 1068 /// Writes a simple identifier. This can handle implicit `this` as well as
976 /// going through the qualified library name if necessary. 1069 /// going through the qualified library name if necessary.
977 @override 1070 @override
978 JS.Expression visitSimpleIdentifier(SimpleIdentifier node) { 1071 JS.Expression visitSimpleIdentifier(SimpleIdentifier node) {
979 var accessor = node.staticElement; 1072 var accessor = node.staticElement;
980 if (accessor == null) { 1073 if (accessor == null) {
981 return js.commentExpression( 1074 return js.commentExpression(
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
1059 JS.ObjectInitializer _emitTypeProperties(Map<String, DartType> types) { 1152 JS.ObjectInitializer _emitTypeProperties(Map<String, DartType> types) {
1060 var properties = <JS.Property>[]; 1153 var properties = <JS.Property>[];
1061 types.forEach((name, type) { 1154 types.forEach((name, type) {
1062 var key = new JS.LiteralString(name); 1155 var key = new JS.LiteralString(name);
1063 var value = _emitTypeName(type); 1156 var value = _emitTypeName(type);
1064 properties.add(new JS.Property(key, value)); 1157 properties.add(new JS.Property(key, value));
1065 }); 1158 });
1066 return new JS.ObjectInitializer(properties); 1159 return new JS.ObjectInitializer(properties);
1067 } 1160 }
1068 1161
1162 List<JS.Expression> _emitFunctionTypeParts(FunctionType type) {
1163 var returnType = type.returnType;
1164 var parameterTypes = type.normalParameterTypes;
1165 var optionalTypes = type.optionalParameterTypes;
1166 var namedTypes = type.namedParameterTypes;
1167 var rt = _emitTypeName(returnType);
1168 var ra = _emitTypeNames(parameterTypes);
1169 if (!namedTypes.isEmpty) {
1170 assert(optionalTypes.isEmpty);
1171 var na = _emitTypeProperties(namedTypes);
1172 return [rt, ra, na];
1173 }
1174 if (!optionalTypes.isEmpty) {
1175 assert(namedTypes.isEmpty);
1176 var oa = _emitTypeNames(optionalTypes);
1177 return [rt, ra, oa];
1178 }
1179 return [rt, ra];
1180 }
1181
1069 /// Emits a Dart [type] into code. 1182 /// Emits a Dart [type] into code.
1070 /// 1183 ///
1071 /// If [lowerTypedef] is set, a typedef will be expanded as if it were a 1184 /// If [lowerTypedef] is set, a typedef will be expanded as if it were a
1072 /// function type. Similarly if [lowerGeneric] is set, the `List$()` form 1185 /// function type. Similarly if [lowerGeneric] is set, the `List$()` form
1073 /// will be used instead of `List`. These flags are used when generating 1186 /// will be used instead of `List`. These flags are used when generating
1074 /// the definitions for typedefs and generic types, respectively. 1187 /// the definitions for typedefs and generic types, respectively.
1075 JS.Expression _emitTypeName(DartType type, 1188 JS.Expression _emitTypeName(DartType type,
1076 {bool lowerTypedef: false, bool lowerGeneric: false}) { 1189 {bool lowerTypedef: false, bool lowerGeneric: false}) {
1077 1190
1078 // The void and dynamic types are not defined in core. 1191 // The void and dynamic types are not defined in core.
1079 if (type.isVoid) { 1192 if (type.isVoid) {
1080 return js.call('dart.void'); 1193 return js.call('dart.void');
1081 } else if (type.isDynamic) { 1194 } else if (type.isDynamic) {
1082 return js.call('dart.dynamic'); 1195 return js.call('dart.dynamic');
1196 } else if (type.isBottom) {
1197 return js.call('dart.bottom');
1083 } 1198 }
1084 1199
1085 _loader.declareBeforeUse(type.element); 1200 _loader.declareBeforeUse(type.element);
1086 1201
1087 // TODO(jmesserly): like constants, should we hoist function types out of 1202 // TODO(jmesserly): like constants, should we hoist function types out of
1088 // methods? Similar issue with generic types. For all of these, we may want 1203 // methods? Similar issue with generic types. For all of these, we may want
1089 // to canonicalize them too, at least when inside the same library. 1204 // to canonicalize them too, at least when inside the same library.
1090 var name = type.name; 1205 var name = type.name;
1091 var element = type.element; 1206 var element = type.element;
1092 if (name == '' || lowerTypedef) { 1207 if (name == '' || name == null || lowerTypedef) {
1093 var fnType = type as FunctionType; 1208 var parts = _emitFunctionTypeParts(type as FunctionType);
1094 var returnType = fnType.returnType; 1209 return js.call('dart.functionType(#)', [parts]);
1095 var parameterTypes = fnType.normalParameterTypes;
1096 var optionalTypes = fnType.optionalParameterTypes;
1097 var namedTypes = fnType.namedParameterTypes;
1098 if (namedTypes.isEmpty) {
1099 if (optionalTypes.isEmpty) {
1100 return js.call('dart.functionType(#, #)', [
1101 _emitTypeName(returnType),
1102 _emitTypeNames(parameterTypes)
1103 ]);
1104 } else {
1105 return js.call('dart.functionType(#, #, #)', [
1106 _emitTypeName(returnType),
1107 _emitTypeNames(parameterTypes),
1108 _emitTypeNames(optionalTypes)
1109 ]);
1110 }
1111 } else {
1112 assert(optionalTypes.isEmpty);
1113 return js.call('dart.functionType(#, #, #)', [
1114 _emitTypeName(returnType),
1115 _emitTypeNames(parameterTypes),
1116 _emitTypeProperties(namedTypes)
1117 ]);
1118 }
1119 } 1210 }
1120 1211
1121 if (type is TypeParameterType) { 1212 if (type is TypeParameterType) {
1122 return new JS.Identifier(name); 1213 return new JS.Identifier(name);
1123 } 1214 }
1124 1215
1125 if (type is ParameterizedType) { 1216 if (type is ParameterizedType) {
1126 var args = type.typeArguments; 1217 var args = type.typeArguments;
1127 var isCurrentClass = 1218 var isCurrentClass =
1128 args.isNotEmpty && _loader.isCurrentElement(type.element); 1219 args.isNotEmpty && _loader.isCurrentElement(type.element);
(...skipping 1346 matching lines...) Expand 10 before | Expand all | Expand 10 after
2475 2566
2476 /// A special kind of element created by the compiler, signifying a temporary 2567 /// A special kind of element created by the compiler, signifying a temporary
2477 /// variable. These objects use instance equality, and should be shared 2568 /// variable. These objects use instance equality, and should be shared
2478 /// everywhere in the tree where they are treated as the same variable. 2569 /// everywhere in the tree where they are treated as the same variable.
2479 class TemporaryVariableElement extends LocalVariableElementImpl { 2570 class TemporaryVariableElement extends LocalVariableElementImpl {
2480 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); 2571 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name);
2481 2572
2482 int get hashCode => identityHashCode(this); 2573 int get hashCode => identityHashCode(this);
2483 bool operator ==(Object other) => identical(this, other); 2574 bool operator ==(Object other) => identical(this, other);
2484 } 2575 }
OLDNEW
« no previous file with comments | « lib/runtime/dart_runtime.js ('k') | lib/src/codegen/reify_coercions.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698