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

Side by Side Diff: lib/src/compiler/code_generator.dart

Issue 1988023008: Name and hoist types (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Try again Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | test/browser/runtime_tests.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 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
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
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
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
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
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
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
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
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
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
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,
Jennifer Messerly 2016/05/24 21:56:39 do we ever set this to false? I wasn't able to fin
Leaf 2016/05/25 00:56:13 It was here so that we can play with different str
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
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.
Jennifer Messerly 2016/05/24 21:56:39 Consider: including an example here of what it loo
Leaf 2016/05/25 00:56:13 Done below.
4638 class _CacheTable {
Jennifer Messerly 2016/05/24 21:56:39 consider: putting these new classes into their own
Leaf 2016/05/25 00:56:13 Done.
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]));
Jennifer Messerly 2016/05/24 21:56:39 nit: parens not needed around return value
Leaf 2016/05/25 00:56:13 Done.
Leaf 2016/05/25 00:56:13 Done.
4647 }
4648 return null;
4649 }
4650
4651 /// Emit a list of statements declaring the cache variables for
Jennifer Messerly 2016/05/24 21:56:39 doc comment style nit -- first sentence should be
Leaf 2016/05/25 00:56:13 Acknowledged.
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];
Jennifer Messerly 2016/05/24 21:56:39 you could also do var temp = _names.putIfAbse
Leaf 2016/05/25 00:56:13 Acknowledged.
4671 if (temp == null) {
4672 ;
Jennifer Messerly 2016/05/24 21:56:39 unintended empty statement?
Leaf 2016/05/25 00:56:13 Done.
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();
Jennifer Messerly 2016/05/24 21:56:39 note: this is not needed
Leaf 2016/05/25 00:56:13 Done.
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) {
Jennifer Messerly 2016/05/24 21:56:39 this looks mostly identical to the super method, m
Leaf 2016/05/25 00:56:13 It's 3 of 5 lines that are duplicated. I don't se
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
Jennifer Messerly 2016/05/24 21:56:39 nit: lowercase "add"
Leaf 2016/05/25 00:56:13 Done.
4804 /// expression which implements the type (but which caches the value).
4805 /// If [hoist] is true, then the JS representation will be hoisted up
Jennifer Messerly 2016/05/24 21:56:39 consider: splitting this into a few paragraphs?
Leaf 2016/05/25 00:56:13 Done.
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).
Jennifer Messerly 2016/05/24 21:56:39 I seem to recall at one point we had considered pu
Leaf 2016/05/25 00:56:13 Acknowledged.
4813 JS.Expression nameType(DartType type, JS.Expression typeRep, bool hoist,
Jennifer Messerly 2016/05/24 21:56:39 any reason to make "hoist" required and "definite"
Leaf 2016/05/25 00:56:13 Made them both named, asserted hoist is set. Defi
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 }
OLDNEW
« no previous file with comments | « no previous file | test/browser/runtime_tests.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698