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

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

Issue 1111893002: small refactor: use JS.* prefix for our other JS extensions (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 years, 7 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/codegen/js_codegen.dart ('k') | lib/src/codegen/js_names.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 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
11 import 'js_names.dart' show JSTemporary; 11 import 'js_names.dart' show TemporaryId;
12 12
13 /// A synthetic `let*` node, similar to that found in Scheme. 13 /// A synthetic `let*` node, similar to that found in Scheme.
14 /// 14 ///
15 /// For example, postfix increment can be desugared as: 15 /// For example, postfix increment can be desugared as:
16 /// 16 ///
17 /// // psuedocode mix of Scheme and JS: 17 /// // psuedocode mix of Scheme and JS:
18 /// (let* (x1=expr1, x2=expr2, t=x1[x2]) { x1[x2] = t + 1; t }) 18 /// (let* (x1=expr1, x2=expr2, t=x1[x2]) { x1[x2] = t + 1; t })
19 /// 19 ///
20 /// [JSMetaLet] will simplify itself automatically when [toExpression], 20 /// [MetaLet] will simplify itself automatically when [toExpression],
21 /// [toStatement], or [toReturn] is called. 21 /// [toStatement], or [toReturn] is called.
22 /// 22 ///
23 /// * variables used once will be inlined. 23 /// * variables used once will be inlined.
24 /// * if used in a statement context they can emit as blocks. 24 /// * if used in a statement context they can emit as blocks.
25 /// * if return value is not used it can be eliminated, see [statelessResult]. 25 /// * if return value is not used it can be eliminated, see [statelessResult].
26 /// * if there are no variables, the codegen will be simplified. 26 /// * if there are no variables, the codegen will be simplified.
27 /// 27 ///
28 /// Because this deals with JS AST nodes, it is not aware of any Dart semantics 28 /// Because this deals with JS AST nodes, it is not aware of any Dart semantics
29 /// around statelessness (such as `final` variables). [variables] should not 29 /// around statelessness (such as `final` variables). [variables] should not
30 /// be created for these Dart expressions. 30 /// be created for these Dart expressions.
31 /// 31 ///
32 class JSMetaLet extends Expression { 32 class MetaLet extends Expression {
33 /// Creates a temporary to contain the value of [expr]. The temporary can be 33 /// Creates a temporary to contain the value of [expr]. The temporary can be
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 /// Conceptually this is like a comma expression: the last value is returned.
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
55 JSMetaLet(this.variables, this.body, {this.statelessResult: false}); 55 MetaLet(this.variables, this.body, {this.statelessResult: false});
56 56
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 return new Call(new ArrowFun([], block), []);
68 } 68 }
69 69
70 Expression toAssignExpression(Expression left) { 70 Expression toAssignExpression(Expression left) {
71 if (left is Identifier) { 71 if (left is Identifier) {
72 var simple = _simplifyAssignment(left); 72 var simple = _simplifyAssignment(left);
73 if (simple != null) return simple; 73 if (simple != null) return simple;
74 74
75 var exprs = body.toList(); 75 var exprs = body.toList();
76 exprs.add(exprs.removeLast().toAssignExpression(left)); 76 exprs.add(exprs.removeLast().toAssignExpression(left));
77 return new JSMetaLet(variables, exprs); 77 return new MetaLet(variables, exprs);
78 } 78 }
79 return super.toAssignExpression(left); 79 return super.toAssignExpression(left);
80 } 80 }
81 81
82 Statement toVariableDeclaration(Identifier name) { 82 Statement toVariableDeclaration(Identifier name) {
83 var simple = _simplifyAssignment(name, isDeclaration: true); 83 var simple = _simplifyAssignment(name, isDeclaration: true);
84 if (simple != null) return simple.toStatement(); 84 if (simple != null) return simple.toStatement();
85 return super.toVariableDeclaration(name); 85 return super.toVariableDeclaration(name);
86 } 86 }
87 87
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
129 129
130 var vars = []; 130 var vars = [];
131 for (int i = 0; i < params.length; i++) { 131 for (int i = 0; i < params.length; i++) {
132 vars.add(new VariableInitialization(params[i], values[i])); 132 vars.add(new VariableInitialization(params[i], values[i]));
133 } 133 }
134 134
135 return new Block( 135 return new Block(
136 [new VariableDeclarationList('let', vars).toStatement(), block]); 136 [new VariableDeclarationList('let', vars).toStatement(), block]);
137 } 137 }
138 138
139 Node _build(List<JSTemporary> params, List<Expression> values, Node node) { 139 Node _build(List<TemporaryId> params, List<Expression> values, Node node) {
140 // Visit the tree and count how many times each temp was used. 140 // Visit the tree and count how many times each temp was used.
141 var counter = new _VariableUseCounter(); 141 var counter = new _VariableUseCounter();
142 node.accept(counter); 142 node.accept(counter);
143 // Also count the init expressions. 143 // Also count the init expressions.
144 for (var init in variables.values) init.accept(counter); 144 for (var init in variables.values) init.accept(counter);
145 145
146 var substitutions = {}; 146 var substitutions = {};
147 _substitute(node) => new Template(null, node).safeCreate(substitutions); 147 _substitute(node) => new Template(null, node).safeCreate(substitutions);
148 148
149 variables.forEach((name, init) { 149 variables.forEach((name, init) {
150 // Since this is let*, subsequent variables can refer to previous ones, 150 // Since this is let*, subsequent variables can refer to previous ones,
151 // so we need to substitute here. 151 // so we need to substitute here.
152 init = _substitute(init); 152 init = _substitute(init);
153 int n = counter.counts[name]; 153 int n = counter.counts[name];
154 if (n == null || n < 2) { 154 if (n == null || n < 2) {
155 substitutions[name] = _substitute(init); 155 substitutions[name] = _substitute(init);
156 } else { 156 } else {
157 params.add(substitutions[name] = new JSTemporary(name)); 157 params.add(substitutions[name] = new TemporaryId(name));
158 values.add(init); 158 values.add(init);
159 } 159 }
160 }); 160 });
161 161
162 // Interpolate the body: 162 // Interpolate the body:
163 // Replace interpolated exprs with their value, if it only occurs once. 163 // Replace interpolated exprs with their value, if it only occurs once.
164 // Otherwise replace it with a temp, which will be assigned once. 164 // Otherwise replace it with a temp, which will be assigned once.
165 return _substitute(node); 165 return _substitute(node);
166 } 166 }
167 167
168 /// If we finish with an assignment to an identifier, try to simplify the 168 /// If we finish with an assignment to an identifier, try to simplify the
169 /// block. For example: 169 /// block. For example:
170 /// 170 ///
171 /// ((_) => _.add(1), _.add(2), result = _)([]) 171 /// ((_) => _.add(1), _.add(2), result = _)([])
172 /// 172 ///
173 /// Can be transformed to: 173 /// Can be transformed to:
174 /// 174 ///
175 /// (result = [], result.add(1), result.add(2), result) 175 /// (result = [], result.add(1), result.add(2), result)
176 /// 176 ///
177 /// However we should not simplify in this case because `result` is read: 177 /// However we should not simplify in this case because `result` is read:
178 /// 178 ///
179 /// ((_) => _.addAll(result), _.add(2), result = _)([]) 179 /// ((_) => _.addAll(result), _.add(2), result = _)([])
180 /// 180 ///
181 JSMetaLet _simplifyAssignment(Identifier left, {bool isDeclaration: false}) { 181 MetaLet _simplifyAssignment(Identifier left, {bool isDeclaration: false}) {
182 // See if the result value is a let* temporary variable. 182 // See if the result value is a let* temporary variable.
183 if (body.last is! InterpolatedExpression) return null; 183 if (body.last is! InterpolatedExpression) return null;
184 184
185 InterpolatedExpression last = body.last; 185 InterpolatedExpression last = body.last;
186 String name = last.nameOrPosition; 186 String name = last.nameOrPosition;
187 if (!variables.containsKey(name)) return null; 187 if (!variables.containsKey(name)) return null;
188 188
189 // Variables declared can't be used inside their initializer. 189 // Variables declared can't be used inside their initializer.
190 if (!isDeclaration) { 190 if (!isDeclaration) {
191 var finder = new _IdentFinder(left.name); 191 var finder = new _IdentFinder(left.name);
192 for (var expr in body) { 192 for (var expr in body) {
193 if (finder.found) break; 193 if (finder.found) break;
194 expr.accept(finder); 194 expr.accept(finder);
195 } 195 }
196 // If the identifier was used elsewhere, bail, because we're going to 196 // If the identifier was used elsewhere, bail, because we're going to
197 // change the order of when the assignment happens. 197 // change the order of when the assignment happens.
198 if (finder.found) return null; 198 if (finder.found) return null;
199 } 199 }
200 200
201 var vars = new Map<String, Expression>.from(variables); 201 var vars = new Map<String, Expression>.from(variables);
202 var value = vars.remove(name); 202 var value = vars.remove(name);
203 Expression assign; 203 Expression assign;
204 if (isDeclaration) { 204 if (isDeclaration) {
205 // Technically, putting one of these in a comma expression is not 205 // Technically, putting one of these in a comma expression is not
206 // legal. However when isDeclaration is true, toStatement will be 206 // legal. However when isDeclaration is true, toStatement will be
207 // called immediately on the JSMetaLet, which results in legal JS. 207 // called immediately on the MetaLet, which results in legal JS.
208 assign = new VariableDeclarationList( 208 assign = new VariableDeclarationList(
209 'let', [new VariableInitialization(left, value)]); 209 'let', [new VariableInitialization(left, value)]);
210 } else { 210 } else {
211 assign = value.toAssignExpression(left); 211 assign = value.toAssignExpression(left);
212 } 212 }
213 213
214 var newBody = new Expression.binary([assign]..addAll(body), ','); 214 var newBody = new Expression.binary([assign]..addAll(body), ',');
215 Binary comma = new Template(null, newBody).safeCreate({name: left}); 215 Binary comma = new Template(null, newBody).safeCreate({name: left});
216 return new JSMetaLet(vars, comma.commaToExpressionList(), 216 return new MetaLet(vars, comma.commaToExpressionList(),
217 statelessResult: statelessResult); 217 statelessResult: statelessResult);
218 } 218 }
219 } 219 }
220 220
221 class _VariableUseCounter extends BaseVisitor { 221 class _VariableUseCounter extends BaseVisitor {
222 final counts = <String, int>{}; 222 final counts = <String, int>{};
223 visitInterpolatedExpression(InterpolatedExpression node) { 223 visitInterpolatedExpression(InterpolatedExpression node) {
224 int n = counts[node.nameOrPosition]; 224 int n = counts[node.nameOrPosition];
225 counts[node.nameOrPosition] = n == null ? 1 : n + 1; 225 counts[node.nameOrPosition] = n == null ? 1 : n + 1;
226 } 226 }
227 } 227 }
228 228
229 class _IdentFinder extends BaseVisitor { 229 class _IdentFinder extends BaseVisitor {
230 final String name; 230 final String name;
231 bool found = false; 231 bool found = false;
232 _IdentFinder(this.name); 232 _IdentFinder(this.name);
233 233
234 visitIdentifier(Identifier node) { 234 visitIdentifier(Identifier node) {
235 if (node.name == name) found = true; 235 if (node.name == name) found = true;
236 } 236 }
237 visitNode(Node node) { 237 visitNode(Node node) {
238 if (!found) super.visitNode(node); 238 if (!found) super.visitNode(node);
239 } 239 }
240 } 240 }
OLDNEW
« no previous file with comments | « lib/src/codegen/js_codegen.dart ('k') | lib/src/codegen/js_names.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698