OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 } |
OLD | NEW |