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 dev_compiler.src.codegen.js_codegen; | 5 library dev_compiler.src.codegen.js_codegen; |
6 | 6 |
7 import 'dart:collection' show HashSet, HashMap; | 7 import 'dart:collection' show HashSet, HashMap; |
8 | 8 |
9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; | 10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; |
11 import 'package:analyzer/src/generated/constant.dart'; | 11 import 'package:analyzer/src/generated/constant.dart'; |
12 import 'package:analyzer/src/generated/element.dart'; | 12 import 'package:analyzer/src/generated/element.dart'; |
13 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; | 13 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; |
14 import 'package:analyzer/src/generated/scanner.dart' | 14 import 'package:analyzer/src/generated/scanner.dart' |
15 show StringToken, Token, TokenType; | 15 show StringToken, Token, TokenType; |
16 import 'package:path/path.dart' as path; | 16 import 'package:path/path.dart' as path; |
17 | 17 |
18 import 'package:dev_compiler/src/codegen/ast_builder.dart' show AstBuilder; | 18 import 'package:dev_compiler/src/codegen/ast_builder.dart' show AstBuilder; |
| 19 import 'package:dev_compiler/src/codegen/reify_coercions.dart' |
| 20 show CoercionReifier; |
19 | 21 |
20 // TODO(jmesserly): import from its own package | 22 // TODO(jmesserly): import from its own package |
21 import 'package:dev_compiler/src/js/js_ast.dart' as JS; | 23 import 'package:dev_compiler/src/js/js_ast.dart' as JS; |
22 import 'package:dev_compiler/src/js/js_ast.dart' show js; | 24 import 'package:dev_compiler/src/js/js_ast.dart' show js; |
23 | 25 |
24 import 'package:dev_compiler/src/checker/rules.dart'; | 26 import 'package:dev_compiler/src/checker/rules.dart'; |
25 import 'package:dev_compiler/src/info.dart'; | 27 import 'package:dev_compiler/src/info.dart'; |
26 import 'package:dev_compiler/src/options.dart'; | 28 import 'package:dev_compiler/src/options.dart'; |
27 import 'package:dev_compiler/src/utils.dart'; | 29 import 'package:dev_compiler/src/utils.dart'; |
28 | 30 |
(...skipping 15 matching lines...) Expand all Loading... |
44 const DCALL = 'dcall'; | 46 const DCALL = 'dcall'; |
45 const DSEND = 'dsend'; | 47 const DSEND = 'dsend'; |
46 | 48 |
47 class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { | 49 class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
48 final LibraryInfo libraryInfo; | 50 final LibraryInfo libraryInfo; |
49 final TypeRules rules; | 51 final TypeRules rules; |
50 | 52 |
51 /// The global extension method table. | 53 /// The global extension method table. |
52 final HashMap<String, List<InterfaceType>> _extensionMethods; | 54 final HashMap<String, List<InterfaceType>> _extensionMethods; |
53 | 55 |
| 56 final CompilerOptions options; |
54 /// The variable for the target of the current `..` cascade expression. | 57 /// The variable for the target of the current `..` cascade expression. |
55 SimpleIdentifier _cascadeTarget; | 58 SimpleIdentifier _cascadeTarget; |
56 /// The variable for the current catch clause | 59 /// The variable for the current catch clause |
57 SimpleIdentifier _catchParameter; | 60 SimpleIdentifier _catchParameter; |
58 | 61 |
59 ClassDeclaration currentClass; | 62 ClassDeclaration currentClass; |
60 ConstantEvaluator _constEvaluator; | 63 ConstantEvaluator _constEvaluator; |
61 | 64 |
62 final _exports = new Set<String>(); | 65 final _exports = new Set<String>(); |
63 final _lazyFields = <VariableDeclaration>[]; | 66 final _lazyFields = <VariableDeclaration>[]; |
(...skipping 14 matching lines...) Expand all Loading... |
78 /// Classes we have not emitted yet. Values can be [ClassDeclaration] or | 81 /// Classes we have not emitted yet. Values can be [ClassDeclaration] or |
79 /// [ClassTypeAlias]. | 82 /// [ClassTypeAlias]. |
80 final _pendingClasses = new HashMap<Element, CompilationUnitMember>(); | 83 final _pendingClasses = new HashMap<Element, CompilationUnitMember>(); |
81 | 84 |
82 /// Memoized results of [_lazyClass]. | 85 /// Memoized results of [_lazyClass]. |
83 final _lazyClassMemo = new HashMap<Element, bool>(); | 86 final _lazyClassMemo = new HashMap<Element, bool>(); |
84 | 87 |
85 /// Memoized results of [_inLibraryCycle]. | 88 /// Memoized results of [_inLibraryCycle]. |
86 final _libraryCycleMemo = new HashMap<LibraryElement, bool>(); | 89 final _libraryCycleMemo = new HashMap<LibraryElement, bool>(); |
87 | 90 |
88 JSCodegenVisitor(this.libraryInfo, this.rules, this._extensionMethods); | 91 JSCodegenVisitor( |
| 92 this.libraryInfo, this.rules, this.options, this._extensionMethods); |
89 | 93 |
90 LibraryElement get currentLibrary => libraryInfo.library; | 94 LibraryElement get currentLibrary => libraryInfo.library; |
91 TypeProvider get types => rules.provider; | 95 TypeProvider get types => rules.provider; |
92 | 96 |
93 JS.Program emitLibrary(LibraryUnit library) { | 97 JS.Program emitLibrary(LibraryUnit library) { |
94 String jsDefaultValue = null; | 98 String jsDefaultValue = null; |
| 99 |
| 100 // Modify the AST to make coercions explicit. |
| 101 new CoercionReifier(library, rules, options).reify(); |
| 102 |
95 var unit = library.library; | 103 var unit = library.library; |
96 if (unit.directives.isNotEmpty) { | 104 if (unit.directives.isNotEmpty) { |
97 var libraryDir = unit.directives.first; | 105 var libraryDir = unit.directives.first; |
98 if (libraryDir is LibraryDirective) { | 106 if (libraryDir is LibraryDirective) { |
99 var jsName = getAnnotationValue(libraryDir, _isJsNameAnnotation); | 107 var jsName = getAnnotationValue(libraryDir, _isJsNameAnnotation); |
100 jsDefaultValue = getConstantField(jsName, 'name', types.stringType); | 108 jsDefaultValue = getConstantField(jsName, 'name', types.stringType); |
101 } | 109 } |
102 } | 110 } |
103 if (jsDefaultValue == null) jsDefaultValue = '{}'; | 111 if (jsDefaultValue == null) jsDefaultValue = '{}'; |
104 | 112 |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
192 | 200 |
193 assert(_pendingPrivateNames.isEmpty); | 201 assert(_pendingPrivateNames.isEmpty); |
194 return _statement(body); | 202 return _statement(body); |
195 } | 203 } |
196 | 204 |
197 bool isPublic(String name) => !name.startsWith('_'); | 205 bool isPublic(String name) => !name.startsWith('_'); |
198 | 206 |
199 /// Conversions that we don't handle end up here. | 207 /// Conversions that we don't handle end up here. |
200 @override | 208 @override |
201 visitConversion(Conversion node) { | 209 visitConversion(Conversion node) { |
202 var from = node.baseType; | 210 throw 'Unlowered conversion ${node.runtimeType}: $node'; |
203 var to = node.convertedType; | 211 } |
| 212 |
| 213 @override |
| 214 visitAsExpression(AsExpression node) { |
| 215 var from = getStaticType(node.expression); |
| 216 var to = node.type.type; |
204 | 217 |
205 // All Dart number types map to a JS double. | 218 // All Dart number types map to a JS double. |
206 if (rules.isNumType(from) && | 219 if (rules.isNumType(from) && |
207 (rules.isIntType(to) || rules.isDoubleType(to))) { | 220 (rules.isIntType(to) || rules.isDoubleType(to))) { |
208 // TODO(jmesserly): a lot of these checks are meaningless, as people use | 221 // TODO(jmesserly): a lot of these checks are meaningless, as people use |
209 // `num` to mean "any kind of number" rather than "could be null". | 222 // `num` to mean "any kind of number" rather than "could be null". |
210 // The core libraries especially suffer from this problem, with many of | 223 // The core libraries especially suffer from this problem, with many of |
211 // the `num` methods returning `num`. | 224 // the `num` methods returning `num`. |
212 if (!rules.isNonNullableType(from) && rules.isNonNullableType(to)) { | 225 if (!rules.isNonNullableType(from) && rules.isNonNullableType(to)) { |
213 // Converting from a nullable number to a non-nullable number | 226 // Converting from a nullable number to a non-nullable number |
214 // only requires a null check. | 227 // only requires a null check. |
215 return js.call('dart.notNull(#)', _visit(node.expression)); | 228 return js.call('dart.notNull(#)', _visit(node.expression)); |
216 } else { | 229 } else { |
217 // A no-op in JavaScript. | 230 // A no-op in JavaScript. |
218 return _visit(node.expression); | 231 return _visit(node.expression); |
219 } | 232 } |
220 } | 233 } |
221 | 234 |
222 return _emitCast(node.expression, to); | 235 return _emitCast(node.expression, to); |
223 } | 236 } |
224 | 237 |
225 // TODO(vsm): This should go away in the future. I'm passing in the type | |
226 // as a String for now to avoid breaking any existing code. In most cases, | |
227 // we currently throw for function types. | |
228 @override | |
229 visitClosureWrapBase(ClosureWrapBase node) { | |
230 var expression = _visit(node.expression); | |
231 var typeString = js.escapedString(node.convertedType.toString()); | |
232 return js.call('dart.closureWrap(#, #)', [expression, typeString]); | |
233 } | |
234 | |
235 @override | |
236 visitAsExpression(AsExpression node) => | |
237 _emitCast(node.expression, node.type.type); | |
238 | |
239 _emitCast(Expression node, DartType type) => | 238 _emitCast(Expression node, DartType type) => |
240 js.call('dart.as(#)', [[_visit(node), _emitTypeName(type)]]); | 239 js.call('dart.as(#)', [[_visit(node), _emitTypeName(type)]]); |
241 | 240 |
242 @override | 241 @override |
243 visitIsExpression(IsExpression node) { | 242 visitIsExpression(IsExpression node) { |
244 // Generate `is` as `dart.is` or `typeof` depending on the RHS type. | 243 // Generate `is` as `dart.is` or `typeof` depending on the RHS type. |
245 JS.Expression result; | 244 JS.Expression result; |
246 var type = node.type.type; | 245 var type = node.type.type; |
247 var lhs = _visit(node.expression); | 246 var lhs = _visit(node.expression); |
248 var typeofName = _jsTypeofName(type); | 247 var typeofName = _jsTypeofName(type); |
(...skipping 2038 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2287 } | 2286 } |
2288 | 2287 |
2289 DartType getStaticType(Expression e) => rules.getStaticType(e); | 2288 DartType getStaticType(Expression e) => rules.getStaticType(e); |
2290 | 2289 |
2291 static bool _needsImplicitThis(Element e) => | 2290 static bool _needsImplicitThis(Element e) => |
2292 e is PropertyAccessorElement && !e.variable.isStatic || | 2291 e is PropertyAccessorElement && !e.variable.isStatic || |
2293 e is ClassMemberElement && !e.isStatic && e is! ConstructorElement; | 2292 e is ClassMemberElement && !e.isStatic && e is! ConstructorElement; |
2294 } | 2293 } |
2295 | 2294 |
2296 class JSGenerator extends CodeGenerator { | 2295 class JSGenerator extends CodeGenerator { |
2297 final JSCodeOptions options; | 2296 final CompilerOptions options; |
2298 | 2297 |
2299 /// For fast lookup of extension methods, we first check the name, then do a | 2298 /// For fast lookup of extension methods, we first check the name, then do a |
2300 /// (possibly expensive) subtype test to see if it matches one of the types | 2299 /// (possibly expensive) subtype test to see if it matches one of the types |
2301 /// that declares that method. | 2300 /// that declares that method. |
2302 final _extensionMethods = new HashMap<String, List<InterfaceType>>(); | 2301 final _extensionMethods = new HashMap<String, List<InterfaceType>>(); |
2303 | 2302 |
2304 JSGenerator(String outDir, Uri root, TypeRules rules, this.options) | 2303 JSGenerator(String outDir, Uri root, TypeRules rules, this.options) |
2305 : super(outDir, root, rules) { | 2304 : super(outDir, root, rules) { |
2306 | 2305 |
2307 // TODO(jacobr): determine the the set of types with extension methods from | 2306 // TODO(jacobr): determine the the set of types with extension methods from |
2308 // the annotations rather than hard coding the list once the analyzer | 2307 // the annotations rather than hard coding the list once the analyzer |
2309 // supports summaries. | 2308 // supports summaries. |
2310 var extensionTypes = [types.listType, types.iterableType]; | 2309 var extensionTypes = [types.listType, types.iterableType]; |
2311 for (var type in extensionTypes) { | 2310 for (var type in extensionTypes) { |
2312 type = fillDynamicTypeArgs(type, rules.provider); | 2311 type = fillDynamicTypeArgs(type, rules.provider); |
2313 var e = type.element; | 2312 var e = type.element; |
2314 var names = new HashSet<String>() | 2313 var names = new HashSet<String>() |
2315 ..addAll(e.methods.map((m) => m.name)) | 2314 ..addAll(e.methods.map((m) => m.name)) |
2316 ..addAll(e.accessors.map((m) => m.name)); | 2315 ..addAll(e.accessors.map((m) => m.name)); |
2317 for (var name in names) { | 2316 for (var name in names) { |
2318 _extensionMethods.putIfAbsent(name, () => []).add(type); | 2317 _extensionMethods.putIfAbsent(name, () => []).add(type); |
2319 } | 2318 } |
2320 } | 2319 } |
2321 } | 2320 } |
2322 | 2321 |
2323 TypeProvider get types => rules.provider; | 2322 TypeProvider get types => rules.provider; |
2324 | 2323 |
2325 String generateLibrary(LibraryUnit unit, LibraryInfo info) { | 2324 String generateLibrary(LibraryUnit unit, LibraryInfo info) { |
2326 var codegen = new JSCodegenVisitor(info, rules, _extensionMethods); | 2325 var codegen = new JSCodegenVisitor(info, rules, options, _extensionMethods); |
2327 var module = codegen.emitLibrary(unit); | 2326 var module = codegen.emitLibrary(unit); |
2328 var dir = path.join(outDir, jsOutputPath(info, root)); | 2327 var dir = path.join(outDir, jsOutputPath(info, root)); |
2329 return writeJsLibrary(module, dir, emitSourceMaps: options.emitSourceMaps); | 2328 return writeJsLibrary(module, dir, emitSourceMaps: options.emitSourceMaps); |
2330 } | 2329 } |
2331 } | 2330 } |
2332 | 2331 |
2333 /// Choose a canonical name from the library element. | 2332 /// Choose a canonical name from the library element. |
2334 /// This never uses the library's name (the identifier in the `library` | 2333 /// This never uses the library's name (the identifier in the `library` |
2335 /// declaration) as it doesn't have any meaningful rules enforced. | 2334 /// declaration) as it doesn't have any meaningful rules enforced. |
2336 String jsLibraryName(LibraryElement library) => canonicalLibraryName(library); | 2335 String jsLibraryName(LibraryElement library) => canonicalLibraryName(library); |
(...skipping 22 matching lines...) Expand all Loading... |
2359 return filepath; | 2358 return filepath; |
2360 } | 2359 } |
2361 | 2360 |
2362 // TODO(jmesserly): validate the library. See issue #135. | 2361 // TODO(jmesserly): validate the library. See issue #135. |
2363 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; | 2362 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; |
2364 | 2363 |
2365 // TODO(jacobr): we would like to do something like the following | 2364 // TODO(jacobr): we would like to do something like the following |
2366 // but we don't have summary support yet. | 2365 // but we don't have summary support yet. |
2367 // bool _supportJsExtensionMethod(AnnotatedNode node) => | 2366 // bool _supportJsExtensionMethod(AnnotatedNode node) => |
2368 // _getAnnotation(node, "SupportJsExtensionMethod") != null; | 2367 // _getAnnotation(node, "SupportJsExtensionMethod") != null; |
OLD | NEW |