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 import 'dart:collection' show HashMap, HashSet; | 5 import 'dart:collection' show HashMap, HashSet; |
6 import 'dart:math' show min, max; | 6 import 'dart:math' show min, max; |
7 | 7 |
8 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 8 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
9 import 'package:analyzer/dart/ast/ast.dart'; | 9 import 'package:analyzer/dart/ast/ast.dart'; |
10 import 'package:analyzer/dart/ast/token.dart' show Token, TokenType; | 10 import 'package:analyzer/dart/ast/token.dart' show Token, TokenType; |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
59 /// We sometimes special case codegen for a single library, as it simplifies | 59 /// We sometimes special case codegen for a single library, as it simplifies |
60 /// name scoping requirements. | 60 /// name scoping requirements. |
61 final _libraries = new Map<LibraryElement, JS.Identifier>(); | 61 final _libraries = new Map<LibraryElement, JS.Identifier>(); |
62 | 62 |
63 /// Imported libraries, and the temporaries used to refer to them. | 63 /// Imported libraries, and the temporaries used to refer to them. |
64 final _imports = new Map<LibraryElement, JS.TemporaryId>(); | 64 final _imports = new Map<LibraryElement, JS.TemporaryId>(); |
65 | 65 |
66 /// The list of output module items, in the order they need to be emitted in. | 66 /// The list of output module items, in the order they need to be emitted in. |
67 final _moduleItems = <JS.ModuleItem>[]; | 67 final _moduleItems = <JS.ModuleItem>[]; |
68 | 68 |
| 69 /// Table of named and possibly hoisted types. |
| 70 final _typeTable = new _TypeTable(); |
| 71 |
69 /// The global extension type table. | 72 /// The global extension type table. |
70 final ExtensionTypeSet _extensionTypes; | 73 final ExtensionTypeSet _extensionTypes; |
71 | 74 |
72 /// The variable for the target of the current `..` cascade expression. | 75 /// The variable for the target of the current `..` cascade expression. |
73 /// | 76 /// |
74 /// Usually a [SimpleIdentifier], but it can also be other expressions | 77 /// Usually a [SimpleIdentifier], but it can also be other expressions |
75 /// that are safe to evaluate multiple times, such as `this`. | 78 /// that are safe to evaluate multiple times, such as `this`. |
76 Expression _cascadeTarget; | 79 Expression _cascadeTarget; |
77 | 80 |
78 /// The variable for the current catch clause | 81 /// The variable for the current catch clause |
(...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
245 // Visit each compilation unit and emit its code. | 248 // Visit each compilation unit and emit its code. |
246 // | 249 // |
247 // NOTE: declarations are not necessarily emitted in this order. | 250 // NOTE: declarations are not necessarily emitted in this order. |
248 // Order will be changed as needed so the resulting code can execute. | 251 // Order will be changed as needed so the resulting code can execute. |
249 // This is done by forward declaring items. | 252 // This is done by forward declaring items. |
250 compilationUnits.forEach(visitCompilationUnit); | 253 compilationUnits.forEach(visitCompilationUnit); |
251 | 254 |
252 // Declare imports | 255 // Declare imports |
253 _finishImports(items); | 256 _finishImports(items); |
254 | 257 |
| 258 // Discharge the type table cache variables and |
| 259 // hoisted definitions. |
| 260 items.addAll(_typeTable.discharge()); |
| 261 |
255 // Add the module's code (produced by visiting compilation units, above) | 262 // Add the module's code (produced by visiting compilation units, above) |
256 _copyAndFlattenBlocks(items, _moduleItems); | 263 _copyAndFlattenBlocks(items, _moduleItems); |
257 | 264 |
258 // Build the module. | 265 // Build the module. |
259 var module = new JS.Program(items, name: _buildUnit.name); | 266 var module = new JS.Program(items, name: _buildUnit.name); |
260 | 267 |
261 // Optional: lower module format. Otherwise just return it. | 268 // Optional: lower module format. Otherwise just return it. |
262 switch (options.moduleFormat) { | 269 switch (options.moduleFormat) { |
263 case ModuleFormat.legacy: | 270 case ModuleFormat.legacy: |
264 return new LegacyModuleBuilder().build(module); | 271 return new LegacyModuleBuilder().build(module); |
(...skipping 296 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
561 return null; | 568 return null; |
562 } | 569 } |
563 | 570 |
564 @override | 571 @override |
565 visitFunctionTypeAlias(FunctionTypeAlias node) { | 572 visitFunctionTypeAlias(FunctionTypeAlias node) { |
566 FunctionTypeAliasElement element = node.element; | 573 FunctionTypeAliasElement element = node.element; |
567 | 574 |
568 JS.Expression body = annotate( | 575 JS.Expression body = annotate( |
569 js.call('dart.typedef(#, () => #)', [ | 576 js.call('dart.typedef(#, () => #)', [ |
570 js.string(element.name, "'"), | 577 js.string(element.name, "'"), |
571 _emitType(element.type, lowerTypedef: true) | 578 _emitType(element.type, nameType: false, lowerTypedef: true) |
572 ]), | 579 ]), |
573 node, | 580 node, |
574 element); | 581 element); |
575 | 582 |
576 var typeFormals = element.typeParameters; | 583 var typeFormals = element.typeParameters; |
577 if (typeFormals.isNotEmpty) { | 584 if (typeFormals.isNotEmpty) { |
578 return _defineClassTypeArguments(element, typeFormals, | 585 return _defineClassTypeArguments(element, typeFormals, |
579 js.statement('const # = #;', [element.name, body])); | 586 js.statement('const # = #;', [element.name, body])); |
580 } else { | 587 } else { |
581 return js.statement('# = #;', [_emitTopLevelName(element), body]); | 588 return js.statement('# = #;', [_emitTopLevelName(element), body]); |
(...skipping 251 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
833 result.add(js.statement( | 840 result.add(js.statement( |
834 '#.values = dart.constList(#, #);', [id, values, _emitType(type)])); | 841 '#.values = dart.constList(#, #);', [id, values, _emitType(type)])); |
835 | 842 |
836 return _statement(result); | 843 return _statement(result); |
837 } | 844 } |
838 | 845 |
839 /// Wraps a possibly generic class in its type arguments. | 846 /// Wraps a possibly generic class in its type arguments. |
840 JS.Statement _defineClassTypeArguments(TypeDefiningElement element, | 847 JS.Statement _defineClassTypeArguments(TypeDefiningElement element, |
841 List<TypeParameterElement> formals, JS.Statement body) { | 848 List<TypeParameterElement> formals, JS.Statement body) { |
842 assert(formals.isNotEmpty); | 849 assert(formals.isNotEmpty); |
843 var genericCall = js.call('dart.generic((#) => { #; return #; })', | 850 var genericCall = js.call('dart.generic((#) => { #; #; return #; })', [ |
844 [_emitTypeFormals(formals), body, element.name]); | 851 _emitTypeFormals(formals), |
| 852 _typeTable.discharge(formals), |
| 853 body, |
| 854 element.name |
| 855 ]); |
845 if (element.library.isDartAsync && | 856 if (element.library.isDartAsync && |
846 (element.name == "Future" || element.name == "_Future")) { | 857 (element.name == "Future" || element.name == "_Future")) { |
847 genericCall = js.call('dart.flattenFutures(#)', [genericCall]); | 858 genericCall = js.call('dart.flattenFutures(#)', [genericCall]); |
848 } | 859 } |
849 var genericDef = js.statement( | 860 var genericDef = js.statement( |
850 '# = #;', [_emitTopLevelName(element, suffix: r'$'), genericCall]); | 861 '# = #;', [_emitTopLevelName(element, suffix: r'$'), genericCall]); |
851 var dynType = fillDynamicTypeArgs(element.type); | 862 var dynType = fillDynamicTypeArgs(element.type); |
852 var genericInst = _emitType(dynType, lowerGeneric: true); | 863 var genericInst = _emitType(dynType, lowerGeneric: true); |
853 return js.statement( | 864 return js.statement( |
854 '{ #; # = #; }', [genericDef, _emitTopLevelName(element), genericInst]); | 865 '{ #; # = #; }', [genericDef, _emitTopLevelName(element), genericInst]); |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
887 _loader.startTopLevel(element); | 898 _loader.startTopLevel(element); |
888 | 899 |
889 // Find the super type | 900 // Find the super type |
890 JS.Expression heritage; | 901 JS.Expression heritage; |
891 var supertype = type.superclass; | 902 var supertype = type.superclass; |
892 if (_deferIfNeeded(supertype, element)) { | 903 if (_deferIfNeeded(supertype, element)) { |
893 // Fall back to raw type. | 904 // Fall back to raw type. |
894 supertype = fillDynamicTypeArgs(supertype.element.type); | 905 supertype = fillDynamicTypeArgs(supertype.element.type); |
895 _hasDeferredSupertype.add(element); | 906 _hasDeferredSupertype.add(element); |
896 } | 907 } |
897 heritage = _emitType(supertype); | 908 // We could choose to name the superclasses, but it's |
| 909 // not clear that there's much benefit |
| 910 heritage = _emitType(supertype, nameType: false); |
898 | 911 |
899 if (type.mixins.isNotEmpty) { | 912 if (type.mixins.isNotEmpty) { |
900 var mixins = type.mixins.map(_emitType).toList(); | 913 var mixins = |
| 914 type.mixins.map((t) => _emitType(t, nameType: false)).toList(); |
901 mixins.insert(0, heritage); | 915 mixins.insert(0, heritage); |
902 heritage = js.call('dart.mixin(#)', [mixins]); | 916 heritage = js.call('dart.mixin(#)', [mixins]); |
903 } | 917 } |
904 | 918 |
905 _loader.finishTopLevel(element); | 919 _loader.finishTopLevel(element); |
906 | 920 |
907 return heritage; | 921 return heritage; |
908 } | 922 } |
909 | 923 |
910 /// Provide Dart getters and setters that forward to the underlying native | 924 /// Provide Dart getters and setters that forward to the underlying native |
(...skipping 241 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1152 | 1166 |
1153 void _setBaseClass(ClassElement classElem, JS.Expression className, | 1167 void _setBaseClass(ClassElement classElem, JS.Expression className, |
1154 String jsPeerName, List<JS.Statement> body) { | 1168 String jsPeerName, List<JS.Statement> body) { |
1155 if (jsPeerName != null && classElem.typeParameters.isNotEmpty) { | 1169 if (jsPeerName != null && classElem.typeParameters.isNotEmpty) { |
1156 // TODO(jmesserly): we should really just extend Array in the first place. | 1170 // TODO(jmesserly): we should really just extend Array in the first place. |
1157 var newBaseClass = js.call('dart.global.#', [jsPeerName]); | 1171 var newBaseClass = js.call('dart.global.#', [jsPeerName]); |
1158 body.add(js.statement( | 1172 body.add(js.statement( |
1159 'dart.setExtensionBaseClass(#, #);', [className, newBaseClass])); | 1173 'dart.setExtensionBaseClass(#, #);', [className, newBaseClass])); |
1160 } else if (_hasDeferredSupertype.contains(classElem)) { | 1174 } else if (_hasDeferredSupertype.contains(classElem)) { |
1161 var newBaseClass = _emitType(classElem.type.superclass, | 1175 var newBaseClass = _emitType(classElem.type.superclass, |
1162 subClass: classElem, className: className); | 1176 nameType: false, subClass: classElem, className: className); |
1163 body.add( | 1177 body.add( |
1164 js.statement('dart.setBaseClass(#, #);', [className, newBaseClass])); | 1178 js.statement('dart.setBaseClass(#, #);', [className, newBaseClass])); |
1165 } | 1179 } |
1166 } | 1180 } |
1167 | 1181 |
1168 void _defineNamedConstructors(List<ConstructorDeclaration> ctors, | 1182 void _defineNamedConstructors(List<ConstructorDeclaration> ctors, |
1169 List<JS.Statement> body, JS.Expression className) { | 1183 List<JS.Statement> body, JS.Expression className) { |
1170 for (ConstructorDeclaration member in ctors) { | 1184 for (ConstructorDeclaration member in ctors) { |
1171 if (member.name != null && member.factoryKeyword == null) { | 1185 if (member.name != null && member.factoryKeyword == null) { |
1172 body.add(js.statement('dart.defineNamedConstructor(#, #);', | 1186 body.add(js.statement('dart.defineNamedConstructor(#, #);', |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1252 if (!(node.isSetter || node.isGetter || node.isAbstract)) { | 1266 if (!(node.isSetter || node.isGetter || node.isAbstract)) { |
1253 var name = node.name.name; | 1267 var name = node.name.name; |
1254 var element = node.element; | 1268 var element = node.element; |
1255 var inheritedElement = | 1269 var inheritedElement = |
1256 classElem.lookUpInheritedConcreteMethod(name, currentLibrary); | 1270 classElem.lookUpInheritedConcreteMethod(name, currentLibrary); |
1257 if (inheritedElement != null && inheritedElement.type == element.type) { | 1271 if (inheritedElement != null && inheritedElement.type == element.type) { |
1258 continue; | 1272 continue; |
1259 } | 1273 } |
1260 var memberName = _elementMemberName(element, | 1274 var memberName = _elementMemberName(element, |
1261 useExtension: _extensionTypes.isNativeClass(classElem)); | 1275 useExtension: _extensionTypes.isNativeClass(classElem)); |
1262 var parts = _emitFunctionTypeParts(element.type); | 1276 // TODO(leafp): Settle on a policy for this. Naming these makes the |
1263 var property = | 1277 // signature more compact, but it generates a lot of noise at the |
1264 new JS.Property(memberName, new JS.ArrayInitializer(parts)); | 1278 // top level, and it's not clear that there's much benefit. |
| 1279 var type = |
| 1280 _emitFunctionType(element.type, nameType: false, definite: true); |
| 1281 var property = new JS.Property(memberName, type); |
1265 if (node.isStatic) { | 1282 if (node.isStatic) { |
1266 tStatics.add(property); | 1283 tStatics.add(property); |
1267 sNames.add(memberName); | 1284 sNames.add(memberName); |
1268 } else { | 1285 } else { |
1269 tMethods.add(property); | 1286 tMethods.add(property); |
1270 } | 1287 } |
1271 } | 1288 } |
1272 } | 1289 } |
1273 | 1290 |
1274 var tCtors = <JS.Property>[]; | 1291 var tCtors = <JS.Property>[]; |
1275 for (ConstructorDeclaration node in ctors) { | 1292 for (ConstructorDeclaration node in ctors) { |
1276 var memberName = _constructorName(node.element); | 1293 var memberName = _constructorName(node.element); |
1277 var element = node.element; | 1294 var element = node.element; |
1278 var parts = _emitFunctionTypeParts(element.type, | 1295 var type = _emitFunctionType(element.type, |
1279 parameters: node.parameters.parameters); | 1296 parameters: node.parameters.parameters, |
1280 var property = | 1297 nameType: false, |
1281 new JS.Property(memberName, new JS.ArrayInitializer(parts)); | 1298 definite: true); |
| 1299 var property = new JS.Property(memberName, type); |
1282 tCtors.add(property); | 1300 tCtors.add(property); |
1283 } | 1301 } |
1284 | 1302 |
1285 JS.Property build(String name, List<JS.Property> elements) { | 1303 JS.Property build(String name, List<JS.Property> elements) { |
1286 var o = | 1304 var o = |
1287 new JS.ObjectInitializer(elements, multiline: elements.length > 1); | 1305 new JS.ObjectInitializer(elements, multiline: elements.length > 1); |
1288 var e = js.call('() => #', o); | 1306 var e = js.call('() => #', o); |
1289 return new JS.Property(_propertyName(name), e); | 1307 return new JS.Property(_propertyName(name), e); |
1290 } | 1308 } |
1291 var sigFields = <JS.Property>[]; | 1309 var sigFields = <JS.Property>[]; |
(...skipping 619 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1911 } | 1929 } |
1912 if (type.isDynamic || type.isVoid || type.isBottom) return true; | 1930 if (type.isDynamic || type.isVoid || type.isBottom) return true; |
1913 if (type is ParameterizedType && !type.typeArguments.every(_typeIsLoaded)) { | 1931 if (type is ParameterizedType && !type.typeArguments.every(_typeIsLoaded)) { |
1914 return false; | 1932 return false; |
1915 } | 1933 } |
1916 return _loader.isLoaded(type.element); | 1934 return _loader.isLoaded(type.element); |
1917 } | 1935 } |
1918 | 1936 |
1919 JS.Expression _emitFunctionTagged(JS.Expression fn, DartType type, | 1937 JS.Expression _emitFunctionTagged(JS.Expression fn, DartType type, |
1920 {topLevel: false}) { | 1938 {topLevel: false}) { |
1921 var name = type.name; | |
1922 var lazy = topLevel && !_typeIsLoaded(type); | 1939 var lazy = topLevel && !_typeIsLoaded(type); |
1923 | 1940 var typeRep = _emitFunctionType(type, definite: true); |
1924 if (type is FunctionType && (name == '' || name == null)) { | 1941 if (lazy) { |
1925 if (type.typeFormals.isEmpty && | 1942 return js.call('dart.lazyFn(#, () => #)', [fn, typeRep]); |
1926 type.returnType.isDynamic && | 1943 } else { |
1927 type.optionalParameterTypes.isEmpty && | 1944 return js.call('dart.fn(#, #)', [fn, typeRep]); |
1928 type.namedParameterTypes.isEmpty && | |
1929 type.normalParameterTypes.every((t) => t.isDynamic)) { | |
1930 return js.call('dart.fn(#)', [fn]); | |
1931 } | |
1932 | |
1933 var parts = _emitFunctionTypeParts(type); | |
1934 if (lazy) { | |
1935 return js.call( | |
1936 'dart.lazyFn(#, () => #)', [fn, new JS.ArrayInitializer(parts)]); | |
1937 } else { | |
1938 return js.call('dart.fn(#, #)', [fn, parts]); | |
1939 } | |
1940 } | 1945 } |
1941 throw 'Function has non function type: $type'; | 1946 throw 'Function has non function type: $type'; |
1942 } | 1947 } |
1943 | 1948 |
1944 /// Emits an arrow FunctionExpression node. | 1949 /// Emits an arrow FunctionExpression node. |
1945 /// | 1950 /// |
1946 /// This should be used for all places in Dart's AST where FunctionExpression | 1951 /// This should be used for all places in Dart's AST where FunctionExpression |
1947 /// appears and the function is actually in an Expression context. These | 1952 /// appears and the function is actually in an Expression context. These |
1948 /// correspond to arrow functions in Dart. | 1953 /// correspond to arrow functions in Dart. |
1949 /// | 1954 /// |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2005 /// Contrast with [visitFunctionExpression]. | 2010 /// Contrast with [visitFunctionExpression]. |
2006 JS.Fun _emitFunction(FunctionExpression node) { | 2011 JS.Fun _emitFunction(FunctionExpression node) { |
2007 var fn = _emitFunctionBody(node.element, node.parameters, node.body); | 2012 var fn = _emitFunctionBody(node.element, node.parameters, node.body); |
2008 return annotate(_makeGenericFunction(fn), node); | 2013 return annotate(_makeGenericFunction(fn), node); |
2009 } | 2014 } |
2010 | 2015 |
2011 JS.Fun _emitFunctionBody(ExecutableElement element, | 2016 JS.Fun _emitFunctionBody(ExecutableElement element, |
2012 FormalParameterList parameters, FunctionBody body) { | 2017 FormalParameterList parameters, FunctionBody body) { |
2013 FunctionType type = element.type; | 2018 FunctionType type = element.type; |
2014 | 2019 |
2015 // sync*, async, async* | 2020 // normal function (sync), vs (sync*, async, async*) |
2016 if (element.isAsynchronous || element.isGenerator) { | 2021 var stdFn = !(element.isAsynchronous || element.isGenerator); |
2017 return new JS.Fun( | 2022 var formals = visitFormalParameterList(parameters, destructure: stdFn); |
2018 visitFormalParameterList(parameters, destructure: false), | 2023 var code = (stdFn) |
2019 new JS.Block([ | 2024 ? _visit(body) |
2020 _emitGeneratorFunctionBody(element, parameters, body).toReturn() | 2025 : new JS.Block( |
2021 ]), | 2026 [_emitGeneratorFunctionBody(element, parameters, body).toReturn()]); |
2022 typeParams: _emitTypeFormals(type.typeFormals), | 2027 var typeFormals = _emitTypeFormals(type.typeFormals); |
2023 returnType: emitTypeRef(type.returnType)); | 2028 var returnType = emitTypeRef(type.returnType); |
| 2029 if (type.typeFormals.isNotEmpty) { |
| 2030 code = new JS.Block( |
| 2031 [new JS.Block(_typeTable.discharge(type.typeFormals)), code]); |
2024 } | 2032 } |
2025 | 2033 return new JS.Fun(formals, code, |
2026 // normal function (sync) | 2034 typeParams: typeFormals, returnType: returnType); |
2027 return new JS.Fun(visitFormalParameterList(parameters), _visit(body), | |
2028 typeParams: _emitTypeFormals(type.typeFormals), | |
2029 returnType: emitTypeRef(type.returnType)); | |
2030 } | 2035 } |
2031 | 2036 |
2032 JS.Expression _emitGeneratorFunctionBody(ExecutableElement element, | 2037 JS.Expression _emitGeneratorFunctionBody(ExecutableElement element, |
2033 FormalParameterList parameters, FunctionBody body) { | 2038 FormalParameterList parameters, FunctionBody body) { |
2034 var kind = element.isSynchronous ? 'sync' : 'async'; | 2039 var kind = element.isSynchronous ? 'sync' : 'async'; |
2035 if (element.isGenerator) kind += 'Star'; | 2040 if (element.isGenerator) kind += 'Star'; |
2036 | 2041 |
2037 // Transforms `sync*` `async` and `async*` function bodies | 2042 // Transforms `sync*` `async` and `async*` function bodies |
2038 // using ES6 generators. | 2043 // using ES6 generators. |
2039 // | 2044 // |
(...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2242 | 2247 |
2243 var type = declaration ? emitTypeRef(element.type) : null; | 2248 var type = declaration ? emitTypeRef(element.type) : null; |
2244 return new JS.Identifier(element.name, type: type); | 2249 return new JS.Identifier(element.name, type: type); |
2245 } | 2250 } |
2246 | 2251 |
2247 List<Annotation> _parameterMetadata(FormalParameter p) => | 2252 List<Annotation> _parameterMetadata(FormalParameter p) => |
2248 (p is NormalFormalParameter) | 2253 (p is NormalFormalParameter) |
2249 ? p.metadata | 2254 ? p.metadata |
2250 : (p as DefaultFormalParameter).parameter.metadata; | 2255 : (p as DefaultFormalParameter).parameter.metadata; |
2251 | 2256 |
2252 JS.ArrayInitializer _emitTypeNames(List<DartType> types, | 2257 JS.ArrayInitializer _emitTypeNames( |
2253 [List<FormalParameter> parameters]) { | 2258 List<DartType> types, List<FormalParameter> parameters, |
| 2259 {bool nameType: true, bool hoistType: true}) { |
2254 var result = <JS.Expression>[]; | 2260 var result = <JS.Expression>[]; |
2255 for (int i = 0; i < types.length; ++i) { | 2261 for (int i = 0; i < types.length; ++i) { |
2256 var metadata = | 2262 var metadata = |
2257 parameters != null ? _parameterMetadata(parameters[i]) : []; | 2263 parameters != null ? _parameterMetadata(parameters[i]) : []; |
2258 var typeName = _emitType(types[i]); | 2264 var typeName = |
| 2265 _emitType(types[i], nameType: nameType, hoistType: hoistType); |
2259 var value = typeName; | 2266 var value = typeName; |
2260 if (options.emitMetadata && metadata.isNotEmpty) { | 2267 if (options.emitMetadata && metadata.isNotEmpty) { |
2261 metadata = metadata.map(_instantiateAnnotation).toList(); | 2268 metadata = metadata.map(_instantiateAnnotation).toList(); |
2262 value = new JS.ArrayInitializer([typeName]..addAll(metadata)); | 2269 value = new JS.ArrayInitializer([typeName]..addAll(metadata)); |
2263 } | 2270 } |
2264 result.add(value); | 2271 result.add(value); |
2265 } | 2272 } |
2266 return new JS.ArrayInitializer(result); | 2273 return new JS.ArrayInitializer(result); |
2267 } | 2274 } |
2268 | 2275 |
2269 JS.ObjectInitializer _emitTypeProperties(Map<String, DartType> types) { | 2276 JS.ObjectInitializer _emitTypeProperties(Map<String, DartType> types) { |
2270 var properties = <JS.Property>[]; | 2277 var properties = <JS.Property>[]; |
2271 types.forEach((name, type) { | 2278 types.forEach((name, type) { |
2272 var key = _propertyName(name); | 2279 var key = _propertyName(name); |
2273 var value = _emitType(type); | 2280 var value = _emitType(type); |
2274 properties.add(new JS.Property(key, value)); | 2281 properties.add(new JS.Property(key, value)); |
2275 }); | 2282 }); |
2276 return new JS.ObjectInitializer(properties); | 2283 return new JS.ObjectInitializer(properties); |
2277 } | 2284 } |
2278 | 2285 |
2279 /// Emit the pieces of a function type, as an array of return type, | 2286 /// Emit the pieces of a function type, as an array of return type, |
2280 /// regular args, and optional/named args. | 2287 /// regular args, and optional/named args. |
| 2288 JS.Expression _emitFunctionType(FunctionType type, |
| 2289 {List<FormalParameter> parameters, |
| 2290 bool lowerTypedef: false, |
| 2291 bool nameType: true, |
| 2292 bool hoistType: true, |
| 2293 definite: false}) { |
| 2294 var parts = _emitFunctionTypeParts(type, |
| 2295 parameters: parameters, |
| 2296 lowerTypedef: lowerTypedef, |
| 2297 nameType: nameType, |
| 2298 hoistType: hoistType); |
| 2299 var helper = (definite) ? 'definiteFunctionType' : 'functionType'; |
| 2300 var fullType = js.call('dart.${helper}(#)', [parts]); |
| 2301 if (!nameType) return fullType; |
| 2302 return _typeTable.nameType(type, fullType, hoistType, definite: definite); |
| 2303 } |
| 2304 |
| 2305 /// Emit the pieces of a function type, as an array of return type, |
| 2306 /// regular args, and optional/named args. |
2281 List<JS.Expression> _emitFunctionTypeParts(FunctionType type, | 2307 List<JS.Expression> _emitFunctionTypeParts(FunctionType type, |
2282 {List<FormalParameter> parameters, bool lowerTypedef: false}) { | 2308 {List<FormalParameter> parameters, |
| 2309 bool lowerTypedef: false, |
| 2310 bool nameType: true, |
| 2311 bool hoistType: true}) { |
2283 var parameterTypes = type.normalParameterTypes; | 2312 var parameterTypes = type.normalParameterTypes; |
2284 var optionalTypes = type.optionalParameterTypes; | 2313 var optionalTypes = type.optionalParameterTypes; |
2285 var namedTypes = type.namedParameterTypes; | 2314 var namedTypes = type.namedParameterTypes; |
2286 var rt = _emitType(type.returnType); | 2315 var rt = |
2287 var ra = _emitTypeNames(parameterTypes, parameters); | 2316 _emitType(type.returnType, nameType: nameType, hoistType: hoistType); |
| 2317 var ra = _emitTypeNames(parameterTypes, parameters, |
| 2318 nameType: nameType, hoistType: hoistType); |
2288 | 2319 |
2289 List<JS.Expression> typeParts; | 2320 List<JS.Expression> typeParts; |
2290 if (namedTypes.isNotEmpty) { | 2321 if (namedTypes.isNotEmpty) { |
2291 assert(optionalTypes.isEmpty); | 2322 assert(optionalTypes.isEmpty); |
2292 // TODO(vsm): Pass in annotations here as well. | 2323 // TODO(vsm): Pass in annotations here as well. |
2293 var na = _emitTypeProperties(namedTypes); | 2324 var na = _emitTypeProperties(namedTypes); |
2294 typeParts = [rt, ra, na]; | 2325 typeParts = [rt, ra, na]; |
2295 } else if (optionalTypes.isNotEmpty) { | 2326 } else if (optionalTypes.isNotEmpty) { |
2296 assert(namedTypes.isEmpty); | 2327 assert(namedTypes.isEmpty); |
2297 var oa = _emitTypeNames( | 2328 var oa = _emitTypeNames( |
2298 optionalTypes, parameters?.sublist(parameterTypes.length)); | 2329 optionalTypes, parameters?.sublist(parameterTypes.length), |
| 2330 nameType: nameType, hoistType: hoistType); |
2299 typeParts = [rt, ra, oa]; | 2331 typeParts = [rt, ra, oa]; |
2300 } else { | 2332 } else { |
2301 typeParts = [rt, ra]; | 2333 typeParts = [rt, ra]; |
2302 } | 2334 } |
2303 | 2335 |
2304 var typeFormals = type.typeFormals; | 2336 var typeFormals = type.typeFormals; |
2305 if (typeFormals.isNotEmpty && !lowerTypedef) { | 2337 if (typeFormals.isNotEmpty && !lowerTypedef) { |
2306 // TODO(jmesserly): this is a suboptimal representation for universal | 2338 // TODO(jmesserly): this is a suboptimal representation for universal |
2307 // function types (as callable functions). See discussion at: | 2339 // function types (as callable functions). See discussion at: |
2308 // https://github.com/dart-lang/dev_compiler/issues/526 | 2340 // https://github.com/dart-lang/dev_compiler/issues/526 |
2309 var tf = _emitTypeFormals(typeFormals); | 2341 var tf = _emitTypeFormals(typeFormals); |
2310 typeParts = [new JS.ArrowFun(tf, new JS.ArrayInitializer(typeParts))]; | 2342 var names = _typeTable.discharge(typeFormals); |
| 2343 var parts = new JS.ArrayInitializer(typeParts); |
| 2344 if (names.isEmpty) { |
| 2345 typeParts = [ |
| 2346 js.call('(#) => #', [tf, parts]) |
| 2347 ]; |
| 2348 } else { |
| 2349 typeParts = [ |
| 2350 js.call('(#) => {#; return #;}', [tf, names, parts]) |
| 2351 ]; |
| 2352 } |
2311 } | 2353 } |
2312 return typeParts; | 2354 return typeParts; |
2313 } | 2355 } |
2314 | 2356 |
2315 /// Emits a Dart [type] into code. | 2357 /// Emits a Dart [type] into code. |
2316 /// | 2358 /// |
2317 /// If [lowerTypedef] is set, a typedef will be expanded as if it were a | 2359 /// If [lowerTypedef] is set, a typedef will be expanded as if it were a |
2318 /// function type. Similarly if [lowerGeneric] is set, the `List$()` form | 2360 /// function type. Similarly if [lowerGeneric] is set, the `List$()` form |
2319 /// will be used instead of `List`. These flags are used when generating | 2361 /// will be used instead of `List`. These flags are used when generating |
2320 /// the definitions for typedefs and generic types, respectively. | 2362 /// the definitions for typedefs and generic types, respectively. |
2321 /// | 2363 /// |
2322 /// If [subClass] is set, then we are setting the base class for the given | 2364 /// If [subClass] is set, then we are setting the base class for the given |
2323 /// class and should emit the given [className], which will already be | 2365 /// class and should emit the given [className], which will already be |
2324 /// defined. | 2366 /// defined. |
2325 JS.Expression _emitType(DartType type, | 2367 JS.Expression _emitType(DartType type, |
2326 {bool lowerTypedef: false, | 2368 {bool lowerTypedef: false, |
2327 bool lowerGeneric: false, | 2369 bool lowerGeneric: false, |
| 2370 bool nameType: true, |
| 2371 bool hoistType: true, |
2328 ClassElement subClass, | 2372 ClassElement subClass, |
2329 JS.Expression className}) { | 2373 JS.Expression className}) { |
2330 // The void and dynamic types are not defined in core. | 2374 // The void and dynamic types are not defined in core. |
2331 if (type.isVoid) { | 2375 if (type.isVoid) { |
2332 return js.call('dart.void'); | 2376 return js.call('dart.void'); |
2333 } else if (type.isDynamic) { | 2377 } else if (type.isDynamic) { |
2334 return js.call('dart.dynamic'); | 2378 return js.call('dart.dynamic'); |
2335 } else if (type.isBottom) { | 2379 } else if (type.isBottom) { |
2336 return js.call('dart.bottom'); | 2380 return js.call('dart.bottom'); |
2337 } | 2381 } |
2338 | 2382 |
2339 _declareBeforeUse(type.element); | 2383 _declareBeforeUse(type.element); |
2340 | 2384 |
2341 // TODO(jmesserly): like constants, should we hoist function types out of | 2385 // TODO(jmesserly): like constants, should we hoist function types out of |
2342 // methods? Similar issue with generic types. For all of these, we may want | 2386 // methods? Similar issue with generic types. For all of these, we may want |
2343 // to canonicalize them too, at least when inside the same library. | 2387 // to canonicalize them too, at least when inside the same library. |
2344 var name = type.name; | 2388 var name = type.name; |
2345 var element = type.element; | 2389 var element = type.element; |
2346 if (name == '' || name == null || lowerTypedef) { | 2390 if (name == '' || name == null || lowerTypedef) { |
2347 // TODO(jmesserly): should we change how typedefs work? They currently | 2391 // TODO(jmesserly): should we change how typedefs work? They currently |
2348 // go through use similar logic as generic classes. This makes them | 2392 // go through use similar logic as generic classes. This makes them |
2349 // different from universal function types. | 2393 // different from universal function types. |
2350 var ft = type as FunctionType; | 2394 return _emitFunctionType(type as FunctionType, |
2351 var parts = _emitFunctionTypeParts(ft, lowerTypedef: lowerTypedef); | 2395 lowerTypedef: lowerTypedef, nameType: nameType, hoistType: hoistType); |
2352 return js.call('dart.functionType(#)', [parts]); | |
2353 } | 2396 } |
2354 | 2397 |
2355 if (type is TypeParameterType) { | 2398 if (type is TypeParameterType) { |
2356 _typeParamInConst?.add(type); | 2399 _typeParamInConst?.add(type); |
2357 return new JS.Identifier(name); | 2400 return new JS.Identifier(name); |
2358 } | 2401 } |
2359 | 2402 |
2360 if (type == subClass?.type) { | 2403 if (type == subClass?.type) { |
2361 return className; | 2404 return className; |
2362 } | 2405 } |
2363 | 2406 |
2364 if (type is ParameterizedType) { | 2407 if (type is ParameterizedType) { |
2365 var args = type.typeArguments; | 2408 var args = type.typeArguments; |
2366 Iterable jsArgs = null; | 2409 Iterable jsArgs = null; |
2367 if (args.any((a) => !a.isDynamic)) { | 2410 if (args.any((a) => !a.isDynamic)) { |
2368 jsArgs = args | 2411 jsArgs = args.map((x) => _emitType(x, |
2369 .map((x) => _emitType(x, subClass: subClass, className: className)); | 2412 nameType: nameType, |
| 2413 hoistType: hoistType, |
| 2414 subClass: subClass, |
| 2415 className: className)); |
2370 } else if (lowerGeneric) { | 2416 } else if (lowerGeneric) { |
2371 jsArgs = []; | 2417 jsArgs = []; |
2372 } | 2418 } |
2373 if (jsArgs != null) { | 2419 if (jsArgs != null) { |
2374 var genericName = _emitTopLevelName(element, suffix: '\$'); | 2420 var genericName = _emitTopLevelName(element, suffix: '\$'); |
2375 return js.call('#(#)', [genericName, jsArgs]); | 2421 var typeRep = js.call('#(#)', [genericName, jsArgs]); |
| 2422 return nameType |
| 2423 ? _typeTable.nameType(type, typeRep, hoistType) |
| 2424 : typeRep; |
2376 } | 2425 } |
2377 } | 2426 } |
2378 | 2427 |
2379 return _emitTopLevelName(element); | 2428 return _emitTopLevelName(element); |
2380 } | 2429 } |
2381 | 2430 |
2382 JS.PropertyAccess _emitTopLevelName(Element e, {String suffix: ''}) { | 2431 JS.PropertyAccess _emitTopLevelName(Element e, {String suffix: ''}) { |
2383 var interop = _emitJSInterop(e); | 2432 var interop = _emitJSInterop(e); |
2384 if (interop != null) return interop; | 2433 if (interop != null) return interop; |
2385 String name = getJSExportName(e) + suffix; | 2434 String name = getJSExportName(e) + suffix; |
(...skipping 2172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4558 } | 4607 } |
4559 | 4608 |
4560 bool isLibraryPrefix(Expression node) => | 4609 bool isLibraryPrefix(Expression node) => |
4561 node is SimpleIdentifier && node.staticElement is PrefixElement; | 4610 node is SimpleIdentifier && node.staticElement is PrefixElement; |
4562 | 4611 |
4563 LibraryElement _getLibrary(AnalysisContext c, String uri) => | 4612 LibraryElement _getLibrary(AnalysisContext c, String uri) => |
4564 c.computeLibraryElement(c.sourceFactory.forUri(uri)); | 4613 c.computeLibraryElement(c.sourceFactory.forUri(uri)); |
4565 | 4614 |
4566 bool _isDartRuntime(LibraryElement l) => | 4615 bool _isDartRuntime(LibraryElement l) => |
4567 l.isInSdk && l.source.uri.toString() == 'dart:_runtime'; | 4616 l.isInSdk && l.source.uri.toString() == 'dart:_runtime'; |
| 4617 |
| 4618 Set<TypeParameterElement> _freeTypeParameters(DartType t) { |
| 4619 var result = new HashSet<TypeParameterElement>(); |
| 4620 void find(DartType t) { |
| 4621 if (t is TypeParameterType) { |
| 4622 result.add(t.element); |
| 4623 } else if (t is FunctionType) { |
| 4624 find(t.returnType); |
| 4625 t.parameters.forEach((p) => find(p.type)); |
| 4626 t.typeFormals.forEach((p) => find(p.bound)); |
| 4627 t.typeFormals.forEach(result.remove); |
| 4628 } else if (t is InterfaceType) { |
| 4629 t.typeArguments.forEach(find); |
| 4630 } |
| 4631 } |
| 4632 find(t); |
| 4633 return result; |
| 4634 } |
| 4635 |
| 4636 /// _CacheTable tracks cache variables for variables that |
| 4637 /// are emitted in place with a hoisted variable for a cache. |
| 4638 class _CacheTable { |
| 4639 /// Mapping from types to their canonical names. |
| 4640 final _names = new Map<DartType, JS.TemporaryId>(); |
| 4641 Iterable<DartType> get keys => _names.keys.toList(); |
| 4642 |
| 4643 JS.Statement _dischargeType(DartType type) { |
| 4644 var name = _names.remove(type); |
| 4645 if (name != null) { |
| 4646 return (js.statement('let #;', [name])); |
| 4647 } |
| 4648 return null; |
| 4649 } |
| 4650 |
| 4651 /// Emit a list of statements declaring the cache variables for |
| 4652 /// types tracked by this table. If [typeFilter] is given, |
| 4653 /// only emit the types listed in the filter. |
| 4654 List<JS.Statement> discharge([Iterable<DartType> typeFilter]) { |
| 4655 var decls = <JS.Statement>[]; |
| 4656 var types = typeFilter ?? keys; |
| 4657 for (var t in types) { |
| 4658 var stmt = _dischargeType(t); |
| 4659 if (stmt != null) decls.add(stmt); |
| 4660 } |
| 4661 return decls; |
| 4662 } |
| 4663 |
| 4664 bool isNamed(DartType type) => _names.containsKey(type); |
| 4665 |
| 4666 /// If [type] is not already in the table, choose a new canonical |
| 4667 /// variable to contain it. Emit an expression which uses [typeRep] to |
| 4668 /// lazily initialize the cache in place. |
| 4669 JS.Expression nameType(DartType type, JS.Expression typeRep) { |
| 4670 var temp = _names[type]; |
| 4671 if (temp == null) { |
| 4672 ; |
| 4673 _names[type] = temp = chooseTypeName(type); |
| 4674 } |
| 4675 return js.call('# || (# = #)', [temp, temp, typeRep]); |
| 4676 } |
| 4677 |
| 4678 String _typeString(DartType type, {bool flat: false}) { |
| 4679 if (type is ParameterizedType && type.name != null) { |
| 4680 var clazz = type.name; |
| 4681 var params = type.typeArguments; |
| 4682 if (params == null) return clazz; |
| 4683 if (params.every((p) => p.isDynamic)) return clazz; |
| 4684 var paramStrings = params.map(_typeString); |
| 4685 var paramString = paramStrings.join("\$"); |
| 4686 return "${clazz}Of${paramString}"; |
| 4687 } |
| 4688 if (type is FunctionType) { |
| 4689 if (flat) return "Fn"; |
| 4690 var rType = _typeString(type.returnType, flat: true); |
| 4691 var paramStrings = type.normalParameterTypes |
| 4692 .take(3) |
| 4693 .map((p) => _typeString(p, flat: true)); |
| 4694 var paramString = paramStrings.join("And"); |
| 4695 var count = type.normalParameterTypes.length; |
| 4696 if (count > 3 || |
| 4697 type.namedParameterTypes.isNotEmpty || |
| 4698 type.optionalParameterTypes.isNotEmpty) { |
| 4699 paramString = "${paramString}__"; |
| 4700 } else if (count == 0) { |
| 4701 paramString = "Void"; |
| 4702 } |
| 4703 return "${paramString}To${rType}"; |
| 4704 } |
| 4705 if (type is TypeParameterType) return type.name; |
| 4706 if (type.name != null) return type.name; |
| 4707 return "type"; |
| 4708 } |
| 4709 |
| 4710 /// Heuristically choose a good name for the cache and generator |
| 4711 /// variables. |
| 4712 JS.Identifier chooseTypeName(DartType type) { |
| 4713 return new JS.TemporaryId(_typeString(type)); |
| 4714 } |
| 4715 } |
| 4716 |
| 4717 /// _GeneratorTable tracks types which have been |
| 4718 /// named and hoisted. |
| 4719 class _GeneratorTable extends _CacheTable { |
| 4720 final _defs = new Map<DartType, JS.Expression>(); |
| 4721 |
| 4722 _GeneratorTable(); |
| 4723 |
| 4724 JS.Statement _dischargeType(DartType t) { |
| 4725 var name = _names.remove(t); |
| 4726 if (name != null) { |
| 4727 JS.Expression init = _defs.remove(t); |
| 4728 assert(init != null); |
| 4729 return js.statement( |
| 4730 'let # = () => ((# = dart.constFn(#))());', [name, name, init]); |
| 4731 } |
| 4732 return null; |
| 4733 } |
| 4734 |
| 4735 /// If [type] does not already have a generator name chosen for it, |
| 4736 /// assign it one, using [typeRep] as the initializer for it. |
| 4737 /// Emit an expression which calls the generator name. |
| 4738 JS.Expression nameType(DartType type, JS.Expression typeRep) { |
| 4739 var temp = _names[type]; |
| 4740 if (temp == null) { |
| 4741 _names[type] = temp = chooseTypeName(type); |
| 4742 _defs[type] = typeRep; |
| 4743 } |
| 4744 return js.call('#()', [temp]); |
| 4745 } |
| 4746 } |
| 4747 |
| 4748 class _TypeTable { |
| 4749 /// Cache variable names for types emitted in place. |
| 4750 final _cacheNames = new _CacheTable(); |
| 4751 |
| 4752 /// Cache variable names for definite function types emitted in place. |
| 4753 final _definiteCacheNames = new _CacheTable(); |
| 4754 |
| 4755 /// Generator variable names for hoisted types. |
| 4756 final _generators = new _GeneratorTable(); |
| 4757 |
| 4758 /// Generator variable names for hoisted definite function types. |
| 4759 final _definiteGenerators = new _GeneratorTable(); |
| 4760 |
| 4761 /// Mapping from type parameters to the types which must have their |
| 4762 /// cache/generator variables discharged at the binding site for the |
| 4763 /// type variable since the type definition depends on the type |
| 4764 /// parameter. |
| 4765 final _scopeDependencies = new Map<TypeParameterElement, List<DartType>>(); |
| 4766 |
| 4767 /// Emit a list of statements declaring the cache variables and generator |
| 4768 /// definitions tracked by the table. If [formals] is present, only |
| 4769 /// emit the definitions which depend on the formals. |
| 4770 List<JS.Statement> discharge([List<TypeParameterElement> formals]) { |
| 4771 var filter = formals?.expand((p) => _scopeDependencies[p] ?? []); |
| 4772 var stmts = [ |
| 4773 _cacheNames, |
| 4774 _definiteCacheNames, |
| 4775 _generators, |
| 4776 _definiteGenerators |
| 4777 ].expand((c) => c.discharge(filter)).toList(); |
| 4778 formals?.forEach(_scopeDependencies.remove); |
| 4779 return stmts; |
| 4780 } |
| 4781 |
| 4782 /// Record the dependencies of the type on its free variables |
| 4783 bool recordScopeDependencies(DartType type) { |
| 4784 var fvs = _freeTypeParameters(type); |
| 4785 // TODO(leafp): This is a hack to avoid trying to hoist out of |
| 4786 // generic functions and generic function types. This often degrades |
| 4787 // readability to little or no benefit. It would be good to do this |
| 4788 // when we know that we can hoist it to an outer scope, but for |
| 4789 // now we just disable it. |
| 4790 if (fvs.any((i) => i.enclosingElement is FunctionTypedElement)) { |
| 4791 return true; |
| 4792 } |
| 4793 void addScope(TypeParameterElement i) { |
| 4794 List<DartType> types = _scopeDependencies[i]; |
| 4795 if (types == null) _scopeDependencies[i] = types = []; |
| 4796 types.add(type); |
| 4797 } |
| 4798 fvs.forEach(addScope); |
| 4799 return false; |
| 4800 } |
| 4801 |
| 4802 /// Given a type [type], and a JS expression [typeRep] which implements it, |
| 4803 /// Add the type and its representation to the table, returning an |
| 4804 /// expression which implements the type (but which caches the value). |
| 4805 /// If [hoist] is true, then the JS representation will be hoisted up |
| 4806 /// as far as possible and shared between instances of the type. |
| 4807 /// If [hoist] is false, the cache variable will be hoisted up as |
| 4808 /// far as possible and shared between instances of the type, but the |
| 4809 /// initializer expression will be emitted in place. |
| 4810 /// The boolean parameter [definite] distinguishes between definite function |
| 4811 /// types and other types (since the same DartType may have different |
| 4812 /// representations as definite and indefinite function types). |
| 4813 JS.Expression nameType(DartType type, JS.Expression typeRep, bool hoist, |
| 4814 {bool definite: false}) { |
| 4815 var table = hoist |
| 4816 ? (definite ? _definiteGenerators : _generators) |
| 4817 : (definite ? _definiteCacheNames : _cacheNames); |
| 4818 if (!table.isNamed(type)) { |
| 4819 if (recordScopeDependencies(type)) return typeRep; |
| 4820 } |
| 4821 return table.nameType(type, typeRep); |
| 4822 } |
| 4823 } |
OLD | NEW |