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 23 matching lines...) Expand all Loading... |
34 /// used multiple times in the resulting expression. For example: | 34 /// used multiple times in the resulting expression. For example: |
35 /// `expr ** 2` could be compiled as `expr * expr`. The temporary scope will | 35 /// `expr ** 2` could be compiled as `expr * expr`. The temporary scope will |
36 /// ensure `expr` is only evaluated once: `(x => x * x)(expr)`. | 36 /// ensure `expr` is only evaluated once: `(x => x * x)(expr)`. |
37 /// | 37 /// |
38 /// If the expression does not end up using `x` more than once, or if those | 38 /// If the expression does not end up using `x` more than once, or if those |
39 /// expressions can be treated as [stateless] (e.g. they are non-mutated | 39 /// expressions can be treated as [stateless] (e.g. they are non-mutated |
40 /// variables), then the resulting code will be simplified automatically. | 40 /// variables), then the resulting code will be simplified automatically. |
41 final Map<String, Expression> variables; | 41 final Map<String, Expression> variables; |
42 | 42 |
43 /// A list of expressions in the body. | 43 /// A list of expressions in the body. |
44 /// Conceptually this is like a comma expression: the last value is returned. | 44 /// The last value should represent the returned value. |
45 final List<Expression> body; | 45 final List<Expression> body; |
46 | 46 |
47 /// True if the final expression in [body] can be skipped in [toStatement]. | 47 /// True if the final expression in [body] can be skipped in [toStatement]. |
48 final bool statelessResult; | 48 final bool statelessResult; |
49 | 49 |
50 /// We run [toExpression] implicitly when the JS AST is visited, to get the | 50 /// We run [toExpression] implicitly when the JS AST is visited, to get the |
51 /// transformation to happen before the tree is printed. | 51 /// transformation to happen before the tree is printed. |
52 /// This happens multiple times, so ensure the expression form is cached. | 52 /// This happens multiple times, so ensure the expression form is cached. |
53 Expression _expression; | 53 Expression _expression; |
54 | 54 |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
113 } | 113 } |
114 | 114 |
115 Block toYieldStatement({bool star: false}) { | 115 Block toYieldStatement({bool star: false}) { |
116 var statements = body | 116 var statements = body |
117 .map((e) => | 117 .map((e) => |
118 e == body.last ? e.toYieldStatement(star: star) : e.toStatement()) | 118 e == body.last ? e.toYieldStatement(star: star) : e.toStatement()) |
119 .toList(); | 119 .toList(); |
120 return _finishStatement(statements); | 120 return _finishStatement(statements); |
121 } | 121 } |
122 | 122 |
123 accept(NodeVisitor visitor) => toExpression().accept(visitor); | 123 accept(NodeVisitor visitor) { |
| 124 // TODO(jmesserly): we special case vistors from js_ast.Template, because it |
| 125 // doesn't know about MetaLet. Should we integrate directly? |
| 126 if (visitor is InstantiatorGeneratorVisitor) { |
| 127 return _templateVisitMetaLet(visitor); |
| 128 } else if (visitor is InterpolatedNodeAnalysis) { |
| 129 return visitor.visitNode(this); |
| 130 } else { |
| 131 return toExpression().accept(visitor); |
| 132 } |
| 133 } |
124 | 134 |
125 void visitChildren(NodeVisitor visitor) { | 135 void visitChildren(NodeVisitor visitor) { |
126 toExpression().visitChildren(visitor); | 136 // TODO(jmesserly): we special case vistors from js_ast.Template, because it |
| 137 // doesn't know about MetaLet. Should we integrate directly? |
| 138 if (visitor is InterpolatedNodeAnalysis || |
| 139 visitor is InstantiatorGeneratorVisitor) { |
| 140 variables.values.forEach((v) => v.accept(visitor)); |
| 141 body.forEach((v) => v.accept(visitor)); |
| 142 } else { |
| 143 toExpression().visitChildren(visitor); |
| 144 } |
127 } | 145 } |
128 | 146 |
129 /// This generates as either a comma expression or a call. | 147 /// This generates as either a comma expression or a call. |
130 int get precedenceLevel => variables.isEmpty ? EXPRESSION : CALL; | 148 int get precedenceLevel => variables.isEmpty ? EXPRESSION : CALL; |
131 | 149 |
| 150 /// Patch to pretend [Template] supports visitMetaLet. |
| 151 Instantiator _templateVisitMetaLet(InstantiatorGeneratorVisitor visitor) { |
| 152 var valueInstantiators = variables.values.map(visitor.visit); |
| 153 var bodyInstantiators = body.map(visitor.visit); |
| 154 |
| 155 return (args) => new MetaLet( |
| 156 new Map.fromIterables( |
| 157 variables.keys, valueInstantiators.map((i) => i(args))), |
| 158 bodyInstantiators.map((i) => i(args)).toList(), |
| 159 statelessResult: statelessResult); |
| 160 } |
| 161 |
132 Expression _toInvokedFunction(Statement block) { | 162 Expression _toInvokedFunction(Statement block) { |
133 var finder = new _YieldFinder(); | 163 var finder = new _YieldFinder(); |
134 block.accept(finder); | 164 block.accept(finder); |
135 if (!finder.hasYield) { | 165 if (!finder.hasYield) { |
136 return new Call(new ArrowFun([], block), []); | 166 return new Call(new ArrowFun([], block), []); |
137 } | 167 } |
138 // If we have a yield, it's more tricky. We'll create a `function*`, which | 168 // 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: | 169 // we `yield*` to immediately invoke. We also may need to bind this: |
140 Expression fn = new Fun([], block, isGenerator: true); | 170 Expression fn = new Fun([], block, isGenerator: true); |
141 if (finder.hasThis) fn = js.call('#.bind(this)', fn); | 171 if (finder.hasThis) fn = js.call('#.bind(this)', fn); |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
202 /// ((_) => _.addAll(result), _.add(2), result = _)([]) | 232 /// ((_) => _.addAll(result), _.add(2), result = _)([]) |
203 /// | 233 /// |
204 MetaLet _simplifyAssignment(Identifier left, {bool isDeclaration: false}) { | 234 MetaLet _simplifyAssignment(Identifier left, {bool isDeclaration: false}) { |
205 // See if the result value is a let* temporary variable. | 235 // See if the result value is a let* temporary variable. |
206 if (body.last is! InterpolatedExpression) return null; | 236 if (body.last is! InterpolatedExpression) return null; |
207 | 237 |
208 InterpolatedExpression last = body.last; | 238 InterpolatedExpression last = body.last; |
209 String name = last.nameOrPosition; | 239 String name = last.nameOrPosition; |
210 if (!variables.containsKey(name)) return null; | 240 if (!variables.containsKey(name)) return null; |
211 | 241 |
212 // Variables declared can't be used inside their initializer. | 242 // Variables declared can't be used inside their initializer, so make |
| 243 // sure we don't transform an assignment into an initializer. |
| 244 // If this already was a declaration, then we know it's legal, so we can |
| 245 // skip the check. |
213 if (!isDeclaration) { | 246 if (!isDeclaration) { |
214 var finder = new _IdentFinder(left.name); | 247 var finder = new _IdentFinder(left.name); |
215 for (var expr in body) { | 248 for (var expr in body) { |
216 if (finder.found) break; | 249 if (finder.found) break; |
217 expr.accept(finder); | 250 expr.accept(finder); |
218 } | 251 } |
219 // If the identifier was used elsewhere, bail, because we're going to | 252 // If the identifier was used elsewhere, bail, because we're going to |
220 // change the order of when the assignment happens. | 253 // change the order of when the assignment happens. |
221 if (finder.found) return null; | 254 if (finder.found) return null; |
222 } | 255 } |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
279 } | 312 } |
280 | 313 |
281 @override visitYield(Yield node) { | 314 @override visitYield(Yield node) { |
282 if (!_nestedFunction) hasYield = true; | 315 if (!_nestedFunction) hasYield = true; |
283 } | 316 } |
284 | 317 |
285 @override visitNode(Node node) { | 318 @override visitNode(Node node) { |
286 if (!hasYield) super.visitNode(node); | 319 if (!hasYield) super.visitNode(node); |
287 } | 320 } |
288 } | 321 } |
OLD | NEW |