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, SplayTreeSet; | 7 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; |
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; |
(...skipping 293 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
304 return 'Symbol'; | 304 return 'Symbol'; |
305 } | 305 } |
306 | 306 |
307 bool isPublic(String name) => !name.startsWith('_'); | 307 bool isPublic(String name) => !name.startsWith('_'); |
308 | 308 |
309 @override | 309 @override |
310 visitAsExpression(AsExpression node) { | 310 visitAsExpression(AsExpression node) { |
311 var from = getStaticType(node.expression); | 311 var from = getStaticType(node.expression); |
312 var to = node.type.type; | 312 var to = node.type.type; |
313 | 313 |
| 314 var fromExpr = _visit(node.expression); |
| 315 |
314 // Skip the cast if it's not needed. | 316 // Skip the cast if it's not needed. |
315 if (rules.isSubTypeOf(from, to)) return _visit(node.expression); | 317 if (rules.isSubTypeOf(from, to)) return fromExpr; |
316 | 318 |
317 // All Dart number types map to a JS double. | 319 // All Dart number types map to a JS double. |
318 if (rules.isNumType(from) && | 320 if (rules.isNumberInJS(from) && rules.isNumberInJS(to)) { |
319 (rules.isIntType(to) || rules.isDoubleType(to))) { | 321 // Make sure to check when converting to int. |
320 // TODO(jmesserly): a lot of these checks are meaningless, as people use | 322 if (!rules.isIntType(from) && rules.isIntType(to)) { |
321 // `num` to mean "any kind of number" rather than "could be null". | 323 return js.call('dart.asInt(#)', [fromExpr]); |
322 // The core libraries especially suffer from this problem, with many of | 324 } |
323 // the `num` methods returning `num`. | 325 |
324 if (!rules.isNonNullableType(from) && rules.isNonNullableType(to)) { | 326 if (!rules.isNonNullableType(from) && rules.isNonNullableType(to)) { |
325 // Converting from a nullable number to a non-nullable number | 327 // Converting from a nullable number to a non-nullable number |
326 // only requires a null check. | 328 // only requires a null check. |
327 return js.call('dart.notNull(#)', _visit(node.expression)); | 329 // TODO(jmesserly): a lot of these checks are meaningless, as people use |
| 330 // `num` to mean "any kind of number" rather than "could be null". |
| 331 // The core libraries especially suffer from this problem, with many of |
| 332 // the `num` methods returning `num`. |
| 333 return js.call('dart.notNull(#)', fromExpr); |
328 } else { | 334 } else { |
329 // A no-op in JavaScript. | 335 // A no-op in JavaScript. |
330 return _visit(node.expression); | 336 return fromExpr; |
331 } | 337 } |
332 } | 338 } |
333 | 339 |
334 return _emitCast(node.expression, to); | 340 return js.call('dart.as(#, #)', [fromExpr, _emitTypeName(to)]); |
335 } | 341 } |
336 | 342 |
337 _emitCast(Expression node, DartType type) => js.call('dart.as(#)', [ | |
338 [_visit(node), _emitTypeName(type)] | |
339 ]); | |
340 | |
341 @override | 343 @override |
342 visitIsExpression(IsExpression node) { | 344 visitIsExpression(IsExpression node) { |
343 // Generate `is` as `dart.is` or `typeof` depending on the RHS type. | 345 // Generate `is` as `dart.is` or `typeof` depending on the RHS type. |
344 JS.Expression result; | 346 JS.Expression result; |
345 var type = node.type.type; | 347 var type = node.type.type; |
346 var lhs = _visit(node.expression); | 348 var lhs = _visit(node.expression); |
347 var typeofName = _jsTypeofName(type); | 349 var typeofName = _jsTypeofName(type); |
348 if (typeofName != null) { | 350 if (typeofName != null) { |
349 result = js.call('typeof # == #', [lhs, js.string(typeofName, "'")]); | 351 result = js.call('typeof # == #', [lhs, js.string(typeofName, "'")]); |
350 } else { | 352 } else { |
351 // Always go through a runtime helper, because implicit interfaces. | 353 // Always go through a runtime helper, because implicit interfaces. |
352 result = js.call('dart.is(#, #)', [lhs, _emitTypeName(type)]); | 354 result = js.call('dart.is(#, #)', [lhs, _emitTypeName(type)]); |
353 } | 355 } |
354 | 356 |
355 if (node.notOperator != null) { | 357 if (node.notOperator != null) { |
356 return js.call('!#', result); | 358 return js.call('!#', result); |
357 } | 359 } |
358 return result; | 360 return result; |
359 } | 361 } |
360 | 362 |
361 String _jsTypeofName(DartType t) { | 363 String _jsTypeofName(DartType t) { |
362 if (rules.isIntType(t) || rules.isDoubleType(t)) return 'number'; | 364 if (rules.isNumberInJS(t)) return 'number'; |
363 if (rules.isStringType(t)) return 'string'; | 365 if (rules.isStringType(t)) return 'string'; |
364 if (rules.isBoolType(t)) return 'boolean'; | 366 if (rules.isBoolType(t)) return 'boolean'; |
365 return null; | 367 return null; |
366 } | 368 } |
367 | 369 |
368 @override | 370 @override |
369 visitFunctionTypeAlias(FunctionTypeAlias node) { | 371 visitFunctionTypeAlias(FunctionTypeAlias node) { |
370 var element = node.element; | 372 var element = node.element; |
371 var type = element.type; | 373 var type = element.type; |
372 var name = element.name; | 374 var name = element.name; |
(...skipping 1786 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2159 element, type, name, node.argumentList, node.isConst); | 2161 element, type, name, node.argumentList, node.isConst); |
2160 } | 2162 } |
2161 | 2163 |
2162 /// True if this type is built-in to JS, and we use the values unwrapped. | 2164 /// True if this type is built-in to JS, and we use the values unwrapped. |
2163 /// For these types we generate a calling convention via static | 2165 /// For these types we generate a calling convention via static |
2164 /// "extension methods". This allows types to be extended without adding | 2166 /// "extension methods". This allows types to be extended without adding |
2165 /// extensions directly on the prototype. | 2167 /// extensions directly on the prototype. |
2166 bool _isJSBuiltinType(DartType t) => | 2168 bool _isJSBuiltinType(DartType t) => |
2167 typeIsPrimitiveInJS(t) || rules.isStringType(t); | 2169 typeIsPrimitiveInJS(t) || rules.isStringType(t); |
2168 | 2170 |
2169 bool typeIsPrimitiveInJS(DartType t) => rules.isIntType(t) || | 2171 bool typeIsPrimitiveInJS(DartType t) => |
2170 rules.isDoubleType(t) || | 2172 rules.isNumberInJS(t) || rules.isBoolType(t); |
2171 rules.isBoolType(t) || | |
2172 rules.isNumType(t); | |
2173 | 2173 |
2174 bool typeIsNonNullablePrimitiveInJS(DartType t) => | 2174 bool typeIsNonNullablePrimitiveInJS(DartType t) => |
2175 typeIsPrimitiveInJS(t) && rules.isNonNullableType(t); | 2175 typeIsPrimitiveInJS(t) && rules.isNonNullableType(t); |
2176 | 2176 |
2177 bool binaryOperationIsPrimitive(DartType leftT, DartType rightT) => | 2177 bool binaryOperationIsPrimitive(DartType leftT, DartType rightT) => |
2178 typeIsPrimitiveInJS(leftT) && typeIsPrimitiveInJS(rightT); | 2178 typeIsPrimitiveInJS(leftT) && typeIsPrimitiveInJS(rightT); |
2179 | 2179 |
2180 bool unaryOperationIsPrimitive(DartType t) => typeIsPrimitiveInJS(t); | 2180 bool unaryOperationIsPrimitive(DartType t) => typeIsPrimitiveInJS(t); |
2181 | 2181 |
2182 bool _isNonNullableExpression(Expression expr) { | 2182 bool _isNonNullableExpression(Expression expr) { |
(...skipping 1073 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3256 class JSGenerator extends CodeGenerator { | 3256 class JSGenerator extends CodeGenerator { |
3257 final _extensionTypes = new HashSet<ClassElement>(); | 3257 final _extensionTypes = new HashSet<ClassElement>(); |
3258 | 3258 |
3259 JSGenerator(AbstractCompiler compiler) : super(compiler) { | 3259 JSGenerator(AbstractCompiler compiler) : super(compiler) { |
3260 // TODO(jacobr): determine the the set of types with extension methods from | 3260 // TODO(jacobr): determine the the set of types with extension methods from |
3261 // the annotations rather than hard coding the list once the analyzer | 3261 // the annotations rather than hard coding the list once the analyzer |
3262 // supports summaries. | 3262 // supports summaries. |
3263 var context = compiler.context; | 3263 var context = compiler.context; |
3264 var src = context.sourceFactory.forUri('dart:_interceptors'); | 3264 var src = context.sourceFactory.forUri('dart:_interceptors'); |
3265 var interceptors = context.computeLibraryElement(src); | 3265 var interceptors = context.computeLibraryElement(src); |
3266 for (var t in ['JSArray', 'JSString', 'JSInt', 'JSDouble', 'JSBool']) { | 3266 for (var t in ['JSArray', 'JSString', 'JSNumber', 'JSBool']) { |
3267 _addExtensionType(interceptors.getType(t).type); | 3267 _addExtensionType(interceptors.getType(t).type); |
3268 } | 3268 } |
| 3269 // TODO(jmesserly): manually add `int` and `double` |
| 3270 // Unfortunately our current analyzer rejects "implements int". |
| 3271 // Fix was landed, so we can remove this hack once we're updated: |
| 3272 // https://github.com/dart-lang/sdk/commit/d7cd11f86a02f55269fc8d9843e7758eb
eeb81c8 |
| 3273 _addExtensionType(context.typeProvider.intType); |
| 3274 _addExtensionType(context.typeProvider.doubleType); |
3269 } | 3275 } |
3270 | 3276 |
3271 void _addExtensionType(InterfaceType t) { | 3277 void _addExtensionType(InterfaceType t) { |
3272 if (t.isObject || !_extensionTypes.add(t.element)) return; | 3278 if (t.isObject || !_extensionTypes.add(t.element)) return; |
3273 t = fillDynamicTypeArgs(t, rules.provider) as InterfaceType; | 3279 t = fillDynamicTypeArgs(t, rules.provider) as InterfaceType; |
3274 t.interfaces.forEach(_addExtensionType); | 3280 t.interfaces.forEach(_addExtensionType); |
3275 t.mixins.forEach(_addExtensionType); | 3281 t.mixins.forEach(_addExtensionType); |
3276 _addExtensionType(t.superclass); | 3282 _addExtensionType(t.superclass); |
3277 } | 3283 } |
3278 | 3284 |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3315 | 3321 |
3316 /// A special kind of element created by the compiler, signifying a temporary | 3322 /// A special kind of element created by the compiler, signifying a temporary |
3317 /// variable. These objects use instance equality, and should be shared | 3323 /// variable. These objects use instance equality, and should be shared |
3318 /// everywhere in the tree where they are treated as the same variable. | 3324 /// everywhere in the tree where they are treated as the same variable. |
3319 class TemporaryVariableElement extends LocalVariableElementImpl { | 3325 class TemporaryVariableElement extends LocalVariableElementImpl { |
3320 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 3326 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
3321 | 3327 |
3322 int get hashCode => identityHashCode(this); | 3328 int get hashCode => identityHashCode(this); |
3323 bool operator ==(Object other) => identical(this, other); | 3329 bool operator ==(Object other) => identical(this, other); |
3324 } | 3330 } |
OLD | NEW |