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

Unified Diff: lib/src/codegen/js_metalet.dart

Issue 1879373004: Implement modular compilation (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/codegen/js_interop.dart ('k') | lib/src/codegen/js_module_item_order.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/src/codegen/js_metalet.dart
diff --git a/lib/src/codegen/js_metalet.dart b/lib/src/codegen/js_metalet.dart
deleted file mode 100644
index 02d7842fcd8671d684763840d7972f8817627a0c..0000000000000000000000000000000000000000
--- a/lib/src/codegen/js_metalet.dart
+++ /dev/null
@@ -1,326 +0,0 @@
-// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// TODO(jmesserly): import from its own package
-import '../js/js_ast.dart';
-import '../js/precedence.dart';
-
-import 'js_names.dart' show TemporaryId;
-
-/// A synthetic `let*` node, similar to that found in Scheme.
-///
-/// For example, postfix increment can be desugared as:
-///
-/// // psuedocode mix of Scheme and JS:
-/// (let* (x1=expr1, x2=expr2, t=x1[x2]) { x1[x2] = t + 1; t })
-///
-/// [MetaLet] will simplify itself automatically when [toExpression],
-/// [toStatement], [toReturn], or [toYieldStatement] is called.
-///
-/// * variables used once will be inlined.
-/// * if used in a statement context they can emit as blocks.
-/// * if return value is not used it can be eliminated, see [statelessResult].
-/// * if there are no variables, the codegen will be simplified.
-///
-/// Because this deals with JS AST nodes, it is not aware of any Dart semantics
-/// around statelessness (such as `final` variables). [variables] should not
-/// be created for these Dart expressions.
-///
-class MetaLet extends Expression {
- /// Creates a temporary to contain the value of [expr]. The temporary can be
- /// used multiple times in the resulting expression. For example:
- /// `expr ** 2` could be compiled as `expr * expr`. The temporary scope will
- /// ensure `expr` is only evaluated once: `(x => x * x)(expr)`.
- ///
- /// 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;
-
- /// A list of expressions in the body.
- /// The last value should represent the returned value.
- final List<Expression> body;
-
- /// True if the final expression in [body] can be skipped in [toStatement].
- final bool statelessResult;
-
- /// We run [toExpression] implicitly when the JS AST is visited, to get the
- /// transformation to happen before the tree is printed.
- /// This happens multiple times, so ensure the expression form is cached.
- Expression _expression;
-
- MetaLet(this.variables, this.body, {this.statelessResult: false});
-
- /// Returns an expression that ignores the result. This is a cross between
- /// [toExpression] and [toStatement]. Used for C-style for-loop updaters,
- /// which is an expression syntactically, but functions more like a statement.
- Expression toVoidExpression() {
- var block = toStatement();
- var s = block.statements;
- if (s.length == 1 && s.first is ExpressionStatement) {
- ExpressionStatement es = s.first;
- return es.expression;
- }
-
- return _toInvokedFunction(block);
- }
-
- Expression toAssignExpression(Expression left) {
- if (left is Identifier) {
- var simple = _simplifyAssignment(left);
- if (simple != null) return simple;
-
- var exprs = body.toList();
- exprs.add(exprs.removeLast().toAssignExpression(left));
- return new MetaLet(variables, exprs);
- }
- return super.toAssignExpression(left);
- }
-
- Statement toVariableDeclaration(Identifier name) {
- var simple = _simplifyAssignment(name, isDeclaration: true);
- if (simple != null) return simple.toStatement();
- return super.toVariableDeclaration(name);
- }
-
- Expression toExpression() {
- if (_expression != null) return _expression;
- var block = toReturn();
- var s = block.statements;
- if (s.length == 1 && s.first is Return) {
- Return es = s.first;
- return _expression = es.value;
- }
- // Wrap it in an immediately called function to get in expression context.
- return _expression = _toInvokedFunction(block);
- }
-
- Block toStatement() {
- // Skip return value if not used.
- var statements = body.map((e) => e.toStatement()).toList();
- if (statelessResult) statements.removeLast();
- return _finishStatement(statements);
- }
-
- Block toReturn() {
- var statements = body
- .map((e) => e == body.last ? e.toReturn() : e.toStatement())
- .toList();
- return _finishStatement(statements);
- }
-
- Block toYieldStatement({bool star: false}) {
- var statements = body
- .map((e) =>
- e == body.last ? e.toYieldStatement(star: star) : e.toStatement())
- .toList();
- return _finishStatement(statements);
- }
-
- accept(NodeVisitor visitor) {
- // TODO(jmesserly): we special case vistors from js_ast.Template, because it
- // doesn't know about MetaLet. Should we integrate directly?
- if (visitor is InstantiatorGeneratorVisitor) {
- return _templateVisitMetaLet(visitor);
- } else if (visitor is InterpolatedNodeAnalysis) {
- return visitor.visitNode(this);
- } else {
- return toExpression().accept(visitor);
- }
- }
-
- void visitChildren(NodeVisitor visitor) {
- // TODO(jmesserly): we special case vistors from js_ast.Template, because it
- // doesn't know about MetaLet. Should we integrate directly?
- if (visitor is InterpolatedNodeAnalysis ||
- visitor is InstantiatorGeneratorVisitor) {
- variables.values.forEach((v) => v.accept(visitor));
- body.forEach((v) => v.accept(visitor));
- } else {
- toExpression().visitChildren(visitor);
- }
- }
-
- /// This generates as either a comma expression or a call.
- int get precedenceLevel => variables.isEmpty ? EXPRESSION : CALL;
-
- /// Patch to pretend [Template] supports visitMetaLet.
- Instantiator _templateVisitMetaLet(InstantiatorGeneratorVisitor visitor) {
- var valueInstantiators = variables.values.map(visitor.visit);
- var bodyInstantiators = body.map(visitor.visit);
-
- return (args) => new MetaLet(
- new Map.fromIterables(
- variables.keys, valueInstantiators.map((i) => i(args))),
- bodyInstantiators.map((i) => i(args)).toList(),
- statelessResult: statelessResult);
- }
-
- Expression _toInvokedFunction(Statement block) {
- var finder = new _YieldFinder();
- block.accept(finder);
- if (!finder.hasYield) {
- return new Call(new ArrowFun([], block), []);
- }
- // If we have a yield, it's more tricky. We'll create a `function*`, which
- // we `yield*` to immediately invoke. We also may need to bind this:
- Expression fn = new Fun([], block, isGenerator: true);
- if (finder.hasThis) fn = js.call('#.bind(this)', fn);
- return new Yield(new Call(fn, []), star: true);
- }
-
- 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();
- 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) {
- // 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);
- } else {
- params.add(substitutions[name] = new TemporaryId(name));
- values.add(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);
- }
-
- /// If we finish with an assignment to an identifier, try to simplify the
- /// block. For example:
- ///
- /// ((_) => _.add(1), _.add(2), result = _)([])
- ///
- /// Can be transformed to:
- ///
- /// (result = [], result.add(1), result.add(2), result)
- ///
- /// However we should not simplify in this case because `result` is read:
- ///
- /// ((_) => _.addAll(result), _.add(2), result = _)([])
- ///
- MetaLet _simplifyAssignment(Identifier left, {bool isDeclaration: false}) {
- // See if the result value is a let* temporary variable.
- if (body.last is! InterpolatedExpression) return null;
-
- InterpolatedExpression last = body.last;
- String name = last.nameOrPosition;
- if (!variables.containsKey(name)) return null;
-
- // Variables declared can't be used inside their initializer, so make
- // sure we don't transform an assignment into an initializer.
- // If this already was a declaration, then we know it's legal, so we can
- // skip the check.
- if (!isDeclaration) {
- var finder = new _IdentFinder(left.name);
- for (var expr in body) {
- if (finder.found) break;
- expr.accept(finder);
- }
- // If the identifier was used elsewhere, bail, because we're going to
- // change the order of when the assignment happens.
- if (finder.found) return null;
- }
-
- var vars = new Map<String, Expression>.from(variables);
- var value = vars.remove(name);
- Expression assign;
- if (isDeclaration) {
- // Technically, putting one of these in a comma expression is not
- // legal. However when isDeclaration is true, toStatement will be
- // called immediately on the MetaLet, which results in legal JS.
- assign = new VariableDeclarationList(
- 'let', [new VariableInitialization(left, value)]);
- } else {
- assign = value.toAssignExpression(left);
- }
-
- var newBody = new Expression.binary([assign]..addAll(body), ',');
- Binary comma = new Template(null, newBody).safeCreate({name: left});
- return new MetaLet(vars, comma.commaToExpressionList(),
- statelessResult: statelessResult);
- }
-}
-
-class _VariableUseCounter extends BaseVisitor {
- final counts = <String, int>{};
- @override
- visitInterpolatedExpression(InterpolatedExpression node) {
- int n = counts[node.nameOrPosition];
- counts[node.nameOrPosition] = n == null ? 1 : n + 1;
- }
-}
-
-class _IdentFinder extends BaseVisitor {
- final String name;
- bool found = false;
- _IdentFinder(this.name);
-
- @override
- visitIdentifier(Identifier node) {
- if (node.name == name) found = true;
- }
-
- @override
- visitNode(Node node) {
- if (!found) super.visitNode(node);
- }
-}
-
-class _YieldFinder extends BaseVisitor {
- bool hasYield = false;
- bool hasThis = false;
- bool _nestedFunction = false;
- @override
- visitThis(This node) {
- hasThis = true;
- }
-
- @override
- visitFunctionExpression(FunctionExpression node) {
- var savedNested = _nestedFunction;
- _nestedFunction = true;
- super.visitFunctionExpression(node);
- _nestedFunction = savedNested;
- }
-
- @override
- visitYield(Yield node) {
- if (!_nestedFunction) hasYield = true;
- }
-
- @override
- visitNode(Node node) {
- if (!hasYield) super.visitNode(node);
- }
-}
« no previous file with comments | « lib/src/codegen/js_interop.dart ('k') | lib/src/codegen/js_module_item_order.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698