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

Unified 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 side-by-side diff with in-line comments
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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/src/compiler/js_metalet.dart
diff --git a/lib/src/compiler/js_metalet.dart b/lib/src/compiler/js_metalet.dart
index 5bddf5a8e65fea24f71ee543016f750da4c3ab60..c0581d638a1ed6f1ac10fdcff68c98b7c63f954c 100644
--- a/lib/src/compiler/js_metalet.dart
+++ b/lib/src/compiler/js_metalet.dart
@@ -36,7 +36,7 @@ class MetaLet extends Expression {
/// If the expression does not end up using `x` more than once, or if those
/// expressions can be treated as [stateless] (e.g. they are non-mutated
/// variables), then the resulting code will be simplified automatically.
- final Map<String, Expression> variables;
+ final Map<MetaLetVariable, Expression> variables;
/// A list of expressions in the body.
/// The last value should represent the returned value.
@@ -171,49 +171,40 @@ class MetaLet extends Expression {
}
Block _finishStatement(List<Statement> statements) {
- var params = <TemporaryId>[];
- var values = <Expression>[];
- var block = _build(params, values, new Block(statements));
- if (params.isEmpty) return block;
-
- var vars = [];
- for (int i = 0; i < params.length; i++) {
- vars.add(new VariableInitialization(params[i], values[i]));
- }
-
- return new Block(<Statement>[
- new VariableDeclarationList('let', vars).toStatement(),
- block
- ]);
- }
-
- Node _build(List<TemporaryId> params, List<Expression> values, Node node) {
// Visit the tree and count how many times each temp was used.
var counter = new _VariableUseCounter();
+ var node = new Block(statements);
node.accept(counter);
// Also count the init expressions.
for (var init in variables.values) init.accept(counter);
- var substitutions = {};
- _substitute(node) => new Template(null, node).safeCreate(substitutions);
-
- variables.forEach((name, init) {
+ var initializers = <VariableInitialization>[];
+ var substitutions = <MetaLetVariable, Expression>{};
+ variables.forEach((variable, init) {
// Since this is let*, subsequent variables can refer to previous ones,
// so we need to substitute here.
- init = _substitute(init);
- int n = counter.counts[name];
- if (n == null || n < 2) {
- substitutions[name] = _substitute(init);
+ init = _substitute(init, substitutions);
+ int n = counter.counts[variable];
+ if (n == 1) {
+ // Replace interpolated exprs with their value, if it only occurs once.
+ substitutions[variable] = init;
} else {
- params.add(substitutions[name] = new TemporaryId(name));
- values.add(init);
+ // Otherwise replace it with a temp, which will be assigned once.
+ var temp = new TemporaryId(variable.displayName);
+ substitutions[variable] = temp;
+ initializers.add(new VariableInitialization(temp, init));
}
});
- // Interpolate the body:
- // Replace interpolated exprs with their value, if it only occurs once.
- // Otherwise replace it with a temp, which will be assigned once.
- return _substitute(node);
+ // Interpolate the body.
+ node = _substitute(node, substitutions);
+ if (initializers.isNotEmpty) {
+ node = new Block([
+ new VariableDeclarationList('let', initializers).toStatement(),
+ node
+ ]);
+ }
+ return node;
}
/// If we finish with an assignment to an identifier, try to simplify the
@@ -231,11 +222,10 @@ class MetaLet extends Expression {
///
MetaLet _simplifyAssignment(Identifier left, {bool isDeclaration: false}) {
// See if the result value is a let* temporary variable.
- if (body.last is! InterpolatedExpression) return null;
+ if (body.last is! MetaLetVariable) return null;
- InterpolatedExpression last = body.last;
- String name = last.nameOrPosition;
- if (!variables.containsKey(name)) return null;
+ MetaLetVariable result = body.last;
+ if (!variables.containsKey(result)) return null;
// Variables declared can't be used inside their initializer, so make
// sure we don't transform an assignment into an initializer.
@@ -252,8 +242,8 @@ class MetaLet extends Expression {
if (finder.found) return null;
}
- var vars = new Map<String, Expression>.from(variables);
- var value = vars.remove(name);
+ var vars = new Map<MetaLetVariable, Expression>.from(variables);
+ var value = vars.remove(result);
Expression assign;
if (isDeclaration) {
// Technically, putting one of these in a comma expression is not
@@ -266,18 +256,59 @@ class MetaLet extends Expression {
}
var newBody = new Expression.binary([assign]..addAll(body), ',');
- Binary comma = new Template(null, newBody).safeCreate({name: left});
- return new MetaLet(vars, comma.commaToExpressionList(),
+ newBody = _substitute(newBody, {result: left});
+ return new MetaLet(vars, newBody.commaToExpressionList(),
statelessResult: statelessResult);
}
}
+/// Similar to [Template.instantiate] but works with free variables.
+Node _substitute(Node tree, Map<MetaLetVariable, Expression> substitutions) {
+ var generator = new InstantiatorGeneratorVisitor(/*forceCopy:*/ false);
+ var instantiator = generator.compile(tree);
+ var nodes = new List<MetaLetVariable>.from(generator
+ .analysis.containsInterpolatedNode
+ .where((n) => n is MetaLetVariable));
+ if (nodes.isEmpty) return tree;
+
+ return instantiator(new Map.fromIterable(nodes,
+ key: (v) => (v as MetaLetVariable).nameOrPosition,
+ value: (v) => substitutions[v] ?? v));
+}
+
+/// A temporary variable used in a [MetaLet].
+///
+/// Each instance of this class represents a fresh variable. The same object
+/// should be used everywhere to refer to the same variable. Different variables
+/// with the same name are different, and will be renamed later on, if needed.
+///
+/// These variables will be replaced when the `let*` is complete, depending on
+/// how often they occur and whether they can be optimized away. See [MetaLet]
+/// for more information.
+///
+/// This class should never reach our final JS code.
+class MetaLetVariable extends InterpolatedExpression {
+ /// The suggested display name of this variable.
+ ///
+ /// This name should not be used
+ final String displayName;
+
+ /// Compute fresh IDs to avoid
+ static int _uniqueId = 0;
+
+ MetaLetVariable(String displayName)
+ : displayName = displayName,
+ super(displayName + '@${++_uniqueId}');
+}
+
class _VariableUseCounter extends BaseVisitor {
- final counts = <String, int>{};
+ final counts = <MetaLetVariable, int>{};
@override
visitInterpolatedExpression(InterpolatedExpression node) {
- int n = counts[node.nameOrPosition];
- counts[node.nameOrPosition] = n == null ? 1 : n + 1;
+ if (node is MetaLetVariable) {
+ int n = counts[node];
+ counts[node] = n == null ? 1 : n + 1;
+ }
}
}
« 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