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