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 import 'dart:io' show Directory, File; | 8 import 'dart:io' show Directory, File; |
9 | 9 |
10 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 10 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
(...skipping 1424 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1435 visitInstanceCreationExpression(InstanceCreationExpression node) { | 1435 visitInstanceCreationExpression(InstanceCreationExpression node) { |
1436 return js.call( | 1436 return js.call( |
1437 'new #(#)', [_visit(node.constructorName), _visit(node.argumentList)]); | 1437 'new #(#)', [_visit(node.constructorName), _visit(node.argumentList)]); |
1438 } | 1438 } |
1439 | 1439 |
1440 /// True if this type is built-in to JS, and we use the values unwrapped. | 1440 /// True if this type is built-in to JS, and we use the values unwrapped. |
1441 /// For these types we generate a calling convention via static | 1441 /// For these types we generate a calling convention via static |
1442 /// "extension methods". This allows types to be extended without adding | 1442 /// "extension methods". This allows types to be extended without adding |
1443 /// extensions directly on the prototype. | 1443 /// extensions directly on the prototype. |
1444 bool _isJSBuiltinType(DartType t) => | 1444 bool _isJSBuiltinType(DartType t) => |
1445 rules.isNumType(t) || rules.isStringType(t) || rules.isBoolType(t); | 1445 typeIsPrimitiveInJS(t) || rules.isStringType(t); |
1446 | 1446 |
1447 bool typeIsPrimitiveInJS(DartType t) => !rules.isDynamic(t) && | 1447 bool typeIsPrimitiveInJS(DartType t) => rules.isIntType(t) || |
1448 (rules.isIntType(t) || | 1448 rules.isDoubleType(t) || |
1449 rules.isDoubleType(t) || | 1449 rules.isBoolType(t) || |
1450 rules.isBoolType(t) || | 1450 rules.isNumType(t); |
1451 rules.isNumType(t)); | |
1452 | 1451 |
1453 bool typeIsNonNullablePrimitiveInJS(DartType t) => | 1452 bool typeIsNonNullablePrimitiveInJS(DartType t) => |
1454 typeIsPrimitiveInJS(t) && rules.isNonNullableType(t); | 1453 typeIsPrimitiveInJS(t) && rules.isNonNullableType(t); |
1455 | 1454 |
1456 bool binaryOperationIsPrimitive(DartType leftT, DartType rightT) => | 1455 bool binaryOperationIsPrimitive(DartType leftT, DartType rightT) => |
1457 typeIsPrimitiveInJS(leftT) && typeIsPrimitiveInJS(rightT); | 1456 typeIsPrimitiveInJS(leftT) && typeIsPrimitiveInJS(rightT); |
1458 | 1457 |
1459 bool unaryOperationIsPrimitive(DartType t) => typeIsPrimitiveInJS(t); | 1458 bool unaryOperationIsPrimitive(DartType t) => typeIsPrimitiveInJS(t); |
1460 | 1459 |
1461 bool _isNonNullableExpression(Expression expr) { | 1460 bool _isNonNullableExpression(Expression expr) { |
(...skipping 12 matching lines...) Expand all Loading... |
1474 return _isNonNullableExpression(expr.expression); | 1473 return _isNonNullableExpression(expr.expression); |
1475 } | 1474 } |
1476 DartType type = null; | 1475 DartType type = null; |
1477 if (expr is BinaryExpression) { | 1476 if (expr is BinaryExpression) { |
1478 type = getStaticType(expr.leftOperand); | 1477 type = getStaticType(expr.leftOperand); |
1479 } else if (expr is PrefixExpression) { | 1478 } else if (expr is PrefixExpression) { |
1480 type = getStaticType(expr.operand); | 1479 type = getStaticType(expr.operand); |
1481 } else if (expr is PostfixExpression) { | 1480 } else if (expr is PostfixExpression) { |
1482 type = getStaticType(expr.operand); | 1481 type = getStaticType(expr.operand); |
1483 } | 1482 } |
1484 if (type != null && typeIsPrimitiveInJS(type)) { | 1483 if (type != null && _isJSBuiltinType(type)) { |
1485 return true; | 1484 return true; |
1486 } | 1485 } |
1487 if (expr is MethodInvocation) { | 1486 if (expr is MethodInvocation) { |
1488 // TODO(vsm): This logic overlaps with the resolver. | 1487 // TODO(vsm): This logic overlaps with the resolver. |
1489 // Where is the best place to put this? | 1488 // Where is the best place to put this? |
1490 var e = expr.methodName.staticElement; | 1489 var e = expr.methodName.staticElement; |
1491 if (e is FunctionElement && | 1490 if (e is FunctionElement && |
1492 e.library.name == '_foreign_helper' && | 1491 e.library.name == '_foreign_helper' && |
1493 e.name == 'JS') { | 1492 e.name == 'JS') { |
1494 // Fix types for JS builtin calls. | 1493 // Fix types for JS builtin calls. |
(...skipping 24 matching lines...) Expand all Loading... |
1519 } | 1518 } |
1520 | 1519 |
1521 @override | 1520 @override |
1522 JS.Expression visitBinaryExpression(BinaryExpression node) { | 1521 JS.Expression visitBinaryExpression(BinaryExpression node) { |
1523 var op = node.operator; | 1522 var op = node.operator; |
1524 var left = node.leftOperand; | 1523 var left = node.leftOperand; |
1525 var right = node.rightOperand; | 1524 var right = node.rightOperand; |
1526 var leftType = getStaticType(left); | 1525 var leftType = getStaticType(left); |
1527 var rightType = getStaticType(right); | 1526 var rightType = getStaticType(right); |
1528 | 1527 |
| 1528 // TODO(jmesserly): this may not work correctly with options.ignoreTypes, |
| 1529 // because that results in unreliable type annotations. See issue #134, |
| 1530 // probably the checker/resolver is the right place to implement that, by |
| 1531 // replacing staticTypes with `dynamic` as needed, so codegen "just works". |
1529 var code; | 1532 var code; |
1530 if (op.type.isEqualityOperator) { | 1533 if (op.type.isEqualityOperator) { |
1531 // If we statically know LHS or RHS is null we can generate a clean check. | 1534 // If we statically know LHS or RHS is null we can generate a clean check. |
1532 // We can also do this if both sides are the same primitive type. | 1535 // We can also do this if both sides are the same primitive type. |
1533 if (_canUsePrimitiveEquality(left, right)) { | 1536 if (_canUsePrimitiveEquality(left, right)) { |
1534 code = op.type == TokenType.EQ_EQ ? '# == #' : '# != #'; | 1537 code = op.type == TokenType.EQ_EQ ? '# == #' : '# != #'; |
1535 } else { | 1538 } else { |
1536 var bang = op.type == TokenType.BANG_EQ ? '!' : ''; | 1539 var bang = op.type == TokenType.BANG_EQ ? '!' : ''; |
1537 code = '${bang}dart.equals(#, #)'; | 1540 code = '${bang}dart.equals(#, #)'; |
1538 } | 1541 } |
1539 return js.call(code, [_visit(left), _visit(right)]); | 1542 return js.call(code, [_visit(left), _visit(right)]); |
1540 } | 1543 } |
1541 | 1544 |
1542 if (binaryOperationIsPrimitive(leftType, rightType)) { | 1545 if (binaryOperationIsPrimitive(leftType, rightType) || |
| 1546 rules.isStringType(leftType) && op.type == TokenType.PLUS) { |
| 1547 |
1543 // special cases where we inline the operation | 1548 // special cases where we inline the operation |
1544 // these values are assumed to be non-null (determined by the checker) | 1549 // these values are assumed to be non-null (determined by the checker) |
1545 // TODO(jmesserly): it would be nice to just inline the method from core, | 1550 // TODO(jmesserly): it would be nice to just inline the method from core, |
1546 // instead of special cases here. | 1551 // instead of special cases here. |
1547 if (op.type == TokenType.TILDE_SLASH) { | 1552 if (op.type == TokenType.TILDE_SLASH) { |
1548 // `a ~/ b` is equivalent to `(a / b).truncate()` | 1553 // `a ~/ b` is equivalent to `(a / b).truncate()` |
1549 code = '(# / #).truncate()'; | 1554 code = '(# / #).truncate()'; |
1550 } else { | 1555 } else { |
1551 // TODO(vsm): When do Dart ops not map to JS? | 1556 // TODO(vsm): When do Dart ops not map to JS? |
1552 code = '# $op #'; | 1557 code = '# $op #'; |
(...skipping 967 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2520 } | 2525 } |
2521 } | 2526 } |
2522 | 2527 |
2523 // TODO(jmesserly): validate the library. See issue #135. | 2528 // TODO(jmesserly): validate the library. See issue #135. |
2524 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; | 2529 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; |
2525 | 2530 |
2526 // TODO(jacobr): we would like to do something like the following | 2531 // TODO(jacobr): we would like to do something like the following |
2527 // but we don't have summary support yet. | 2532 // but we don't have summary support yet. |
2528 // bool _supportJsExtensionMethod(AnnotatedNode node) => | 2533 // bool _supportJsExtensionMethod(AnnotatedNode node) => |
2529 // _getAnnotation(node, "SupportJsExtensionMethod") != null; | 2534 // _getAnnotation(node, "SupportJsExtensionMethod") != null; |
OLD | NEW |