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

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: 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
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 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
416 for (var member in node.members) { 422 for (var member in node.members) {
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(
427 _classHeritage(classElem), _emitClassMethods(node, ctors, fields)); 433 new JS.Identifier(type.name),
434 _classHeritage(classElem),
435 _emitClassMethods(node, ctors, fields),
436 _emitTypeArgs(classElem).toList(),
437 _emitFieldDeclarations(classElem, fields, staticFields).toList());
428 438
429 String jsPeerName; 439 String jsPeerName;
430 var jsPeer = findAnnotation(classElem, isJsPeerInterface); 440 var jsPeer = findAnnotation(classElem, isJsPeerInterface);
431 if (jsPeer != null) { 441 if (jsPeer != null) {
432 jsPeerName = 442 jsPeerName =
433 getConstantField(jsPeer, 'name', types.stringType)?.toStringValue(); 443 getConstantField(jsPeer, 'name', types.stringType)?.toStringValue();
434 } 444 }
435 445
436 var body = _finishClassMembers(classElem, classExpr, ctors, fields, 446 var body = _finishClassMembers(classElem, classExpr, ctors, fields,
437 staticFields, methods, node.metadata, jsPeerName); 447 staticFields, methods, node.metadata, jsPeerName);
(...skipping 12 matching lines...) Expand all
450 // want to support construction of instances with generic types other 460 // want to support construction of instances with generic types other
451 // than dynamic. See issue #154 for Array and List<E> related bug. 461 // than dynamic. See issue #154 for Array and List<E> related bug.
452 var copyMembers = js.statement( 462 var copyMembers = js.statement(
453 'dart.registerExtension(dart.global.#, #);', 463 'dart.registerExtension(dart.global.#, #);',
454 [_propertyName(jsPeerName), classElem.name]); 464 [_propertyName(jsPeerName), classElem.name]);
455 return _statement([result, copyMembers]); 465 return _statement([result, copyMembers]);
456 } 466 }
457 return result; 467 return result;
458 } 468 }
459 469
470 Iterable<JS.Identifier> _emitTypeArgs(TypeParameterizedElement e) sync* {
471 if (!options.closure) return;
472 for (var typeParam in e.typeParameters) {
473 yield new JS.Identifier(typeParam.name);
474 }
475 }
476
477 /// Emit field declarations for TypeScript & Closure's ES6_TYPED
478 /// (e.g. `class Foo { i: string; }`)
479 Iterable<JS.VariableDeclarationList> _emitFieldDeclarations(
480 ClassElement classElem,
481 List<FieldDeclaration> fields,
482 List<FieldDeclaration> staticFields) sync* {
483 if (!options.closure) return;
484
485 makeInitialization(VariableDeclaration decl) =>
486 new JS.VariableInitialization(
487 new JS.Identifier(
488 // TODO(ochafik): use a refactored _emitMemberName instead.
489 decl.name.name,
490 type: emitTypeRef(decl.element.type)),
491 null);
492
493 for (var field in fields) {
494 yield new JS.VariableDeclarationList(
495 null, field.fields.variables.map(makeInitialization).toList());
496 }
497 for (var field in staticFields) {
498 yield new JS.VariableDeclarationList(
499 'static', field.fields.variables.map(makeInitialization).toList());
500 }
501 }
502
460 @override 503 @override
461 JS.Statement visitEnumDeclaration(EnumDeclaration node) { 504 JS.Statement visitEnumDeclaration(EnumDeclaration node) {
462 var element = node.element; 505 var element = node.element;
463 var type = element.type; 506 var type = element.type;
464 var name = js.string(type.name); 507 var name = js.string(type.name);
465 var id = new JS.Identifier(type.name); 508 var id = new JS.Identifier(type.name);
466 509
467 // Generate a class per section 13 of the spec. 510 // Generate a class per section 13 of the spec.
468 // TODO(vsm): Generate any accompanying metadata 511 // TODO(vsm): Generate any accompanying metadata
469 512
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after
682 node.selector is JS.LiteralString; 725 node.selector is JS.LiteralString;
683 726
684 /// Workaround for Closure: super classes must be qualified paths. 727 /// Workaround for Closure: super classes must be qualified paths.
685 JS.Statement _emitClassHeritageWorkaround(JS.ClassExpression cls) { 728 JS.Statement _emitClassHeritageWorkaround(JS.ClassExpression cls) {
686 if (options.closure && 729 if (options.closure &&
687 cls.heritage != null && 730 cls.heritage != null &&
688 !_isQualifiedPath(cls.heritage)) { 731 !_isQualifiedPath(cls.heritage)) {
689 var superVar = new JS.TemporaryId(cls.name.name + r'$super'); 732 var superVar = new JS.TemporaryId(cls.name.name + r'$super');
690 return _statement([ 733 return _statement([
691 js.statement('const # = #;', [superVar, cls.heritage]), 734 js.statement('const # = #;', [superVar, cls.heritage]),
692 new JS.ClassDeclaration( 735 new JS.ClassDeclaration(new JS.ClassExpression(
693 new JS.ClassExpression(cls.name, superVar, cls.methods)) 736 cls.name, superVar, cls.methods, cls.typeArgs, 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 // Factory constructors are essentially static methods. 1013 // Factory constructors are essentially static methods.
967 if (node.factoryKeyword != null) { 1014 if (node.factoryKeyword != null) {
968 var body = <JS.Statement>[]; 1015 var body = <JS.Statement>[];
969 var init = _emitArgumentInitializers(node, constructor: true); 1016 var init = _emitArgumentInitializers(node, constructor: true);
970 if (init != null) body.add(init); 1017 if (init != null) body.add(init);
971 body.add(_visit(node.body)); 1018 body.add(_visit(node.body));
972 var fun = new JS.Fun( 1019 var fun = new JS.Fun(
973 _visit(node.parameters) as List<JS.Parameter>, new JS.Block(body)); 1020 _visit(node.parameters) as List<JS.Parameter>, new JS.Block(body),
1021 returnType: returnType);
974 return annotate( 1022 return annotate(
975 new JS.Method(name, fun, isStatic: true)..sourceInformation = node, 1023 new JS.Method(name, fun, isStatic: true), node, node.element);
976 node.element);
977 } 1024 }
978 1025
979 // Code generation for Object's constructor. 1026 // Code generation for Object's constructor.
980 JS.Block body; 1027 JS.Block body;
981 if (isObject && 1028 if (isObject &&
982 node.body is EmptyFunctionBody && 1029 node.body is EmptyFunctionBody &&
983 node.constKeyword != null && 1030 node.constKeyword != null &&
984 node.name == null) { 1031 node.name == null) {
985 // Implements Dart constructor behavior. Because of V8 `super` 1032 // Implements Dart constructor behavior. Because of V8 `super`
986 // [constructor restrictions] 1033 // [constructor restrictions]
(...skipping 15 matching lines...) Expand all
1002 return result === void 0 ? this : result; 1049 return result === void 0 ? this : result;
1003 }''') as JS.Block; 1050 }''') as JS.Block;
1004 } else { 1051 } else {
1005 body = _emitConstructorBody(node, fields); 1052 body = _emitConstructorBody(node, fields);
1006 } 1053 }
1007 1054
1008 // We generate constructors as initializer methods in the class; 1055 // We generate constructors as initializer methods in the class;
1009 // this allows use of `super` for instance methods/properties. 1056 // this allows use of `super` for instance methods/properties.
1010 // It also avoids V8 restrictions on `super` in default constructors. 1057 // It also avoids V8 restrictions on `super` in default constructors.
1011 return annotate( 1058 return annotate(
1012 new JS.Method(name, 1059 new JS.Method(
1013 new JS.Fun(_visit(node.parameters) as List<JS.Parameter>, body)) 1060 name,
1014 ..sourceInformation = node, 1061 new JS.Fun(_visit(node.parameters) as List<JS.Parameter>, body,
1062 returnType: returnType)),
1063 node,
1015 node.element); 1064 node.element);
1016 } 1065 }
1017 1066
1018 JS.Expression _constructorName(ConstructorElement ctor) { 1067 JS.Expression _constructorName(ConstructorElement ctor) {
1019 var name = ctor.name; 1068 var name = ctor.name;
1020 if (name != '') { 1069 if (name != '') {
1021 return _emitMemberName(name, isStatic: true); 1070 return _emitMemberName(name, isStatic: true);
1022 } 1071 }
1023 1072
1024 // Factory default constructors use `new` as their name, for readability 1073 // Factory default constructors use `new` as their name, for readability
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
1101 1150
1102 if (superCtor == null) { 1151 if (superCtor == null) {
1103 print('Error generating: ${element.displayName}'); 1152 print('Error generating: ${element.displayName}');
1104 } 1153 }
1105 if (superCtor.name == '' && !_shouldCallUnnamedSuperCtor(element)) { 1154 if (superCtor.name == '' && !_shouldCallUnnamedSuperCtor(element)) {
1106 return null; 1155 return null;
1107 } 1156 }
1108 1157
1109 var name = _constructorName(superCtor); 1158 var name = _constructorName(superCtor);
1110 var args = node != null ? _visit(node.argumentList) : []; 1159 var args = node != null ? _visit(node.argumentList) : [];
1111 return js.statement('super.#(#);', [name, args])..sourceInformation = node; 1160 return annotate(js.statement('super.#(#);', [name, args]), node);
1112 } 1161 }
1113 1162
1114 bool _shouldCallUnnamedSuperCtor(ClassElement e) { 1163 bool _shouldCallUnnamedSuperCtor(ClassElement e) {
1115 var supertype = e.supertype; 1164 var supertype = e.supertype;
1116 if (supertype == null) return false; 1165 if (supertype == null) return false;
1117 if (_hasUnnamedConstructor(supertype.element)) return true; 1166 if (_hasUnnamedConstructor(supertype.element)) return true;
1118 for (var mixin in e.mixins) { 1167 for (var mixin in e.mixins) {
1119 if (_hasUnnamedConstructor(mixin.element)) return true; 1168 if (_hasUnnamedConstructor(mixin.element)) return true;
1120 } 1169 }
1121 return false; 1170 return false;
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
1153 fields[element as FieldElement] = _visitInitializer(fieldNode); 1202 fields[element as FieldElement] = _visitInitializer(fieldNode);
1154 } 1203 }
1155 } 1204 }
1156 } 1205 }
1157 1206
1158 // Initialize fields from `this.fieldName` parameters. 1207 // Initialize fields from `this.fieldName` parameters.
1159 if (ctor != null) { 1208 if (ctor != null) {
1160 for (var p in ctor.parameters.parameters) { 1209 for (var p in ctor.parameters.parameters) {
1161 var element = p.element; 1210 var element = p.element;
1162 if (element is FieldFormalParameterElement) { 1211 if (element is FieldFormalParameterElement) {
1163 fields[element.field] = _visit(p); 1212 fields[element.field] = _emitFormalParameter(p, allowType: false);
1164 } 1213 }
1165 } 1214 }
1166 1215
1167 // Run constructor field initializers such as `: foo = bar.baz` 1216 // Run constructor field initializers such as `: foo = bar.baz`
1168 for (var init in ctor.initializers) { 1217 for (var init in ctor.initializers) {
1169 if (init is ConstructorFieldInitializer) { 1218 if (init is ConstructorFieldInitializer) {
1170 fields[init.fieldName.staticElement as FieldElement] = 1219 fields[init.fieldName.staticElement as FieldElement] =
1171 _visit(init.expression); 1220 _visit(init.expression);
1172 } 1221 }
1173 } 1222 }
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
1209 /// Emits argument initializers, which handles optional/named args, as well 1258 /// Emits argument initializers, which handles optional/named args, as well
1210 /// as generic type checks needed due to our covariance. 1259 /// as generic type checks needed due to our covariance.
1211 JS.Statement _emitArgumentInitializers(node, {bool constructor: false}) { 1260 JS.Statement _emitArgumentInitializers(node, {bool constructor: false}) {
1212 // Constructor argument initializers are emitted earlier in the code, rather 1261 // Constructor argument initializers are emitted earlier in the code, rather
1213 // than always when we visit the function body, so we control it explicitly. 1262 // than always when we visit the function body, so we control it explicitly.
1214 if (node is ConstructorDeclaration != constructor) return null; 1263 if (node is ConstructorDeclaration != constructor) return null;
1215 1264
1216 var parameters = _parametersOf(node); 1265 var parameters = _parametersOf(node);
1217 if (parameters == null) return null; 1266 if (parameters == null) return null;
1218 1267
1268 var destructure = _canDestructureParams(parameters);
1219 var body = <JS.Statement>[]; 1269 var body = <JS.Statement>[];
1220 for (var param in parameters.parameters) { 1270 for (var param in parameters.parameters) {
1221 var jsParam = _visit(param.identifier); 1271 var jsParam = _emitSimpleIdentifier(param.identifier, allowType: false);
1222 1272
1223 if (param.kind == ParameterKind.NAMED) { 1273 if (param.kind == ParameterKind.NAMED) {
1224 if (!_isDestructurableNamedParam(param)) { 1274 if (!destructure) {
1225 // Parameters will be passed using their real names, not the (possibly 1275 // Parameters will be passed using their real names, not the (possibly
1226 // renamed) local variable. 1276 // renamed) local variable.
1227 var paramName = js.string(param.identifier.name, "'"); 1277 var paramName = js.string(param.identifier.name, "'");
1228 1278
1229 // TODO(ochafik): Fix `'prop' in obj` to please Closure's renaming. 1279 // TODO(ochafik): Fix `'prop' in obj` to please Closure's renaming.
1230 body.add(js.statement('let # = # && # in # ? #.# : #;', [ 1280 body.add(js.statement('let # = # && # in # ? #.# : #;', [
1231 jsParam, 1281 jsParam,
1232 _namedArgTemp, 1282 _namedArgTemp,
1233 paramName, 1283 paramName,
1234 _namedArgTemp, 1284 _namedArgTemp,
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
1269 } 1319 }
1270 1320
1271 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { 1321 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) {
1272 if (node.isAbstract || _externalOrNative(node)) { 1322 if (node.isAbstract || _externalOrNative(node)) {
1273 return null; 1323 return null;
1274 } 1324 }
1275 1325
1276 var params = _visit(node.parameters) as List<JS.Parameter>; 1326 var params = _visit(node.parameters) as List<JS.Parameter>;
1277 if (params == null) params = <JS.Parameter>[]; 1327 if (params == null) params = <JS.Parameter>[];
1278 1328
1279 JS.Fun fn = _emitFunctionBody(params, node.body); 1329 var typeArgs = _emitTypeArgs(node.element).toList();
1330 var returnType = emitTypeRef(node.element.returnType);
1331 JS.Fun fn = _emitFunctionBody(params, node.body, typeArgs, returnType);
1280 if (node.operatorKeyword != null && 1332 if (node.operatorKeyword != null &&
1281 node.name.name == '[]=' && 1333 node.name.name == '[]=' &&
1282 params.isNotEmpty) { 1334 params.isNotEmpty) {
1283 // []= methods need to return the value. We could also address this at 1335 // []= methods need to return the value. We could also address this at
1284 // call sites, but it's cleaner to instead transform the operator method. 1336 // call sites, but it's cleaner to instead transform the operator method.
1285 var returnValue = new JS.Return(params.last); 1337 var returnValue = new JS.Return(params.last);
1286 var body = fn.body; 1338 var body = fn.body;
1287 if (JS.Return.foundIn(fn)) { 1339 if (JS.Return.foundIn(fn)) {
1288 // If a return is inside body, transform `(params) { body }` to 1340 // If a return is inside body, transform `(params) { body }` to
1289 // `(params) { (() => { body })(); return value; }`. 1341 // `(params) { (() => { body })(); return value; }`.
1290 // TODO(jmesserly): we could instead generate the return differently, 1342 // TODO(jmesserly): we could instead generate the return differently,
1291 // and avoid the immediately invoked function. 1343 // and avoid the immediately invoked function.
1292 body = new JS.Call(new JS.ArrowFun([], fn.body), []).toStatement(); 1344 body = new JS.Call(new JS.ArrowFun([], fn.body), []).toStatement();
1293 } 1345 }
1294 // Rewrite the function to include the return. 1346 // Rewrite the function to include the return.
1295 fn = new JS.Fun(fn.params, new JS.Block([body, returnValue])) 1347 fn = new JS.Fun(fn.params, new JS.Block([body, returnValue]),
1296 ..sourceInformation = fn.sourceInformation; 1348 typeArgs: fn.typeArgs,
1349 returnType: fn.returnType)..sourceInformation = fn.sourceInformation;
1297 } 1350 }
1298 1351
1299 return annotate( 1352 return annotate(
1300 new JS.Method(_elementMemberName(node.element), fn, 1353 new JS.Method(_elementMemberName(node.element), fn,
1301 isGetter: node.isGetter, 1354 isGetter: node.isGetter,
1302 isSetter: node.isSetter, 1355 isSetter: node.isSetter,
1303 isStatic: node.isStatic), 1356 isStatic: node.isStatic),
1357 node,
1304 node.element); 1358 node.element);
1305 } 1359 }
1306 1360
1307 /// Returns the name value of the `JSExportName` annotation (when compiling 1361 /// Returns the name value of the `JSExportName` annotation (when compiling
1308 /// the SDK), or `null` if there's none. This is used to control the name 1362 /// the SDK), or `null` if there's none. This is used to control the name
1309 /// under which functions are compiled and exported. 1363 /// under which functions are compiled and exported.
1310 String _getJSExportName(Element e) { 1364 String _getJSExportName(Element e) {
1311 if (!currentLibrary.source.isInSystemLibrary) { 1365 if (!e.source.isInSystemLibrary) {
1312 return null; 1366 return null;
1313 } 1367 }
1314 var jsName = findAnnotation(e, isJSExportNameAnnotation); 1368 var jsName = findAnnotation(e, isJSExportNameAnnotation);
1315 return getConstantField(jsName, 'name', types.stringType)?.toStringValue(); 1369 return getConstantField(jsName, 'name', types.stringType)?.toStringValue();
1316 } 1370 }
1317 1371
1318 @override 1372 @override
1319 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { 1373 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) {
1320 assert(node.parent is CompilationUnit); 1374 assert(node.parent is CompilationUnit);
1321 1375
(...skipping 11 matching lines...) Expand all
1333 var name = node.name.name; 1387 var name = node.name.name;
1334 1388
1335 var fn = _visit(node.functionExpression); 1389 var fn = _visit(node.functionExpression);
1336 1390
1337 if (currentLibrary.source.isInSystemLibrary && 1391 if (currentLibrary.source.isInSystemLibrary &&
1338 _isInlineJSFunction(node.functionExpression)) { 1392 _isInlineJSFunction(node.functionExpression)) {
1339 fn = _simplifyPassThroughArrowFunCallBody(fn); 1393 fn = _simplifyPassThroughArrowFunCallBody(fn);
1340 } 1394 }
1341 1395
1342 var id = new JS.Identifier(name); 1396 var id = new JS.Identifier(name);
1343 body.add(annotate(new JS.FunctionDeclaration(id, fn), node.element)); 1397 body.add(annotate(new JS.FunctionDeclaration(id, fn), node, node.element));
1344 if (!_isDartRuntime) { 1398 if (!_isDartRuntime) {
1345 body.add(_emitFunctionTagged(id, node.element.type, topLevel: true) 1399 body.add(_emitFunctionTagged(id, node.element.type, topLevel: true)
1346 .toStatement()); 1400 .toStatement());
1347 } 1401 }
1348 1402
1349 if (isPublic(name)) { 1403 if (isPublic(name)) {
1350 _addExport(name, _getJSExportName(node.element) ?? name); 1404 _addExport(name, _getJSExportName(node.element) ?? name);
1351 } 1405 }
1352 return _statement(body); 1406 return _statement(body);
1353 } 1407 }
(...skipping 20 matching lines...) Expand all
1374 // Note: this allows silently passing args through to the body, which only 1428 // Note: this allows silently passing args through to the body, which only
1375 // works if we don't do weird renamings of Dart params. 1429 // works if we don't do weird renamings of Dart params.
1376 JS.Fun _simplifyPassThroughArrowFunCallBody(JS.Fun fn) { 1430 JS.Fun _simplifyPassThroughArrowFunCallBody(JS.Fun fn) {
1377 if (fn.body is JS.Block && fn.body.statements.length == 1) { 1431 if (fn.body is JS.Block && fn.body.statements.length == 1) {
1378 var stat = fn.body.statements.single; 1432 var stat = fn.body.statements.single;
1379 if (stat is JS.Return && stat.value is JS.Call) { 1433 if (stat is JS.Return && stat.value is JS.Call) {
1380 JS.Call call = stat.value; 1434 JS.Call call = stat.value;
1381 if (call.target is JS.ArrowFun && call.arguments.isEmpty) { 1435 if (call.target is JS.ArrowFun && call.arguments.isEmpty) {
1382 JS.ArrowFun innerFun = call.target; 1436 JS.ArrowFun innerFun = call.target;
1383 if (innerFun.params.isEmpty) { 1437 if (innerFun.params.isEmpty) {
1384 return new JS.Fun(fn.params, innerFun.body); 1438 return new JS.Fun(fn.params, innerFun.body,
1439 typeArgs: fn.typeArgs, returnType: fn.returnType);
1385 } 1440 }
1386 } 1441 }
1387 } 1442 }
1388 } 1443 }
1389 return fn; 1444 return fn;
1390 } 1445 }
1391 1446
1392 JS.Method _emitTopLevelProperty(FunctionDeclaration node) { 1447 JS.Method _emitTopLevelProperty(FunctionDeclaration node) {
1393 var name = node.name.name; 1448 var name = node.name.name;
1394 return annotate( 1449 return annotate(
1395 new JS.Method(_propertyName(name), _visit(node.functionExpression), 1450 new JS.Method(_propertyName(name), _visit(node.functionExpression),
1396 isGetter: node.isGetter, isSetter: node.isSetter), 1451 isGetter: node.isGetter, isSetter: node.isSetter),
1452 node,
1397 node.element); 1453 node.element);
1398 } 1454 }
1399 1455
1400 bool _executesAtTopLevel(AstNode node) { 1456 bool _executesAtTopLevel(AstNode node) {
1401 var ancestor = node.getAncestor((n) => 1457 var ancestor = node.getAncestor((n) =>
1402 n is FunctionBody || 1458 n is FunctionBody ||
1403 (n is FieldDeclaration && n.staticKeyword == null) || 1459 (n is FieldDeclaration && n.staticKeyword == null) ||
1404 (n is ConstructorDeclaration && n.constKeyword == null)); 1460 (n is ConstructorDeclaration && n.constKeyword == null));
1405 return ancestor == null; 1461 return ancestor == null;
1406 } 1462 }
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
1439 throw 'Function has non function type: $type'; 1495 throw 'Function has non function type: $type';
1440 } 1496 }
1441 1497
1442 @override 1498 @override
1443 JS.Expression visitFunctionExpression(FunctionExpression node) { 1499 JS.Expression visitFunctionExpression(FunctionExpression node) {
1444 var params = _visit(node.parameters) as List<JS.Parameter>; 1500 var params = _visit(node.parameters) as List<JS.Parameter>;
1445 if (params == null) params = <JS.Parameter>[]; 1501 if (params == null) params = <JS.Parameter>[];
1446 1502
1447 var parent = node.parent; 1503 var parent = node.parent;
1448 var inStmt = parent.parent is FunctionDeclarationStatement; 1504 var inStmt = parent.parent is FunctionDeclarationStatement;
1505 var typeArgs = _emitTypeArgs(node.element).toList();
1506 var returnType = emitTypeRef(node.element.returnType);
1449 if (parent is FunctionDeclaration) { 1507 if (parent is FunctionDeclaration) {
1450 return _emitFunctionBody(params, node.body); 1508 return _emitFunctionBody(params, node.body, typeArgs, returnType);
1451 } else { 1509 } else {
1452 // Chrome Canary does not accept default values with destructuring in 1510 // Chrome Canary does not accept default values with destructuring in
1453 // arrow functions yet (e.g. `({a} = {}) => 1`) but happily accepts them 1511 // arrow functions yet (e.g. `({a} = {}) => 1`) but happily accepts them
1454 // with regular functions (e.g. `function({a} = {}) { return 1 }`). 1512 // with regular functions (e.g. `function({a} = {}) { return 1 }`).
1455 // Note that Firefox accepts both syntaxes just fine. 1513 // Note that Firefox accepts both syntaxes just fine.
1456 // TODO(ochafik): Simplify this code when Chrome Canary catches up. 1514 // TODO(ochafik): Simplify this code when Chrome Canary catches up.
1457 var canUseArrowFun = !node.parameters.parameters.any(_isNamedParam); 1515 var canUseArrowFun = !node.parameters.parameters.any(_isNamedParam);
1458 1516
1459 String code = canUseArrowFun ? '(#) => #' : 'function(#) { return # }';
1460 JS.Node jsBody; 1517 JS.Node jsBody;
1461 var body = node.body; 1518 var body = node.body;
1462 if (body.isGenerator || body.isAsynchronous) { 1519 if (body.isGenerator || body.isAsynchronous) {
1463 jsBody = _emitGeneratorFunctionBody(params, body); 1520 jsBody = _emitGeneratorFunctionBody(params, body, returnType);
1464 } else if (body is ExpressionFunctionBody) { 1521 } else if (body is ExpressionFunctionBody) {
1465 jsBody = _visit(body.expression); 1522 jsBody = _visit(body.expression);
1466 } else { 1523 } else {
1467 code = canUseArrowFun ? '(#) => { #; }' : 'function(#) { #; }';
1468 jsBody = _visit(body); 1524 jsBody = _visit(body);
1469 } 1525 }
1470 var clos = js.call(code, [params, jsBody]); 1526 if (jsBody is JS.Expression && !canUseArrowFun) {
1527 jsBody = js.statement("{ return #; }", [jsBody]);
1528 }
1529 var clos = canUseArrowFun
1530 ? new JS.ArrowFun(params, jsBody,
1531 typeArgs: typeArgs, returnType: returnType)
1532 : new JS.Fun(params, jsBody,
1533 typeArgs: typeArgs, returnType: returnType);
1471 if (!inStmt) { 1534 if (!inStmt) {
1472 var type = getStaticType(node); 1535 var type = getStaticType(node);
1473 return _emitFunctionTagged(clos, type, 1536 return _emitFunctionTagged(clos, type,
1474 topLevel: _executesAtTopLevel(node)); 1537 topLevel: _executesAtTopLevel(node));
1475 } 1538 }
1476 return clos; 1539 return clos;
1477 } 1540 }
1478 } 1541 }
1479 1542
1480 JS.Fun _emitFunctionBody(List<JS.Parameter> params, FunctionBody body) { 1543 JS.Fun _emitFunctionBody(List<JS.Parameter> params, FunctionBody body,
1544 List<JS.Identifier> typeArgs, JS.TypeRef returnType) {
1481 // sync*, async, async* 1545 // sync*, async, async*
1482 if (body.isAsynchronous || body.isGenerator) { 1546 if (body.isAsynchronous || body.isGenerator) {
1483 return new JS.Fun( 1547 return new JS.Fun(
1484 params, 1548 params,
1485 js.statement( 1549 js.statement('{ return #; }',
1486 '{ return #; }', [_emitGeneratorFunctionBody(params, body)])); 1550 [_emitGeneratorFunctionBody(params, body, returnType)]),
1551 returnType: returnType);
1487 } 1552 }
1488 // normal function (sync) 1553 // normal function (sync)
1489 return new JS.Fun(params, _visit(body)); 1554 return new JS.Fun(params, _visit(body),
1555 typeArgs: typeArgs, returnType: returnType);
1490 } 1556 }
1491 1557
1492 JS.Expression _emitGeneratorFunctionBody( 1558 JS.Expression _emitGeneratorFunctionBody(
1493 List<JS.Parameter> params, FunctionBody body) { 1559 List<JS.Parameter> params, FunctionBody body, JS.TypeRef returnType) {
1494 var kind = body.isSynchronous ? 'sync' : 'async'; 1560 var kind = body.isSynchronous ? 'sync' : 'async';
1495 if (body.isGenerator) kind += 'Star'; 1561 if (body.isGenerator) kind += 'Star';
1496 1562
1497 // Transforms `sync*` `async` and `async*` function bodies 1563 // Transforms `sync*` `async` and `async*` function bodies
1498 // using ES6 generators. 1564 // using ES6 generators.
1499 // 1565 //
1500 // `sync*` wraps a generator in a Dart Iterable<T>: 1566 // `sync*` wraps a generator in a Dart Iterable<T>:
1501 // 1567 //
1502 // function name(<args>) { 1568 // function name(<args>) {
1503 // return dart.syncStar(function*(<args>) { 1569 // return dart.syncStar(function*(<args>) {
(...skipping 23 matching lines...) Expand all
1527 // runtime/_generators.js has an example of what the code is generated as. 1593 // runtime/_generators.js has an example of what the code is generated as.
1528 var savedController = _asyncStarController; 1594 var savedController = _asyncStarController;
1529 List jsParams; 1595 List jsParams;
1530 if (kind == 'asyncStar') { 1596 if (kind == 'asyncStar') {
1531 _asyncStarController = new JS.TemporaryId('stream'); 1597 _asyncStarController = new JS.TemporaryId('stream');
1532 jsParams = [_asyncStarController]..addAll(params); 1598 jsParams = [_asyncStarController]..addAll(params);
1533 } else { 1599 } else {
1534 _asyncStarController = null; 1600 _asyncStarController = null;
1535 jsParams = params; 1601 jsParams = params;
1536 } 1602 }
1537 JS.Expression gen = new JS.Fun(jsParams, _visit(body), isGenerator: true); 1603 JS.Expression gen = new JS.Fun(jsParams, _visit(body),
1604 isGenerator: true, returnType: returnType);
1538 if (JS.This.foundIn(gen)) { 1605 if (JS.This.foundIn(gen)) {
1539 gen = js.call('#.bind(this)', gen); 1606 gen = js.call('#.bind(this)', gen);
1540 } 1607 }
1541 _asyncStarController = savedController; 1608 _asyncStarController = savedController;
1542 1609
1543 var T = _emitTypeName(_getExpectedReturnType(body)); 1610 var T = _emitTypeName(_getExpectedReturnType(body));
1544 return js.call('dart.#(#)', [ 1611 return js.call('dart.#(#)', [
1545 kind, 1612 kind,
1546 [gen, T]..addAll(params) 1613 [gen, T]..addAll(params)
1547 ]); 1614 ]);
1548 } 1615 }
1549 1616
1550 @override 1617 @override
1551 JS.Statement visitFunctionDeclarationStatement( 1618 JS.Statement visitFunctionDeclarationStatement(
1552 FunctionDeclarationStatement node) { 1619 FunctionDeclarationStatement node) {
1553 var func = node.functionDeclaration; 1620 var func = node.functionDeclaration;
1554 if (func.isGetter || func.isSetter) { 1621 if (func.isGetter || func.isSetter) {
1555 return js.comment('Unimplemented function get/set statement: $node'); 1622 return js.comment('Unimplemented function get/set statement: $node');
1556 } 1623 }
1557 1624
1558 var fn = _visit(func.functionExpression); 1625 var fn = _visit(func.functionExpression);
1559 1626
1560 var name = new JS.Identifier(func.name.name); 1627 var name = new JS.Identifier(func.name.name);
1561 JS.Statement declareFn; 1628 JS.Statement declareFn;
1562 if (JS.This.foundIn(fn)) { 1629 if (JS.This.foundIn(fn)) {
1563 declareFn = js.statement('const # = #.bind(this);', [name, fn]); 1630 declareFn = js.statement('const # = #.bind(this);', [name, fn]);
1564 } else { 1631 } else {
1565 declareFn = new JS.FunctionDeclaration(name, fn); 1632 declareFn = new JS.FunctionDeclaration(name, fn);
1566 } 1633 }
1567 declareFn = annotate(declareFn, node.functionDeclaration.element); 1634 declareFn = annotate(declareFn, node, node.functionDeclaration.element);
1568 1635
1569 return new JS.Block([ 1636 return new JS.Block([
1570 declareFn, 1637 declareFn,
1571 _emitFunctionTagged(name, func.element.type).toStatement() 1638 _emitFunctionTagged(name, func.element.type).toStatement()
1572 ]); 1639 ]);
1573 } 1640 }
1574 1641
1642 @override
1643 JS.Expression visitSimpleIdentifier(SimpleIdentifier node) =>
1644 _emitSimpleIdentifier(node);
1645
1575 /// Writes a simple identifier. This can handle implicit `this` as well as 1646 /// Writes a simple identifier. This can handle implicit `this` as well as
1576 /// going through the qualified library name if necessary. 1647 /// going through the qualified library name if necessary.
1577 @override 1648 JS.Expression _emitSimpleIdentifier(SimpleIdentifier node,
1578 JS.Expression visitSimpleIdentifier(SimpleIdentifier node) { 1649 {bool allowType: false}) {
1579 var accessor = node.staticElement; 1650 var accessor = node.staticElement;
1580 if (accessor == null) { 1651 if (accessor == null) {
1581 return js.commentExpression( 1652 return js.commentExpression(
1582 'Unimplemented unknown name', new JS.Identifier(node.name)); 1653 'Unimplemented unknown name', new JS.Identifier(node.name));
1583 } 1654 }
1584 1655
1585 // Get the original declaring element. If we had a property accessor, this 1656 // Get the original declaring element. If we had a property accessor, this
1586 // indirects back to a (possibly synthetic) field. 1657 // indirects back to a (possibly synthetic) field.
1587 var element = accessor; 1658 var element = accessor;
1588 if (accessor is PropertyAccessorElement) element = accessor.variable; 1659 if (accessor is PropertyAccessorElement) element = accessor.variable;
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
1637 } 1708 }
1638 1709
1639 if (element is TemporaryVariableElement) { 1710 if (element is TemporaryVariableElement) {
1640 if (name[0] == '#') { 1711 if (name[0] == '#') {
1641 return new JS.InterpolatedExpression(name.substring(1)); 1712 return new JS.InterpolatedExpression(name.substring(1));
1642 } else { 1713 } else {
1643 return _getTemp(element, name); 1714 return _getTemp(element, name);
1644 } 1715 }
1645 } 1716 }
1646 1717
1647 return new JS.Identifier(name); 1718 return annotate(
1719 new JS.Identifier(name,
1720 type: allowType ? emitTypeRef(node.bestType) : null),
1721 node);
1648 } 1722 }
1649 1723
1650 JS.TemporaryId _getTemp(Element key, String name) => 1724 JS.TemporaryId _getTemp(Element key, String name) =>
1651 _temps.putIfAbsent(key, () => new JS.TemporaryId(name)); 1725 _temps.putIfAbsent(key, () => new JS.TemporaryId(name));
1652 1726
1653 List<Annotation> _parameterMetadata(FormalParameter p) => 1727 List<Annotation> _parameterMetadata(FormalParameter p) =>
1654 (p is NormalFormalParameter) 1728 (p is NormalFormalParameter)
1655 ? p.metadata 1729 ? p.metadata
1656 : (p as DefaultFormalParameter).parameter.metadata; 1730 : (p as DefaultFormalParameter).parameter.metadata;
1657 1731
(...skipping 379 matching lines...) Expand 10 before | Expand all | Expand 10 after
2037 @override 2111 @override
2038 JS.Property visitNamedExpression(NamedExpression node) { 2112 JS.Property visitNamedExpression(NamedExpression node) {
2039 assert(node.parent is ArgumentList); 2113 assert(node.parent is ArgumentList);
2040 return new JS.Property( 2114 return new JS.Property(
2041 _propertyName(node.name.label.name), _visit(node.expression)); 2115 _propertyName(node.name.label.name), _visit(node.expression));
2042 } 2116 }
2043 2117
2044 bool _isNamedParam(FormalParameter param) => 2118 bool _isNamedParam(FormalParameter param) =>
2045 param.kind == ParameterKind.NAMED; 2119 param.kind == ParameterKind.NAMED;
2046 2120
2047 /// We cannot destructure named params that clash with JS reserved names: 2121 bool _canDestructureParams(FormalParameterList params) =>
2048 /// see discussion in https://github.com/dart-lang/dev_compiler/issues/392. 2122 canDestructureNamedParams(
2049 bool _isDestructurableNamedParam(FormalParameter param) => 2123 params.parameters.where(_isNamedParam).map((p) => p.identifier.name),
2050 _isNamedParam(param) && 2124 options);
2051 !invalidVariableName(param.identifier.name) &&
2052 options.destructureNamedParams;
2053 2125
2054 @override 2126 @override
2055 List<JS.Parameter> visitFormalParameterList(FormalParameterList node) => 2127 List<JS.Parameter> visitFormalParameterList(FormalParameterList node) =>
2056 _emitFormalParameterList(node); 2128 _emitFormalParameterList(node);
2057 2129
2058 List<JS.Parameter> _emitFormalParameterList(FormalParameterList node, 2130 List<JS.Parameter> _emitFormalParameterList(FormalParameterList node,
2059 {bool allowDestructuring: true}) { 2131 {bool allowDestructuring: true}) {
2060 var result = <JS.Parameter>[]; 2132 var result = <JS.Parameter>[];
2061 2133
2062 var namedVars = <JS.DestructuredVariable>[]; 2134 var namedVars = <JS.DestructuredVariable>[];
2063 var destructure = allowDestructuring && 2135 var destructure = allowDestructuring && _canDestructureParams(node);
2064 node.parameters.where(_isNamedParam).every(_isDestructurableNamedParam);
2065 var hasNamedArgsConflictingWithObjectProperties = false; 2136 var hasNamedArgsConflictingWithObjectProperties = false;
2066 var needsOpts = false; 2137 var needsOpts = false;
2067 2138
2068 for (FormalParameter param in node.parameters) { 2139 for (FormalParameter param in node.parameters) {
2069 if (param.kind == ParameterKind.NAMED) { 2140 if (param.kind == ParameterKind.NAMED) {
2070 if (destructure) { 2141 if (destructure) {
2071 if (_jsObjectProperties.contains(param.identifier.name)) { 2142 if (_jsObjectProperties.contains(param.identifier.name)) {
2072 hasNamedArgsConflictingWithObjectProperties = true; 2143 hasNamedArgsConflictingWithObjectProperties = true;
2073 } 2144 }
2074 namedVars.add(new JS.DestructuredVariable( 2145 namedVars.add(new JS.DestructuredVariable(
2075 name: _visit(param.identifier), 2146 name: _emitSimpleIdentifier(param.identifier),
2076 defaultValue: _defaultParamValue(param))); 2147 defaultValue: _defaultParamValue(param)));
2077 } else { 2148 } else {
2078 needsOpts = true; 2149 needsOpts = true;
2079 } 2150 }
2080 } else { 2151 } else {
2081 result.add(_visit(param)); 2152 result.add(_visit(param));
2082 } 2153 }
2083 } 2154 }
2084 2155
2085 if (needsOpts) { 2156 if (needsOpts) {
2086 result.add(_namedArgTemp); 2157 result.add(_namedArgTemp);
2087 } else if (namedVars.isNotEmpty) { 2158 } else if (namedVars.isNotEmpty) {
2088 // Note: `var {valueOf} = {}` extracts `Object.prototype.valueOf`, so 2159 // Note: `var {valueOf} = {}` extracts `Object.prototype.valueOf`, so
2089 // in case there are conflicting names we create an object without 2160 // in case there are conflicting names we create an object without
2090 // any prototype. 2161 // any prototype.
2091 var defaultOpts = hasNamedArgsConflictingWithObjectProperties 2162 var defaultOpts = hasNamedArgsConflictingWithObjectProperties
2092 ? js.call('Object.create(null)') 2163 ? js.call('Object.create(null)')
2093 : js.call('{}'); 2164 : js.call('{}');
2094 result.add(new JS.DestructuredVariable( 2165 result.add(new JS.DestructuredVariable(
2095 structure: new JS.ObjectBindingPattern(namedVars), 2166 structure: new JS.ObjectBindingPattern(namedVars),
2167 type: emitNamedParamsArgType(node.parameterElements),
2096 defaultValue: defaultOpts)); 2168 defaultValue: defaultOpts));
2097 } 2169 }
2098 return result; 2170 return result;
2099 } 2171 }
2100 2172
2101 /// See ES6 spec (and `Object.getOwnPropertyNames(Object.prototype)`): 2173 /// See ES6 spec (and `Object.getOwnPropertyNames(Object.prototype)`):
2102 /// http://www.ecma-international.org/ecma-262/6.0/#sec-properties-of-the-obje ct-prototype-object 2174 /// http://www.ecma-international.org/ecma-262/6.0/#sec-properties-of-the-obje ct-prototype-object
2103 /// http://www.ecma-international.org/ecma-262/6.0/#sec-additional-properties- of-the-object.prototype-object 2175 /// http://www.ecma-international.org/ecma-262/6.0/#sec-additional-properties- of-the-object.prototype-object
2104 static final Set<String> _jsObjectProperties = new Set<String>() 2176 static final Set<String> _jsObjectProperties = new Set<String>()
2105 ..addAll([ 2177 ..addAll([
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after
2217 } 2289 }
2218 2290
2219 @override 2291 @override
2220 visitVariableDeclaration(VariableDeclaration node) { 2292 visitVariableDeclaration(VariableDeclaration node) {
2221 if (node.element is PropertyInducingElement) { 2293 if (node.element is PropertyInducingElement) {
2222 // Static and instance fields are handled elsewhere. 2294 // Static and instance fields are handled elsewhere.
2223 assert(node.element is TopLevelVariableElement); 2295 assert(node.element is TopLevelVariableElement);
2224 return _emitTopLevelField(node); 2296 return _emitTopLevelField(node);
2225 } 2297 }
2226 2298
2227 var name = new JS.Identifier(node.name.name); 2299 var name =
2300 new JS.Identifier(node.name.name, type: emitTypeRef(node.element.type));
2228 return new JS.VariableInitialization(name, _visitInitializer(node)); 2301 return new JS.VariableInitialization(name, _visitInitializer(node));
2229 } 2302 }
2230 2303
2231 bool _isFinalJSDecl(AstNode field) => 2304 bool _isFinalJSDecl(AstNode field) =>
2232 field is VariableDeclaration && 2305 field is VariableDeclaration &&
2233 field.isFinal && 2306 field.isFinal &&
2234 _isJSInvocation(field.initializer); 2307 _isJSInvocation(field.initializer);
2235 2308
2236 /// Try to emit a constant static field. 2309 /// Try to emit a constant static field.
2237 /// 2310 ///
(...skipping 15 matching lines...) Expand all
2253 2326
2254 _loader.startCheckingReferences(); 2327 _loader.startCheckingReferences();
2255 JS.Expression jsInit = _visitInitializer(field); 2328 JS.Expression jsInit = _visitInitializer(field);
2256 bool isLoaded = _loader.finishCheckingReferences(); 2329 bool isLoaded = _loader.finishCheckingReferences();
2257 2330
2258 bool eagerInit = 2331 bool eagerInit =
2259 isLoaded && (field.isConst || _constField.isFieldInitConstant(field)); 2332 isLoaded && (field.isConst || _constField.isFieldInitConstant(field));
2260 2333
2261 var fieldName = field.name.name; 2334 var fieldName = field.name.name;
2262 if (eagerInit && !JS.invalidStaticFieldName(fieldName)) { 2335 if (eagerInit && !JS.invalidStaticFieldName(fieldName)) {
2263 return annotateVariable( 2336 return annotate(
2264 js.statement('#.# = #;', [ 2337 js.statement('#.# = #;', [
2265 classElem.name, 2338 classElem.name,
2266 _emitMemberName(fieldName, isStatic: true), 2339 _emitMemberName(fieldName, isStatic: true),
2267 jsInit 2340 jsInit
2268 ]), 2341 ]),
2342 field,
2269 field.element); 2343 field.element);
2270 } 2344 }
2271 2345
2272 // This means it should be treated as a lazy field. 2346 // This means it should be treated as a lazy field.
2273 // TODO(jmesserly): we're throwing away the initializer expression, 2347 // TODO(jmesserly): we're throwing away the initializer expression,
2274 // which will force us to regenerate it. 2348 // which will force us to regenerate it.
2275 return null; 2349 return null;
2276 } 2350 }
2277 2351
2278 /// Emits a top-level field. 2352 /// Emits a top-level field.
(...skipping 27 matching lines...) Expand all
2306 exportName = _getJSExportName(element) ?? fieldName; 2380 exportName = _getJSExportName(element) ?? fieldName;
2307 } 2381 }
2308 if ((field.isConst && eagerInit && element is TopLevelVariableElement) || 2382 if ((field.isConst && eagerInit && element is TopLevelVariableElement) ||
2309 isJSTopLevel) { 2383 isJSTopLevel) {
2310 // constant fields don't change, so we can generate them as `let` 2384 // constant fields don't change, so we can generate them as `let`
2311 // but add them to the module's exports. However, make sure we generate 2385 // but add them to the module's exports. However, make sure we generate
2312 // anything they depend on first. 2386 // anything they depend on first.
2313 2387
2314 if (isPublic(fieldName)) _addExport(fieldName, exportName); 2388 if (isPublic(fieldName)) _addExport(fieldName, exportName);
2315 var declKeyword = field.isConst || field.isFinal ? 'const' : 'let'; 2389 var declKeyword = field.isConst || field.isFinal ? 'const' : 'let';
2316 return annotateVariable( 2390 return js.statement('#;', [
2317 js.statement( 2391 annotate(
2318 '$declKeyword # = #;', [new JS.Identifier(fieldName), jsInit]), 2392 new JS.VariableDeclarationList(declKeyword, [
2319 field.element); 2393 new JS.VariableInitialization(
2394 new JS.Identifier(fieldName,
2395 type: emitTypeRef(field.element.type)),
2396 jsInit)
2397 ]),
2398 field,
2399 field.element)
2400 ]);
2320 } 2401 }
2321 2402
2322 if (eagerInit && !JS.invalidStaticFieldName(fieldName)) { 2403 if (eagerInit && !JS.invalidStaticFieldName(fieldName)) {
2323 return annotateVariable( 2404 return annotate(js.statement('# = #;', [_visit(field.name), jsInit]),
2324 js.statement('# = #;', [_visit(field.name), jsInit]), field.element); 2405 field, field.element);
2325 } 2406 }
2326 2407
2327 return _emitLazyFields(currentLibrary, [field]); 2408 return _emitLazyFields(currentLibrary, [field]);
2328 } 2409 }
2329 2410
2330 JS.Expression _visitInitializer(VariableDeclaration node) { 2411 JS.Expression _visitInitializer(VariableDeclaration node) {
2331 var value = _visit(node.initializer); 2412 var value = _visit(node.initializer);
2332 // explicitly initialize to null, to avoid getting `undefined`. 2413 // explicitly initialize to null, to avoid getting `undefined`.
2333 // TODO(jmesserly): do this only for vars that aren't definitely assigned. 2414 // TODO(jmesserly): do this only for vars that aren't definitely assigned.
2334 return value ?? new JS.LiteralNull(); 2415 return value ?? new JS.LiteralNull();
2335 } 2416 }
2336 2417
2337 JS.Statement _emitLazyFields( 2418 JS.Statement _emitLazyFields(
2338 Element target, List<VariableDeclaration> fields) { 2419 Element target, List<VariableDeclaration> fields) {
2339 var methods = []; 2420 var methods = [];
2340 for (var node in fields) { 2421 for (var node in fields) {
2341 var name = node.name.name; 2422 var name = node.name.name;
2342 var element = node.element; 2423 var element = node.element;
2343 var access = _emitMemberName(name, isStatic: true); 2424 var access = _emitMemberName(name, isStatic: true);
2344 methods.add(annotate( 2425 methods.add(annotate(
2345 new JS.Method( 2426 new JS.Method(
2346 access, 2427 access,
2347 js.call('function() { return #; }', _visit(node.initializer)) 2428 js.call('function() { return #; }', _visit(node.initializer))
2348 as JS.Fun, 2429 as JS.Fun,
2349 isGetter: true), 2430 isGetter: true),
2431 node,
2350 _findAccessor(element, getter: true))); 2432 _findAccessor(element, getter: true)));
2351 2433
2352 // TODO(jmesserly): currently uses a dummy setter to indicate writable. 2434 // TODO(jmesserly): currently uses a dummy setter to indicate writable.
2353 if (!node.isFinal && !node.isConst) { 2435 if (!node.isFinal && !node.isConst) {
2354 methods.add(annotate( 2436 methods.add(annotate(
2355 new JS.Method(access, js.call('function(_) {}') as JS.Fun, 2437 new JS.Method(access, js.call('function(_) {}') as JS.Fun,
2356 isSetter: true), 2438 isSetter: true),
2439 node,
2357 _findAccessor(element, getter: false))); 2440 _findAccessor(element, getter: false)));
2358 } 2441 }
2359 } 2442 }
2360 2443
2361 JS.Expression objExpr; 2444 JS.Expression objExpr;
2362 if (target is ClassElement) { 2445 if (target is ClassElement) {
2363 objExpr = new JS.Identifier(target.type.name); 2446 objExpr = new JS.Identifier(target.type.name);
2364 } else { 2447 } else {
2365 objExpr = _libraryName(target); 2448 objExpr = _libraryName(target);
2366 } 2449 }
(...skipping 368 matching lines...) Expand 10 before | Expand all | Expand 10 after
2735 _cascadeTarget = savedCascadeTemp; 2818 _cascadeTarget = savedCascadeTemp;
2736 return result; 2819 return result;
2737 } 2820 }
2738 2821
2739 @override 2822 @override
2740 visitParenthesizedExpression(ParenthesizedExpression node) => 2823 visitParenthesizedExpression(ParenthesizedExpression node) =>
2741 // The printer handles precedence so we don't need to. 2824 // The printer handles precedence so we don't need to.
2742 _visit(node.expression); 2825 _visit(node.expression);
2743 2826
2744 @override 2827 @override
2745 visitFormalParameter(FormalParameter node) { 2828 visitFormalParameter(FormalParameter node) => _emitFormalParameter(node);
2746 var id = visitSimpleIdentifier(node.identifier); 2829
2830 _emitFormalParameter(FormalParameter node, {bool allowType: true}) {
2831 var id = _emitSimpleIdentifier(node.identifier, allowType: allowType);
2747 2832
2748 var isRestArg = findAnnotation(node.element, isJsRestAnnotation) != null; 2833 var isRestArg = findAnnotation(node.element, isJsRestAnnotation) != null;
2749 return isRestArg ? new JS.RestParameter(id) : id; 2834 return isRestArg ? new JS.RestParameter(id) : id;
2750 } 2835 }
2751 2836
2752 @override 2837 @override
2753 JS.This visitThisExpression(ThisExpression node) => new JS.This(); 2838 JS.This visitThisExpression(ThisExpression node) => new JS.This();
2754 2839
2755 @override 2840 @override
2756 JS.Super visitSuperExpression(SuperExpression node) => new JS.Super(); 2841 JS.Super visitSuperExpression(SuperExpression node) => new JS.Super();
(...skipping 539 matching lines...) Expand 10 before | Expand all | Expand 10 after
3296 3381
3297 @override 3382 @override
3298 visitNode(AstNode node) { 3383 visitNode(AstNode node) {
3299 // TODO(jmesserly): verify this is unreachable. 3384 // TODO(jmesserly): verify this is unreachable.
3300 throw 'Unimplemented ${node.runtimeType}: $node'; 3385 throw 'Unimplemented ${node.runtimeType}: $node';
3301 } 3386 }
3302 3387
3303 _visit(AstNode node) { 3388 _visit(AstNode node) {
3304 if (node == null) return null; 3389 if (node == null) return null;
3305 var result = node.accept(this); 3390 var result = node.accept(this);
3306 if (result is JS.Node) result.sourceInformation = node; 3391 if (result is JS.Node) result = annotate(result, node);
3307 return result; 3392 return result;
3308 } 3393 }
3309 3394
3310 // TODO(jmesserly): this will need to be a generic method, if we ever want to 3395 // TODO(jmesserly): this will need to be a generic method, if we ever want to
3311 // self-host strong mode. 3396 // self-host strong mode.
3312 List /*<T>*/ _visitList /*<T>*/ (Iterable<AstNode> nodes) { 3397 List /*<T>*/ _visitList /*<T>*/ (Iterable<AstNode> nodes) {
3313 if (nodes == null) return null; 3398 if (nodes == null) return null;
3314 var result = /*<T>*/ []; 3399 var result = /*<T>*/ [];
3315 for (var node in nodes) result.add(_visit(node)); 3400 for (var node in nodes) result.add(_visit(node));
3316 return result; 3401 return result;
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after
3439 JS.Identifier _libraryName(LibraryElement library) { 3524 JS.Identifier _libraryName(LibraryElement library) {
3440 if (library == currentLibrary) return _exportsVar; 3525 if (library == currentLibrary) return _exportsVar;
3441 if (library.name == 'dart._runtime') return _runtimeLibVar; 3526 if (library.name == 'dart._runtime') return _runtimeLibVar;
3442 return _imports.putIfAbsent( 3527 return _imports.putIfAbsent(
3443 library, () => new JS.TemporaryId(jsLibraryName(library))); 3528 library, () => new JS.TemporaryId(jsLibraryName(library)));
3444 } 3529 }
3445 3530
3446 DartType getStaticType(Expression e) => 3531 DartType getStaticType(Expression e) =>
3447 e.staticType ?? DynamicTypeImpl.instance; 3532 e.staticType ?? DynamicTypeImpl.instance;
3448 3533
3449 @override 3534 JS.Node annotate(JS.Node node, AstNode original, [Element element]) {
3450 String getQualifiedName(TypeDefiningElement type) { 3535 if (options.closure && element != null) {
3451 JS.TemporaryId id = _imports[type.library]; 3536 node = node.withClosureAnnotation(
3452 return id == null ? type.name : '${id.name}.${type.name}'; 3537 closureAnnotationFor(node, original, element, _namedArgTemp.name));
3538 }
3539 return node..sourceInformation = original;
3453 } 3540 }
3454 3541
3455 JS.Node annotate(JS.Node method, ExecutableElement e) =>
3456 options.closure && e != null
3457 ? method.withClosureAnnotation(
3458 closureAnnotationFor(e, _namedArgTemp.name))
3459 : method;
3460
3461 JS.Node annotateDefaultConstructor(JS.Node method, ClassElement e) =>
3462 options.closure && e != null
3463 ? method
3464 .withClosureAnnotation(closureAnnotationForDefaultConstructor(e))
3465 : method;
3466
3467 JS.Node annotateVariable(JS.Node node, VariableElement e) =>
3468 options.closure && e != null
3469 ? node.withClosureAnnotation(closureAnnotationForVariable(e))
3470 : node;
3471
3472 JS.Node annotateTypeDef(JS.Node node, FunctionTypeAliasElement e) =>
3473 options.closure && e != null
3474 ? node.withClosureAnnotation(closureAnnotationForTypeDef(e))
3475 : node;
3476
3477 /// Returns true if this is any kind of object represented by `Number` in JS. 3542 /// Returns true if this is any kind of object represented by `Number` in JS.
3478 /// 3543 ///
3479 /// In practice, this is 4 types: num, int, double, and JSNumber. 3544 /// In practice, this is 4 types: num, int, double, and JSNumber.
3480 /// 3545 ///
3481 /// JSNumber is the type that actually "implements" all numbers, hence it's 3546 /// JSNumber is the type that actually "implements" all numbers, hence it's
3482 /// a subtype of int and double (and num). It's in our "dart:_interceptors". 3547 /// a subtype of int and double (and num). It's in our "dart:_interceptors".
3483 bool _isNumberInJS(DartType t) => rules.isSubtypeOf(t, _types.numType); 3548 bool _isNumberInJS(DartType t) => rules.isSubtypeOf(t, _types.numType);
3484 3549
3485 bool _isObjectGetter(String name) { 3550 bool _isObjectGetter(String name) {
3486 PropertyAccessorElement element = _types.objectType.element.getGetter(name); 3551 PropertyAccessorElement element = _types.objectType.element.getGetter(name);
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after
3612 3677
3613 /// A special kind of element created by the compiler, signifying a temporary 3678 /// A special kind of element created by the compiler, signifying a temporary
3614 /// variable. These objects use instance equality, and should be shared 3679 /// variable. These objects use instance equality, and should be shared
3615 /// everywhere in the tree where they are treated as the same variable. 3680 /// everywhere in the tree where they are treated as the same variable.
3616 class TemporaryVariableElement extends LocalVariableElementImpl { 3681 class TemporaryVariableElement extends LocalVariableElementImpl {
3617 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); 3682 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name);
3618 3683
3619 int get hashCode => identityHashCode(this); 3684 int get hashCode => identityHashCode(this);
3620 bool operator ==(Object other) => identical(this, other); 3685 bool operator ==(Object other) => identical(this, other);
3621 } 3686 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698