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 1458 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1469 Expression target = null; | 1469 Expression target = null; |
1470 SimpleIdentifier id; | 1470 SimpleIdentifier id; |
1471 if (lhs is PropertyAccess) { | 1471 if (lhs is PropertyAccess) { |
1472 target = _getTarget(lhs); | 1472 target = _getTarget(lhs); |
1473 id = lhs.propertyName; | 1473 id = lhs.propertyName; |
1474 } else if (lhs is PrefixedIdentifier) { | 1474 } else if (lhs is PrefixedIdentifier) { |
1475 target = lhs.prefix; | 1475 target = lhs.prefix; |
1476 id = lhs.identifier; | 1476 id = lhs.identifier; |
1477 } | 1477 } |
1478 | 1478 |
1479 if (target != null && rules.isDynamicTarget(target)) { | 1479 if (target != null && DynamicInvoke.get(target)) { |
1480 return js.call('dart.$DPUT(#, #, #)', [ | 1480 return js.call('dart.$DPUT(#, #, #)', [ |
1481 _visit(target), | 1481 _visit(target), |
1482 _emitMemberName(id.name, type: getStaticType(target)), | 1482 _emitMemberName(id.name, type: getStaticType(target)), |
1483 _visit(rhs) | 1483 _visit(rhs) |
1484 ]); | 1484 ]); |
1485 } | 1485 } |
1486 | 1486 |
1487 return _visit(rhs).toAssignExpression(_visit(lhs)); | 1487 return _visit(rhs).toAssignExpression(_visit(lhs)); |
1488 } | 1488 } |
1489 | 1489 |
(...skipping 20 matching lines...) Expand all Loading... | |
1510 | 1510 |
1511 @override | 1511 @override |
1512 visitMethodInvocation(MethodInvocation node) { | 1512 visitMethodInvocation(MethodInvocation node) { |
1513 var target = node.isCascaded ? _cascadeTarget : node.target; | 1513 var target = node.isCascaded ? _cascadeTarget : node.target; |
1514 | 1514 |
1515 var result = _emitForeignJS(node); | 1515 var result = _emitForeignJS(node); |
1516 if (result != null) return result; | 1516 if (result != null) return result; |
1517 | 1517 |
1518 String code; | 1518 String code; |
1519 if (target == null || isLibraryPrefix(target)) { | 1519 if (target == null || isLibraryPrefix(target)) { |
1520 if (rules.isDynamicCall(node.methodName)) { | 1520 if (DynamicInvoke.get(node.methodName)) { |
1521 code = 'dart.$DCALL(#, #)'; | 1521 code = 'dart.$DCALL(#, #)'; |
1522 } else { | 1522 } else { |
1523 code = '#(#)'; | 1523 code = '#(#)'; |
1524 } | 1524 } |
1525 return js.call( | 1525 return js.call( |
1526 code, [_visit(node.methodName), _visit(node.argumentList)]); | 1526 code, [_visit(node.methodName), _visit(node.argumentList)]); |
1527 } | 1527 } |
1528 | 1528 |
1529 var type = getStaticType(target); | 1529 var type = getStaticType(target); |
1530 var name = node.methodName.name; | 1530 var name = node.methodName.name; |
1531 var element = node.methodName.staticElement; | 1531 var element = node.methodName.staticElement; |
1532 bool isStatic = element is ExecutableElement && element.isStatic; | 1532 bool isStatic = element is ExecutableElement && element.isStatic; |
1533 var memberName = _emitMemberName(name, type: type, isStatic: isStatic); | 1533 var memberName = _emitMemberName(name, type: type, isStatic: isStatic); |
1534 | 1534 |
1535 if (rules.isDynamicTarget(target)) { | 1535 if (DynamicInvoke.get(target)) { |
1536 code = 'dart.$DSEND(#, #, #)'; | 1536 code = 'dart.$DSEND(#, #, #)'; |
1537 } else if (rules.isDynamicCall(node.methodName)) { | 1537 } else if (DynamicInvoke.get(node.methodName)) { |
1538 // This is a dynamic call to a statically know target. For example: | 1538 // This is a dynamic call to a statically know target. For example: |
1539 // class Foo { Function bar; } | 1539 // class Foo { Function bar; } |
1540 // new Foo().bar(); // dynamic call | 1540 // new Foo().bar(); // dynamic call |
1541 code = 'dart.$DCALL(#.#, #)'; | 1541 code = 'dart.$DCALL(#.#, #)'; |
1542 } else if (_requiresStaticDispatch(target, name)) { | 1542 } else if (_requiresStaticDispatch(target, name)) { |
1543 assert(rules.objectMembers[name] is FunctionType); | 1543 assert(rules.objectMembers[name] is FunctionType); |
1544 // Object methods require a helper for null checks. | 1544 // Object methods require a helper for null checks. |
1545 return js.call('dart.#(#, #)', [ | 1545 return js.call('dart.#(#, #)', [ |
1546 memberName, | 1546 memberName, |
1547 _visit(target), | 1547 _visit(target), |
(...skipping 21 matching lines...) Expand all Loading... | |
1569 assert(result is JS.Expression || node.parent is ExpressionStatement); | 1569 assert(result is JS.Expression || node.parent is ExpressionStatement); |
1570 return result; | 1570 return result; |
1571 } | 1571 } |
1572 return null; | 1572 return null; |
1573 } | 1573 } |
1574 | 1574 |
1575 @override | 1575 @override |
1576 JS.Expression visitFunctionExpressionInvocation( | 1576 JS.Expression visitFunctionExpressionInvocation( |
1577 FunctionExpressionInvocation node) { | 1577 FunctionExpressionInvocation node) { |
1578 var code; | 1578 var code; |
1579 if (rules.isDynamicCall(node.function)) { | 1579 if (DynamicInvoke.get(node.function)) { |
1580 code = 'dart.$DCALL(#, #)'; | 1580 code = 'dart.$DCALL(#, #)'; |
1581 } else { | 1581 } else { |
1582 code = '#(#)'; | 1582 code = '#(#)'; |
1583 } | 1583 } |
1584 return js.call(code, [_visit(node.function), _visit(node.argumentList)]); | 1584 return js.call(code, [_visit(node.function), _visit(node.argumentList)]); |
1585 } | 1585 } |
1586 | 1586 |
1587 @override | 1587 @override |
1588 List<JS.Expression> visitArgumentList(ArgumentList node) { | 1588 List<JS.Expression> visitArgumentList(ArgumentList node) { |
1589 var args = <JS.Expression>[]; | 1589 var args = <JS.Expression>[]; |
(...skipping 390 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1980 // See [_isTemporary]. | 1980 // See [_isTemporary]. |
1981 // TODO(jmesserly): alternatives are | 1981 // TODO(jmesserly): alternatives are |
1982 // * (ab)use Element.isSynthetic, which isn't currently used for | 1982 // * (ab)use Element.isSynthetic, which isn't currently used for |
1983 // LocalVariableElementImpl, so we could repurpose to mean "temp". | 1983 // LocalVariableElementImpl, so we could repurpose to mean "temp". |
1984 // * add a new property to LocalVariableElementImpl. | 1984 // * add a new property to LocalVariableElementImpl. |
1985 // * create a new subtype of LocalVariableElementImpl to mark a temp. | 1985 // * create a new subtype of LocalVariableElementImpl to mark a temp. |
1986 var id = | 1986 var id = |
1987 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, name, -1)); | 1987 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, name, -1)); |
1988 id.staticElement = new TemporaryVariableElement.forNode(id); | 1988 id.staticElement = new TemporaryVariableElement.forNode(id); |
1989 id.staticType = type; | 1989 id.staticType = type; |
1990 DynamicInvoke.set(id, type.isDynamic); | |
Jennifer Messerly
2015/06/12 18:18:09
this is the downside, it's one more bit to copy ..
| |
1990 return id; | 1991 return id; |
1991 } | 1992 } |
1992 | 1993 |
1993 JS.Expression _emitConst(JS.Expression expr()) { | 1994 JS.Expression _emitConst(JS.Expression expr()) { |
1994 // TODO(jmesserly): emit the constants at top level if possible. | 1995 // TODO(jmesserly): emit the constants at top level if possible. |
1995 // This wasn't quite working, so disabled for now. | 1996 // This wasn't quite working, so disabled for now. |
1996 return js.call('dart.const(#)', expr()); | 1997 return js.call('dart.const(#)', expr()); |
1997 } | 1998 } |
1998 | 1999 |
1999 /// Returns a new expression, which can be be used safely *once* on the | 2000 /// Returns a new expression, which can be be used safely *once* on the |
2000 /// left hand side, and *once* on the right side of an assignment. | 2001 /// left hand side, and *once* on the right side of an assignment. |
2001 /// For example: `expr1[expr2] += y` can be compiled as | 2002 /// For example: `expr1[expr2] += y` can be compiled as |
2002 /// `expr1[expr2] = expr1[expr2] + y`. | 2003 /// `expr1[expr2] = expr1[expr2] + y`. |
2003 /// | 2004 /// |
2004 /// The temporary scope will ensure `expr1` and `expr2` are only evaluated | 2005 /// The temporary scope will ensure `expr1` and `expr2` are only evaluated |
2005 /// once: `((x1, x2) => x1[x2] = x1[x2] + y)(expr1, expr2)`. | 2006 /// once: `((x1, x2) => x1[x2] = x1[x2] + y)(expr1, expr2)`. |
2006 /// | 2007 /// |
2007 /// If the expression does not end up using `x1` or `x2` more than once, or | 2008 /// If the expression does not end up using `x1` or `x2` more than once, or |
2008 /// if those expressions can be treated as stateless (e.g. they are | 2009 /// if those expressions can be treated as stateless (e.g. they are |
2009 /// non-mutated variables), then the resulting code will be simplified | 2010 /// non-mutated variables), then the resulting code will be simplified |
2010 /// automatically. | 2011 /// automatically. |
2011 /// | 2012 /// |
2012 /// [scope] can be mutated to contain any new temporaries that were created, | 2013 /// [scope] can be mutated to contain any new temporaries that were created, |
2013 /// unless [expr] is a SimpleIdentifier, in which case a temporary is not | 2014 /// unless [expr] is a SimpleIdentifier, in which case a temporary is not |
2014 /// needed. | 2015 /// needed. |
2015 Expression _bindLeftHandSide( | 2016 Expression _bindLeftHandSide( |
2016 Map<String, JS.Expression> scope, Expression expr, {Expression context}) { | 2017 Map<String, JS.Expression> scope, Expression expr, {Expression context}) { |
2018 Expression result; | |
2017 if (expr is IndexExpression) { | 2019 if (expr is IndexExpression) { |
2018 IndexExpression index = expr; | 2020 IndexExpression index = expr; |
2019 return new IndexExpression.forTarget( | 2021 result = new IndexExpression.forTarget( |
2020 _bindValue(scope, 'o', index.target, context: context), | 2022 _bindValue(scope, 'o', index.target, context: context), |
2021 index.leftBracket, | 2023 index.leftBracket, |
2022 _bindValue(scope, 'i', index.index, context: context), | 2024 _bindValue(scope, 'i', index.index, context: context), |
2023 index.rightBracket)..staticType = expr.staticType; | 2025 index.rightBracket); |
2024 } else if (expr is PropertyAccess) { | 2026 } else if (expr is PropertyAccess) { |
2025 PropertyAccess prop = expr; | 2027 PropertyAccess prop = expr; |
2026 return new PropertyAccess( | 2028 result = new PropertyAccess( |
2027 _bindValue(scope, 'o', _getTarget(prop), context: context), | 2029 _bindValue(scope, 'o', _getTarget(prop), context: context), |
2028 prop.operator, prop.propertyName)..staticType = expr.staticType; | 2030 prop.operator, prop.propertyName); |
2029 } else if (expr is PrefixedIdentifier) { | 2031 } else if (expr is PrefixedIdentifier) { |
2030 PrefixedIdentifier ident = expr; | 2032 PrefixedIdentifier ident = expr; |
2031 return new PrefixedIdentifier( | 2033 result = new PrefixedIdentifier( |
2032 _bindValue(scope, 'o', ident.prefix, context: context), ident.period, | 2034 _bindValue(scope, 'o', ident.prefix, context: context), ident.period, |
2033 ident.identifier)..staticType = expr.staticType; | 2035 ident.identifier); |
2036 } else { | |
2037 return expr as SimpleIdentifier; | |
2034 } | 2038 } |
2035 return expr as SimpleIdentifier; | 2039 result.staticType = expr.staticType; |
2040 DynamicInvoke.set(result, DynamicInvoke.get(expr)); | |
2041 return result; | |
2036 } | 2042 } |
2037 | 2043 |
2038 /// Creates a temporary to contain the value of [expr]. The temporary can be | 2044 /// Creates a temporary to contain the value of [expr]. The temporary can be |
2039 /// used multiple times in the resulting expression. For example: | 2045 /// used multiple times in the resulting expression. For example: |
2040 /// `expr ** 2` could be compiled as `expr * expr`. The temporary scope will | 2046 /// `expr ** 2` could be compiled as `expr * expr`. The temporary scope will |
2041 /// ensure `expr` is only evaluated once: `(x => x * x)(expr)`. | 2047 /// ensure `expr` is only evaluated once: `(x => x * x)(expr)`. |
2042 /// | 2048 /// |
2043 /// If the expression does not end up using `x` more than once, or if those | 2049 /// If the expression does not end up using `x` more than once, or if those |
2044 /// expressions can be treated as stateless (e.g. they are non-mutated | 2050 /// expressions can be treated as stateless (e.g. they are non-mutated |
2045 /// variables), then the resulting code will be simplified automatically. | 2051 /// variables), then the resulting code will be simplified automatically. |
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2201 var member = memberId.staticElement; | 2207 var member = memberId.staticElement; |
2202 if (member is PropertyAccessorElement) { | 2208 if (member is PropertyAccessorElement) { |
2203 member = (member as PropertyAccessorElement).variable; | 2209 member = (member as PropertyAccessorElement).variable; |
2204 } | 2210 } |
2205 bool isStatic = member is ClassMemberElement && member.isStatic; | 2211 bool isStatic = member is ClassMemberElement && member.isStatic; |
2206 if (isStatic) { | 2212 if (isStatic) { |
2207 _loader.declareBeforeUse(member); | 2213 _loader.declareBeforeUse(member); |
2208 } | 2214 } |
2209 var name = _emitMemberName(memberId.name, | 2215 var name = _emitMemberName(memberId.name, |
2210 type: getStaticType(target), isStatic: isStatic); | 2216 type: getStaticType(target), isStatic: isStatic); |
2211 if (rules.isDynamicTarget(target)) { | 2217 if (DynamicInvoke.get(target)) { |
2212 return js.call('dart.$DLOAD(#, #)', [_visit(target), name]); | 2218 return js.call('dart.$DLOAD(#, #)', [_visit(target), name]); |
2213 } | 2219 } |
2214 | 2220 |
2215 String code; | 2221 String code; |
2216 if (member != null && member is MethodElement && !isStatic) { | 2222 if (member != null && member is MethodElement && !isStatic) { |
2217 // Tear-off methods: explicitly bind it. | 2223 // Tear-off methods: explicitly bind it. |
2218 // TODO(leafp): Attach runtime types to these static tearoffs | 2224 // TODO(leafp): Attach runtime types to these static tearoffs |
2219 if (_requiresStaticDispatch(target, memberId.name)) { | 2225 if (_requiresStaticDispatch(target, memberId.name)) { |
2220 return js.call('dart.#.bind(#)', [name, _visit(target)]); | 2226 return js.call('dart.#.bind(#)', [name, _visit(target)]); |
2221 } | 2227 } |
2222 code = 'dart.bind(#, #)'; | 2228 code = 'dart.bind(#, #)'; |
2223 } else if (_requiresStaticDispatch(target, memberId.name)) { | 2229 } else if (_requiresStaticDispatch(target, memberId.name)) { |
2224 return js.call('dart.#(#)', [name, _visit(target)]); | 2230 return js.call('dart.#(#)', [name, _visit(target)]); |
2225 } else { | 2231 } else { |
2226 code = '#.#'; | 2232 code = '#.#'; |
2227 } | 2233 } |
2228 | 2234 |
2229 return js.call(code, [_visit(target), name]); | 2235 return js.call(code, [_visit(target), name]); |
2230 } | 2236 } |
2231 | 2237 |
2232 /// Emits a generic send, like an operator method. | 2238 /// Emits a generic send, like an operator method. |
2233 /// | 2239 /// |
2234 /// **Please note** this function does not support method invocation syntax | 2240 /// **Please note** this function does not support method invocation syntax |
2235 /// `obj.name(args)` because that could be a getter followed by a call. | 2241 /// `obj.name(args)` because that could be a getter followed by a call. |
2236 /// See [visitMethodInvocation]. | 2242 /// See [visitMethodInvocation]. |
2237 JS.Expression _emitSend( | 2243 JS.Expression _emitSend( |
2238 Expression target, String name, List<Expression> args) { | 2244 Expression target, String name, List<Expression> args) { |
2239 var type = getStaticType(target); | 2245 var type = getStaticType(target); |
2240 var memberName = _emitMemberName(name, unary: args.isEmpty, type: type); | 2246 var memberName = _emitMemberName(name, unary: args.isEmpty, type: type); |
2241 if (rules.isDynamicTarget(target)) { | 2247 if (DynamicInvoke.get(target)) { |
2242 // dynamic dispatch | 2248 // dynamic dispatch |
2243 var dynamicHelper = const {'[]': DINDEX, '[]=': DSETINDEX}[name]; | 2249 var dynamicHelper = const {'[]': DINDEX, '[]=': DSETINDEX}[name]; |
2244 if (dynamicHelper != null) { | 2250 if (dynamicHelper != null) { |
2245 return js.call( | 2251 return js.call( |
2246 'dart.$dynamicHelper(#, #)', [_visit(target), _visitList(args)]); | 2252 'dart.$dynamicHelper(#, #)', [_visit(target), _visitList(args)]); |
2247 } | 2253 } |
2248 return js.call('dart.$DSEND(#, #, #)', [ | 2254 return js.call('dart.$DSEND(#, #, #)', [ |
2249 _visit(target), | 2255 _visit(target), |
2250 memberName, | 2256 memberName, |
2251 _visitList(args) | 2257 _visitList(args) |
(...skipping 534 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2786 | 2792 |
2787 /// A special kind of element created by the compiler, signifying a temporary | 2793 /// A special kind of element created by the compiler, signifying a temporary |
2788 /// variable. These objects use instance equality, and should be shared | 2794 /// variable. These objects use instance equality, and should be shared |
2789 /// everywhere in the tree where they are treated as the same variable. | 2795 /// everywhere in the tree where they are treated as the same variable. |
2790 class TemporaryVariableElement extends LocalVariableElementImpl { | 2796 class TemporaryVariableElement extends LocalVariableElementImpl { |
2791 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 2797 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
2792 | 2798 |
2793 int get hashCode => identityHashCode(this); | 2799 int get hashCode => identityHashCode(this); |
2794 bool operator ==(Object other) => identical(this, other); | 2800 bool operator ==(Object other) => identical(this, other); |
2795 } | 2801 } |
OLD | NEW |