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

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

Issue 1676463002: Type annotations instead of closure comments. (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: merged master Created 4 years, 10 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/src/closure/closure_annotator.dart ('k') | lib/src/codegen/js_names.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 js_codegen;
6
5 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; 7 import 'dart:collection' show HashSet, HashMap, SplayTreeSet;
6 8
7 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator;
8 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; 10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator;
9 import 'package:analyzer/src/generated/constant.dart'; 11 import 'package:analyzer/src/generated/constant.dart';
10 import 'package:analyzer/src/generated/element.dart'; 12 import 'package:analyzer/src/generated/element.dart';
11 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; 13 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
12 import 'package:analyzer/src/generated/scanner.dart' 14 import 'package:analyzer/src/generated/scanner.dart'
13 show StringToken, Token, TokenType; 15 show StringToken, Token, TokenType;
14 import 'package:analyzer/src/generated/type_system.dart' 16 import 'package:analyzer/src/generated/type_system.dart'
(...skipping 19 matching lines...) Expand all
34 import 'js_interop.dart'; 36 import 'js_interop.dart';
35 import 'js_names.dart' as JS; 37 import 'js_names.dart' as JS;
36 import 'js_metalet.dart' as JS; 38 import 'js_metalet.dart' as JS;
37 import 'js_module_item_order.dart'; 39 import 'js_module_item_order.dart';
38 import 'js_names.dart'; 40 import 'js_names.dart';
39 import 'js_printer.dart' show writeJsLibrary; 41 import 'js_printer.dart' show writeJsLibrary;
40 import 'module_builder.dart'; 42 import 'module_builder.dart';
41 import 'nullability_inferrer.dart'; 43 import 'nullability_inferrer.dart';
42 import 'side_effect_analysis.dart'; 44 import 'side_effect_analysis.dart';
43 45
46 part 'js_typeref_codegen.dart';
47
44 // Various dynamic helpers we call. 48 // Various dynamic helpers we call.
45 // If renaming these, make sure to check other places like the 49 // If renaming these, make sure to check other places like the
46 // _runtime.js file and comments. 50 // _runtime.js file and comments.
47 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can 51 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can
48 // import and generate calls to, rather than dart_runtime.js 52 // import and generate calls to, rather than dart_runtime.js
49 const DPUT = 'dput'; 53 const DPUT = 'dput';
50 const DLOAD = 'dload'; 54 const DLOAD = 'dload';
51 const DINDEX = 'dindex'; 55 const DINDEX = 'dindex';
52 const DSETINDEX = 'dsetindex'; 56 const DSETINDEX = 'dsetindex';
53 const DCALL = 'dcall'; 57 const DCALL = 'dcall';
54 const DSEND = 'dsend'; 58 const DSEND = 'dsend';
55 59
56 class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { 60 class JSCodegenVisitor extends GeneralizingAstVisitor
61 with ClosureAnnotator, JsTypeRefCodegen {
57 final AbstractCompiler compiler; 62 final AbstractCompiler compiler;
58 final CodegenOptions options; 63 final CodegenOptions options;
59 final LibraryElement currentLibrary; 64 final LibraryElement currentLibrary;
60 final StrongTypeSystemImpl rules; 65 final StrongTypeSystemImpl rules;
61 66
62 /// The global extension type table. 67 /// The global extension type table.
63 final HashSet<ClassElement> _extensionTypes; 68 final HashSet<ClassElement> _extensionTypes;
64 69
65 /// Information that is precomputed for this library, indicates which fields 70 /// Information that is precomputed for this library, indicates which fields
66 /// need storage slots. 71 /// need storage slots.
(...skipping 274 matching lines...) Expand 10 before | Expand all | Expand 10 after
341 if (t == _types.boolType) return 'boolean'; 346 if (t == _types.boolType) return 'boolean';
342 return null; 347 return null;
343 } 348 }
344 349
345 @override 350 @override
346 visitFunctionTypeAlias(FunctionTypeAlias node) { 351 visitFunctionTypeAlias(FunctionTypeAlias node) {
347 var element = node.element; 352 var element = node.element;
348 var type = element.type; 353 var type = element.type;
349 var name = element.name; 354 var name = element.name;
350 355
351 var fnType = annotateTypeDef( 356 var fnType = annotate(
352 js.statement('const # = dart.typedef(#, () => #);', [ 357 js.statement('const # = dart.typedef(#, () => #);', [
353 name, 358 name,
354 js.string(name, "'"), 359 js.string(name, "'"),
355 _emitTypeName(type, lowerTypedef: true) 360 _emitTypeName(type, lowerTypedef: true)
356 ]), 361 ]),
362 node,
357 node.element); 363 node.element);
358 364
359 return _finishClassDef(type, fnType); 365 return _finishClassDef(type, fnType);
360 } 366 }
361 367
362 @override 368 @override
363 JS.Expression visitTypeName(TypeName node) => _emitTypeName(node.type); 369 JS.Expression visitTypeName(TypeName node) => _emitTypeName(node.type);
364 370
365 @override 371 @override
366 JS.Statement visitClassTypeAlias(ClassTypeAlias node) { 372 JS.Statement visitClassTypeAlias(ClassTypeAlias node) {
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
417 if (member is ConstructorDeclaration) { 423 if (member is ConstructorDeclaration) {
418 ctors.add(member); 424 ctors.add(member);
419 } else if (member is FieldDeclaration) { 425 } else if (member is FieldDeclaration) {
420 (member.isStatic ? staticFields : fields).add(member); 426 (member.isStatic ? staticFields : fields).add(member);
421 } else if (member is MethodDeclaration) { 427 } else if (member is MethodDeclaration) {
422 methods.add(member); 428 methods.add(member);
423 } 429 }
424 } 430 }
425 431
426 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), 432 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name),
427 _classHeritage(classElem), _emitClassMethods(node, ctors, fields)); 433 _classHeritage(classElem), _emitClassMethods(node, ctors, fields),
434 typeParams: _emitTypeParams(classElem).toList(),
435 fields:
436 _emitFieldDeclarations(classElem, fields, staticFields).toList());
428 437
429 String jsPeerName; 438 String jsPeerName;
430 var jsPeer = findAnnotation(classElem, isJsPeerInterface); 439 var jsPeer = findAnnotation(classElem, isJsPeerInterface);
431 if (jsPeer != null) { 440 if (jsPeer != null) {
432 jsPeerName = 441 jsPeerName =
433 getConstantField(jsPeer, 'name', types.stringType)?.toStringValue(); 442 getConstantField(jsPeer, 'name', types.stringType)?.toStringValue();
434 } 443 }
435 444
436 var body = _finishClassMembers(classElem, classExpr, ctors, fields, 445 var body = _finishClassMembers(classElem, classExpr, ctors, fields,
437 staticFields, methods, node.metadata, jsPeerName); 446 staticFields, methods, node.metadata, jsPeerName);
(...skipping 12 matching lines...) Expand all
450 // want to support construction of instances with generic types other 459 // want to support construction of instances with generic types other
451 // than dynamic. See issue #154 for Array and List<E> related bug. 460 // than dynamic. See issue #154 for Array and List<E> related bug.
452 var copyMembers = js.statement( 461 var copyMembers = js.statement(
453 'dart.registerExtension(dart.global.#, #);', 462 'dart.registerExtension(dart.global.#, #);',
454 [_propertyName(jsPeerName), classElem.name]); 463 [_propertyName(jsPeerName), classElem.name]);
455 return _statement([result, copyMembers]); 464 return _statement([result, copyMembers]);
456 } 465 }
457 return result; 466 return result;
458 } 467 }
459 468
469 Iterable<JS.Identifier> _emitTypeParams(TypeParameterizedElement e) sync* {
470 if (!options.closure) return;
471 for (var typeParam in e.typeParameters) {
472 yield new JS.Identifier(typeParam.name);
473 }
474 }
475
476 /// Emit field declarations for TypeScript & Closure's ES6_TYPED
477 /// (e.g. `class Foo { i: string; }`)
478 Iterable<JS.VariableDeclarationList> _emitFieldDeclarations(
479 ClassElement classElem,
480 List<FieldDeclaration> fields,
481 List<FieldDeclaration> staticFields) sync* {
482 if (!options.closure) return;
483
484 makeInitialization(VariableDeclaration decl) =>
485 new JS.VariableInitialization(
486 new JS.Identifier(
487 // TODO(ochafik): use a refactored _emitMemberName instead.
488 decl.name.name,
489 type: emitTypeRef(decl.element.type)),
490 null);
491
492 for (var field in fields) {
493 yield new JS.VariableDeclarationList(
494 null, field.fields.variables.map(makeInitialization).toList());
495 }
496 for (var field in staticFields) {
497 yield new JS.VariableDeclarationList(
498 'static', field.fields.variables.map(makeInitialization).toList());
499 }
500 }
501
460 @override 502 @override
461 JS.Statement visitEnumDeclaration(EnumDeclaration node) { 503 JS.Statement visitEnumDeclaration(EnumDeclaration node) {
462 var element = node.element; 504 var element = node.element;
463 var type = element.type; 505 var type = element.type;
464 var name = js.string(type.name); 506 var name = js.string(type.name);
465 var id = new JS.Identifier(type.name); 507 var id = new JS.Identifier(type.name);
466 508
467 // Generate a class per section 13 of the spec. 509 // Generate a class per section 13 of the spec.
468 // TODO(vsm): Generate any accompanying metadata 510 // TODO(vsm): Generate any accompanying metadata
469 511
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after
682 node.selector is JS.LiteralString; 724 node.selector is JS.LiteralString;
683 725
684 /// Workaround for Closure: super classes must be qualified paths. 726 /// Workaround for Closure: super classes must be qualified paths.
685 JS.Statement _emitClassHeritageWorkaround(JS.ClassExpression cls) { 727 JS.Statement _emitClassHeritageWorkaround(JS.ClassExpression cls) {
686 if (options.closure && 728 if (options.closure &&
687 cls.heritage != null && 729 cls.heritage != null &&
688 !_isQualifiedPath(cls.heritage)) { 730 !_isQualifiedPath(cls.heritage)) {
689 var superVar = new JS.TemporaryId(cls.name.name + r'$super'); 731 var superVar = new JS.TemporaryId(cls.name.name + r'$super');
690 return _statement([ 732 return _statement([
691 js.statement('const # = #;', [superVar, cls.heritage]), 733 js.statement('const # = #;', [superVar, cls.heritage]),
692 new JS.ClassDeclaration( 734 new JS.ClassDeclaration(new JS.ClassExpression(
693 new JS.ClassExpression(cls.name, superVar, cls.methods)) 735 cls.name, superVar, cls.methods,
736 typeParams: cls.typeParams, fields: cls.fields))
694 ]); 737 ]);
695 } 738 }
696 return new JS.ClassDeclaration(cls); 739 return new JS.ClassDeclaration(cls);
697 } 740 }
698 741
699 /// Emit class members that need to come after the class declaration, such 742 /// Emit class members that need to come after the class declaration, such
700 /// as static fields. See [_emitClassMethods] for things that are emitted 743 /// as static fields. See [_emitClassMethods] for things that are emitted
701 /// inside the ES6 `class { ... }` node. 744 /// inside the ES6 `class { ... }` node.
702 JS.Statement _finishClassMembers( 745 JS.Statement _finishClassMembers(
703 ClassElement classElem, 746 ClassElement classElem,
(...skipping 224 matching lines...) Expand 10 before | Expand all | Expand 10 after
928 var superCall = _superConstructorCall(node.element); 971 var superCall = _superConstructorCall(node.element);
929 if (fields.isEmpty && superCall == null) return null; 972 if (fields.isEmpty && superCall == null) return null;
930 973
931 dynamic body = _initializeFields(node, fields); 974 dynamic body = _initializeFields(node, fields);
932 if (superCall != null) { 975 if (superCall != null) {
933 body = [ 976 body = [
934 [body, superCall] 977 [body, superCall]
935 ]; 978 ];
936 } 979 }
937 var name = _constructorName(node.element.unnamedConstructor); 980 var name = _constructorName(node.element.unnamedConstructor);
938 return annotateDefaultConstructor( 981 return annotate(
939 new JS.Method(name, js.call('function() { #; }', body) as JS.Fun), 982 new JS.Method(name, js.call('function() { #; }', body) as JS.Fun),
983 node,
940 node.element); 984 node.element);
941 } 985 }
942 986
943 JS.Method _emitConstructor(ConstructorDeclaration node, InterfaceType type, 987 JS.Method _emitConstructor(ConstructorDeclaration node, InterfaceType type,
944 List<FieldDeclaration> fields, bool isObject) { 988 List<FieldDeclaration> fields, bool isObject) {
945 if (_externalOrNative(node)) return null; 989 if (_externalOrNative(node)) return null;
946 990
947 var name = _constructorName(node.element); 991 var name = _constructorName(node.element);
992 var returnType = emitTypeRef(node.element.enclosingElement.type);
948 993
949 // Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz; 994 // Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz;
950 var redirect = node.redirectedConstructor; 995 var redirect = node.redirectedConstructor;
951 if (redirect != null) { 996 if (redirect != null) {
952 var newKeyword = redirect.staticElement.isFactory ? '' : 'new'; 997 var newKeyword = redirect.staticElement.isFactory ? '' : 'new';
953 // Pass along all arguments verbatim, and let the callee handle them. 998 // Pass along all arguments verbatim, and let the callee handle them.
954 // TODO(jmesserly): we'll need something different once we have 999 // TODO(jmesserly): we'll need something different once we have
955 // rest/spread support, but this should work for now. 1000 // rest/spread support, but this should work for now.
956 var params = 1001 var params =
957 _emitFormalParameterList(node.parameters, allowDestructuring: false); 1002 _emitFormalParameterList(node.parameters, allowDestructuring: false);
958 1003
959 var fun = js.call('function(#) { return $newKeyword #(#); }', 1004 var fun = new JS.Fun(
960 [params, _visit(redirect), params]) as JS.Fun; 1005 params,
1006 js.statement(
1007 '{ return $newKeyword #(#); }', [_visit(redirect), params]),
1008 returnType: returnType);
961 return annotate( 1009 return annotate(
962 new JS.Method(name, fun, isStatic: true)..sourceInformation = node, 1010 new JS.Method(name, fun, isStatic: true), node, node.element);
963 node.element);
964 } 1011 }
965 1012
966 // For const constructors we need to ensure default values are 1013 // For const constructors we need to ensure default values are
967 // available for use by top-level constant initializers. 1014 // available for use by top-level constant initializers.
968 ClassDeclaration cls = node.parent; 1015 ClassDeclaration cls = node.parent;
969 if (node.constKeyword != null) _loader.startTopLevel(cls.element); 1016 if (node.constKeyword != null) _loader.startTopLevel(cls.element);
970 var params = _emitFormalParameterList(node.parameters); 1017 var params = _emitFormalParameterList(node.parameters);
971 if (node.constKeyword != null) _loader.finishTopLevel(cls.element); 1018 if (node.constKeyword != null) _loader.finishTopLevel(cls.element);
972 1019
973 // Factory constructors are essentially static methods. 1020 // Factory constructors are essentially static methods.
974 if (node.factoryKeyword != null) { 1021 if (node.factoryKeyword != null) {
975 var body = <JS.Statement>[]; 1022 var body = <JS.Statement>[];
976 var init = _emitArgumentInitializers(node, constructor: true); 1023 var init = _emitArgumentInitializers(node, constructor: true);
977 if (init != null) body.add(init); 1024 if (init != null) body.add(init);
978 body.add(_visit(node.body)); 1025 body.add(_visit(node.body));
979 var fun = new JS.Fun(params, new JS.Block(body)); 1026 var fun = new JS.Fun(params, new JS.Block(body), returnType: returnType);
980 return annotate( 1027 return annotate(
981 new JS.Method(name, fun, isStatic: true)..sourceInformation = node, 1028 new JS.Method(name, fun, isStatic: true), node, node.element);
982 node.element);
983 } 1029 }
984 1030
985 // Code generation for Object's constructor. 1031 // Code generation for Object's constructor.
986 JS.Block body; 1032 JS.Block body;
987 if (isObject && 1033 if (isObject &&
988 node.body is EmptyFunctionBody && 1034 node.body is EmptyFunctionBody &&
989 node.constKeyword != null && 1035 node.constKeyword != null &&
990 node.name == null) { 1036 node.name == null) {
991 // Implements Dart constructor behavior. Because of V8 `super` 1037 // Implements Dart constructor behavior. Because of V8 `super`
992 // [constructor restrictions] 1038 // [constructor restrictions]
(...skipping 15 matching lines...) Expand all
1008 return result === void 0 ? this : result; 1054 return result === void 0 ? this : result;
1009 }''') as JS.Block; 1055 }''') as JS.Block;
1010 } else { 1056 } else {
1011 body = _emitConstructorBody(node, fields); 1057 body = _emitConstructorBody(node, fields);
1012 } 1058 }
1013 1059
1014 // We generate constructors as initializer methods in the class; 1060 // We generate constructors as initializer methods in the class;
1015 // this allows use of `super` for instance methods/properties. 1061 // this allows use of `super` for instance methods/properties.
1016 // It also avoids V8 restrictions on `super` in default constructors. 1062 // It also avoids V8 restrictions on `super` in default constructors.
1017 return annotate( 1063 return annotate(
1018 new JS.Method(name, new JS.Fun(params, body))..sourceInformation = node, 1064 new JS.Method(name, new JS.Fun(params, body, returnType: returnType)),
1065 node,
1019 node.element); 1066 node.element);
1020 } 1067 }
1021 1068
1022 JS.Expression _constructorName(ConstructorElement ctor) { 1069 JS.Expression _constructorName(ConstructorElement ctor) {
1023 var name = ctor.name; 1070 var name = ctor.name;
1024 if (name != '') { 1071 if (name != '') {
1025 return _emitMemberName(name, isStatic: true); 1072 return _emitMemberName(name, isStatic: true);
1026 } 1073 }
1027 1074
1028 // Factory default constructors use `new` as their name, for readability 1075 // Factory default constructors use `new` as their name, for readability
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
1105 1152
1106 if (superCtor == null) { 1153 if (superCtor == null) {
1107 print('Error generating: ${element.displayName}'); 1154 print('Error generating: ${element.displayName}');
1108 } 1155 }
1109 if (superCtor.name == '' && !_shouldCallUnnamedSuperCtor(element)) { 1156 if (superCtor.name == '' && !_shouldCallUnnamedSuperCtor(element)) {
1110 return null; 1157 return null;
1111 } 1158 }
1112 1159
1113 var name = _constructorName(superCtor); 1160 var name = _constructorName(superCtor);
1114 var args = node != null ? _visit(node.argumentList) : []; 1161 var args = node != null ? _visit(node.argumentList) : [];
1115 return js.statement('super.#(#);', [name, args])..sourceInformation = node; 1162 return annotate(js.statement('super.#(#);', [name, args]), node);
1116 } 1163 }
1117 1164
1118 bool _shouldCallUnnamedSuperCtor(ClassElement e) { 1165 bool _shouldCallUnnamedSuperCtor(ClassElement e) {
1119 var supertype = e.supertype; 1166 var supertype = e.supertype;
1120 if (supertype == null) return false; 1167 if (supertype == null) return false;
1121 if (_hasUnnamedConstructor(supertype.element)) return true; 1168 if (_hasUnnamedConstructor(supertype.element)) return true;
1122 for (var mixin in e.mixins) { 1169 for (var mixin in e.mixins) {
1123 if (_hasUnnamedConstructor(mixin.element)) return true; 1170 if (_hasUnnamedConstructor(mixin.element)) return true;
1124 } 1171 }
1125 return false; 1172 return false;
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
1157 fields[element as FieldElement] = _visitInitializer(fieldNode); 1204 fields[element as FieldElement] = _visitInitializer(fieldNode);
1158 } 1205 }
1159 } 1206 }
1160 } 1207 }
1161 1208
1162 // Initialize fields from `this.fieldName` parameters. 1209 // Initialize fields from `this.fieldName` parameters.
1163 if (ctor != null) { 1210 if (ctor != null) {
1164 for (var p in ctor.parameters.parameters) { 1211 for (var p in ctor.parameters.parameters) {
1165 var element = p.element; 1212 var element = p.element;
1166 if (element is FieldFormalParameterElement) { 1213 if (element is FieldFormalParameterElement) {
1167 fields[element.field] = _visit(p); 1214 fields[element.field] = _emitFormalParameter(p, allowType: false);
1168 } 1215 }
1169 } 1216 }
1170 1217
1171 // Run constructor field initializers such as `: foo = bar.baz` 1218 // Run constructor field initializers such as `: foo = bar.baz`
1172 for (var init in ctor.initializers) { 1219 for (var init in ctor.initializers) {
1173 if (init is ConstructorFieldInitializer) { 1220 if (init is ConstructorFieldInitializer) {
1174 fields[init.fieldName.staticElement as FieldElement] = 1221 fields[init.fieldName.staticElement as FieldElement] =
1175 _visit(init.expression); 1222 _visit(init.expression);
1176 } 1223 }
1177 } 1224 }
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
1215 JS.Statement _emitArgumentInitializers(node, {bool constructor: false}) { 1262 JS.Statement _emitArgumentInitializers(node, {bool constructor: false}) {
1216 // Constructor argument initializers are emitted earlier in the code, rather 1263 // Constructor argument initializers are emitted earlier in the code, rather
1217 // than always when we visit the function body, so we control it explicitly. 1264 // than always when we visit the function body, so we control it explicitly.
1218 if (node is ConstructorDeclaration != constructor) return null; 1265 if (node is ConstructorDeclaration != constructor) return null;
1219 1266
1220 var parameters = _parametersOf(node); 1267 var parameters = _parametersOf(node);
1221 if (parameters == null) return null; 1268 if (parameters == null) return null;
1222 1269
1223 var body = <JS.Statement>[]; 1270 var body = <JS.Statement>[];
1224 for (var param in parameters.parameters) { 1271 for (var param in parameters.parameters) {
1225 var jsParam = _visit(param.identifier); 1272 var jsParam = _emitSimpleIdentifier(param.identifier, allowType: false);
1226 1273
1227 if (param.kind == ParameterKind.NAMED) { 1274 if (param.kind == ParameterKind.NAMED) {
1228 if (!options.destructureNamedParams) { 1275 if (!options.destructureNamedParams) {
1229 // Parameters will be passed using their real names, not the (possibly 1276 // Parameters will be passed using their real names, not the (possibly
1230 // renamed) local variable. 1277 // renamed) local variable.
1231 var paramName = js.string(param.identifier.name, "'"); 1278 var paramName = js.string(param.identifier.name, "'");
1232 1279
1233 // TODO(ochafik): Fix `'prop' in obj` to please Closure's renaming. 1280 // TODO(ochafik): Fix `'prop' in obj` to please Closure's renaming.
1234 body.add(js.statement('let # = # && # in # ? #.# : #;', [ 1281 body.add(js.statement('let # = # && # in # ? #.# : #;', [
1235 jsParam, 1282 jsParam,
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
1274 } 1321 }
1275 1322
1276 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { 1323 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) {
1277 if (node.isAbstract || _externalOrNative(node)) { 1324 if (node.isAbstract || _externalOrNative(node)) {
1278 return null; 1325 return null;
1279 } 1326 }
1280 1327
1281 var params = _visit(node.parameters) as List<JS.Parameter>; 1328 var params = _visit(node.parameters) as List<JS.Parameter>;
1282 if (params == null) params = <JS.Parameter>[]; 1329 if (params == null) params = <JS.Parameter>[];
1283 1330
1284 JS.Fun fn = _emitFunctionBody(params, node.body); 1331 var typeParams = _emitTypeParams(node.element).toList();
1332 var returnType = emitTypeRef(node.element.returnType);
1333 JS.Fun fn = _emitFunctionBody(params, node.body, typeParams, returnType);
1285 if (node.operatorKeyword != null && 1334 if (node.operatorKeyword != null &&
1286 node.name.name == '[]=' && 1335 node.name.name == '[]=' &&
1287 params.isNotEmpty) { 1336 params.isNotEmpty) {
1288 // []= methods need to return the value. We could also address this at 1337 // []= methods need to return the value. We could also address this at
1289 // call sites, but it's cleaner to instead transform the operator method. 1338 // call sites, but it's cleaner to instead transform the operator method.
1290 var returnValue = new JS.Return(params.last); 1339 var returnValue = new JS.Return(params.last);
1291 var body = fn.body; 1340 var body = fn.body;
1292 if (JS.Return.foundIn(fn)) { 1341 if (JS.Return.foundIn(fn)) {
1293 // If a return is inside body, transform `(params) { body }` to 1342 // If a return is inside body, transform `(params) { body }` to
1294 // `(params) { (() => { body })(); return value; }`. 1343 // `(params) { (() => { body })(); return value; }`.
1295 // TODO(jmesserly): we could instead generate the return differently, 1344 // TODO(jmesserly): we could instead generate the return differently,
1296 // and avoid the immediately invoked function. 1345 // and avoid the immediately invoked function.
1297 body = new JS.Call(new JS.ArrowFun([], fn.body), []).toStatement(); 1346 body = new JS.Call(new JS.ArrowFun([], fn.body), []).toStatement();
1298 } 1347 }
1299 // Rewrite the function to include the return. 1348 // Rewrite the function to include the return.
1300 fn = new JS.Fun(fn.params, new JS.Block([body, returnValue])) 1349 fn = new JS.Fun(fn.params, new JS.Block([body, returnValue]),
1301 ..sourceInformation = fn.sourceInformation; 1350 typeParams: fn.typeParams,
1351 returnType: fn.returnType)..sourceInformation = fn.sourceInformation;
1302 } 1352 }
1303 1353
1304 return annotate( 1354 return annotate(
1305 new JS.Method(_elementMemberName(node.element), fn, 1355 new JS.Method(_elementMemberName(node.element), fn,
1306 isGetter: node.isGetter, 1356 isGetter: node.isGetter,
1307 isSetter: node.isSetter, 1357 isSetter: node.isSetter,
1308 isStatic: node.isStatic), 1358 isStatic: node.isStatic),
1359 node,
1309 node.element); 1360 node.element);
1310 } 1361 }
1311 1362
1312 /// Returns the name value of the `JSExportName` annotation (when compiling 1363 /// Returns the name value of the `JSExportName` annotation (when compiling
1313 /// the SDK), or `null` if there's none. This is used to control the name 1364 /// the SDK), or `null` if there's none. This is used to control the name
1314 /// under which functions are compiled and exported. 1365 /// under which functions are compiled and exported.
1315 String _getJSExportName(Element e) { 1366 String _getJSExportName(Element e) {
1316 if (!currentLibrary.source.isInSystemLibrary) { 1367 if (!e.source.isInSystemLibrary) {
1317 return null; 1368 return null;
1318 } 1369 }
1319 var jsName = findAnnotation(e, isJSExportNameAnnotation); 1370 var jsName = findAnnotation(e, isJSExportNameAnnotation);
1320 return getConstantField(jsName, 'name', types.stringType)?.toStringValue(); 1371 return getConstantField(jsName, 'name', types.stringType)?.toStringValue();
1321 } 1372 }
1322 1373
1323 @override 1374 @override
1324 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { 1375 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) {
1325 assert(node.parent is CompilationUnit); 1376 assert(node.parent is CompilationUnit);
1326 1377
(...skipping 11 matching lines...) Expand all
1338 var name = node.name.name; 1389 var name = node.name.name;
1339 1390
1340 var fn = _visit(node.functionExpression); 1391 var fn = _visit(node.functionExpression);
1341 1392
1342 if (currentLibrary.source.isInSystemLibrary && 1393 if (currentLibrary.source.isInSystemLibrary &&
1343 _isInlineJSFunction(node.functionExpression)) { 1394 _isInlineJSFunction(node.functionExpression)) {
1344 fn = _simplifyPassThroughArrowFunCallBody(fn); 1395 fn = _simplifyPassThroughArrowFunCallBody(fn);
1345 } 1396 }
1346 1397
1347 var id = new JS.Identifier(name); 1398 var id = new JS.Identifier(name);
1348 body.add(annotate(new JS.FunctionDeclaration(id, fn), node.element)); 1399 body.add(annotate(new JS.FunctionDeclaration(id, fn), node, node.element));
1349 if (!_isDartRuntime) { 1400 if (!_isDartRuntime) {
1350 body.add(_emitFunctionTagged(id, node.element.type, topLevel: true) 1401 body.add(_emitFunctionTagged(id, node.element.type, topLevel: true)
1351 .toStatement()); 1402 .toStatement());
1352 } 1403 }
1353 1404
1354 if (isPublic(name)) { 1405 if (isPublic(name)) {
1355 _addExport(name, _getJSExportName(node.element) ?? name); 1406 _addExport(name, _getJSExportName(node.element) ?? name);
1356 } 1407 }
1357 return _statement(body); 1408 return _statement(body);
1358 } 1409 }
(...skipping 20 matching lines...) Expand all
1379 // Note: this allows silently passing args through to the body, which only 1430 // Note: this allows silently passing args through to the body, which only
1380 // works if we don't do weird renamings of Dart params. 1431 // works if we don't do weird renamings of Dart params.
1381 JS.Fun _simplifyPassThroughArrowFunCallBody(JS.Fun fn) { 1432 JS.Fun _simplifyPassThroughArrowFunCallBody(JS.Fun fn) {
1382 if (fn.body is JS.Block && fn.body.statements.length == 1) { 1433 if (fn.body is JS.Block && fn.body.statements.length == 1) {
1383 var stat = fn.body.statements.single; 1434 var stat = fn.body.statements.single;
1384 if (stat is JS.Return && stat.value is JS.Call) { 1435 if (stat is JS.Return && stat.value is JS.Call) {
1385 JS.Call call = stat.value; 1436 JS.Call call = stat.value;
1386 if (call.target is JS.ArrowFun && call.arguments.isEmpty) { 1437 if (call.target is JS.ArrowFun && call.arguments.isEmpty) {
1387 JS.ArrowFun innerFun = call.target; 1438 JS.ArrowFun innerFun = call.target;
1388 if (innerFun.params.isEmpty) { 1439 if (innerFun.params.isEmpty) {
1389 return new JS.Fun(fn.params, innerFun.body); 1440 return new JS.Fun(fn.params, innerFun.body,
1441 typeParams: fn.typeParams, returnType: fn.returnType);
1390 } 1442 }
1391 } 1443 }
1392 } 1444 }
1393 } 1445 }
1394 return fn; 1446 return fn;
1395 } 1447 }
1396 1448
1397 JS.Method _emitTopLevelProperty(FunctionDeclaration node) { 1449 JS.Method _emitTopLevelProperty(FunctionDeclaration node) {
1398 var name = node.name.name; 1450 var name = node.name.name;
1399 return annotate( 1451 return annotate(
1400 new JS.Method(_propertyName(name), _visit(node.functionExpression), 1452 new JS.Method(_propertyName(name), _visit(node.functionExpression),
1401 isGetter: node.isGetter, isSetter: node.isSetter), 1453 isGetter: node.isGetter, isSetter: node.isSetter),
1454 node,
1402 node.element); 1455 node.element);
1403 } 1456 }
1404 1457
1405 bool _executesAtTopLevel(AstNode node) { 1458 bool _executesAtTopLevel(AstNode node) {
1406 var ancestor = node.getAncestor((n) => 1459 var ancestor = node.getAncestor((n) =>
1407 n is FunctionBody || 1460 n is FunctionBody ||
1408 (n is FieldDeclaration && n.staticKeyword == null) || 1461 (n is FieldDeclaration && n.staticKeyword == null) ||
1409 (n is ConstructorDeclaration && n.constKeyword == null)); 1462 (n is ConstructorDeclaration && n.constKeyword == null));
1410 return ancestor == null; 1463 return ancestor == null;
1411 } 1464 }
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
1444 throw 'Function has non function type: $type'; 1497 throw 'Function has non function type: $type';
1445 } 1498 }
1446 1499
1447 @override 1500 @override
1448 JS.Expression visitFunctionExpression(FunctionExpression node) { 1501 JS.Expression visitFunctionExpression(FunctionExpression node) {
1449 var params = _visit(node.parameters) as List<JS.Parameter>; 1502 var params = _visit(node.parameters) as List<JS.Parameter>;
1450 if (params == null) params = <JS.Parameter>[]; 1503 if (params == null) params = <JS.Parameter>[];
1451 1504
1452 var parent = node.parent; 1505 var parent = node.parent;
1453 var inStmt = parent.parent is FunctionDeclarationStatement; 1506 var inStmt = parent.parent is FunctionDeclarationStatement;
1507 var typeParams = _emitTypeParams(node.element).toList();
1508 var returnType = emitTypeRef(node.element.returnType);
1454 if (parent is FunctionDeclaration) { 1509 if (parent is FunctionDeclaration) {
1455 return _emitFunctionBody(params, node.body); 1510 return _emitFunctionBody(params, node.body, typeParams, returnType);
1456 } else { 1511 } else {
1457 // Chrome Canary does not accept default values with destructuring in 1512 // Chrome Canary does not accept default values with destructuring in
1458 // arrow functions yet (e.g. `({a} = {}) => 1`) but happily accepts them 1513 // arrow functions yet (e.g. `({a} = {}) => 1`) but happily accepts them
1459 // with regular functions (e.g. `function({a} = {}) { return 1 }`). 1514 // with regular functions (e.g. `function({a} = {}) { return 1 }`).
1460 // Note that Firefox accepts both syntaxes just fine. 1515 // Note that Firefox accepts both syntaxes just fine.
1461 // TODO(ochafik): Simplify this code when Chrome Canary catches up. 1516 // TODO(ochafik): Simplify this code when Chrome Canary catches up.
1462 var canUseArrowFun = !node.parameters.parameters.any(_isNamedParam); 1517 var canUseArrowFun = !node.parameters.parameters.any(_isNamedParam);
1463 1518
1464 String code = canUseArrowFun ? '(#) => #' : 'function(#) { return # }';
1465 JS.Node jsBody; 1519 JS.Node jsBody;
1466 var body = node.body; 1520 var body = node.body;
1467 if (body.isGenerator || body.isAsynchronous) { 1521 if (body.isGenerator || body.isAsynchronous) {
1468 jsBody = _emitGeneratorFunctionBody(params, body); 1522 jsBody = _emitGeneratorFunctionBody(params, body, returnType);
1469 } else if (body is ExpressionFunctionBody) { 1523 } else if (body is ExpressionFunctionBody) {
1470 jsBody = _visit(body.expression); 1524 jsBody = _visit(body.expression);
1471 } else { 1525 } else {
1472 code = canUseArrowFun ? '(#) => { #; }' : 'function(#) { #; }';
1473 jsBody = _visit(body); 1526 jsBody = _visit(body);
1474 } 1527 }
1475 var clos = js.call(code, [params, jsBody]); 1528 if (jsBody is JS.Expression && !canUseArrowFun) {
1529 jsBody = js.statement("{ return #; }", [jsBody]);
1530 }
1531 var clos = canUseArrowFun
1532 ? new JS.ArrowFun(params, jsBody,
1533 typeParams: typeParams, returnType: returnType)
1534 : new JS.Fun(params, jsBody,
1535 typeParams: typeParams, returnType: returnType);
1476 if (!inStmt) { 1536 if (!inStmt) {
1477 var type = getStaticType(node); 1537 var type = getStaticType(node);
1478 return _emitFunctionTagged(clos, type, 1538 return _emitFunctionTagged(clos, type,
1479 topLevel: _executesAtTopLevel(node)); 1539 topLevel: _executesAtTopLevel(node));
1480 } 1540 }
1481 return clos; 1541 return clos;
1482 } 1542 }
1483 } 1543 }
1484 1544
1485 JS.Fun _emitFunctionBody(List<JS.Parameter> params, FunctionBody body) { 1545 JS.Fun _emitFunctionBody(List<JS.Parameter> params, FunctionBody body,
1546 List<JS.Identifier> typeParams, JS.TypeRef returnType) {
1486 // sync*, async, async* 1547 // sync*, async, async*
1487 if (body.isAsynchronous || body.isGenerator) { 1548 if (body.isAsynchronous || body.isGenerator) {
1488 return new JS.Fun( 1549 return new JS.Fun(
1489 params, 1550 params,
1490 js.statement( 1551 js.statement('{ return #; }',
1491 '{ return #; }', [_emitGeneratorFunctionBody(params, body)])); 1552 [_emitGeneratorFunctionBody(params, body, returnType)]),
1553 returnType: returnType);
1492 } 1554 }
1493 // normal function (sync) 1555 // normal function (sync)
1494 return new JS.Fun(params, _visit(body)); 1556 return new JS.Fun(params, _visit(body),
1557 typeParams: typeParams, returnType: returnType);
1495 } 1558 }
1496 1559
1497 JS.Expression _emitGeneratorFunctionBody( 1560 JS.Expression _emitGeneratorFunctionBody(
1498 List<JS.Parameter> params, FunctionBody body) { 1561 List<JS.Parameter> params, FunctionBody body, JS.TypeRef returnType) {
1499 var kind = body.isSynchronous ? 'sync' : 'async'; 1562 var kind = body.isSynchronous ? 'sync' : 'async';
1500 if (body.isGenerator) kind += 'Star'; 1563 if (body.isGenerator) kind += 'Star';
1501 1564
1502 // Transforms `sync*` `async` and `async*` function bodies 1565 // Transforms `sync*` `async` and `async*` function bodies
1503 // using ES6 generators. 1566 // using ES6 generators.
1504 // 1567 //
1505 // `sync*` wraps a generator in a Dart Iterable<T>: 1568 // `sync*` wraps a generator in a Dart Iterable<T>:
1506 // 1569 //
1507 // function name(<args>) { 1570 // function name(<args>) {
1508 // return dart.syncStar(function*(<args>) { 1571 // return dart.syncStar(function*(<args>) {
(...skipping 23 matching lines...) Expand all
1532 // runtime/_generators.js has an example of what the code is generated as. 1595 // runtime/_generators.js has an example of what the code is generated as.
1533 var savedController = _asyncStarController; 1596 var savedController = _asyncStarController;
1534 List jsParams; 1597 List jsParams;
1535 if (kind == 'asyncStar') { 1598 if (kind == 'asyncStar') {
1536 _asyncStarController = new JS.TemporaryId('stream'); 1599 _asyncStarController = new JS.TemporaryId('stream');
1537 jsParams = [_asyncStarController]..addAll(params); 1600 jsParams = [_asyncStarController]..addAll(params);
1538 } else { 1601 } else {
1539 _asyncStarController = null; 1602 _asyncStarController = null;
1540 jsParams = params; 1603 jsParams = params;
1541 } 1604 }
1542 JS.Expression gen = new JS.Fun(jsParams, _visit(body), isGenerator: true); 1605 JS.Expression gen = new JS.Fun(jsParams, _visit(body),
1606 isGenerator: true, returnType: returnType);
1543 if (JS.This.foundIn(gen)) { 1607 if (JS.This.foundIn(gen)) {
1544 gen = js.call('#.bind(this)', gen); 1608 gen = js.call('#.bind(this)', gen);
1545 } 1609 }
1546 _asyncStarController = savedController; 1610 _asyncStarController = savedController;
1547 1611
1548 var T = _emitTypeName(_getExpectedReturnType(body)); 1612 var T = _emitTypeName(_getExpectedReturnType(body));
1549 return js.call('dart.#(#)', [ 1613 return js.call('dart.#(#)', [
1550 kind, 1614 kind,
1551 [gen, T]..addAll(params) 1615 [gen, T]..addAll(params)
1552 ]); 1616 ]);
1553 } 1617 }
1554 1618
1555 @override 1619 @override
1556 JS.Statement visitFunctionDeclarationStatement( 1620 JS.Statement visitFunctionDeclarationStatement(
1557 FunctionDeclarationStatement node) { 1621 FunctionDeclarationStatement node) {
1558 var func = node.functionDeclaration; 1622 var func = node.functionDeclaration;
1559 if (func.isGetter || func.isSetter) { 1623 if (func.isGetter || func.isSetter) {
1560 return js.comment('Unimplemented function get/set statement: $node'); 1624 return js.comment('Unimplemented function get/set statement: $node');
1561 } 1625 }
1562 1626
1563 var fn = _visit(func.functionExpression); 1627 var fn = _visit(func.functionExpression);
1564 1628
1565 var name = new JS.Identifier(func.name.name); 1629 var name = new JS.Identifier(func.name.name);
1566 JS.Statement declareFn; 1630 JS.Statement declareFn;
1567 if (JS.This.foundIn(fn)) { 1631 if (JS.This.foundIn(fn)) {
1568 declareFn = js.statement('const # = #.bind(this);', [name, fn]); 1632 declareFn = js.statement('const # = #.bind(this);', [name, fn]);
1569 } else { 1633 } else {
1570 declareFn = new JS.FunctionDeclaration(name, fn); 1634 declareFn = new JS.FunctionDeclaration(name, fn);
1571 } 1635 }
1572 declareFn = annotate(declareFn, node.functionDeclaration.element); 1636 declareFn = annotate(declareFn, node, node.functionDeclaration.element);
1573 1637
1574 return new JS.Block([ 1638 return new JS.Block([
1575 declareFn, 1639 declareFn,
1576 _emitFunctionTagged(name, func.element.type).toStatement() 1640 _emitFunctionTagged(name, func.element.type).toStatement()
1577 ]); 1641 ]);
1578 } 1642 }
1579 1643
1644 @override
1645 JS.Expression visitSimpleIdentifier(SimpleIdentifier node) =>
1646 _emitSimpleIdentifier(node);
1647
1580 /// Writes a simple identifier. This can handle implicit `this` as well as 1648 /// Writes a simple identifier. This can handle implicit `this` as well as
1581 /// going through the qualified library name if necessary. 1649 /// going through the qualified library name if necessary.
1582 @override 1650 JS.Expression _emitSimpleIdentifier(SimpleIdentifier node,
1583 JS.Expression visitSimpleIdentifier(SimpleIdentifier node) { 1651 {bool allowType: false}) {
1584 var accessor = node.staticElement; 1652 var accessor = node.staticElement;
1585 if (accessor == null) { 1653 if (accessor == null) {
1586 return js.commentExpression( 1654 return js.commentExpression(
1587 'Unimplemented unknown name', new JS.Identifier(node.name)); 1655 'Unimplemented unknown name', new JS.Identifier(node.name));
1588 } 1656 }
1589 1657
1590 // Get the original declaring element. If we had a property accessor, this 1658 // Get the original declaring element. If we had a property accessor, this
1591 // indirects back to a (possibly synthetic) field. 1659 // indirects back to a (possibly synthetic) field.
1592 var element = accessor; 1660 var element = accessor;
1593 if (accessor is PropertyAccessorElement) element = accessor.variable; 1661 if (accessor is PropertyAccessorElement) element = accessor.variable;
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
1642 } 1710 }
1643 1711
1644 if (element is TemporaryVariableElement) { 1712 if (element is TemporaryVariableElement) {
1645 if (name[0] == '#') { 1713 if (name[0] == '#') {
1646 return new JS.InterpolatedExpression(name.substring(1)); 1714 return new JS.InterpolatedExpression(name.substring(1));
1647 } else { 1715 } else {
1648 return _getTemp(element, name); 1716 return _getTemp(element, name);
1649 } 1717 }
1650 } 1718 }
1651 1719
1652 return new JS.Identifier(name); 1720 return annotate(
1721 new JS.Identifier(name,
1722 type: allowType ? emitTypeRef(node.bestType) : null),
1723 node);
1653 } 1724 }
1654 1725
1655 JS.TemporaryId _getTemp(Element key, String name) => 1726 JS.TemporaryId _getTemp(Element key, String name) =>
1656 _temps.putIfAbsent(key, () => new JS.TemporaryId(name)); 1727 _temps.putIfAbsent(key, () => new JS.TemporaryId(name));
1657 1728
1658 List<Annotation> _parameterMetadata(FormalParameter p) => 1729 List<Annotation> _parameterMetadata(FormalParameter p) =>
1659 (p is NormalFormalParameter) 1730 (p is NormalFormalParameter)
1660 ? p.metadata 1731 ? p.metadata
1661 : (p as DefaultFormalParameter).parameter.metadata; 1732 : (p as DefaultFormalParameter).parameter.metadata;
1662 1733
(...skipping 435 matching lines...) Expand 10 before | Expand all | Expand 10 after
2098 result.add(_namedArgTemp); 2169 result.add(_namedArgTemp);
2099 } else if (namedVars.isNotEmpty) { 2170 } else if (namedVars.isNotEmpty) {
2100 // Note: `var {valueOf} = {}` extracts `Object.prototype.valueOf`, so 2171 // Note: `var {valueOf} = {}` extracts `Object.prototype.valueOf`, so
2101 // in case there are conflicting names we create an object without 2172 // in case there are conflicting names we create an object without
2102 // any prototype. 2173 // any prototype.
2103 var defaultOpts = hasNamedArgsConflictingWithObjectProperties 2174 var defaultOpts = hasNamedArgsConflictingWithObjectProperties
2104 ? js.call('Object.create(null)') 2175 ? js.call('Object.create(null)')
2105 : js.call('{}'); 2176 : js.call('{}');
2106 result.add(new JS.DestructuredVariable( 2177 result.add(new JS.DestructuredVariable(
2107 structure: new JS.ObjectBindingPattern(namedVars), 2178 structure: new JS.ObjectBindingPattern(namedVars),
2179 type: emitNamedParamsArgType(node.parameterElements),
2108 defaultValue: defaultOpts)); 2180 defaultValue: defaultOpts));
2109 } 2181 }
2110 return result; 2182 return result;
2111 } 2183 }
2112 2184
2113 /// See ES6 spec (and `Object.getOwnPropertyNames(Object.prototype)`): 2185 /// See ES6 spec (and `Object.getOwnPropertyNames(Object.prototype)`):
2114 /// http://www.ecma-international.org/ecma-262/6.0/#sec-properties-of-the-obje ct-prototype-object 2186 /// http://www.ecma-international.org/ecma-262/6.0/#sec-properties-of-the-obje ct-prototype-object
2115 /// http://www.ecma-international.org/ecma-262/6.0/#sec-additional-properties- of-the-object.prototype-object 2187 /// http://www.ecma-international.org/ecma-262/6.0/#sec-additional-properties- of-the-object.prototype-object
2116 static final Set<String> _jsObjectProperties = new Set<String>() 2188 static final Set<String> _jsObjectProperties = new Set<String>()
2117 ..addAll([ 2189 ..addAll([
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after
2229 } 2301 }
2230 2302
2231 @override 2303 @override
2232 visitVariableDeclaration(VariableDeclaration node) { 2304 visitVariableDeclaration(VariableDeclaration node) {
2233 if (node.element is PropertyInducingElement) { 2305 if (node.element is PropertyInducingElement) {
2234 // Static and instance fields are handled elsewhere. 2306 // Static and instance fields are handled elsewhere.
2235 assert(node.element is TopLevelVariableElement); 2307 assert(node.element is TopLevelVariableElement);
2236 return _emitTopLevelField(node); 2308 return _emitTopLevelField(node);
2237 } 2309 }
2238 2310
2239 var name = new JS.Identifier(node.name.name); 2311 var name =
2312 new JS.Identifier(node.name.name, type: emitTypeRef(node.element.type));
2240 return new JS.VariableInitialization(name, _visitInitializer(node)); 2313 return new JS.VariableInitialization(name, _visitInitializer(node));
2241 } 2314 }
2242 2315
2243 bool _isFinalJSDecl(AstNode field) => 2316 bool _isFinalJSDecl(AstNode field) =>
2244 field is VariableDeclaration && 2317 field is VariableDeclaration &&
2245 field.isFinal && 2318 field.isFinal &&
2246 _isJSInvocation(field.initializer); 2319 _isJSInvocation(field.initializer);
2247 2320
2248 /// Try to emit a constant static field. 2321 /// Try to emit a constant static field.
2249 /// 2322 ///
(...skipping 15 matching lines...) Expand all
2265 2338
2266 _loader.startCheckingReferences(); 2339 _loader.startCheckingReferences();
2267 JS.Expression jsInit = _visitInitializer(field); 2340 JS.Expression jsInit = _visitInitializer(field);
2268 bool isLoaded = _loader.finishCheckingReferences(); 2341 bool isLoaded = _loader.finishCheckingReferences();
2269 2342
2270 bool eagerInit = 2343 bool eagerInit =
2271 isLoaded && (field.isConst || _constField.isFieldInitConstant(field)); 2344 isLoaded && (field.isConst || _constField.isFieldInitConstant(field));
2272 2345
2273 var fieldName = field.name.name; 2346 var fieldName = field.name.name;
2274 if (eagerInit && !JS.invalidStaticFieldName(fieldName)) { 2347 if (eagerInit && !JS.invalidStaticFieldName(fieldName)) {
2275 return annotateVariable( 2348 return annotate(
2276 js.statement('#.# = #;', [ 2349 js.statement('#.# = #;', [
2277 classElem.name, 2350 classElem.name,
2278 _emitMemberName(fieldName, isStatic: true), 2351 _emitMemberName(fieldName, isStatic: true),
2279 jsInit 2352 jsInit
2280 ]), 2353 ]),
2354 field,
2281 field.element); 2355 field.element);
2282 } 2356 }
2283 2357
2284 // This means it should be treated as a lazy field. 2358 // This means it should be treated as a lazy field.
2285 // TODO(jmesserly): we're throwing away the initializer expression, 2359 // TODO(jmesserly): we're throwing away the initializer expression,
2286 // which will force us to regenerate it. 2360 // which will force us to regenerate it.
2287 return null; 2361 return null;
2288 } 2362 }
2289 2363
2290 /// Emits a top-level field. 2364 /// Emits a top-level field.
(...skipping 27 matching lines...) Expand all
2318 exportName = _getJSExportName(element) ?? fieldName; 2392 exportName = _getJSExportName(element) ?? fieldName;
2319 } 2393 }
2320 if ((field.isConst && eagerInit && element is TopLevelVariableElement) || 2394 if ((field.isConst && eagerInit && element is TopLevelVariableElement) ||
2321 isJSTopLevel) { 2395 isJSTopLevel) {
2322 // constant fields don't change, so we can generate them as `let` 2396 // constant fields don't change, so we can generate them as `let`
2323 // but add them to the module's exports. However, make sure we generate 2397 // but add them to the module's exports. However, make sure we generate
2324 // anything they depend on first. 2398 // anything they depend on first.
2325 2399
2326 if (isPublic(fieldName)) _addExport(fieldName, exportName); 2400 if (isPublic(fieldName)) _addExport(fieldName, exportName);
2327 var declKeyword = field.isConst || field.isFinal ? 'const' : 'let'; 2401 var declKeyword = field.isConst || field.isFinal ? 'const' : 'let';
2328 return annotateVariable( 2402 return js.statement('#;', [
2329 js.statement( 2403 annotate(
2330 '$declKeyword # = #;', [new JS.Identifier(fieldName), jsInit]), 2404 new JS.VariableDeclarationList(declKeyword, [
2331 field.element); 2405 new JS.VariableInitialization(
2406 new JS.Identifier(fieldName,
2407 type: emitTypeRef(field.element.type)),
2408 jsInit)
2409 ]),
2410 field,
2411 field.element)
2412 ]);
2332 } 2413 }
2333 2414
2334 if (eagerInit && !JS.invalidStaticFieldName(fieldName)) { 2415 if (eagerInit && !JS.invalidStaticFieldName(fieldName)) {
2335 return annotateVariable( 2416 return annotate(js.statement('# = #;', [_visit(field.name), jsInit]),
2336 js.statement('# = #;', [_visit(field.name), jsInit]), field.element); 2417 field, field.element);
2337 } 2418 }
2338 2419
2339 return _emitLazyFields(currentLibrary, [field]); 2420 return _emitLazyFields(currentLibrary, [field]);
2340 } 2421 }
2341 2422
2342 JS.Expression _visitInitializer(VariableDeclaration node) { 2423 JS.Expression _visitInitializer(VariableDeclaration node) {
2343 var value = _visit(node.initializer); 2424 var value = _visit(node.initializer);
2344 // explicitly initialize to null, to avoid getting `undefined`. 2425 // explicitly initialize to null, to avoid getting `undefined`.
2345 // TODO(jmesserly): do this only for vars that aren't definitely assigned. 2426 // TODO(jmesserly): do this only for vars that aren't definitely assigned.
2346 return value ?? new JS.LiteralNull(); 2427 return value ?? new JS.LiteralNull();
2347 } 2428 }
2348 2429
2349 JS.Statement _emitLazyFields( 2430 JS.Statement _emitLazyFields(
2350 Element target, List<VariableDeclaration> fields) { 2431 Element target, List<VariableDeclaration> fields) {
2351 var methods = []; 2432 var methods = [];
2352 for (var node in fields) { 2433 for (var node in fields) {
2353 var name = node.name.name; 2434 var name = node.name.name;
2354 var element = node.element; 2435 var element = node.element;
2355 var access = _emitMemberName(name, isStatic: true); 2436 var access = _emitMemberName(name, isStatic: true);
2356 methods.add(annotate( 2437 methods.add(annotate(
2357 new JS.Method( 2438 new JS.Method(
2358 access, 2439 access,
2359 js.call('function() { return #; }', _visit(node.initializer)) 2440 js.call('function() { return #; }', _visit(node.initializer))
2360 as JS.Fun, 2441 as JS.Fun,
2361 isGetter: true), 2442 isGetter: true),
2443 node,
2362 _findAccessor(element, getter: true))); 2444 _findAccessor(element, getter: true)));
2363 2445
2364 // TODO(jmesserly): currently uses a dummy setter to indicate writable. 2446 // TODO(jmesserly): currently uses a dummy setter to indicate writable.
2365 if (!node.isFinal && !node.isConst) { 2447 if (!node.isFinal && !node.isConst) {
2366 methods.add(annotate( 2448 methods.add(annotate(
2367 new JS.Method(access, js.call('function(_) {}') as JS.Fun, 2449 new JS.Method(access, js.call('function(_) {}') as JS.Fun,
2368 isSetter: true), 2450 isSetter: true),
2451 node,
2369 _findAccessor(element, getter: false))); 2452 _findAccessor(element, getter: false)));
2370 } 2453 }
2371 } 2454 }
2372 2455
2373 JS.Expression objExpr; 2456 JS.Expression objExpr;
2374 if (target is ClassElement) { 2457 if (target is ClassElement) {
2375 objExpr = new JS.Identifier(target.type.name); 2458 objExpr = new JS.Identifier(target.type.name);
2376 } else { 2459 } else {
2377 objExpr = _libraryName(target); 2460 objExpr = _libraryName(target);
2378 } 2461 }
(...skipping 368 matching lines...) Expand 10 before | Expand all | Expand 10 after
2747 _cascadeTarget = savedCascadeTemp; 2830 _cascadeTarget = savedCascadeTemp;
2748 return result; 2831 return result;
2749 } 2832 }
2750 2833
2751 @override 2834 @override
2752 visitParenthesizedExpression(ParenthesizedExpression node) => 2835 visitParenthesizedExpression(ParenthesizedExpression node) =>
2753 // The printer handles precedence so we don't need to. 2836 // The printer handles precedence so we don't need to.
2754 _visit(node.expression); 2837 _visit(node.expression);
2755 2838
2756 @override 2839 @override
2757 visitFormalParameter(FormalParameter node) { 2840 visitFormalParameter(FormalParameter node) => _emitFormalParameter(node);
2758 var id = visitSimpleIdentifier(node.identifier); 2841
2842 _emitFormalParameter(FormalParameter node, {bool allowType: true}) {
2843 var id = _emitSimpleIdentifier(node.identifier, allowType: allowType);
2759 2844
2760 var isRestArg = findAnnotation(node.element, isJsRestAnnotation) != null; 2845 var isRestArg = findAnnotation(node.element, isJsRestAnnotation) != null;
2761 return isRestArg ? new JS.RestParameter(id) : id; 2846 return isRestArg ? new JS.RestParameter(id) : id;
2762 } 2847 }
2763 2848
2764 @override 2849 @override
2765 JS.This visitThisExpression(ThisExpression node) => new JS.This(); 2850 JS.This visitThisExpression(ThisExpression node) => new JS.This();
2766 2851
2767 @override 2852 @override
2768 JS.Super visitSuperExpression(SuperExpression node) => new JS.Super(); 2853 JS.Super visitSuperExpression(SuperExpression node) => new JS.Super();
(...skipping 539 matching lines...) Expand 10 before | Expand all | Expand 10 after
3308 3393
3309 @override 3394 @override
3310 visitNode(AstNode node) { 3395 visitNode(AstNode node) {
3311 // TODO(jmesserly): verify this is unreachable. 3396 // TODO(jmesserly): verify this is unreachable.
3312 throw 'Unimplemented ${node.runtimeType}: $node'; 3397 throw 'Unimplemented ${node.runtimeType}: $node';
3313 } 3398 }
3314 3399
3315 _visit(AstNode node) { 3400 _visit(AstNode node) {
3316 if (node == null) return null; 3401 if (node == null) return null;
3317 var result = node.accept(this); 3402 var result = node.accept(this);
3318 if (result is JS.Node) result.sourceInformation = node; 3403 if (result is JS.Node) result = annotate(result, node);
3319 return result; 3404 return result;
3320 } 3405 }
3321 3406
3322 // TODO(jmesserly): this will need to be a generic method, if we ever want to 3407 // TODO(jmesserly): this will need to be a generic method, if we ever want to
3323 // self-host strong mode. 3408 // self-host strong mode.
3324 List /*<T>*/ _visitList /*<T>*/ (Iterable<AstNode> nodes) { 3409 List /*<T>*/ _visitList /*<T>*/ (Iterable<AstNode> nodes) {
3325 if (nodes == null) return null; 3410 if (nodes == null) return null;
3326 var result = /*<T>*/ []; 3411 var result = /*<T>*/ [];
3327 for (var node in nodes) result.add(_visit(node)); 3412 for (var node in nodes) result.add(_visit(node));
3328 return result; 3413 return result;
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after
3451 JS.Identifier _libraryName(LibraryElement library) { 3536 JS.Identifier _libraryName(LibraryElement library) {
3452 if (library == currentLibrary) return _exportsVar; 3537 if (library == currentLibrary) return _exportsVar;
3453 if (library.name == 'dart._runtime') return _runtimeLibVar; 3538 if (library.name == 'dart._runtime') return _runtimeLibVar;
3454 return _imports.putIfAbsent( 3539 return _imports.putIfAbsent(
3455 library, () => new JS.TemporaryId(jsLibraryName(library))); 3540 library, () => new JS.TemporaryId(jsLibraryName(library)));
3456 } 3541 }
3457 3542
3458 DartType getStaticType(Expression e) => 3543 DartType getStaticType(Expression e) =>
3459 e.staticType ?? DynamicTypeImpl.instance; 3544 e.staticType ?? DynamicTypeImpl.instance;
3460 3545
3461 @override 3546 JS.Node annotate(JS.Node node, AstNode original, [Element element]) {
3462 String getQualifiedName(TypeDefiningElement type) { 3547 if (options.closure && element != null) {
3463 JS.TemporaryId id = _imports[type.library]; 3548 node = node.withClosureAnnotation(
3464 return id == null ? type.name : '${id.name}.${type.name}'; 3549 closureAnnotationFor(node, original, element, _namedArgTemp.name));
3550 }
3551 return node..sourceInformation = original;
3465 } 3552 }
3466 3553
3467 JS.Node annotate(JS.Node method, ExecutableElement e) =>
3468 options.closure && e != null
3469 ? method.withClosureAnnotation(
3470 closureAnnotationFor(e, _namedArgTemp.name))
3471 : method;
3472
3473 JS.Node annotateDefaultConstructor(JS.Node method, ClassElement e) =>
3474 options.closure && e != null
3475 ? method
3476 .withClosureAnnotation(closureAnnotationForDefaultConstructor(e))
3477 : method;
3478
3479 JS.Node annotateVariable(JS.Node node, VariableElement e) =>
3480 options.closure && e != null
3481 ? node.withClosureAnnotation(closureAnnotationForVariable(e))
3482 : node;
3483
3484 JS.Node annotateTypeDef(JS.Node node, FunctionTypeAliasElement e) =>
3485 options.closure && e != null
3486 ? node.withClosureAnnotation(closureAnnotationForTypeDef(e))
3487 : node;
3488
3489 /// Returns true if this is any kind of object represented by `Number` in JS. 3554 /// Returns true if this is any kind of object represented by `Number` in JS.
3490 /// 3555 ///
3491 /// In practice, this is 4 types: num, int, double, and JSNumber. 3556 /// In practice, this is 4 types: num, int, double, and JSNumber.
3492 /// 3557 ///
3493 /// JSNumber is the type that actually "implements" all numbers, hence it's 3558 /// JSNumber is the type that actually "implements" all numbers, hence it's
3494 /// a subtype of int and double (and num). It's in our "dart:_interceptors". 3559 /// a subtype of int and double (and num). It's in our "dart:_interceptors".
3495 bool _isNumberInJS(DartType t) => rules.isSubtypeOf(t, _types.numType); 3560 bool _isNumberInJS(DartType t) => rules.isSubtypeOf(t, _types.numType);
3496 3561
3497 bool _isObjectGetter(String name) { 3562 bool _isObjectGetter(String name) {
3498 PropertyAccessorElement element = _types.objectType.element.getGetter(name); 3563 PropertyAccessorElement element = _types.objectType.element.getGetter(name);
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after
3624 3689
3625 /// A special kind of element created by the compiler, signifying a temporary 3690 /// A special kind of element created by the compiler, signifying a temporary
3626 /// variable. These objects use instance equality, and should be shared 3691 /// variable. These objects use instance equality, and should be shared
3627 /// everywhere in the tree where they are treated as the same variable. 3692 /// everywhere in the tree where they are treated as the same variable.
3628 class TemporaryVariableElement extends LocalVariableElementImpl { 3693 class TemporaryVariableElement extends LocalVariableElementImpl {
3629 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); 3694 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name);
3630 3695
3631 int get hashCode => identityHashCode(this); 3696 int get hashCode => identityHashCode(this);
3632 bool operator ==(Object other) => identical(this, other); 3697 bool operator ==(Object other) => identical(this, other);
3633 } 3698 }
OLDNEW
« no previous file with comments | « lib/src/closure/closure_annotator.dart ('k') | lib/src/codegen/js_names.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698