| 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 |