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_metalet; | 5 library dev_compiler.src.codegen.js_metalet; |
6 | 6 |
7 // TODO(jmesserly): import from its own package | 7 // TODO(jmesserly): import from its own package |
8 import 'package:dev_compiler/src/js/js_ast.dart'; | 8 import 'package:dev_compiler/src/js/js_ast.dart'; |
9 import 'package:dev_compiler/src/js/precedence.dart'; | 9 import 'package:dev_compiler/src/js/precedence.dart'; |
10 | 10 |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
57 /// Returns an expression that ignores the result. This is a cross between | 57 /// Returns an expression that ignores the result. This is a cross between |
58 /// [toExpression] and [toStatement]. Used for C-style for-loop updaters, | 58 /// [toExpression] and [toStatement]. Used for C-style for-loop updaters, |
59 /// which is an expression syntactically, but functions more like a statement. | 59 /// which is an expression syntactically, but functions more like a statement. |
60 Expression toVoidExpression() { | 60 Expression toVoidExpression() { |
61 var block = toStatement(); | 61 var block = toStatement(); |
62 var s = block.statements; | 62 var s = block.statements; |
63 if (s.length == 1 && s.first is ExpressionStatement) { | 63 if (s.length == 1 && s.first is ExpressionStatement) { |
64 ExpressionStatement es = s.first; | 64 ExpressionStatement es = s.first; |
65 return es.expression; | 65 return es.expression; |
66 } | 66 } |
67 return new Call(new ArrowFun([], block), []); | 67 |
| 68 return _toInvokedFunction(block); |
68 } | 69 } |
69 | 70 |
70 Expression toAssignExpression(Expression left) { | 71 Expression toAssignExpression(Expression left) { |
71 if (left is Identifier) { | 72 if (left is Identifier) { |
72 var simple = _simplifyAssignment(left); | 73 var simple = _simplifyAssignment(left); |
73 if (simple != null) return simple; | 74 if (simple != null) return simple; |
74 | 75 |
75 var exprs = body.toList(); | 76 var exprs = body.toList(); |
76 exprs.add(exprs.removeLast().toAssignExpression(left)); | 77 exprs.add(exprs.removeLast().toAssignExpression(left)); |
77 return new MetaLet(variables, exprs); | 78 return new MetaLet(variables, exprs); |
78 } | 79 } |
79 return super.toAssignExpression(left); | 80 return super.toAssignExpression(left); |
80 } | 81 } |
81 | 82 |
82 Statement toVariableDeclaration(Identifier name) { | 83 Statement toVariableDeclaration(Identifier name) { |
83 var simple = _simplifyAssignment(name, isDeclaration: true); | 84 var simple = _simplifyAssignment(name, isDeclaration: true); |
84 if (simple != null) return simple.toStatement(); | 85 if (simple != null) return simple.toStatement(); |
85 return super.toVariableDeclaration(name); | 86 return super.toVariableDeclaration(name); |
86 } | 87 } |
87 | 88 |
88 Expression toExpression() { | 89 Expression toExpression() { |
89 if (_expression != null) return _expression; | 90 if (_expression != null) return _expression; |
90 var block = toReturn(); | 91 var block = toReturn(); |
91 var s = block.statements; | 92 var s = block.statements; |
92 if (s.length == 1 && s.first is Return) { | 93 if (s.length == 1 && s.first is Return) { |
93 Return es = s.first; | 94 Return es = s.first; |
94 return _expression = es.value; | 95 return _expression = es.value; |
95 } | 96 } |
96 // Wrap it in an immediately called function to get in expression context. | 97 // Wrap it in an immediately called function to get in expression context. |
97 // TODO(jmesserly): | 98 return _expression = _toInvokedFunction(block); |
98 return _expression = new Call(new ArrowFun([], block), []); | |
99 } | 99 } |
100 | 100 |
101 Block toStatement() { | 101 Block toStatement() { |
102 // Skip return value if not used. | 102 // Skip return value if not used. |
103 var statements = body.map((e) => e.toStatement()).toList(); | 103 var statements = body.map((e) => e.toStatement()).toList(); |
104 if (statelessResult) statements.removeLast(); | 104 if (statelessResult) statements.removeLast(); |
105 return _finishStatement(statements); | 105 return _finishStatement(statements); |
106 } | 106 } |
107 | 107 |
108 Block toReturn() { | 108 Block toReturn() { |
(...skipping 13 matching lines...) Expand all Loading... |
122 | 122 |
123 accept(NodeVisitor visitor) => toExpression().accept(visitor); | 123 accept(NodeVisitor visitor) => toExpression().accept(visitor); |
124 | 124 |
125 void visitChildren(NodeVisitor visitor) { | 125 void visitChildren(NodeVisitor visitor) { |
126 toExpression().visitChildren(visitor); | 126 toExpression().visitChildren(visitor); |
127 } | 127 } |
128 | 128 |
129 /// This generates as either a comma expression or a call. | 129 /// This generates as either a comma expression or a call. |
130 int get precedenceLevel => variables.isEmpty ? EXPRESSION : CALL; | 130 int get precedenceLevel => variables.isEmpty ? EXPRESSION : CALL; |
131 | 131 |
| 132 Expression _toInvokedFunction(Statement block) { |
| 133 var finder = new _YieldFinder(); |
| 134 block.accept(finder); |
| 135 if (!finder.hasYield) { |
| 136 return new Call(new ArrowFun([], block), []); |
| 137 } |
| 138 // If we have a yield, it's more tricky. We'll create a `function*`, which |
| 139 // we `yield*` to immediately invoke. We also may need to bind this: |
| 140 Expression fn = new Fun([], block, isGenerator: true); |
| 141 if (finder.hasThis) fn = js.call('#.bind(this)', fn); |
| 142 return new Yield(new Call(fn, []), star: true); |
| 143 } |
| 144 |
132 Block _finishStatement(List<Statement> statements) { | 145 Block _finishStatement(List<Statement> statements) { |
133 var params = <TemporaryId>[]; | 146 var params = <TemporaryId>[]; |
134 var values = <Expression>[]; | 147 var values = <Expression>[]; |
135 var block = _build(params, values, new Block(statements)); | 148 var block = _build(params, values, new Block(statements)); |
136 if (params.isEmpty) return block; | 149 if (params.isEmpty) return block; |
137 | 150 |
138 var vars = []; | 151 var vars = []; |
139 for (int i = 0; i < params.length; i++) { | 152 for (int i = 0; i < params.length; i++) { |
140 vars.add(new VariableInitialization(params[i], values[i])); | 153 vars.add(new VariableInitialization(params[i], values[i])); |
141 } | 154 } |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
223 | 236 |
224 var newBody = new Expression.binary([assign]..addAll(body), ','); | 237 var newBody = new Expression.binary([assign]..addAll(body), ','); |
225 Binary comma = new Template(null, newBody).safeCreate({name: left}); | 238 Binary comma = new Template(null, newBody).safeCreate({name: left}); |
226 return new MetaLet(vars, comma.commaToExpressionList(), | 239 return new MetaLet(vars, comma.commaToExpressionList(), |
227 statelessResult: statelessResult); | 240 statelessResult: statelessResult); |
228 } | 241 } |
229 } | 242 } |
230 | 243 |
231 class _VariableUseCounter extends BaseVisitor { | 244 class _VariableUseCounter extends BaseVisitor { |
232 final counts = <String, int>{}; | 245 final counts = <String, int>{}; |
233 visitInterpolatedExpression(InterpolatedExpression node) { | 246 @override visitInterpolatedExpression(InterpolatedExpression node) { |
234 int n = counts[node.nameOrPosition]; | 247 int n = counts[node.nameOrPosition]; |
235 counts[node.nameOrPosition] = n == null ? 1 : n + 1; | 248 counts[node.nameOrPosition] = n == null ? 1 : n + 1; |
236 } | 249 } |
237 } | 250 } |
238 | 251 |
239 class _IdentFinder extends BaseVisitor { | 252 class _IdentFinder extends BaseVisitor { |
240 final String name; | 253 final String name; |
241 bool found = false; | 254 bool found = false; |
242 _IdentFinder(this.name); | 255 _IdentFinder(this.name); |
243 | 256 |
244 visitIdentifier(Identifier node) { | 257 @override visitIdentifier(Identifier node) { |
245 if (node.name == name) found = true; | 258 if (node.name == name) found = true; |
246 } | 259 } |
247 visitNode(Node node) { | 260 @override visitNode(Node node) { |
248 if (!found) super.visitNode(node); | 261 if (!found) super.visitNode(node); |
249 } | 262 } |
250 } | 263 } |
| 264 |
| 265 class _YieldFinder extends BaseVisitor { |
| 266 bool hasYield = false; |
| 267 bool hasThis = false; |
| 268 bool _nestedFunction = false; |
| 269 @override visitThis(This node) { |
| 270 hasThis = true; |
| 271 } |
| 272 @override visitFunctionExpression(FunctionExpression node) { |
| 273 var savedNested = _nestedFunction; |
| 274 _nestedFunction = true; |
| 275 super.visitFunctionExpression(node); |
| 276 _nestedFunction = savedNested; |
| 277 } |
| 278 @override visitYield(Yield node) { |
| 279 if (!_nestedFunction) hasYield = true; |
| 280 } |
| 281 @override visitNode(Node node) { |
| 282 if (!hasYield) super.visitNode(node); |
| 283 } |
| 284 } |
OLD | NEW |