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 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; | 5 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; |
6 | 6 |
7 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 7 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
8 import 'package:analyzer/dart/ast/token.dart'; | 8 import 'package:analyzer/dart/ast/token.dart'; |
9 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; | 9 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; |
10 import 'package:analyzer/src/generated/constant.dart'; | 10 import 'package:analyzer/src/generated/constant.dart'; |
(...skipping 1451 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1462 if (node.isGetter || node.isSetter) { | 1462 if (node.isGetter || node.isSetter) { |
1463 // Add these later so we can use getter/setter syntax. | 1463 // Add these later so we can use getter/setter syntax. |
1464 _properties.add(node); | 1464 _properties.add(node); |
1465 return null; | 1465 return null; |
1466 } | 1466 } |
1467 | 1467 |
1468 var body = <JS.Statement>[]; | 1468 var body = <JS.Statement>[]; |
1469 _flushLibraryProperties(body); | 1469 _flushLibraryProperties(body); |
1470 | 1470 |
1471 var name = node.name.name; | 1471 var name = node.name.name; |
1472 | 1472 var fn = _emitFunction(node.functionExpression); |
1473 var fn = _visit(node.functionExpression); | |
1474 | 1473 |
1475 if (currentLibrary.source.isInSystemLibrary && | 1474 if (currentLibrary.source.isInSystemLibrary && |
1476 _isInlineJSFunction(node.functionExpression)) { | 1475 _isInlineJSFunction(node.functionExpression)) { |
1477 fn = _simplifyPassThroughArrowFunCallBody(fn); | 1476 fn = _simplifyPassThroughArrowFunCallBody(fn); |
1478 } | 1477 } |
1479 | 1478 |
1480 var id = new JS.Identifier(name); | 1479 var id = new JS.Identifier(name); |
1481 body.add(annotate(new JS.FunctionDeclaration(id, fn), node, node.element)); | 1480 body.add(annotate(new JS.FunctionDeclaration(id, fn), node, node.element)); |
1482 if (!_isDartRuntime) { | 1481 if (!_isDartRuntime) { |
1483 body.add(_emitFunctionTagged(id, node.element.type, topLevel: true) | 1482 body.add(_emitFunctionTagged(id, node.element.type, topLevel: true) |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1524 } | 1523 } |
1525 } | 1524 } |
1526 } | 1525 } |
1527 } | 1526 } |
1528 return fn; | 1527 return fn; |
1529 } | 1528 } |
1530 | 1529 |
1531 JS.Method _emitTopLevelProperty(FunctionDeclaration node) { | 1530 JS.Method _emitTopLevelProperty(FunctionDeclaration node) { |
1532 var name = node.name.name; | 1531 var name = node.name.name; |
1533 return annotate( | 1532 return annotate( |
1534 new JS.Method(_propertyName(name), _visit(node.functionExpression), | 1533 new JS.Method( |
1534 _propertyName(name), _emitFunction(node.functionExpression), | |
1535 isGetter: node.isGetter, isSetter: node.isSetter), | 1535 isGetter: node.isGetter, isSetter: node.isSetter), |
1536 node, | 1536 node, |
1537 node.element); | 1537 node.element); |
1538 } | 1538 } |
1539 | 1539 |
1540 bool _executesAtTopLevel(AstNode node) { | 1540 bool _executesAtTopLevel(AstNode node) { |
1541 var ancestor = node.getAncestor((n) => | 1541 var ancestor = node.getAncestor((n) => |
1542 n is FunctionBody || | 1542 n is FunctionBody || |
1543 (n is FieldDeclaration && n.staticKeyword == null) || | 1543 (n is FieldDeclaration && n.staticKeyword == null) || |
1544 (n is ConstructorDeclaration && n.constKeyword == null)); | 1544 (n is ConstructorDeclaration && n.constKeyword == null)); |
1545 return ancestor == null; | 1545 return ancestor == null; |
1546 } | 1546 } |
1547 | 1547 |
1548 bool _typeIsLoaded(DartType type) { | 1548 bool _typeIsLoaded(DartType type) { |
1549 if (type is FunctionType && (type.name == '' || type.name == null)) { | 1549 if (type is FunctionType && (type.name == '' || type.name == null)) { |
1550 return (_typeIsLoaded(type.returnType) && | 1550 return (_typeIsLoaded(type.returnType) && |
1551 type.optionalParameterTypes.every(_typeIsLoaded) && | 1551 type.optionalParameterTypes.every(_typeIsLoaded) && |
1552 type.namedParameterTypes.values.every(_typeIsLoaded) && | 1552 type.namedParameterTypes.values.every(_typeIsLoaded) && |
1553 type.normalParameterTypes.every(_typeIsLoaded)); | 1553 type.normalParameterTypes.every(_typeIsLoaded)); |
1554 } | 1554 } |
1555 if (type.isDynamic || type.isVoid || type.isBottom) return true; | 1555 if (type.isDynamic || type.isVoid || type.isBottom) return true; |
1556 if (type is ParameterizedType && !type.typeArguments.every(_typeIsLoaded)) { | 1556 if (type is ParameterizedType && !type.typeArguments.every(_typeIsLoaded)) { |
1557 return false; | 1557 return false; |
1558 } | 1558 } |
1559 return _loader.isLoaded(type.element); | 1559 return _loader.isLoaded(type.element); |
1560 } | 1560 } |
1561 | 1561 |
1562 JS.Expression _emitFunctionTagged(JS.Expression clos, DartType type, | 1562 JS.Expression _emitFunctionTagged(JS.Expression fn, DartType type, |
1563 {topLevel: false}) { | 1563 {topLevel: false}) { |
1564 var name = type.name; | 1564 var name = type.name; |
1565 var lazy = topLevel && !_typeIsLoaded(type); | 1565 var lazy = topLevel && !_typeIsLoaded(type); |
1566 | 1566 |
1567 if (type is FunctionType && (name == '' || name == null)) { | 1567 if (type is FunctionType && (name == '' || name == null)) { |
1568 if (type.returnType.isDynamic && | 1568 if (type.returnType.isDynamic && |
1569 type.optionalParameterTypes.isEmpty && | 1569 type.optionalParameterTypes.isEmpty && |
1570 type.namedParameterTypes.isEmpty && | 1570 type.namedParameterTypes.isEmpty && |
1571 type.normalParameterTypes.every((t) => t.isDynamic)) { | 1571 type.normalParameterTypes.every((t) => t.isDynamic)) { |
1572 return js.call('dart.fn(#)', [clos]); | 1572 return js.call('dart.fn(#)', [fn]); |
1573 } | 1573 } |
1574 if (lazy) { | 1574 if (lazy) { |
1575 return js.call('dart.fn(#, () => #)', [clos, _emitFunctionRTTI(type)]); | 1575 return js.call('dart.fn(#, () => #)', [fn, _emitFunctionRTTI(type)]); |
1576 } | 1576 } |
1577 return js.call('dart.fn(#, #)', [clos, _emitFunctionTypeParts(type)]); | 1577 return js.call('dart.fn(#, #)', [fn, _emitFunctionTypeParts(type)]); |
1578 } | 1578 } |
1579 throw 'Function has non function type: $type'; | 1579 throw 'Function has non function type: $type'; |
1580 } | 1580 } |
1581 | 1581 |
1582 /// Emits an arrow FunctionExpression node. | |
1583 /// | |
1584 /// This should be used for all places in Dart's AST where FunctionExpression | |
1585 /// appears and the function is actually in an Expression context. These | |
1586 /// correspond to arrow functions in Dart. | |
1587 /// | |
1588 /// Contrast with [_emitFunction]. | |
1582 @override | 1589 @override |
1583 JS.Expression visitFunctionExpression(FunctionExpression node) { | 1590 JS.Expression visitFunctionExpression(FunctionExpression node) { |
1584 var params = visitFormalParameterList(node.parameters); | 1591 assert(node.parent is! FunctionDeclaration && |
1592 node.parent is! MethodDeclaration); | |
1593 return _emitFunctionTagged(_emitArrowFunction(node), getStaticType(node), | |
1594 topLevel: _executesAtTopLevel(node)); | |
1595 } | |
1596 | |
1597 JS.ArrowFun _emitArrowFunction(FunctionExpression node) { | |
1598 List<JS.Parameter> params; | |
1585 | 1599 |
1586 var body = node.body; | 1600 var body = node.body; |
1587 var parent = node.parent; | |
1588 var inStmt = parent.parent is FunctionDeclarationStatement; | |
1589 if (parent is FunctionDeclaration) { | |
1590 return _emitFunctionBody(node.element, node.parameters, body); | |
1591 } | |
1592 | |
1593 JS.Node jsBody; | 1601 JS.Node jsBody; |
1594 if (body.isGenerator || body.isAsynchronous) { | 1602 if (body.isGenerator || body.isAsynchronous) { |
1603 params = visitFormalParameterList(node.parameters, destructure: false); | |
1595 jsBody = _emitGeneratorFunctionBody(node.element, node.parameters, body); | 1604 jsBody = _emitGeneratorFunctionBody(node.element, node.parameters, body); |
1596 } else if (body is ExpressionFunctionBody) { | |
1597 jsBody = _visit(body.expression); | |
1598 } else { | |
1599 jsBody = _visit(body); | |
1600 } | |
1601 | |
1602 var type = node.element.type; | |
1603 var typeFormals = _emitTypeFormals(type.typeFormals); | |
1604 var returnType = emitTypeRef(type.returnType); | |
1605 | |
1606 JS.FunctionExpression fn; | |
1607 if (node.parameters.parameters | |
1608 .every((p) => p.kind != ParameterKind.NAMED)) { | |
1609 fn = new JS.ArrowFun(params, jsBody, | |
1610 typeParams: typeFormals, returnType: returnType); | |
1611 } else { | 1605 } else { |
1612 // Chrome Canary does not accept default values with destructuring in | 1606 // Chrome Canary does not accept default values with destructuring in |
1613 // arrow functions yet (e.g. `({a} = {}) => 1`) but happily accepts them | 1607 // arrow functions yet (e.g. `({a} = {}) => 1`) but happily accepts them |
1614 // with regular functions (e.g. `function({a} = {}) { return 1 }`). | 1608 // with regular functions (e.g. `function({a} = {}) { return 1 }`). |
1615 // Note that Firefox accepts both syntaxes just fine. | 1609 // Note that Firefox accepts both syntaxes just fine. |
1616 // TODO(ochafik): Simplify this code when Chrome Canary catches up. | 1610 // TODO(ochafik): Simplify this code when Chrome Canary catches up. |
1611 bool hasNamedParams = | |
1612 node.parameters.parameters.any((p) => p.kind == ParameterKind.NAMED); | |
1617 | 1613 |
1618 if (jsBody is JS.Expression) { | 1614 params = visitFormalParameterList(node.parameters, |
1619 jsBody = js.statement("{ return #; }", [jsBody]); | 1615 destructure: !hasNamedParams); |
1616 | |
1617 if (body is ExpressionFunctionBody) { | |
1618 // An arrow function can use the expression directly. | |
1619 jsBody = _visit(body.expression); | |
1620 } else { | |
1621 jsBody = _visit(body); | |
1620 } | 1622 } |
1621 fn = new JS.Fun(params, jsBody, | |
1622 typeParams: typeFormals, returnType: returnType); | |
1623 } | 1623 } |
1624 | 1624 |
1625 if (!inStmt) { | 1625 var type = node.element.type; |
1626 var type = getStaticType(node); | 1626 var fn = new JS.ArrowFun(params, jsBody, |
1627 return _emitFunctionTagged(fn, type, topLevel: _executesAtTopLevel(node)); | 1627 typeParams: _emitTypeFormals(type.typeFormals), |
1628 } | 1628 returnType: emitTypeRef(type.returnType)); |
1629 return fn; | 1629 return annotate(fn, node); |
1630 } | |
1631 | |
1632 /// Emits a non-arrow FunctionExpression node. | |
1633 /// | |
1634 /// This should be used for all places in Dart's AST where FunctionExpression | |
1635 /// appears but the function is not actually in an Expression context, such | |
1636 /// as methods, properties, and top-level functions. | |
1637 /// | |
1638 /// Contrast with [visitFunctionExpression]. | |
1639 JS.Fun _emitFunction(FunctionExpression node) { | |
1640 var fn = _emitFunctionBody(node.element, node.parameters, node.body); | |
1641 return annotate(fn, node); | |
1630 } | 1642 } |
1631 | 1643 |
1632 JS.Fun _emitFunctionBody(ExecutableElement element, | 1644 JS.Fun _emitFunctionBody(ExecutableElement element, |
1633 FormalParameterList parameters, FunctionBody body) { | 1645 FormalParameterList parameters, FunctionBody body) { |
1634 var returnType = emitTypeRef(element.returnType); | 1646 var returnType = emitTypeRef(element.returnType); |
1635 | 1647 |
1636 // sync*, async, async* | 1648 // sync*, async, async* |
1637 if (element.isAsynchronous || element.isGenerator) { | 1649 if (element.isAsynchronous || element.isGenerator) { |
1638 return new JS.Fun( | 1650 return new JS.Fun( |
1639 visitFormalParameterList(parameters, destructure: false), | 1651 visitFormalParameterList(parameters, destructure: false), |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1710 } | 1722 } |
1711 | 1723 |
1712 @override | 1724 @override |
1713 JS.Statement visitFunctionDeclarationStatement( | 1725 JS.Statement visitFunctionDeclarationStatement( |
1714 FunctionDeclarationStatement node) { | 1726 FunctionDeclarationStatement node) { |
1715 var func = node.functionDeclaration; | 1727 var func = node.functionDeclaration; |
1716 if (func.isGetter || func.isSetter) { | 1728 if (func.isGetter || func.isSetter) { |
1717 return js.comment('Unimplemented function get/set statement: $node'); | 1729 return js.comment('Unimplemented function get/set statement: $node'); |
1718 } | 1730 } |
1719 | 1731 |
1720 var fn = _visit(func.functionExpression); | 1732 var fn = _emitFunction(func.functionExpression); |
1721 | 1733 |
1722 var name = new JS.Identifier(func.name.name); | 1734 var name = new JS.Identifier(func.name.name); |
1723 JS.Statement declareFn; | 1735 JS.Statement declareFn; |
1724 if (JS.This.foundIn(fn)) { | 1736 if (JS.This.foundIn(fn)) { |
1725 declareFn = js.statement('const # = #.bind(this);', [name, fn]); | 1737 declareFn = js.statement('const # = #.bind(this);', [name, fn]); |
1726 } else { | 1738 } else { |
1727 declareFn = new JS.FunctionDeclaration(name, fn); | 1739 declareFn = new JS.FunctionDeclaration(name, fn); |
1728 } | 1740 } |
1729 declareFn = annotate(declareFn, node, node.functionDeclaration.element); | 1741 declareFn = annotate(declareFn, node, node.functionDeclaration.element); |
1730 | 1742 |
(...skipping 1760 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3491 } | 3503 } |
3492 | 3504 |
3493 _visit(AstNode node) { | 3505 _visit(AstNode node) { |
3494 if (node == null) return null; | 3506 if (node == null) return null; |
3495 var result = node.accept(this); | 3507 var result = node.accept(this); |
3496 if (result is JS.Node) result = annotate(result, node); | 3508 if (result is JS.Node) result = annotate(result, node); |
3497 return result; | 3509 return result; |
3498 } | 3510 } |
3499 | 3511 |
3500 // TODO(jmesserly): this will need to be a generic method, if we ever want to | 3512 // TODO(jmesserly): this will need to be a generic method, if we ever want to |
3501 // self-host strong mode. | 3513 // self-host strong mode. |
vsm
2016/02/25 00:48:04
Probably don't need the TODO anymore. :-)
Jennifer Messerly
2016/02/25 00:53:45
Yup! Have this fixed in a follow up CL I'm working
| |
3502 List/*<T>*/ _visitList/*<T>*/(Iterable<AstNode> nodes) { | 3514 List /*<T>*/ _visitList /*<T>*/ (Iterable<AstNode> nodes) { |
3503 if (nodes == null) return null; | 3515 if (nodes == null) return null; |
3504 var result = /*<T>*/ []; | 3516 var result = /*<T>*/ []; |
3505 for (var node in nodes) result.add(_visit(node)); | 3517 for (var node in nodes) result.add(_visit(node)); |
3506 return result; | 3518 return result; |
3507 } | 3519 } |
3508 | 3520 |
3509 /// Visits a list of expressions, creating a comma expression if needed in JS. | 3521 /// Visits a list of expressions, creating a comma expression if needed in JS. |
3510 JS.Expression _visitListToBinary(List<Expression> nodes, String operator) { | 3522 JS.Expression _visitListToBinary(List<Expression> nodes, String operator) { |
3511 if (nodes == null || nodes.isEmpty) return null; | 3523 if (nodes == null || nodes.isEmpty) return null; |
3512 return new JS.Expression.binary( | 3524 return new JS.Expression.binary( |
(...skipping 289 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3802 | 3814 |
3803 /// A special kind of element created by the compiler, signifying a temporary | 3815 /// A special kind of element created by the compiler, signifying a temporary |
3804 /// variable. These objects use instance equality, and should be shared | 3816 /// variable. These objects use instance equality, and should be shared |
3805 /// everywhere in the tree where they are treated as the same variable. | 3817 /// everywhere in the tree where they are treated as the same variable. |
3806 class TemporaryVariableElement extends LocalVariableElementImpl { | 3818 class TemporaryVariableElement extends LocalVariableElementImpl { |
3807 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 3819 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
3808 | 3820 |
3809 int get hashCode => identityHashCode(this); | 3821 int get hashCode => identityHashCode(this); |
3810 bool operator ==(Object other) => identical(this, other); | 3822 bool operator ==(Object other) => identical(this, other); |
3811 } | 3823 } |
OLD | NEW |