Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(145)

Side by Side Diff: lib/src/compiler/js_metalet.dart

Issue 1910233002: fixes #516, nested cascade code generation (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/src/compiler/code_generator.dart ('k') | lib/src/js_ast/template.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 // TODO(jmesserly): import from its own package 5 // TODO(jmesserly): import from its own package
6 import '../js_ast/js_ast.dart'; 6 import '../js_ast/js_ast.dart';
7 import '../js_ast/precedence.dart'; 7 import '../js_ast/precedence.dart';
8 8
9 import 'js_names.dart' show TemporaryId; 9 import 'js_names.dart' show TemporaryId;
10 10
(...skipping 18 matching lines...) Expand all
29 /// 29 ///
30 class MetaLet extends Expression { 30 class MetaLet extends Expression {
31 /// Creates a temporary to contain the value of [expr]. The temporary can be 31 /// Creates a temporary to contain the value of [expr]. The temporary can be
32 /// used multiple times in the resulting expression. For example: 32 /// used multiple times in the resulting expression. For example:
33 /// `expr ** 2` could be compiled as `expr * expr`. The temporary scope will 33 /// `expr ** 2` could be compiled as `expr * expr`. The temporary scope will
34 /// ensure `expr` is only evaluated once: `(x => x * x)(expr)`. 34 /// ensure `expr` is only evaluated once: `(x => x * x)(expr)`.
35 /// 35 ///
36 /// If the expression does not end up using `x` more than once, or if those 36 /// If the expression does not end up using `x` more than once, or if those
37 /// expressions can be treated as [stateless] (e.g. they are non-mutated 37 /// expressions can be treated as [stateless] (e.g. they are non-mutated
38 /// variables), then the resulting code will be simplified automatically. 38 /// variables), then the resulting code will be simplified automatically.
39 final Map<String, Expression> variables; 39 final Map<MetaLetVariable, Expression> variables;
40 40
41 /// A list of expressions in the body. 41 /// A list of expressions in the body.
42 /// The last value should represent the returned value. 42 /// The last value should represent the returned value.
43 final List<Expression> body; 43 final List<Expression> body;
44 44
45 /// True if the final expression in [body] can be skipped in [toStatement]. 45 /// True if the final expression in [body] can be skipped in [toStatement].
46 final bool statelessResult; 46 final bool statelessResult;
47 47
48 /// We run [toExpression] implicitly when the JS AST is visited, to get the 48 /// We run [toExpression] implicitly when the JS AST is visited, to get the
49 /// transformation to happen before the tree is printed. 49 /// transformation to happen before the tree is printed.
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
164 return new Call(new ArrowFun([], block), []); 164 return new Call(new ArrowFun([], block), []);
165 } 165 }
166 // If we have a yield, it's more tricky. We'll create a `function*`, which 166 // If we have a yield, it's more tricky. We'll create a `function*`, which
167 // we `yield*` to immediately invoke. We also may need to bind this: 167 // we `yield*` to immediately invoke. We also may need to bind this:
168 Expression fn = new Fun([], block, isGenerator: true); 168 Expression fn = new Fun([], block, isGenerator: true);
169 if (finder.hasThis) fn = js.call('#.bind(this)', fn); 169 if (finder.hasThis) fn = js.call('#.bind(this)', fn);
170 return new Yield(new Call(fn, []), star: true); 170 return new Yield(new Call(fn, []), star: true);
171 } 171 }
172 172
173 Block _finishStatement(List<Statement> statements) { 173 Block _finishStatement(List<Statement> statements) {
174 var params = <TemporaryId>[];
175 var values = <Expression>[];
176 var block = _build(params, values, new Block(statements));
177 if (params.isEmpty) return block;
178
179 var vars = [];
180 for (int i = 0; i < params.length; i++) {
181 vars.add(new VariableInitialization(params[i], values[i]));
182 }
183
184 return new Block(<Statement>[
185 new VariableDeclarationList('let', vars).toStatement(),
186 block
187 ]);
188 }
189
190 Node _build(List<TemporaryId> params, List<Expression> values, Node node) {
191 // Visit the tree and count how many times each temp was used. 174 // Visit the tree and count how many times each temp was used.
192 var counter = new _VariableUseCounter(); 175 var counter = new _VariableUseCounter();
176 var node = new Block(statements);
193 node.accept(counter); 177 node.accept(counter);
194 // Also count the init expressions. 178 // Also count the init expressions.
195 for (var init in variables.values) init.accept(counter); 179 for (var init in variables.values) init.accept(counter);
196 180
197 var substitutions = {}; 181 var initializers = <VariableInitialization>[];
198 _substitute(node) => new Template(null, node).safeCreate(substitutions); 182 var substitutions = <MetaLetVariable, Expression>{};
199 183 variables.forEach((variable, init) {
200 variables.forEach((name, init) {
201 // Since this is let*, subsequent variables can refer to previous ones, 184 // Since this is let*, subsequent variables can refer to previous ones,
202 // so we need to substitute here. 185 // so we need to substitute here.
203 init = _substitute(init); 186 init = _substitute(init, substitutions);
204 int n = counter.counts[name]; 187 int n = counter.counts[variable];
205 if (n == null || n < 2) { 188 if (n == 1) {
206 substitutions[name] = _substitute(init); 189 // Replace interpolated exprs with their value, if it only occurs once.
190 substitutions[variable] = init;
207 } else { 191 } else {
208 params.add(substitutions[name] = new TemporaryId(name)); 192 // Otherwise replace it with a temp, which will be assigned once.
209 values.add(init); 193 var temp = new TemporaryId(variable.displayName);
194 substitutions[variable] = temp;
195 initializers.add(new VariableInitialization(temp, init));
210 } 196 }
211 }); 197 });
212 198
213 // Interpolate the body: 199 // Interpolate the body.
214 // Replace interpolated exprs with their value, if it only occurs once. 200 node = _substitute(node, substitutions);
215 // Otherwise replace it with a temp, which will be assigned once. 201 if (initializers.isNotEmpty) {
216 return _substitute(node); 202 node = new Block([
203 new VariableDeclarationList('let', initializers).toStatement(),
204 node
205 ]);
206 }
207 return node;
217 } 208 }
218 209
219 /// If we finish with an assignment to an identifier, try to simplify the 210 /// If we finish with an assignment to an identifier, try to simplify the
220 /// block. For example: 211 /// block. For example:
221 /// 212 ///
222 /// ((_) => _.add(1), _.add(2), result = _)([]) 213 /// ((_) => _.add(1), _.add(2), result = _)([])
223 /// 214 ///
224 /// Can be transformed to: 215 /// Can be transformed to:
225 /// 216 ///
226 /// (result = [], result.add(1), result.add(2), result) 217 /// (result = [], result.add(1), result.add(2), result)
227 /// 218 ///
228 /// However we should not simplify in this case because `result` is read: 219 /// However we should not simplify in this case because `result` is read:
229 /// 220 ///
230 /// ((_) => _.addAll(result), _.add(2), result = _)([]) 221 /// ((_) => _.addAll(result), _.add(2), result = _)([])
231 /// 222 ///
232 MetaLet _simplifyAssignment(Identifier left, {bool isDeclaration: false}) { 223 MetaLet _simplifyAssignment(Identifier left, {bool isDeclaration: false}) {
233 // See if the result value is a let* temporary variable. 224 // See if the result value is a let* temporary variable.
234 if (body.last is! InterpolatedExpression) return null; 225 if (body.last is! MetaLetVariable) return null;
235 226
236 InterpolatedExpression last = body.last; 227 MetaLetVariable result = body.last;
237 String name = last.nameOrPosition; 228 if (!variables.containsKey(result)) return null;
238 if (!variables.containsKey(name)) return null;
239 229
240 // Variables declared can't be used inside their initializer, so make 230 // Variables declared can't be used inside their initializer, so make
241 // sure we don't transform an assignment into an initializer. 231 // sure we don't transform an assignment into an initializer.
242 // If this already was a declaration, then we know it's legal, so we can 232 // If this already was a declaration, then we know it's legal, so we can
243 // skip the check. 233 // skip the check.
244 if (!isDeclaration) { 234 if (!isDeclaration) {
245 var finder = new _IdentFinder(left.name); 235 var finder = new _IdentFinder(left.name);
246 for (var expr in body) { 236 for (var expr in body) {
247 if (finder.found) break; 237 if (finder.found) break;
248 expr.accept(finder); 238 expr.accept(finder);
249 } 239 }
250 // If the identifier was used elsewhere, bail, because we're going to 240 // If the identifier was used elsewhere, bail, because we're going to
251 // change the order of when the assignment happens. 241 // change the order of when the assignment happens.
252 if (finder.found) return null; 242 if (finder.found) return null;
253 } 243 }
254 244
255 var vars = new Map<String, Expression>.from(variables); 245 var vars = new Map<MetaLetVariable, Expression>.from(variables);
256 var value = vars.remove(name); 246 var value = vars.remove(result);
257 Expression assign; 247 Expression assign;
258 if (isDeclaration) { 248 if (isDeclaration) {
259 // Technically, putting one of these in a comma expression is not 249 // Technically, putting one of these in a comma expression is not
260 // legal. However when isDeclaration is true, toStatement will be 250 // legal. However when isDeclaration is true, toStatement will be
261 // called immediately on the MetaLet, which results in legal JS. 251 // called immediately on the MetaLet, which results in legal JS.
262 assign = new VariableDeclarationList( 252 assign = new VariableDeclarationList(
263 'let', [new VariableInitialization(left, value)]); 253 'let', [new VariableInitialization(left, value)]);
264 } else { 254 } else {
265 assign = value.toAssignExpression(left); 255 assign = value.toAssignExpression(left);
266 } 256 }
267 257
268 var newBody = new Expression.binary([assign]..addAll(body), ','); 258 var newBody = new Expression.binary([assign]..addAll(body), ',');
269 Binary comma = new Template(null, newBody).safeCreate({name: left}); 259 newBody = _substitute(newBody, {result: left});
270 return new MetaLet(vars, comma.commaToExpressionList(), 260 return new MetaLet(vars, newBody.commaToExpressionList(),
271 statelessResult: statelessResult); 261 statelessResult: statelessResult);
272 } 262 }
273 } 263 }
274 264
265 /// Similar to [Template.instantiate] but works with free variables.
266 Node _substitute(Node tree, Map<MetaLetVariable, Expression> substitutions) {
267 var generator = new InstantiatorGeneratorVisitor(/*forceCopy:*/ false);
268 var instantiator = generator.compile(tree);
269 var nodes = new List<MetaLetVariable>.from(generator
270 .analysis.containsInterpolatedNode
271 .where((n) => n is MetaLetVariable));
272 if (nodes.isEmpty) return tree;
273
274 return instantiator(new Map.fromIterable(nodes,
275 key: (v) => (v as MetaLetVariable).nameOrPosition,
276 value: (v) => substitutions[v] ?? v));
277 }
278
279 /// A temporary variable used in a [MetaLet].
280 ///
281 /// Each instance of this class represents a fresh variable. The same object
282 /// should be used everywhere to refer to the same variable. Different variables
283 /// with the same name are different, and will be renamed later on, if needed.
284 ///
285 /// These variables will be replaced when the `let*` is complete, depending on
286 /// how often they occur and whether they can be optimized away. See [MetaLet]
287 /// for more information.
288 ///
289 /// This class should never reach our final JS code.
290 class MetaLetVariable extends InterpolatedExpression {
291 /// The suggested display name of this variable.
292 ///
293 /// This name should not be used
294 final String displayName;
295
296 /// Compute fresh IDs to avoid
297 static int _uniqueId = 0;
298
299 MetaLetVariable(String displayName)
300 : displayName = displayName,
301 super(displayName + '@${++_uniqueId}');
302 }
303
275 class _VariableUseCounter extends BaseVisitor { 304 class _VariableUseCounter extends BaseVisitor {
276 final counts = <String, int>{}; 305 final counts = <MetaLetVariable, int>{};
277 @override 306 @override
278 visitInterpolatedExpression(InterpolatedExpression node) { 307 visitInterpolatedExpression(InterpolatedExpression node) {
279 int n = counts[node.nameOrPosition]; 308 if (node is MetaLetVariable) {
280 counts[node.nameOrPosition] = n == null ? 1 : n + 1; 309 int n = counts[node];
310 counts[node] = n == null ? 1 : n + 1;
311 }
281 } 312 }
282 } 313 }
283 314
284 class _IdentFinder extends BaseVisitor { 315 class _IdentFinder extends BaseVisitor {
285 final String name; 316 final String name;
286 bool found = false; 317 bool found = false;
287 _IdentFinder(this.name); 318 _IdentFinder(this.name);
288 319
289 @override 320 @override
290 visitIdentifier(Identifier node) { 321 visitIdentifier(Identifier node) {
(...skipping 26 matching lines...) Expand all
317 @override 348 @override
318 visitYield(Yield node) { 349 visitYield(Yield node) {
319 if (!_nestedFunction) hasYield = true; 350 if (!_nestedFunction) hasYield = true;
320 } 351 }
321 352
322 @override 353 @override
323 visitNode(Node node) { 354 visitNode(Node node) {
324 if (!hasYield) super.visitNode(node); 355 if (!hasYield) super.visitNode(node);
325 } 356 }
326 } 357 }
OLDNEW
« no previous file with comments | « lib/src/compiler/code_generator.dart ('k') | lib/src/js_ast/template.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698