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

Unified Diff: lib/compiler/implementation/ssa/codegen.dart

Issue 10825180: Add JavaScript AST. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: updated. Created 8 years, 4 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
Index: lib/compiler/implementation/ssa/codegen.dart
diff --git a/lib/compiler/implementation/ssa/codegen.dart b/lib/compiler/implementation/ssa/codegen.dart
index 24424c202070d2ef525efbb7fa56bd7cb4644a49..6b35781bd68b0248217017f6bb642673a88095b3 100644
--- a/lib/compiler/implementation/ssa/codegen.dart
+++ b/lib/compiler/implementation/ssa/codegen.dart
@@ -11,37 +11,18 @@ class SsaCodeGeneratorTask extends CompilerTask {
NativeEmitter get nativeEmitter() => backend.emitter.nativeEmitter;
- CodeBuffer buildJavaScriptFunction(FunctionElement element,
- String parameters,
- CodeBuffer body) {
- String extraSpace = "";
- // Members are emitted inside a JavaScript object literal. To line up the
- // indentation we want the closing curly brace to be indented by one space.
- // Example:
- // defineClass("A", "B", ... , {
- // foo$1: function(..) {
- // }, /* <========== indent by 1. */
- // bar$2: function(..) {
- // }, /* <========== indent by 1. */
- //
- // For static functions this is not necessary:
- // $.staticFun = function() {
- // ...
- // };
- if (element.isInstanceMember() ||
- element.kind == ElementKind.GENERATIVE_CONSTRUCTOR_BODY) {
- extraSpace = " ";
- }
-
+ js.Fun buildJavaScriptFunction(FunctionElement element,
+ List<js.Param> parameters,
+ js.Block body) {
FunctionExpression expression = element.cachedNode;
- CodeBuffer buffer = new CodeBuffer();
- buffer.setSourceLocation(element, expression.getBeginToken());
- buffer.add('function($parameters) {\n');
- buffer.add(body);
- buffer.add(extraSpace);
- buffer.setSourceLocation(element, expression.getEndToken());
- buffer.add('}');
- return buffer;
+ js.Fun result = new js.Fun(parameters, body);
+ result.sourcePosition = expression.getBeginToken();
+ result.endSourcePosition = expression.getEndToken();
+ return result;
+ }
+
+ CodeBuffer prettyPrint(js.Node node, Element positionElement) {
+ return js.prettyPrint(node, compiler, positionElement);
}
CodeBuffer generateMethod(WorkItem work, HGraph graph) {
@@ -51,13 +32,17 @@ class SsaCodeGeneratorTask extends CompilerTask {
parameterNames.forEach((element, name) {
compiler.enqueuer.codegen.addToWorkList(element);
});
- String parameters = Strings.join(parameterNames.getValues(), ', ');
+ List<js.Param> parameters = <js.Param>[];
+ parameterNames.forEach((element, name) {
+ parameters.add(new js.Param(name));
+ });
+ String parametersString = Strings.join(parameterNames.getValues(), ", ");
SsaOptimizedCodeGenerator codegen = new SsaOptimizedCodeGenerator(
backend, work, parameters, parameterNames);
codegen.visitGraph(graph);
FunctionElement element = work.element;
- CodeBuffer code;
+ js.Block body;
if (element.isInstanceMember()
&& element.enclosingElement.isClass()
&& element.enclosingElement.isNative()
@@ -68,14 +53,16 @@ class SsaCodeGeneratorTask extends CompilerTask {
// and needs to know if the method is overridden.
nativeEmitter.overriddenMethods.add(element);
StringBuffer buffer = new StringBuffer();
+ String codeString = prettyPrint(codegen.body, work.element).toString();
native.generateMethodWithPrototypeCheckForElement(
- compiler, buffer, element, '${codegen.buffer}', parameters);
- code = new CodeBuffer();
- code.add(buffer);
+ compiler, buffer, element, codeString, parametersString);
+ js.Node blob = new js.StatementBlob(buffer.toString());
+ body = new js.Block(<js.Statement>[new js.ExpressionStatement(blob)]);
} else {
- code = codegen.buffer;
+ body = codegen.body;
}
- return buildJavaScriptFunction(element, parameters, code);
+ js.Fun fun = buildJavaScriptFunction(element, parameters, body);
+ return prettyPrint(fun, work.element);
});
}
@@ -84,16 +71,20 @@ class SsaCodeGeneratorTask extends CompilerTask {
compiler.tracer.traceGraph("codegen-bailout", graph);
Map<Element, String> parameterNames = getParameterNames(work);
- String parameters = Strings.join(parameterNames.getValues(), ', ');
+ List<js.Param> parameters = <js.Param>[];
+ parameterNames.forEach((element, name) {
+ parameters.add(new js.Param(name));
+ });
SsaUnoptimizedCodeGenerator codegen = new SsaUnoptimizedCodeGenerator(
backend, work, parameters, parameterNames);
codegen.visitGraph(graph);
- CodeBuffer code = new CodeBuffer();
- code.add(codegen.setup);
- code.add(codegen.buffer);
- return buildJavaScriptFunction(
- work.element, codegen.newParameters.toString(), code);
+ js.Block body = new js.Block(<js.Statement>[]);
+ body.elements.add(codegen.setup);
+ body.elements.add(codegen.body);
+ js.Fun fun =
+ buildJavaScriptFunction(work.element, codegen.newParameters, body);
+ return prettyPrint(fun, work.element);
});
}
@@ -117,27 +108,6 @@ typedef void ElementAction(Element element);
class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
/**
- * Current state for generating simple (non-local-control) code.
- * It is generated as either statements (indented and ';'-terminated),
- * expressions (comma separated) or declarations (also comma separated,
- * but expected to be preceeded by a 'var' so it declares its variables);
- */
- static final int STATE_STATEMENT = 0;
- static final int STATE_FIRST_EXPRESSION = 1;
- static final int STATE_FIRST_DECLARATION = 2;
- static final int STATE_EXPRESSION = 3;
- static final int STATE_DECLARATION = 4;
-
- /**
- * When analyzing a [HStatementGraph] we try to recognize if it has
- * the following properties.
- */
- static final int ONE_STATEMENT = 0;
- static final int ONE_EXPRESSION = 1;
- static final int EMPTY = 2;
- static final int MULTIPLE_STATEMENTS = 3;
-
- /**
* Returned by [expressionType] to tell how code can be generated for
* a subgraph.
* - [TYPE_STATEMENT] means that the graph must be generated as a statement,
@@ -153,10 +123,14 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
static final int TYPE_EXPRESSION = 1;
static final int TYPE_DECLARATION = 2;
+ /**
+ * Whether we are currently generating expressions instead of statements.
+ * This includes declarations, which are generated as expressions.
+ */
+ bool isGeneratingExpression = false;
+
final JavaScriptBackend backend;
final WorkItem work;
- final CodeBuffer buffer;
- final String parameters;
final Set<HInstruction> generateAtUseSite;
final Set<HInstruction> controlFlowOperators;
@@ -164,6 +138,11 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
final Map<Element, ElementAction> continueAction;
final Map<Element, String> parameterNames;
+ js.Block currentContainer;
+ js.Block get body() => currentContainer;
+ List<js.Expression> expressionStack;
+ List<js.Block> oldContainerStack;
+
/**
* Contains the names of the instructions, as well as the parallel
* copies to perform on block transitioning.
@@ -184,14 +163,7 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
Element equalsNullElement;
Element boolifiedEqualsNullElement;
int indent = 0;
- int expectedPrecedence = JSPrecedence.STATEMENT_PRECEDENCE;
- JSBinaryOperatorPrecedence unsignedShiftPrecedences;
HGraph currentGraph;
- /**
- * Whether the code-generation should try to generate an expression
- * instead of a sequence of statements.
- */
- int generationState = STATE_STATEMENT;
HBasicBlock currentBlock;
// Records a block-information that is being handled specially.
@@ -222,13 +194,13 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
}
bool hasNonBitOpUser(HInstruction instruction, Set<HPhi> phiSet) {
- for (HInstruction use in instruction.usedBy) {
- if (use is HPhi) {
- if (!phiSet.contains(use)) {
- phiSet.add(use);
- if (hasNonBitOpUser(use, phiSet)) return true;
+ for (HInstruction user in instruction.usedBy) {
+ if (user is HPhi) {
+ if (!phiSet.contains(user)) {
+ phiSet.add(user);
+ if (hasNonBitOpUser(user, phiSet)) return true;
}
- } else if (use is! HBitNot && use is! HBinaryBitOp) {
+ } else if (user is! HBitNot && user is! HBinaryBitOp) {
return true;
}
}
@@ -249,19 +221,71 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
return hasNonBitOpUser(instruction, new Set<HPhi>());
}
+ /**
+ * If the [instruction] is not `null` it will be used to attach the position
+ * to the [statement].
+ */
+ void pushStatement(js.Statement statement, [HInstruction instruction]) {
+ assert(expressionStack.isEmpty());
+ if (instruction != null) {
+ attachLocation(statement, instruction);
+ }
+ currentContainer.elements.add(statement);
+ }
+
+ /**
+ * If the [instruction] is not `null` it will be used to attach the position
+ * to the [expression].
+ */
+ pushExpressionAsStatement(js.Expression expression,
+ [HInstruction instruction]) {
+ pushStatement(new js.ExpressionStatement(expression), instruction);
+ }
+
+ /**
+ * If the [instruction] is not `null` it will be used to attach the position
+ * to the [expression].
+ */
+ push(js.Expression expression, [HInstruction instruction]) {
+ if (instruction != null) {
+ attachLocation(expression, instruction);
+ }
+ expressionStack.add(expression);
+ }
+
+ js.Expression pop() {
+ return expressionStack.removeLast();
+ }
+
+ attachLocationToLast(HInstruction instruction) {
+ attachLocation(expressionStack.last(), instruction);
+ }
+
+ js.Node attachLocation(js.Node jsNode, HInstruction instruction) {
+ if (instruction.sourcePosition !== null) {
+ jsNode.sourcePosition = instruction.sourcePosition;
+ }
+ return jsNode;
+ }
+
+ js.Node attachLocationRange(js.Node jsNode, Node node) {
+ jsNode.sourcePosition = node.getBeginToken();
+ jsNode.endSourcePosition = node.getEndToken();
+ return jsNode;
+ }
+
SsaCodeGenerator(this.backend,
this.work,
- this.parameters,
this.parameterNames)
: declaredVariables = new Set<String>(),
delayedVariableDeclarations = new Set<String>(),
- buffer = new CodeBuffer(),
+ currentContainer = new js.Block.empty(),
+ expressionStack = <js.Expression>[],
+ oldContainerStack = <js.Block>[],
generateAtUseSite = new Set<HInstruction>(),
controlFlowOperators = new Set<HInstruction>(),
breakAction = new Map<Element, ElementAction>(),
- continueAction = new Map<Element, ElementAction>(),
- unsignedShiftPrecedences = JSPrecedence.binary['>>>'] {
- }
+ continueAction = new Map<Element, ElementAction>();
abstract visitTypeGuard(HTypeGuard node);
abstract visitBailoutTarget(HBailoutTarget node);
@@ -277,27 +301,6 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
abstract startLabeledBlock(HLabeledBlockInformation labeledBlockInfo);
abstract endLabeledBlock(HLabeledBlockInformation labeledBlockInfo);
- void beginExpression(int precedence) {
- if (precedence < expectedPrecedence) {
- buffer.add('(');
- }
- }
-
- void endExpression(int precedence) {
- if (precedence < expectedPrecedence) {
- buffer.add(')');
- }
- }
-
- void withPrecedence(int precedence, void action()) {
- int oldPrecedence = expectedPrecedence;
- beginExpression(precedence);
- expectedPrecedence = precedence;
- action();
- expectedPrecedence = oldPrecedence;
- endExpression(precedence);
- }
-
void preGenerateMethod(HGraph graph) {
new SsaInstructionMerger(generateAtUseSite).visitGraph(graph);
new SsaConditionMerger(generateAtUseSite,
@@ -323,10 +326,11 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
HBasicBlock start = beginGraph(graph);
visitBasicBlock(start);
if (!delayedVariableDeclarations.isEmpty()) {
- addIndented("var ");
- buffer.add(Strings.join(
- new List<String>.from(delayedVariableDeclarations), ', '));
- buffer.add(";\n");
+ List<js.Init> declarations = <js.Init>[];
+ delayedVariableDeclarations.forEach((String name) {
+ declarations.add(new js.Init(new js.Decl(name), null));
+ });
+ pushExpressionAsStatement(new js.VariableDeclarationList(declarations));
}
endGraph(graph);
}
@@ -423,268 +427,210 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
* the condition.
*/
void generateStatements(HBlockInformation block) {
- int oldState = generationState;
- generationState = STATE_STATEMENT;
if (block is HStatementInformation) {
block.accept(this);
} else {
HSubExpressionBlockInformation expression = block;
visitSubGraph(expression.subExpression);
}
- generationState = oldState;
+ }
+
+ js.Block generateStatementsInNewBlock(HBlockInformation block) {
+ js.Block result = new js.Block.empty();
+ js.Block oldContainer = currentContainer;
+ currentContainer = result;
+ generateStatements(block);
+ currentContainer = oldContainer;
+ return result;
}
/**
- * Generate expressions from block information.
+ * If the [block] only contains one statement returns that statement. If the
+ * that statement itself is a block, recursively calls this method.
+ *
+ * If the block is empty, returns a new instance of [js.NOP].
*/
- void generateExpression(HExpressionInformation expression) {
- // Currently we only handle sub-expression graphs.
- assert(expression is HSubExpressionBlockInformation);
- // [visitSubGraph] will reset the [expectedPrecedence]. Make sure we don't
- // need parenthesis. I.e., this only expects to be called for top-level
- // expressions, not sub-expressions.
- assert(expectedPrecedence == JSPrecedence.STATEMENT_PRECEDENCE
- || expectedPrecedence == JSPrecedence.EXPRESSION_PRECEDENCE);
-
- HSubExpressionBlockInformation expressionSubGraph = expression;
-
- int oldState = generationState;
- generationState = STATE_FIRST_EXPRESSION;
- visitSubGraph(expressionSubGraph.subExpression);
- generationState = oldState;
+ js.Statement unwrapStatement(js.Block block) {
+ int len = block.elements.length;
+ if (len == 0) return new js.NOP();
+ if (len == 1) {
+ js.Statement result = block.elements[0];
+ if (result is Block) return unwrapStatement(result);
+ return result;
+ }
+ return block;
}
- void generateDeclaration(HExpressionInformation expression) {
+ /**
+ * Generate expressions from block information.
+ */
+ js.Expression generateExpression(HExpressionInformation expression) {
// Currently we only handle sub-expression graphs.
assert(expression is HSubExpressionBlockInformation);
- HSubExpressionBlockInformation expressionSubGraph = expression;
- int oldState = generationState;
- generationState = STATE_FIRST_DECLARATION;
+ bool oldIsGeneratingExpression = isGeneratingExpression;
+ isGeneratingExpression = true;
+ List<js.Expression> oldExpressionStack = expressionStack;
+ List<js.Expression> sequenceElements = <js.Expression>[];
+ expressionStack = sequenceElements;
+ HSubExpressionBlockInformation expressionSubGraph = expression;
visitSubGraph(expressionSubGraph.subExpression);
- generationState = oldState;
- }
-
- void generateCondition(HBlockInformation condition) {
- generateExpression(condition);
+ expressionStack = oldExpressionStack;
+ isGeneratingExpression = oldIsGeneratingExpression;
+ if (sequenceElements.isEmpty()) {
+ // Happens when the initializer, condition or update of a loop is empty.
+ return null;
+ } else if (sequenceElements.length == 1) {
+ return sequenceElements[0];
+ } else {
+ return new js.Sequence(sequenceElements);
+ }
}
/**
* Only visits the arguments starting at inputs[HInvoke.ARGUMENTS_OFFSET].
*/
- void visitArguments(List<HInstruction> inputs) {
+ List<js.Expression> visitArguments(List<HInstruction> inputs) {
assert(inputs.length >= HInvoke.ARGUMENTS_OFFSET);
- buffer.add('(');
+ List<js.Expression> result = <js.Expression>[];
for (int i = HInvoke.ARGUMENTS_OFFSET; i < inputs.length; i++) {
- if (i != HInvoke.ARGUMENTS_OFFSET) buffer.add(', ');
- use(inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE);
+ use(inputs[i]);
+ result.add(pop());
}
- buffer.add(')');
- }
-
- /**
- * Whether we are currently generating expressions instead of statements.
- * This includes declarations, which are generated as expressions.
- */
- bool isGeneratingExpression() {
- return generationState != STATE_STATEMENT;
- }
-
- /**
- * Whether we are generating a declaration.
- */
- bool isGeneratingDeclaration() {
- return (generationState == STATE_DECLARATION ||
- generationState == STATE_FIRST_DECLARATION);
- }
-
- /**
- * Called before writing an expression.
- * Ensures that expressions are comma spearated.
- */
- void addExpressionSeparator() {
- if (generationState == STATE_FIRST_EXPRESSION) {
- generationState = STATE_EXPRESSION;
- } else if (generationState != STATE_FIRST_DECLARATION) {
- buffer.add(", ");
- }
- // If the state is [STATE_FIRST_DECLARATION] the potential
- // declaration of the variable will be done by the instruction.
+ return result;
}
bool isVariableDeclared(String variableName) {
return declaredVariables.contains(variableName);
}
- void declareVariable(String variableName) {
- if (isGeneratingExpression()) {
- if (generationState == STATE_FIRST_DECLARATION) {
- if (!isVariableDeclared(variableName)) {
- declaredVariables.add(variableName);
- buffer.add("var ");
- generationState = STATE_DECLARATION;
- } else {
- generationState = STATE_EXPRESSION;
+ js.Expression generateExpressionAssignment(String variableName,
+ js.Expression value) {
+ if (value is js.Binary) {
+ js.Binary binary = value;
+ String op = binary.op;
+ if (op == '+' || op == '-' || op == '/' || op == '*' || op == '%' ||
+ op == '^' || op == '&' || op == '|') {
+ if (binary.left is js.Ref &&
+ (binary.left as js.Ref).id == variableName) {
+ // We know now, that we can shorten x = x + y into x += y.
+ // Also check for the shortcut where y equals 1: x++ and x--.
+ if ((op == '+' || op == '-') &&
+ binary.right is js.NumberLiteral &&
+ (binary.right as js.NumberLiteral).value == "1") {
+ return new js.Unary(op == '+' ? '++' : '--', binary.left);
+ }
+ return new js.VassignOp(binary.left, op, binary.right);
}
+ }
+ }
+ return new js.Vassign(new js.Ref(variableName), value);
+ }
- } else if (!isVariableDeclared(variableName)) {
- if (!isGeneratingDeclaration()) {
- delayedVariableDeclarations.add(variableName);
- }
- // No matter if we are declaring the variable now or if we are
- // delaying the declaration we can treat the variable as
- // being declared from this point on.
+ void assignVariable(String variableName, js.Expression value) {
+ if (isGeneratingExpression) {
+ if (!isVariableDeclared(variableName)) {
+ delayedVariableDeclarations.add(variableName);
+ // We can treat the variable as being declared from this point on.
declaredVariables.add(variableName);
}
- } else if (!isVariableDeclared(variableName)) {
+ push(generateExpressionAssignment(variableName, value));
+ } else if (!isVariableDeclared(variableName) ||
+ delayedVariableDeclarations.contains(variableName)) {
declaredVariables.add(variableName);
- buffer.add("var ");
+ delayedVariableDeclarations.remove(variableName);
+ js.Decl decl = new js.Decl(variableName);
+ pushExpressionAsStatement(
+ new js.VariableDeclarationList(<js.Init>[new js.Init(decl, value)]));
+ } else {
+ pushExpressionAsStatement(
+ generateExpressionAssignment(variableName, value));
}
- buffer.add(variableName);
}
- void declareInstruction(HInstruction instruction) {
- declareVariable(variableNames.getName(instruction));
- }
-
- // For simple updates of the form 'i = i op constant' generate
- // 'i op= constant' instead.
- bool handleSimpleUpdateDefinition(HInstruction instruction, String name) {
- // If the variable is not declared the short update syntax cannot
- // be used since it is a declaration and not an update.
- if (!isVariableDeclared(name)) return false;
-
- // Check that the operation is one of +, *, - or /. Record whether
- // or not the operation is commutative.
- var isCommutative = false;
- if (instruction is HAdd || instruction is HMultiply) {
- isCommutative = true;
- } else if (instruction is !HSubtract && instruction is !HDivide) {
- return false;
- }
-
- // Is it a builtin operation involving +, -, /, or *?
- HBinaryArithmetic binaryInstruction = instruction;
- assert(binaryInstruction.inputs.length == 3);
- if (binaryInstruction.builtin) {
- var left = binaryInstruction.left;
- var right = binaryInstruction.right;
- if (isCommutative && variableNames.getName(right) == name) {
- var tmp = right;
- right = left;
- left = tmp;
- }
-
- // Check that left has the same name as the definition and emit
- // the short update definition if it is.
- if (variableNames.getName(left) == name) {
- // Check if the right operand is constant one.
- bool rightIsOne = false;
- if (right.isConstantNumber()) {
- HConstant rightConstant = right;
- NumConstant numConstant = rightConstant.constant;
- rightIsOne = (numConstant.value == 1);
- }
- if (binaryInstruction is HAdd && rightIsOne) {
- beginExpression(JSPrecedence.PREFIX_PRECEDENCE);
- buffer.add('++');
- declareVariable(name);
- endExpression(JSPrecedence.PREFIX_PRECEDENCE);
- } else if (binaryInstruction is HSubtract && rightIsOne) {
- beginExpression(JSPrecedence.PREFIX_PRECEDENCE);
- buffer.add('--');
- declareVariable(name);
- endExpression(JSPrecedence.PREFIX_PRECEDENCE);
- } else {
- var operation = binaryInstruction.operation.name;
- beginExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE);
- declareVariable(name);
- buffer.add(' ${operation}= ');
- use(right, JSPrecedence.ASSIGNMENT_PRECEDENCE);
- endExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE);
- }
- return true;
+ void define(HInstruction instruction) {
+ // For simple type checks like i = intTypeCheck(i), we don't have to
+ // emit an assignment, because the intTypeCheck just returns its
+ // argument.
+ bool needsAssignment = true;
+ if (instruction is HTypeConversion) {
+ String inputName = variableNames.getName(instruction.checkedInput);
+ if (variableNames.getName(instruction) == inputName) {
+ needsAssignment = false;
}
}
- return false;
- }
- // For simple type checks like i = intTypeCheck(i), we don't have to
- // emit an assignment, because the intTypeCheck just returns its
- // argument.
- bool handleTypeConversion(instruction, name) {
- if (instruction is !HTypeConversion) return false;
- String inputName = variableNames.getName(instruction.checkedInput);
- if (name != inputName) return false;
- visit(instruction, JSPrecedence.STATEMENT_PRECEDENCE);
- return true;
- }
+ if (needsAssignment &&
+ !instruction.isControlFlow() && variableNames.hasName(instruction)) {
+ visitExpression(instruction);
+ assignVariable(variableNames.getName(instruction), pop());
+ return;
+ }
- void define(HInstruction instruction) {
- if (isGeneratingExpression()) {
- addExpressionSeparator();
- } else {
- assert(expectedPrecedence == JSPrecedence.STATEMENT_PRECEDENCE);
- addIndentation();
- }
- if (!instruction.isControlFlow() && variableNames.hasName(instruction)) {
- var name = variableNames.getName(instruction);
- if (!handleSimpleUpdateDefinition(instruction, name)
- && !handleTypeConversion(instruction, name)) {
- withPrecedence(JSPrecedence.ASSIGNMENT_PRECEDENCE, () {
- declareInstruction(instruction);
- buffer.add(" = ");
- visit(instruction, JSPrecedence.ASSIGNMENT_PRECEDENCE);
- });
- }
+ if (isGeneratingExpression) {
+ visitExpression(instruction);
} else {
- visit(instruction, expectedPrecedence);
+ visitStatement(instruction);
}
- if (!isGeneratingExpression()) buffer.add(';\n');
}
- void use(HInstruction argument, int expectedPrecedenceForArgument) {
+ void use(HInstruction argument) {
if (isGenerateAtUseSite(argument)) {
- visit(argument, expectedPrecedenceForArgument);
+ visitExpression(argument);
} else if (argument is HCheck && argument.isControlFlow()) {
// A [HCheck] that has control flow can never be used as an
// expression and may not have a name. Therefore we just use the
// checked instruction.
HCheck check = argument;
- use(check.checkedInput, expectedPrecedenceForArgument);
+ use(check.checkedInput);
} else {
- buffer.add(variableNames.getName(argument));
+ push(new js.Ref(variableNames.getName(argument)), argument);
}
}
- visit(HInstruction node, int expectedPrecedenceForNode) {
- int oldPrecedence = this.expectedPrecedence;
- this.expectedPrecedence = expectedPrecedenceForNode;
- if (node.sourcePosition !== null) {
- buffer.setSourceLocation(work.element, node.sourcePosition);
- }
+ visit(HInstruction node) {
node.accept(this);
- this.expectedPrecedence = oldPrecedence;
+ }
+
+ visitExpression(HInstruction node) {
+ bool oldIsGeneratingExpression = isGeneratingExpression;
+ isGeneratingExpression = true;
+ visit(node);
+ isGeneratingExpression = oldIsGeneratingExpression;
+ }
+
+ visitStatement(HInstruction node) {
+ assert(!isGeneratingExpression);
+ visit(node);
+ if (!expressionStack.isEmpty()) {
+ assert(expressionStack.length == 1);
+ pushExpressionAsStatement(pop());
+ }
}
void continueAsBreak(LabelElement target) {
- addIndented("break ");
- writeContinueLabel(target);
- buffer.add(";\n");
+ pushStatement(new js.Break(compiler.namer.continueLabelName(target)));
}
void implicitContinueAsBreak(TargetElement target) {
- addIndented("break ");
- writeImplicitContinueLabel(target);
- buffer.add(";\n");
+ pushStatement(new js.Break(
+ compiler.namer.implicitContinueLabelName(target)));
}
void implicitBreakWithLabel(TargetElement target) {
- addIndented("break ");
- writeImplicitLabel(target);
- buffer.add(";\n");
+ pushStatement(new js.Break(compiler.namer.implicitBreakLabelName(target)));
+ }
+
+ js.Statement wrapIntoLabels(js.Statement result, List<LabelElement> labels) {
+ for (LabelElement label in labels) {
+ if (label.isTarget) {
+ result = new js.Labeled(compiler.namer.breakLabelName(label), result);
+ }
+ }
+ return result;
}
+
// The regular [visitIf] method implements the needed logic.
bool visitIfInfo(HIfBlockInformation info) => false;
@@ -693,37 +639,32 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
if (!isExpression) {
generateStatements(info.expression);
}
- addIndentation();
- for (LabelElement label in info.labels) {
- if (label.isTarget) {
- writeLabel(label);
- buffer.add(":");
- }
- }
- buffer.add("switch (");
+
if (isExpression) {
- generateExpression(info.expression);
+ push(generateExpression(info.expression));
} else {
- use(info.expression.conditionExpression,
- JSPrecedence.EXPRESSION_PRECEDENCE);
+ use(info.expression.conditionExpression);
}
- buffer.add(") {\n");
- indent++;
+ js.Expression key = pop();
+ List<js.SwitchClause> cases = <js.SwitchClause>[];
+
+ js.Block oldContainer = currentContainer;
for (int i = 0; i < info.matchExpressions.length; i++) {
for (Constant constant in info.matchExpressions[i]) {
- addIndented("case ");
generateConstant(constant);
- buffer.add(":\n");
+ currentContainer = new js.Block.empty();
+ cases.add(new js.Case(pop(), currentContainer));
}
if (i == info.matchExpressions.length - 1 && info.hasDefault) {
- addIndented("default:\n");
+ currentContainer = new js.Block.empty();
+ cases.add(new js.Default(currentContainer));
}
- indent++;
generateStatements(info.statements[i]);
- indent--;
}
- indent--;
- addIndented("}\n");
+ currentContainer = oldContainer;
+
+ js.Statement result = new js.Switch(key, cases);
+ pushStatement(wrapIntoLabels(result, info.labels));
return true;
}
@@ -745,31 +686,21 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
}
bool visitTryInfo(HTryBlockInformation info) {
- addIndented("try {\n");
- indent++;
- generateStatements(info.body);
- indent--;
- addIndented("}");
+ js.Block body = generateStatementsInNewBlock(info.body);
+ js.Catch catchPart = null;
+ js.Block finallyPart = null;
if (info.catchBlock !== null) {
- // Printing the catch part.
HParameterValue exception = info.catchVariable;
String name = variableNames.getName(exception);
parameterNames[exception.sourceElement] = name;
- buffer.add(' catch ($name) {\n');
- indent++;
- generateStatements(info.catchBlock);
- parameterNames.remove(exception.sourceElement);
- indent--;
- addIndented('}');
+ js.Decl decl = new js.Decl(name);
+ js.Block catchBlock = generateStatementsInNewBlock(info.catchBlock);
+ catchPart = new js.Catch(decl, catchBlock);
}
if (info.finallyBlock != null) {
- buffer.add(" finally {\n");
- indent++;
- generateStatements(info.finallyBlock);
- indent--;
- addIndented("}");
+ finallyPart = generateStatementsInNewBlock(info.finallyBlock);
}
- buffer.add("\n");
+ pushStatement(new js.Try(body, catchPart, finallyPart));
return true;
}
@@ -787,7 +718,8 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
bool visitLoopInfo(HLoopBlockInformation info) {
HExpressionInformation condition = info.condition;
bool isConditionExpression = isJSCondition(condition);
- buffer.setSourceLocation(work.element, info.sourcePosition.getBeginToken());
+
+ js.Loop loop;
switch (info.kind) {
// Treate all three "test-first" loops the same way.
@@ -803,55 +735,75 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
initialization = null;
}
}
- for (LabelElement label in info.labels) {
- if (label.isTarget) {
- writeLabel(label);
- buffer.add(":");
- }
- }
if (isConditionExpression &&
info.updates !== null && isJSExpression(info.updates)) {
// If we have an updates graph, and it's expressible as an
// expression, generate a for-loop.
- addIndented("for (");
+ js.Expression jsInitialization = null;
if (initialization !== null) {
- if (initializationType != TYPE_DECLARATION) {
- generateExpression(initialization);
- } else {
- generateDeclaration(initialization);
+ int delayedVariablesCount = delayedVariableDeclarations.length;
+ jsInitialization = generateExpression(initialization);
+ if (delayedVariablesCount < delayedVariableDeclarations.length) {
+ // We just added a new delayed variable-declaration. See if we
+ // can put in a 'var' in front of the initialization to make it
+ // go away.
+ List<js.Expression> expressions;
+ if (jsInitialization is js.Sequence) {
+ expressions = jsInitialization.expressions;
+ } else {
+ expressions = <js.Expression>[jsInitialization];
+ }
+ bool canTransformToVariableDeclaration = true;
+ for (js.Expression expression in expressions) {
+ if (expression is! js.Vassign || expression is js.VassignOp) {
+ canTransformToVariableDeclaration = false;
+ break;
+ }
+ }
+ if (canTransformToVariableDeclaration) {
+ List<js.Init> inits = <js.Init>[];
+ for (js.Vassign assignment in expressions) {
+ String id = assignment.lhs.id;
+ inits.add(new js.Init(new js.Decl(id), assignment.value));
+ delayedVariableDeclarations.remove(id);
+ }
+ jsInitialization = new js.VariableDeclarationList(inits);
+ }
}
}
- buffer.add("; ");
- generateCondition(condition);
- buffer.add("; ");
- generateExpression(info.updates);
- buffer.add(") {\n");
- indent++;
+ js.Expression jsCondition = generateExpression(condition);
+ js.Expression jsUpdates = generateExpression(info.updates);
// The body might be labeled. Ignore this when recursing on the
// subgraph.
// TODO(lrn): Remove this extra labeling when handling all loops
// using subgraphs.
+ js.Block oldContainer = currentContainer;
+ js.Statement body = new js.Block.empty();
+ currentContainer = body;
visitBodyIgnoreLabels(info);
-
- indent--;
+ currentContainer = oldContainer;
+ body = unwrapStatement(body);
+ loop = new js.For(jsInitialization, jsCondition, jsUpdates, body);
} else {
// We have either no update graph, or it's too complex to
// put in an expression.
if (initialization !== null) {
generateStatements(initialization);
}
- addIndented("while (");
+ js.Expression jsCondition;
+ js.Block oldContainer = currentContainer;
+ js.Statement body = new js.Block.empty();
if (isConditionExpression) {
- generateCondition(condition);
- buffer.add(") {\n");
- indent++;
+ jsCondition = generateExpression(condition);
+ currentContainer = body;
} else {
- buffer.add("true) {\n");
- indent++;
+ jsCondition = new js.BoolLiteral(true);
+ currentContainer = body;
generateStatements(condition);
- addIndented("if (!");
- use(condition.conditionExpression, JSPrecedence.PREFIX_PRECEDENCE);
- buffer.add(") break;\n");
+ use(condition.conditionExpression);
+ js.Expression ifTest = new js.Unary("!", pop());
+ js.Break jsBreak = new js.Break(null);
+ pushStatement(new js.If.then(ifTest, jsBreak));
}
if (info.updates !== null) {
wrapLoopBodyForContinue(info);
@@ -859,9 +811,10 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
} else {
visitBodyIgnoreLabels(info);
}
- indent--;
+ currentContainer = oldContainer;
+ body = unwrapStatement(body);
+ loop = new js.While(jsCondition, body);
}
- addIndented("}\n");
break;
}
case HLoopBlockInformation.DO_WHILE_LOOP: {
@@ -869,15 +822,9 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
if (info.initializer !== null) {
generateStatements(info.initializer);
}
- addIndentation();
- for (LabelElement label in info.labels) {
- if (label.isTarget) {
- writeLabel(label);
- buffer.add(":");
- }
- }
- buffer.add("do {\n");
- indent++;
+ js.Block oldContainer = currentContainer;
+ js.Statement body = new js.Block.empty();
+ currentContainer = body;
if (!isConditionExpression || info.updates !== null) {
wrapLoopBodyForContinue(info);
} else {
@@ -887,17 +834,15 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
generateStatements(info.updates);
}
if (isConditionExpression) {
- indent--;
- addIndented("} while (");
- generateExpression(condition);
- buffer.add(");\n");
+ push(generateExpression(condition));
} else {
generateStatements(condition);
- indent--;
- addIndented("} while (");
- use(condition.conditionExpression, JSPrecedence.PREFIX_PRECEDENCE);
- buffer.add(");\n");
+ use(condition.conditionExpression);
}
+ js.Expression jsCondition = pop();
+ currentContainer = oldContainer;
+ body = unwrapStatement(body);
+ loop = new js.Do(body, jsCondition);
break;
}
default:
@@ -905,14 +850,21 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
'Unexpected loop kind: ${info.kind}',
instruction: condition.conditionExpression);
}
- buffer.setSourceLocation(work.element, info.sourcePosition.getEndToken());
+ attachLocationRange(loop, info.sourcePosition);
+ pushStatement(wrapIntoLabels(loop, info.labels));
return true;
}
bool visitLabeledBlockInfo(HLabeledBlockInformation labeledBlockInfo) {
preLabeledBlock(labeledBlockInfo);
- addIndentation();
Link<Element> continueOverrides = const EmptyLink<Element>();
+
+ js.Block oldContainer = currentContainer;
+ js.Block body = new js.Block.empty();
+ js.Statement result = body;
+
+ currentContainer = body;
+
// If [labeledBlockInfo.isContinue], the block is an artificial
// block around the body of a loop with an update block, so that
// continues of the loop can be written as breaks of the body
@@ -920,8 +872,8 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
if (labeledBlockInfo.isContinue) {
for (LabelElement label in labeledBlockInfo.labels) {
if (label.isContinueTarget) {
- writeContinueLabel(label);
- buffer.add(':');
+ String labelName = compiler.namer.continueLabelName(label);
+ result = new js.Labeled(labelName, result);
continueAction[label] = continueAsBreak;
continueOverrides = continueOverrides.prepend(label);
}
@@ -930,15 +882,15 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
// TODO(lrn): Consider recording whether the target is in fact
// a target of an unlabeled continue, and not generate this if it isn't.
TargetElement target = labeledBlockInfo.target;
- writeImplicitContinueLabel(target);
- buffer.add(':');
+ String labelName = compiler.namer.implicitContinueLabelName(target);
+ result = new js.Labeled(labelName, result);
continueAction[target] = implicitContinueAsBreak;
continueOverrides = continueOverrides.prepend(target);
} else {
for (LabelElement label in labeledBlockInfo.labels) {
if (label.isBreakTarget) {
- writeLabel(label);
- buffer.add(':');
+ String labelName = compiler.namer.breakLabelName(label);
+ result = new js.Labeled(labelName, result);
}
}
TargetElement target = labeledBlockInfo.target;
@@ -946,21 +898,17 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
// This is an extra block around a switch that is generated
// as a nested if/else chain. We add an extra break target
// so that case code can break.
- writeImplicitLabel(target);
- buffer.add(':');
+ String labelName = compiler.namer.implicitBreakLabelName(target);
+ result = new js.Labeled(labelName, result);
breakAction[target] = implicitBreakWithLabel;
}
}
- buffer.add('{\n');
- indent++;
+ currentContainer = body;
startLabeledBlock(labeledBlockInfo);
generateStatements(labeledBlockInfo.body);
endLabeledBlock(labeledBlockInfo);
- indent--;
- addIndented('}\n');
-
if (labeledBlockInfo.isContinue) {
while (!continueOverrides.isEmpty()) {
continueAction.remove(continueOverrides.head);
@@ -969,6 +917,9 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
} else {
breakAction.remove(labeledBlockInfo.target);
}
+
+ currentContainer = oldContainer;
+ pushStatement(result);
return true;
}
@@ -977,27 +928,29 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
void wrapLoopBodyForContinue(HLoopBlockInformation info) {
TargetElement target = info.target;
if (target !== null && target.isContinueTarget) {
- addIndentation();
+ js.Block oldContainer = currentContainer;
+ js.Block body = new js.Block.empty();
+ currentContainer = body;
+ js.Statement result = body;
for (LabelElement label in info.labels) {
if (label.isContinueTarget) {
- writeContinueLabel(label);
- buffer.add(":");
+ String labelName = compiler.namer.continueLabelName(label);
+ result = new js.Labeled(labelName, result);
continueAction[label] = continueAsBreak;
}
}
- writeImplicitContinueLabel(target);
- buffer.add(":{\n");
+ String labelName = compiler.namer.implicitContinueLabelName(target);
+ result = new js.Labeled(labelName, result);
continueAction[info.target] = implicitContinueAsBreak;
- indent++;
visitBodyIgnoreLabels(info);
- indent--;
- addIndented("}\n");
continueAction.remove(info.target);
for (LabelElement label in info.labels) {
if (label.isContinueTarget) {
continueAction.remove(label);
}
}
+ currentContainer = oldContainer;
+ pushStatement(result);
} else {
// Loop body contains no continues, so we don't need a break target.
generateStatements(info.body);
@@ -1047,16 +1000,7 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
}
void emitAssignment(String destination, String source) {
- if (isGeneratingExpression()) {
- addExpressionSeparator();
- } else {
- addIndentation();
- }
- declareVariable(destination);
- buffer.add(' = $source');
- if (!isGeneratingExpression()) {
- buffer.add(';\n');
- }
+ assignVariable(destination, new js.Ref(source));
}
/**
@@ -1149,20 +1093,9 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
sequentializeCopies(handler.copies);
for (Copy copy in handler.assignments) {
- if (isGeneratingExpression()) {
- addExpressionSeparator();
- } else {
- addIndentation();
- }
String name = variableNames.getName(copy.destination);
- if (!handleSimpleUpdateDefinition(copy.source, name)) {
- declareVariable(name);
- buffer.add(' = ');
- use(copy.source, JSPrecedence.ASSIGNMENT_PRECEDENCE);
- }
- if (!isGeneratingExpression()) {
- buffer.add(';\n');
- }
+ use(copy.source);
+ assignVariable(name, pop());
}
}
@@ -1170,28 +1103,22 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
HInstruction instruction = node.first;
while (instruction !== node.last) {
if (instruction is HTypeGuard || instruction is HBailoutTarget) {
- visit(instruction, JSPrecedence.STATEMENT_PRECEDENCE);
+ visit(instruction);
} else if (!isGenerateAtUseSite(instruction)) {
- expectedPrecedence = JSPrecedence.STATEMENT_PRECEDENCE;
define(instruction);
}
instruction = instruction.next;
}
assignPhisOfSuccessors(node);
- if (instruction is HLoopBranch && isGeneratingExpression()) {
- addExpressionSeparator();
- }
- visit(instruction, JSPrecedence.STATEMENT_PRECEDENCE);
+ visit(instruction);
}
visitInvokeBinary(HInvokeBinary node, String op) {
if (node.builtin) {
- JSBinaryOperatorPrecedence operatorPrecedences = JSPrecedence.binary[op];
- beginExpression(operatorPrecedences.precedence);
- use(node.left, operatorPrecedences.left);
- buffer.add(' $op ');
- use(node.right, operatorPrecedences.right);
- endExpression(operatorPrecedences.precedence);
+ use(node.left);
+ js.Expression jsLeft = pop();
+ use(node.right);
+ push(new js.Binary(op, jsLeft, pop()), node);
} else {
visitInvokeStatic(node);
}
@@ -1200,25 +1127,16 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
// We want the outcome of bit-operations to be positive. We use the unsigned
// shift operator to achieve this.
visitBitInvokeBinary(HBinaryBitOp node, String op) {
+ visitInvokeBinary(node, op);
if (node.builtin && requiresUintConversion(node)) {
- beginExpression(unsignedShiftPrecedences.precedence);
- int oldPrecedence = this.expectedPrecedence;
- this.expectedPrecedence = JSPrecedence.SHIFT_PRECEDENCE;
- visitInvokeBinary(node, op);
- buffer.add(' >>> 0');
- this.expectedPrecedence = oldPrecedence;
- endExpression(unsignedShiftPrecedences.precedence);
- } else {
- visitInvokeBinary(node, op);
+ push(new js.Binary(">>>", pop(), new js.NumberLiteral("0")), node);
}
}
visitInvokeUnary(HInvokeUnary node, String op) {
if (node.builtin) {
- beginExpression(JSPrecedence.PREFIX_PRECEDENCE);
- buffer.add('$op');
- use(node.operand, JSPrecedence.PREFIX_PRECEDENCE);
- endExpression(JSPrecedence.PREFIX_PRECEDENCE);
+ use(node.operand);
+ push(new js.Unary(op, pop()), node);
} else {
visitInvokeStatic(node);
}
@@ -1227,49 +1145,32 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
// We want the outcome of bit-operations to be positive. We use the unsigned
// shift operator to achieve this.
visitBitInvokeUnary(HInvokeUnary node, String op) {
+ visitInvokeUnary(node, op);
if (node.builtin && requiresUintConversion(node)) {
- beginExpression(unsignedShiftPrecedences.precedence);
- int oldPrecedence = this.expectedPrecedence;
- this.expectedPrecedence = JSPrecedence.SHIFT_PRECEDENCE;
- visitInvokeUnary(node, op);
- buffer.add(' >>> 0');
- this.expectedPrecedence = oldPrecedence;
- endExpression(unsignedShiftPrecedences.precedence);
- } else {
- visitInvokeUnary(node, op);
+ push(new js.Binary(">>>", pop(), new js.NumberLiteral("0")), node);
}
}
void emitIdentityComparison(HInstruction left, HInstruction right) {
String op = singleIdentityComparison(left, right);
if (op != null) {
- beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
- use(left, JSPrecedence.EQUALITY_PRECEDENCE);
- buffer.add(' $op ');
- use(right, JSPrecedence.RELATIONAL_PRECEDENCE);
- endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
+ use(left);
+ js.Expression jsLeft = pop();
+ use(right);
+ push(new js.Binary(op, jsLeft, pop()));
} else {
assert(NullConstant.JsNull == 'null');
- withPrecedence(JSPrecedence.CONDITIONAL_PRECEDENCE, () {
- beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
- use(left, JSPrecedence.EQUALITY_PRECEDENCE);
- buffer.add(' == null');
- endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
- buffer.add(' ? ');
- this.expectedPrecedence = JSPrecedence.ASSIGNMENT_PRECEDENCE;
- withPrecedence(JSPrecedence.LOGICAL_AND_PRECEDENCE, () {
- beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
- use(right, JSPrecedence.EQUALITY_PRECEDENCE);
- buffer.add(' == null');
- endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
- buffer.add(" : ");
- beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
- use(left, JSPrecedence.EQUALITY_PRECEDENCE);
- buffer.add(' === ');
- use(right, JSPrecedence.EQUALITY_PRECEDENCE);
- endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
- });
- });
+ use(left);
+ js.Binary leftEqualsNull =
+ new js.Binary("==", pop(), new js.NullLiteral());
+ use(right);
+ js.Binary rightEqualsNull =
+ new js.Binary("==", pop(), new js.NullLiteral());
+ use(right);
+ use(left);
+ js.Binary tripleEq = new js.Binary("===", pop(), pop());
+
+ push(new js.Conditional(leftEqualsNull, rightEqualsNull, tripleEq));
}
}
@@ -1310,11 +1211,9 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
visitGreaterEqual(HGreaterEqual node) => visitInvokeBinary(node, '>=');
visitBoolify(HBoolify node) {
- beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
assert(node.inputs.length == 1);
- use(node.inputs[0], JSPrecedence.EQUALITY_PRECEDENCE);
- buffer.add(' === true');
- endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
+ use(node.inputs[0]);
+ push(new js.Binary('===', pop(), new js.BoolLiteral(true)), node);
}
visitExit(HExit node) {
@@ -1342,25 +1241,6 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
visitBasicBlock(dominated[0]);
}
- // Used to write the name of labels.
- void writeLabel(LabelElement label) {
- buffer.add('\$${label.labelName}\$${label.target.nestingLevel}');
- }
-
- void writeImplicitLabel(TargetElement target) {
- buffer.add('\$${target.nestingLevel}');
- }
-
- // We sometimes handle continue targets differently from break targets,
- // so we have special continue-only labels.
- void writeContinueLabel(LabelElement label) {
- buffer.add('c\$${label.labelName}\$${label.target.nestingLevel}');
- }
-
- void writeImplicitContinueLabel(TargetElement target) {
- buffer.add('c\$${target.nestingLevel}');
- }
-
/**
* Checks if [map] contains an [ElementAction] for [element], and
* if so calls that action and returns true.
@@ -1378,14 +1258,12 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
if (node.label !== null) {
LabelElement label = node.label;
if (!tryCallAction(breakAction, label)) {
- addIndented("break ");
- writeLabel(label);
- buffer.add(";\n");
+ pushStatement(new js.Break(compiler.namer.breakLabelName(label)), node);
}
} else {
TargetElement target = node.target;
if (!tryCallAction(breakAction, target)) {
- addIndented("break;\n");
+ pushStatement(new js.Break(null), node);
}
}
}
@@ -1395,14 +1273,14 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
if (node.label !== null) {
LabelElement label = node.label;
if (!tryCallAction(continueAction, label)) {
- addIndented("continue ");
- writeLabel(label);
- buffer.add(";\n");
+ // TODO(floitsch): should this really be the breakLabelName?
Lasse Reichstein Nielsen 2012/08/08 11:16:38 Good question. I really can't remember if there is
+ pushStatement(new js.Continue(compiler.namer.breakLabelName(label)),
+ node);
}
} else {
TargetElement target = node.target;
if (!tryCallAction(continueAction, target)) {
- addIndented("continue;\n");
+ pushStatement(new js.Continue(null), node);
}
}
}
@@ -1414,61 +1292,6 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
compiler.internalError('visitTry should not be called', instruction: node);
}
- /**
- * Analyzes the given [graph] to know whether it is empty, or
- * contains one statement, one expression, or multiple statements.
- */
- int analyzeGraphForCodegen(HStatementInformation graph) {
- HBasicBlock start = graph.start;
- HBasicBlock end = graph.end;
- // Only deal with single blocks for now. TODO(ngeoffray): analyze
- // all blocks.
- if (start !== end) return MULTIPLE_STATEMENTS;
-
- int kind = EMPTY;
- bool updateKind(int newKind) {
- if (kind != EMPTY) return false;
- kind = newKind;
- return true;
- }
-
- for (HInstruction instruction = start.first;
- instruction != start.last;
- instruction = instruction.next) {
- if (instruction.isStatement) {
- if (!updateKind(ONE_STATEMENT)) return MULTIPLE_STATEMENTS;
- } else if (!isGenerateAtUseSite(instruction)) {
- if (!updateKind(ONE_EXPRESSION)) return MULTIPLE_STATEMENTS;
- }
- }
-
- HInstruction last = start.last;
- if (last is !HGoto) {
- if (!updateKind(last.isStatement ? ONE_STATEMENT : ONE_EXPRESSION)) {
- return MULTIPLE_STATEMENTS;
- }
- }
-
- CopyHandler handler = variableNames.getCopyHandler(start);
- if (handler !== null && !handler.isEmpty()) {
- if (handler.assignments.length > 1) return MULTIPLE_STATEMENTS;
- if (handler.assignments.length == 1) {
- if (!updateKind(ONE_STATEMENT)) return MULTIPLE_STATEMENTS;
- }
- // If the block has a copy where the destination and source are
- // different, we will emit that copy, and therefore the block is
- // not empty.
- for (Copy copy in handler.copies) {
- String sourceName = variableNames.getName(copy.source);
- String destinationName = variableNames.getName(copy.destination);
- if (sourceName != destinationName) {
- if (!updateKind(ONE_STATEMENT)) return MULTIPLE_STATEMENTS;
- }
- }
- }
- return kind;
- }
-
bool tryControlFlowOperation(HIf node) {
if (!controlFlowOperators.contains(node)) return false;
HPhi phi = node.joinBlock.phis.first;
@@ -1489,196 +1312,22 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
}
void generateIf(HIf node, HIfBlockInformation info) {
+ use(node.inputs[0]);
+ js.Expression test = pop();
+
HStatementInformation thenGraph = info.thenGraph;
HStatementInformation elseGraph = info.elseGraph;
- int thenKind = analyzeGraphForCodegen(thenGraph);
- int elseKind = analyzeGraphForCodegen(elseGraph);
-
- void visitWithoutIndent(HStatementInformation toVisit) {
- int oldIndent = indent;
- indent = 0;
- generateStatements(toVisit);
- indent = oldIndent;
- }
-
- void visitExpression(HStatementInformation toVisit) {
- // [generateExpression] only works if the [expectedPrecedence] is a
- // statement or an expression. We therefore have to duplicate some
- // work here.
- assert(toVisit.start == toVisit.end);
- assert(toVisit.start.last is HGoto);
- // Find the expression (there must only be one).
- HInstruction expression = toVisit.start.first;
- while (generateAtUseSite.contains(expression)) {
- expression = expression.next;
- }
- assert(() {
- HInstruction remaining = expression.next;
- while (remaining is !HGoto) {
- if (!generateAtUseSite.contains(remaining)) return false;
- remaining = remaining.next;
- }
- return true;
- });
-
- int oldState = generationState;
- generationState = STATE_FIRST_EXPRESSION;
- define(expression);
- generationState = oldState;
- }
-
- void visitWithIndent(HStatementInformation toVisit) {
- buffer.add('{\n');
- indent++;
- generateStatements(toVisit);
- indent--;
- addIndented('}');
- }
-
- void emitIf() {
- addIndented('if (');
- use(node.inputs[0], JSPrecedence.EXPRESSION_PRECEDENCE);
- buffer.add(') ');
- }
-
- JSBinaryOperatorPrecedence operatorPrecedence = JSPrecedence.binary['&&'];
- void generateAnd(HStatementInformation toVisit, Function condition) {
- addIndentation();
- beginExpression(operatorPrecedence.precedence);
- var oldPrecedence = expectedPrecedence;
- expectedPrecedence = operatorPrecedence.left;
- condition();
- buffer.add(" && ");
- expectedPrecedence = operatorPrecedence.right;
- visitExpression(toVisit);
- expectedPrecedence = oldPrecedence;
- endExpression(operatorPrecedence.precedence);
- buffer.add(";\n");
- }
-
- List<HBasicBlock> thenSuccessors = thenGraph.end.successors;
- bool thenGraphHasSuccessor = thenSuccessors.length != 0
- && thenSuccessors[0] !== currentGraph.exit;
-
- switch (thenKind) {
- case EMPTY:
- switch (elseKind) {
- case EMPTY:
- if (isGenerateAtUseSite(node.inputs[0])) {
- addIndentation();
- use(node.inputs[0], JSPrecedence.STATEMENT_PRECEDENCE);
- buffer.add(';\n');
- }
- break;
-
- case ONE_EXPRESSION:
- generateAnd(elseGraph, () { generateNot(node.inputs[0]); });
- break;
-
- case ONE_STATEMENT:
- addIndented('if (');
- generateNot(node.inputs[0]);
- buffer.add(') ');
- visitWithoutIndent(elseGraph);
- break;
-
- case MULTIPLE_STATEMENTS:
- addIndented('if (');
- generateNot(node.inputs[0]);
- buffer.add(') ');
- visitWithIndent(elseGraph);
- buffer.add('\n');
- break;
- }
+ js.Statement thenPart =
+ unwrapStatement(generateStatementsInNewBlock(thenGraph));
+ js.Statement elsePart =
+ unwrapStatement(generateStatementsInNewBlock(elseGraph));
- break;
-
- case ONE_EXPRESSION:
- case ONE_STATEMENT:
- switch (elseKind) {
- case EMPTY:
- if (thenKind == ONE_EXPRESSION) {
- int precedence = operatorPrecedence.left;
- generateAnd(thenGraph, () { use(node.inputs[0], precedence); });
- } else {
- emitIf();
- visitWithoutIndent(thenGraph);
- }
- break;
-
- case ONE_EXPRESSION:
- case ONE_STATEMENT:
- // TODO(ngeoffray): Generate a conditional.
- emitIf();
- visitWithoutIndent(thenGraph);
- if (thenGraphHasSuccessor) {
- addIndented('else ');
- visitWithoutIndent(elseGraph);
- } else {
- generateStatements(elseGraph);
- }
- break;
-
- case MULTIPLE_STATEMENTS:
- emitIf();
- visitWithoutIndent(thenGraph);
- if (thenGraphHasSuccessor) {
- addIndented('else ');
- visitWithIndent(elseGraph);
- buffer.add('\n');
- } else {
- generateStatements(elseGraph);
- }
- break;
- }
- break;
-
- case MULTIPLE_STATEMENTS:
- emitIf();
- visitWithIndent(thenGraph);
-
- switch (elseKind) {
- case EMPTY:
- buffer.add('\n');
- break;
-
- case ONE_EXPRESSION:
- case ONE_STATEMENT:
- if (thenGraphHasSuccessor) {
- buffer.add(' else ');
- visitWithoutIndent(elseGraph);
- } else {
- buffer.add('\n');
- generateStatements(elseGraph);
- }
- break;
-
- case MULTIPLE_STATEMENTS:
- if (thenGraphHasSuccessor) {
- buffer.add(' else ');
- visitWithIndent(elseGraph);
- buffer.add('\n');
- } else {
- buffer.add('\n');
- generateStatements(elseGraph);
- }
- break;
- }
- break;
- }
+ pushStatement(new js.If(test, thenPart, elsePart), node);
}
-
visitIf(HIf node) {
if (tryControlFlowOperation(node)) return;
- if (subGraph !== null && node.block === subGraph.end) {
- if (isGeneratingExpression()) {
- use(node.inputs[0], JSPrecedence.EXPRESSION_PRECEDENCE);
- }
- return;
- }
-
HInstruction condition = node.inputs[0];
HIfBlockInformation info = node.blockInformation.body;
@@ -1711,25 +1360,29 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
}
}
+ js.Call jsPropertyCall(js.Expression receiver,
+ String fieldName,
+ List<js.Expression> arguments) {
+ return new js.Call(new js.Access.field(receiver, fieldName), arguments);
+ }
+
visitInvokeDynamicMethod(HInvokeDynamicMethod node) {
- beginExpression(JSPrecedence.CALL_PRECEDENCE);
- use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE);
- buffer.add('.');
+ use(node.receiver);
+ js.Expression object = pop();
+ String methodName;
+ List<js.Expression> arguments;
+
// Avoid adding the generative constructor name to the list of
// seen selectors.
if (node.inputs[0] is HForeignNew) {
- HForeignNew foreignNew = node.inputs[0];
- // Remove 'this' from the number of arguments.
- int argumentCount = node.inputs.length - 1;
-
// TODO(ahe): The constructor name was statically resolved in
// SsaBuilder.buildFactory. Is there a cleaner way to do this?
- node.name.printOn(buffer);
- visitArguments(node.inputs);
+ methodName = node.name.slowToString();
+ arguments = visitArguments(node.inputs);
} else {
- buffer.add(compiler.namer.instanceMethodInvocationName(
- currentLibrary, node.name, node.selector));
- visitArguments(node.inputs);
+ methodName = compiler.namer.instanceMethodInvocationName(
+ currentLibrary, node.name, node.selector);
+ arguments = visitArguments(node.inputs);
bool inLoop = node.block.enclosingLoopHeader !== null;
if (node.element !== null) {
// If we know we're calling a specific method, register that
@@ -1744,7 +1397,7 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
if (inLoop) backend.builder.selectorsCalledInLoop[node.name] = selector;
}
}
- endExpression(JSPrecedence.CALL_PRECEDENCE);
+ push(jsPropertyCall(object, methodName, arguments), node);
}
Selector getOptimizedSelectorFor(HInvoke node, Selector defaultSelector) {
@@ -1757,94 +1410,85 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
}
visitInvokeDynamicSetter(HInvokeDynamicSetter node) {
- beginExpression(JSPrecedence.CALL_PRECEDENCE);
- use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE);
- buffer.add('.');
- buffer.add(compiler.namer.setterName(currentLibrary, node.name));
- visitArguments(node.inputs);
+ use(node.receiver);
+ push(jsPropertyCall(pop(),
+ compiler.namer.setterName(currentLibrary, node.name),
+ visitArguments(node.inputs)),
+ node);
world.registerDynamicSetter(
node.name, getOptimizedSelectorFor(node, Selector.SETTER));
- endExpression(JSPrecedence.CALL_PRECEDENCE);
}
visitInvokeDynamicGetter(HInvokeDynamicGetter node) {
- beginExpression(JSPrecedence.CALL_PRECEDENCE);
- use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE);
- buffer.add('.');
- buffer.add(compiler.namer.getterName(currentLibrary, node.name));
- visitArguments(node.inputs);
+ use(node.receiver);
+ push(jsPropertyCall(pop(),
+ compiler.namer.getterName(currentLibrary, node.name),
+ visitArguments(node.inputs)),
+ node);
world.registerDynamicGetter(
node.name, getOptimizedSelectorFor(node, Selector.GETTER));
- endExpression(JSPrecedence.CALL_PRECEDENCE);
}
visitInvokeClosure(HInvokeClosure node) {
- beginExpression(JSPrecedence.CALL_PRECEDENCE);
- use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE);
- buffer.add('.');
- buffer.add(compiler.namer.closureInvocationName(node.selector));
- visitArguments(node.inputs);
+ use(node.receiver);
+ push(jsPropertyCall(pop(),
+ compiler.namer.closureInvocationName(node.selector),
+ visitArguments(node.inputs)),
+ node);
// TODO(floitsch): we should have a separate list for closure invocations.
world.registerDynamicInvocation(compiler.namer.CLOSURE_INVOCATION_NAME,
node.selector);
- endExpression(JSPrecedence.CALL_PRECEDENCE);
}
visitInvokeStatic(HInvokeStatic node) {
- beginExpression(JSPrecedence.CALL_PRECEDENCE);
- use(node.target, JSPrecedence.CALL_PRECEDENCE);
- visitArguments(node.inputs);
- endExpression(JSPrecedence.CALL_PRECEDENCE);
+ use(node.target);
+ push(new js.Call(pop(), visitArguments(node.inputs)), node);
}
visitInvokeSuper(HInvokeSuper node) {
- beginExpression(JSPrecedence.CALL_PRECEDENCE);
Element superMethod = node.element;
Element superClass = superMethod.enclosingElement;
// Remove the element and 'this'.
int argumentCount = node.inputs.length - 2;
String className = compiler.namer.isolateAccess(superClass);
- if (superMethod.kind == ElementKind.FUNCTION ||
- superMethod.kind == ElementKind.GENERATIVE_CONSTRUCTOR) {
- String methodName = compiler.namer.instanceMethodName(
- currentLibrary, superMethod.name, argumentCount);
- buffer.add('$className.prototype.$methodName.call');
- visitArguments(node.inputs);
- } else if (superMethod.kind == ElementKind.FIELD) {
+ if (superMethod.kind == ElementKind.FIELD) {
ClassElement currentClass = work.element.enclosingElement;
+ String fieldName;
if (currentClass.isShadowedByField(superMethod)) {
- buffer.add('this.${compiler.namer.shadowedFieldName(superMethod)}');
+ fieldName = compiler.namer.shadowedFieldName(superMethod);
} else {
LibraryElement library = superMethod.getLibrary();
SourceString name = superMethod.name;
- buffer.add('this.${compiler.namer.instanceFieldName(library, name)}');
+ fieldName = compiler.namer.instanceFieldName(library, name);
}
+ push(new js.Access.field(new js.This(), fieldName), node);
} else {
- assert(superMethod.kind == ElementKind.GETTER ||
- superMethod.kind == ElementKind.SETTER);
String methodName;
- if (superMethod.kind == ElementKind.GETTER) {
+ if (superMethod.kind == ElementKind.FUNCTION ||
+ superMethod.kind == ElementKind.GENERATIVE_CONSTRUCTOR) {
+ methodName = compiler.namer.instanceMethodName(
+ currentLibrary, superMethod.name, argumentCount);
+ } else if (superMethod.kind == ElementKind.GETTER) {
methodName =
compiler.namer.getterName(currentLibrary, superMethod.name);
} else {
+ assert(superMethod.kind == ElementKind.SETTER);
methodName =
compiler.namer.setterName(currentLibrary, superMethod.name);
}
- buffer.add('$className.prototype.$methodName.call');
- visitArguments(node.inputs);
+ js.Access prototype =
+ new js.Access.field(new js.Ref(className), "prototype");
+ js.Access method = new js.Access.field(prototype, methodName);
+ push(jsPropertyCall(method, "call", visitArguments(node.inputs)), node);
}
- endExpression(JSPrecedence.CALL_PRECEDENCE);
world.registerStaticUse(superMethod);
}
visitFieldGet(HFieldGet node) {
String name =
compiler.namer.instanceFieldName(node.libraryElement, node.fieldName);
- beginExpression(JSPrecedence.MEMBER_PRECEDENCE);
- use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE);
- buffer.add('.');
- buffer.add(name);
- beginExpression(JSPrecedence.MEMBER_PRECEDENCE);
+ use(node.receiver);
+ push(new js.Access.field(pop(), name), node);
Type type = node.receiver.propagatedType.computeType(compiler);
if (type != null) {
world.registerFieldGetter(node.element.name, type);
@@ -1874,10 +1518,6 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
}
String name =
compiler.namer.instanceFieldName(node.libraryElement, node.fieldName);
- beginExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE);
- use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE);
- buffer.add('.');
- buffer.add(name);
Type type = node.receiver.propagatedType.computeType(compiler);
if (type != null) {
if (!work.element.isGenerativeConstructorBody()) {
@@ -1898,36 +1538,38 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
node.value.propagatedType);
}
}
- buffer.add(' = ');
- use(node.value, JSPrecedence.ASSIGNMENT_PRECEDENCE);
- endExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE);
+ use(node.receiver);
+ js.Expression receiver = pop();
+ use(node.value);
+ push(new js.Accsign(new js.Access.field(receiver, name), pop()), node);
}
visitLocalGet(HLocalGet node) {
- use(node.receiver, JSPrecedence.EXPRESSION_PRECEDENCE);
+ use(node.receiver);
}
visitLocalSet(HLocalSet node) {
- declareInstruction(node.receiver);
- buffer.add(' = ');
- use(node.value, JSPrecedence.ASSIGNMENT_PRECEDENCE);
+ use(node.value);
+ assignVariable(variableNames.getName(node.receiver), pop());
}
visitForeign(HForeign node) {
String code = node.code.slowToString();
List<HInstruction> inputs = node.inputs;
- List<String> parts = code.split('#');
- if (parts.length != inputs.length + 1) {
- compiler.internalError(
- 'Wrong number of arguments for JS', instruction: node);
- }
- beginExpression(JSPrecedence.EXPRESSION_PRECEDENCE);
- buffer.add(parts[0]);
- for (int i = 0; i < inputs.length; i++) {
- use(inputs[i], JSPrecedence.EXPRESSION_PRECEDENCE);
- buffer.add(parts[i + 1]);
+ if (node.isStatement) {
+ if (!inputs.isEmpty()) {
+ compiler.internalError("foreign statement with inputs: $code",
+ instruction: node);
+ }
+ pushStatement(new js.StatementBlob(code), node);
+ } else {
+ List<js.Expression> data = <js.Expression>[];
+ for (int i = 0; i < inputs.length; i++) {
+ use(inputs[i]);
+ data.add(pop());
+ }
+ push(new js.ExpressionBlob.withData(code, data), node);
}
- endExpression(JSPrecedence.EXPRESSION_PRECEDENCE);
}
visitForeignNew(HForeignNew node) {
@@ -1941,40 +1583,44 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
j++;
});
String jsClassReference = compiler.namer.isolateAccess(node.element);
- beginExpression(JSPrecedence.MEMBER_PRECEDENCE);
- buffer.add('new $jsClassReference(');
- // We can't use 'visitArguments', since our arguments start at input[0].
List<HInstruction> inputs = node.inputs;
+ // We can't use 'visitArguments', since our arguments start at input[0].
+ List<js.Expression> arguments = <js.Expression>[];
for (int i = 0; i < inputs.length; i++) {
- if (i != 0) buffer.add(', ');
- use(inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE);
+ use(inputs[i]);
+ arguments.add(pop());
}
- buffer.add(')');
- endExpression(JSPrecedence.MEMBER_PRECEDENCE);
+ // TODO(floitsch): jsClassReference is an Access. We shouldn't treat it
+ // as if it was a string.
+ push(new js.New(new js.Ref(jsClassReference), arguments), node);
}
void generateConstant(Constant constant) {
- // TODO(floitsch): the compile-time constant handler and the codegen
- // need to work together to avoid the parenthesis. See r4928 for an
- // implementation that still dealt with precedence.
ConstantHandler handler = compiler.constantHandler;
String name = handler.getNameForConstant(constant);
if (name === null) {
assert(!constant.isObject());
- if (constant.isNum()
- && expectedPrecedence == JSPrecedence.MEMBER_PRECEDENCE) {
- buffer.add('(');
+ if (constant.isBool()) {
+ push(new js.BoolLiteral((constant as BoolConstant).value));
+ } else if (constant.isNum()) {
+ // TODO(floitsch): get rid of the code buffer.
+ CodeBuffer buffer = new CodeBuffer();
handler.writeConstant(buffer, constant);
- buffer.add(')');
- } else {
+ push(new js.NumberLiteral(buffer.toString()));
+ } else if (constant.isNull()) {
+ push(new js.NullLiteral());
+ } else if (constant.isString()) {
+ // TODO(floitsch): get rid of the code buffer.
+ CodeBuffer buffer = new CodeBuffer();
handler.writeConstant(buffer, constant);
+ push(new js.StringLiteral(buffer.toString()));
+ } else {
+ compiler.internalError("Forgot constant $constant");
}
} else {
- buffer.add(compiler.namer.CURRENT_ISOLATE);
- buffer.add(".");
- buffer.add(name);
+ push(new js.Access.field(new js.Ref(compiler.namer.CURRENT_ISOLATE),
+ name));
}
-
}
visitConstant(HConstant node) {
@@ -1992,13 +1638,12 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
// condition will be generated as the expression.
// Otherwise, we don't generate the expression, and leave that
// to the code that called [visitSubGraph].
- if (isGeneratingExpression()) {
- use(node.inputs[0], JSPrecedence.EXPRESSION_PRECEDENCE);
+ if (isGeneratingExpression) {
+ use(node.inputs[0]);
}
return;
}
HBasicBlock branchBlock = currentBlock;
- addIndentation();
handleLoopCondition(node);
List<HBasicBlock> dominated = currentBlock.dominatedBlocks;
// For a do while loop, the body has already been visited.
@@ -2023,6 +1668,7 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
visitNot(HNot node) {
assert(node.inputs.length == 1);
generateNot(node.inputs[0]);
+ attachLocationToLast(node);
}
@@ -2034,10 +1680,8 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
}
if (input is HBoolify && isGenerateAtUseSite(input)) {
- beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
- use(input.inputs[0], JSPrecedence.EQUALITY_PRECEDENCE);
- buffer.add(' !== true');
- endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
+ use(input.inputs[0]);
+ push(new js.Binary("!==", pop(), new js.BoolLiteral(true)), input);
} else if (isBuiltinRelational(input) &&
isGenerateAtUseSite(input) &&
input.inputs[0].propagatedType.isUseful() &&
@@ -2060,10 +1704,8 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
visitInvokeBinary(input,
inverseOperator[relational.operation.name.stringValue]);
} else {
- beginExpression(JSPrecedence.PREFIX_PRECEDENCE);
- buffer.add('!');
- use(input, JSPrecedence.PREFIX_PRECEDENCE);
- endExpression(JSPrecedence.PREFIX_PRECEDENCE);
+ use(input);
+ push(new js.Unary("!", pop()));
}
}
@@ -2071,7 +1713,7 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
visitLocalValue(HLocalValue node) {
assert(isGenerateAtUseSite(node));
- buffer.add(variableNames.getName(node));
+ push(new js.Ref(variableNames.getName(node)), node);
}
visitPhi(HPhi node) {
@@ -2082,117 +1724,112 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
assert(controlFlowOperators.contains(ifBlock.last));
HInstruction input = ifBlock.last.inputs[0];
if (input.isConstantFalse()) {
- use(node.inputs[1], expectedPrecedence);
+ use(node.inputs[1]);
} else if (input.isConstantTrue()) {
- use(node.inputs[0], expectedPrecedence);
+ use(node.inputs[0]);
} else if (node.inputs[1].isConstantBoolean()) {
String operation = node.inputs[1].isConstantFalse() ? '&&' : '||';
- JSBinaryOperatorPrecedence operatorPrecedence =
- JSPrecedence.binary[operation];
- beginExpression(operatorPrecedence.precedence);
if (operation == '||') {
if (input is HNot) {
- use(input.inputs[0], operatorPrecedence.left);
+ use(input.inputs[0]);
} else {
generateNot(input);
}
} else {
- use(input, operatorPrecedence.left);
+ use(input);
}
- buffer.add(" $operation ");
- use(node.inputs[0], operatorPrecedence.right);
- endExpression(operatorPrecedence.precedence);
+ js.Expression left = pop();
+ use(node.inputs[0]);
+ push(new js.Binary(operation, left, pop()));
} else {
- beginExpression(JSPrecedence.CONDITIONAL_PRECEDENCE);
- use(input, JSPrecedence.LOGICAL_OR_PRECEDENCE);
- buffer.add(' ? ');
- use(node.inputs[0], JSPrecedence.ASSIGNMENT_PRECEDENCE);
- buffer.add(' : ');
- use(node.inputs[1], JSPrecedence.ASSIGNMENT_PRECEDENCE);
- endExpression(JSPrecedence.CONDITIONAL_PRECEDENCE);
+ use(input);
+ js.Expression test = pop();
+ use(node.inputs[0]);
+ js.Expression then = pop();
+ use(node.inputs[1]);
+ push(new js.Conditional(test, then, pop()));
}
}
visitReturn(HReturn node) {
- addIndentation();
assert(node.inputs.length == 1);
HInstruction input = node.inputs[0];
if (input.isConstantNull()) {
- buffer.add('return;\n');
+ pushStatement(new js.Return(null), node);
} else {
- buffer.add('return ');
- use(node.inputs[0], JSPrecedence.EXPRESSION_PRECEDENCE);
- buffer.add(';\n');
+ use(node.inputs[0]);
+ pushStatement(new js.Return(pop()), node);
}
}
visitThis(HThis node) {
- buffer.add('this');
+ push(new js.This());
}
visitThrow(HThrow node) {
- addIndentation();
if (node.isRethrow) {
- buffer.add('throw ');
- use(node.inputs[0], JSPrecedence.EXPRESSION_PRECEDENCE);
+ use(node.inputs[0]);
+ pushStatement(new js.Throw(pop()), node);
} else {
generateThrowWithHelper('captureStackTrace', node.inputs[0]);
}
- buffer.add(';\n');
}
visitBoundsCheck(HBoundsCheck node) {
// TODO(ngeoffray): Separate the two checks of the bounds check, so,
// e.g., the zero checks can be shared if possible.
- // If the checks always succeede, we would have removed the bounds check
+ // If the checks always succeeds, we would have removed the bounds check
// completely.
assert(node.staticChecks != HBoundsCheck.ALWAYS_TRUE);
if (node.staticChecks != HBoundsCheck.ALWAYS_FALSE) {
- buffer.add('if (');
+ js.Binary under;
if (node.staticChecks != HBoundsCheck.ALWAYS_ABOVE_ZERO) {
assert(node.staticChecks == HBoundsCheck.FULL_CHECK);
- use(node.index, JSPrecedence.RELATIONAL_PRECEDENCE);
- buffer.add(' < 0 || ');
+ use(node.index);
+ under = new js.Binary("<", pop(), new js.NumberLiteral("0"));
}
- use(node.index, JSPrecedence.RELATIONAL_PRECEDENCE);
- buffer.add(' >= ');
- use(node.length, JSPrecedence.SHIFT_PRECEDENCE);
- buffer.add(") ");
+ use(node.index);
+ js.Expression index = pop();
+ use(node.length);
+ js.Binary over = new js.Binary(">=", index, pop());
+ js.Binary underOver =
+ under == null ? over : new js.Binary("||", under, over);
+ js.Statement thenBody = new js.Block.empty();
+ js.Block oldContainer = currentContainer;
+ currentContainer = thenBody;
+ generateThrowWithHelper('ioore', node.index);
+ currentContainer = oldContainer;
+ thenBody = unwrapStatement(thenBody);
+ pushStatement(new js.If.then(underOver, thenBody), node);
+ } else {
+ generateThrowWithHelper('ioore', node.index);
}
- generateThrowWithHelper('ioore', node.index);
}
visitIntegerCheck(HIntegerCheck node) {
if (!node.alwaysFalse) {
- buffer.add('if (');
checkInt(node.value, '!==');
- buffer.add(') ');
+ js.Expression test = pop();
+ js.Statement thenBody = new js.Block.empty();
+ js.Block oldContainer = currentContainer;
+ currentContainer = thenBody;
+ generateThrowWithHelper('iae', node.value);
+ currentContainer = oldContainer;
+ thenBody = unwrapStatement(thenBody);
+ pushStatement(new js.If.then(test, thenBody), node);
+ } else {
+ generateThrowWithHelper('iae', node.value);
}
- generateThrowWithHelper('iae', node.value);
}
void generateThrowWithHelper(String helperName, HInstruction argument) {
Element helper = compiler.findHelper(new SourceString(helperName));
world.registerStaticUse(helper);
- buffer.add('throw ');
- beginExpression(JSPrecedence.EXPRESSION_PRECEDENCE);
- beginExpression(JSPrecedence.CALL_PRECEDENCE);
- buffer.add(compiler.namer.isolateAccess(helper));
- visitArguments([null, argument]);
- endExpression(JSPrecedence.CALL_PRECEDENCE);
- endExpression(JSPrecedence.EXPRESSION_PRECEDENCE);
- }
-
- void addIndentation() {
- for (int i = 0; i < indent; i++) {
- buffer.add(' ');
- }
- }
-
- void addIndented(String text) {
- addIndentation();
- buffer.add(text);
+ js.Ref jsHelper = new js.Ref(compiler.namer.isolateAccess(helper));
+ js.Call value = new js.Call(jsHelper, visitArguments([null, argument]));
+ attachLocation(value, argument);
+ pushStatement(new js.Throw(value));
}
void visitSwitch(HSwitch node) {
@@ -2201,35 +1838,26 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
void visitStatic(HStatic node) {
world.registerStaticUse(node.element);
- buffer.add(compiler.namer.isolateAccess(node.element));
+ push(new js.Ref(compiler.namer.isolateAccess(node.element)));
}
void visitStaticStore(HStaticStore node) {
world.registerStaticUse(node.element);
- beginExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE);
- buffer.add(compiler.namer.isolateAccess(node.element));
- buffer.add(' = ');
- use(node.inputs[0], JSPrecedence.ASSIGNMENT_PRECEDENCE);
- endExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE);
+ js.Ref ref = new js.Ref(compiler.namer.isolateAccess(node.element));
+ use(node.inputs[0]);
+ push(new js.Vassign(ref, pop()), node);
}
void visitStringConcat(HStringConcat node) {
if (isEmptyString(node.left)) {
- useStringified(node.right, expectedPrecedence);
+ useStringified(node.right);
} else if (isEmptyString(node.right)) {
- useStringified(node.left, expectedPrecedence);
+ useStringified(node.left);
} else {
- JSBinaryOperatorPrecedence operatorPrecedences = JSPrecedence.binary['+'];
- beginExpression(operatorPrecedences.precedence);
- useStringified(node.left, operatorPrecedences.left);
- buffer.add(' + ');
- // If the right hand side is a string concatenation itself it is
- // safe to make it left associative.
- int rightPrecedence = (node.right is HStringConcat)
- ? JSPrecedence.ADDITIVE_PRECEDENCE
- : operatorPrecedences.right;
- useStringified(node.right, rightPrecedence);
- endExpression(operatorPrecedences.precedence);
+ useStringified(node.left);
+ js.Expression left = pop();
+ useStringified(node.right);
+ push(new js.Binary("+", left, pop()), node);
}
}
@@ -2240,16 +1868,15 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
return string.value.length == 0;
}
- void useStringified(HInstruction node, int precedence) {
+ void useStringified(HInstruction node) {
if (node.isString()) {
- use(node, precedence);
+ use(node);
} else {
Element convertToString = compiler.findHelper(const SourceString("S"));
world.registerStaticUse(convertToString);
- buffer.add(compiler.namer.isolateAccess(convertToString));
- buffer.add('(');
- use(node, JSPrecedence.EXPRESSION_PRECEDENCE);
- buffer.add(')');
+ js.Ref ref = new js.Ref(compiler.namer.isolateAccess(convertToString));
+ use(node);
+ push(new js.Call(ref, <js.Expression>[pop()]), node);
}
}
@@ -2258,23 +1885,21 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
}
void generateArrayLiteral(HLiteralList node) {
- buffer.add('[');
int len = node.inputs.length;
+ List<js.ArrayElement> elements = <js.ArrayElement>[];
for (int i = 0; i < len; i++) {
- if (i != 0) buffer.add(', ');
- use(node.inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE);
+ use(node.inputs[i]);
+ elements.add(new js.ArrayElement(i, pop()));
}
- buffer.add(']');
+ push(new js.ArrayLiteral(len, elements), node);
}
void visitIndex(HIndex node) {
if (node.builtin) {
- beginExpression(JSPrecedence.MEMBER_PRECEDENCE);
- use(node.inputs[1], JSPrecedence.MEMBER_PRECEDENCE);
- buffer.add('[');
- use(node.inputs[2], JSPrecedence.EXPRESSION_PRECEDENCE);
- buffer.add(']');
- endExpression(JSPrecedence.MEMBER_PRECEDENCE);
+ use(node.inputs[1]);
+ js.Expression receiver = pop();
+ use(node.inputs[2]);
+ push(new js.Access(receiver, pop()), node);
} else {
visitInvokeStatic(node);
}
@@ -2282,13 +1907,12 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
void visitIndexAssign(HIndexAssign node) {
if (node.builtin) {
- beginExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE);
- use(node.inputs[1], JSPrecedence.MEMBER_PRECEDENCE);
- buffer.add('[');
- use(node.inputs[2], JSPrecedence.EXPRESSION_PRECEDENCE);
- buffer.add('] = ');
- use(node.inputs[3], JSPrecedence.ASSIGNMENT_PRECEDENCE);
- endExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE);
+ use(node.inputs[1]);
+ js.Expression receiver = pop();
+ use(node.inputs[2]);
+ js.Expression index = pop();
+ use(node.inputs[3]);
+ push(new js.Accsign(new js.Access(receiver, index), pop()), node);
} else {
visitInvokeStatic(node);
}
@@ -2325,24 +1949,23 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
String builtin = builtinJsName(node);
if (builtin !== null) {
if (builtin == '+') {
- beginExpression(JSPrecedence.ADDITIVE_PRECEDENCE);
- use(node.inputs[1], JSPrecedence.ADDITIVE_PRECEDENCE);
- buffer.add(' + ');
- use(node.inputs[2], JSPrecedence.MULTIPLICATIVE_PRECEDENCE);
- endExpression(JSPrecedence.ADDITIVE_PRECEDENCE);
+ use(node.inputs[1]);
+ js.Expression left = pop();
+ use(node.inputs[2]);
+ push(new js.Binary("+", left, pop()), node);
} else {
- beginExpression(JSPrecedence.CALL_PRECEDENCE);
- use(node.inputs[1], JSPrecedence.MEMBER_PRECEDENCE);
- buffer.add('.');
- buffer.add(builtin);
- if (node.getter) return;
- buffer.add('(');
+ use(node.inputs[1]);
+ js.Access access = new js.Access.field(pop(), builtin);
+ if (node.getter) {
+ push(access, node);
+ return;
+ }
+ List<js.Expression> arguments = <js.Expression>[];
for (int i = 2; i < node.inputs.length; i++) {
- if (i != 2) buffer.add(', ');
- use(node.inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE);
+ use(node.inputs[i]);
+ arguments.add(pop());
}
- buffer.add(")");
- endExpression(JSPrecedence.CALL_PRECEDENCE);
+ push(new js.Call(access, arguments), node);
}
} else {
return visitInvokeStatic(node);
@@ -2350,144 +1973,103 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
}
void checkInt(HInstruction input, String cmp) {
- beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
- use(input, JSPrecedence.EQUALITY_PRECEDENCE);
- buffer.add(' $cmp (');
- use(input, JSPrecedence.BITWISE_OR_PRECEDENCE);
- buffer.add(' | 0)');
- endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
+ use(input);
+ js.Expression left = pop();
+ use(input);
+ js.Expression or0 = new js.Binary("|", pop(), new js.NumberLiteral("0"));
+ push(new js.Binary(cmp, left, or0));
}
- void checkNum(HInstruction input, String cmp) {
- beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
- buffer.add('typeof ');
- use(input, JSPrecedence.PREFIX_PRECEDENCE);
- buffer.add(" $cmp 'number'");
- endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
+ void checkTypeOf(HInstruction input, String cmp, String typeName) {
+ use(input);
+ js.Expression typeOf = new js.Unary("typeof", pop());
+ push(new js.Binary(cmp, typeOf, new js.StringLiteral("'$typeName'")));
}
- void checkDouble(HInstruction input, String cmp) {
- checkNum(input, cmp);
- }
+ void checkNum(HInstruction input, String cmp)
+ => checkTypeOf(input, cmp, 'number');
- void checkString(HInstruction input, String cmp) {
- beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
- buffer.add('typeof ');
- use(input, JSPrecedence.PREFIX_PRECEDENCE);
- buffer.add(" $cmp 'string'");
- endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
- }
+ void checkDouble(HInstruction input, String cmp) => checkNum(input, cmp);
- void checkBool(HInstruction input, String cmp) {
- beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
- buffer.add('typeof ');
- use(input, JSPrecedence.PREFIX_PRECEDENCE);
- buffer.add(" $cmp 'boolean'");
- endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
- }
+ void checkString(HInstruction input, String cmp)
+ => checkTypeOf(input, cmp, 'string');
+
+ void checkBool(HInstruction input, String cmp)
+ => checkTypeOf(input, cmp, 'boolean');
void checkObject(HInstruction input, String cmp) {
assert(NullConstant.JsNull == 'null');
if (cmp == "===") {
- withPrecedence(JSPrecedence.LOGICAL_AND_PRECEDENCE, () {
- beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
- buffer.add('typeof ');
- use(input, JSPrecedence.PREFIX_PRECEDENCE);
- buffer.add(" === 'object'");
- endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
- buffer.add(" && ");
- beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
- use(input, JSPrecedence.PREFIX_PRECEDENCE);
- buffer.add(" !== null");
- endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
- });
+ checkTypeOf(input, '===', 'object');
+ js.Expression left = pop();
+ use(input);
+ js.Expression notNull = new js.Binary("!==", pop(), new js.NullLiteral());
+ push(new js.Binary("&&", left, notNull));
} else {
assert(cmp == "!==");
- withPrecedence(JSPrecedence.LOGICAL_OR_PRECEDENCE, () {
- beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
- buffer.add('typeof ');
- use(input, JSPrecedence.PREFIX_PRECEDENCE);
- buffer.add(" !== 'object'");
- endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
- buffer.add(" || ");
- beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
- use(input, JSPrecedence.PREFIX_PRECEDENCE);
- buffer.add(" === null");
- endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
- });
+ checkTypeOf(input, '!==', 'object');
+ js.Expression left = pop();
+ use(input);
+ js.Expression eqNull = new js.Binary("===", pop(), new js.NullLiteral());
+ push(new js.Binary("||", left, eqNull));
}
}
void checkArray(HInstruction input, String cmp) {
- beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
- use(input, JSPrecedence.MEMBER_PRECEDENCE);
- buffer.add('.constructor $cmp Array');
- endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
+ use(input);
+ js.Access constructor = new js.Access.field(pop(), 'constructor');
+ push(new js.Binary(cmp, constructor, new js.Ref('Array')));
+ }
+
+ void checkFieldExists(HInstruction input, String fieldName) {
+ use(input);
+ js.Access field = new js.Access.field(pop(), fieldName);
+ // Double negate to boolify the result.
+ push(new js.Unary('!', new js.Unary('!', field)));
}
void checkImmutableArray(HInstruction input) {
- beginExpression(JSPrecedence.PREFIX_PRECEDENCE);
- buffer.add('!!');
- use(input, JSPrecedence.MEMBER_PRECEDENCE);
- buffer.add('.immutable\$list');
- endExpression(JSPrecedence.PREFIX_PRECEDENCE);
+ checkFieldExists(input, 'immutable\$list');
}
void checkExtendableArray(HInstruction input) {
- beginExpression(JSPrecedence.PREFIX_PRECEDENCE);
- buffer.add('!!');
- use(input, JSPrecedence.MEMBER_PRECEDENCE);
- buffer.add('.fixed\$length');
- endExpression(JSPrecedence.PREFIX_PRECEDENCE);
+ checkFieldExists(input, 'fixed\$length');
}
void checkFixedArray(HInstruction input) {
- beginExpression(JSPrecedence.PREFIX_PRECEDENCE);
- use(input, JSPrecedence.MEMBER_PRECEDENCE);
- buffer.add('.fixed\$length');
- endExpression(JSPrecedence.PREFIX_PRECEDENCE);
+ checkFieldExists(input, 'fixed\$length');
}
void checkNull(HInstruction input) {
- beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
- use(input, JSPrecedence.EQUALITY_PRECEDENCE);
- buffer.add(" == null");
- endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
+ use(input);
+ push(new js.Binary('==', pop(), new js.NullLiteral()));
}
void checkFunction(HInstruction input, Element element) {
- withPrecedence(JSPrecedence.LOGICAL_OR_PRECEDENCE, () {
- beginExpression(JSPrecedence.EQUALITY_PRECEDENCE);
- buffer.add('typeof ');
- use(input, JSPrecedence.PREFIX_PRECEDENCE);
- buffer.add(" === 'function'");
- endExpression(JSPrecedence.EQUALITY_PRECEDENCE);
- buffer.add(" || ");
- beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
- checkObject(input, '===');
- buffer.add(" && ");
- checkType(input, element);
- endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
- });
+ checkTypeOf(input, '===', 'function');
+ js.Expression functionTest = pop();
+ checkObject(input, '===');
+ js.Expression objectTest = pop();
+ checkType(input, element);
+ push(new js.Binary('||',
+ functionTest,
+ new js.Binary('&&', objectTest, pop())));
}
void checkType(HInstruction input, Element element, [bool negative = false]) {
world.registerIsCheck(element);
- bool requiresNativeIsCheck =
- backend.emitter.nativeEmitter.requiresNativeIsCheck(element);
- if (!requiresNativeIsCheck) {
- if (negative) {
- buffer.add('!');
- } else {
- buffer.add('!!');
- }
- } else if (negative) {
- buffer.add('!');
+ use(input);
+ js.Access field =
+ new js.Access.field(pop(), compiler.namer.operatorIs(element));
+ if (backend.emitter.nativeEmitter.requiresNativeIsCheck(element)) {
+ push(new js.Call(field, <js.Expression>[]));
+ if (negative) push(new js.Unary('!', pop()));
+ } else {
+ // We always negate at least once so that the result is boolified.
+ push(new js.Unary('!', field));
+ // If the result is not negated, put another '!' in front.
+ if (!negative) push(new js.Unary('!', pop()));
}
- use(input, JSPrecedence.MEMBER_PRECEDENCE);
- buffer.add('.');
- buffer.add(compiler.namer.operatorIs(element));
- if (requiresNativeIsCheck) buffer.add('()');
}
void handleStringSupertypeCheck(HInstruction input, Element element) {
@@ -2495,15 +2077,14 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
// would need to check for List too.
assert(element !== compiler.listClass
&& !Elements.isListSupertype(element, compiler));
- withPrecedence(JSPrecedence.LOGICAL_OR_PRECEDENCE, () {
- checkString(input, '===');
- buffer.add(' || ');
- withPrecedence(JSPrecedence.LOGICAL_AND_PRECEDENCE, () {
- checkObject(input, '===');
- buffer.add(' && ');
- checkType(input, element);
- });
- });
+ checkString(input, '===');
+ js.Expression stringTest = pop();
+ checkObject(input, '===');
+ js.Expression objectTest = pop();
+ checkType(input, element);
+ push(new js.Binary('||',
+ stringTest,
+ new js.Binary('&&', objectTest, pop())));
}
void handleListOrSupertypeCheck(HInstruction input, Element element) {
@@ -2511,16 +2092,14 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
// would need to check for String too.
assert(element !== compiler.stringClass
&& !Elements.isStringSupertype(element, compiler));
- beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
checkObject(input, '===');
- buffer.add(' && (');
- beginExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE);
+ js.Expression objectTest = pop();
checkArray(input, '===');
- buffer.add(' || ');
+ js.Expression arrayTest = pop();
checkType(input, element);
- buffer.add(')');
- endExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE);
- endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
+ push(new js.Binary('&&',
+ objectTest,
+ new js.Binary('||', arrayTest, pop())));
}
void visitIs(HIs node) {
@@ -2535,66 +2114,66 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
ClassElement objectClass = compiler.objectClass;
HInstruction input = node.expression;
- int oldPrecedence;
- if (node.nullOk) {
- oldPrecedence = expectedPrecedence;
- beginExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE);
- expectedPrecedence = JSPrecedence.LOGICAL_OR_PRECEDENCE;
- checkNull(input);
- buffer.add(' || ');
- }
if (element === objectClass || element === compiler.dynamicClass) {
// The constant folder also does this optimization, but we make
// it safe by assuming it may have not run.
- buffer.add('true');
+ push(new js.BoolLiteral(true), node);
} else if (element == compiler.stringClass) {
checkString(input, '===');
+ attachLocationToLast(node);
} else if (element == compiler.doubleClass) {
checkDouble(input, '===');
+ attachLocationToLast(node);
} else if (element == compiler.numClass) {
checkNum(input, '===');
+ attachLocationToLast(node);
} else if (element == compiler.boolClass) {
checkBool(input, '===');
+ attachLocationToLast(node);
} else if (element == compiler.functionClass) {
checkFunction(input, element);
+ attachLocationToLast(node);
} else if (element == compiler.intClass) {
- beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
checkNum(input, '===');
- buffer.add(' && ');
+ js.Expression numTest = pop();
checkInt(input, '===');
- endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
+ push(new js.Binary('&&', numTest, pop()), node);
} else if (Elements.isStringSupertype(element, compiler)) {
handleStringSupertypeCheck(input, element);
+ attachLocationToLast(node);
} else if (element === compiler.listClass
|| Elements.isListSupertype(element, compiler)) {
handleListOrSupertypeCheck(input, element);
+ attachLocationToLast(node);
} else if (input.propagatedType.canBePrimitive()
|| input.propagatedType.canBeNull()) {
- beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
checkObject(input, '===');
- buffer.add(' && ');
+ js.Expression objectTest = pop();
checkType(input, element);
- endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
+ push(new js.Binary('&&', objectTest, pop()), node);
} else {
checkType(input, element);
+ attachLocationToLast(node);
}
if (compiler.codegenWorld.rti.hasTypeArguments(type)) {
InterfaceType interfaceType = type;
ClassElement cls = type.element;
Link<Type> arguments = interfaceType.arguments;
- buffer.add(' && ');
+ js.Expression result = pop();
checkObject(node.typeInfoCall, '===');
+ result = new js.Binary('&&', result, pop());
cls.typeParameters.forEach((name, _) {
- buffer.add(' && ');
- beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
- use(node.typeInfoCall, JSPrecedence.EQUALITY_PRECEDENCE);
- buffer.add(".${name.slowToString()} === '${arguments.head}'");
- endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE);
+ use(node.typeInfoCall);
+ js.Access field = new js.Access.field(pop(), name.slowToString());
+ js.Expression genericName = new js.StringLiteral("'${arguments.head}'");
+ js.Binary eqTest = new js.Binary('===', field, genericName);
+ result = new js.Binary('&&', result, eqTest);
});
+ push(result, node);
}
if (node.nullOk) {
- expectedPrecedence = oldPrecedence;
- endExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE);
+ checkNull(input);
+ push(new js.Binary('||', pop(), pop()), node);
}
}
@@ -2635,18 +2214,22 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
String additionalArgument;
bool nativeCheck =
backend.emitter.nativeEmitter.requiresNativeIsCheck(element);
- beginExpression(JSPrecedence.CALL_PRECEDENCE);
if (node.isArgumentTypeCheck) {
- buffer.add('if (');
if (element == compiler.intClass) {
checkInt(node.checkedInput, '!==');
} else {
assert(element == compiler.numClass);
checkNum(node.checkedInput, '!==');
}
- buffer.add(') ');
+ js.Expression test = pop();
+ js.Block oldContainer = currentContainer;
+ js.Statement body = new js.Block.empty();
+ currentContainer = body;
generateThrowWithHelper('iae', node.checkedInput);
+ currentContainer = oldContainer;
+ body = unwrapStatement(body);
+ pushStatement(new js.If.then(test, body), node);
return;
}
@@ -2691,21 +2274,23 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
}
Element helperElement = compiler.findHelper(helper);
world.registerStaticUse(helperElement);
- buffer.add(compiler.namer.isolateAccess(helperElement));
- buffer.add('(');
- use(node.checkedInput, JSPrecedence.EXPRESSION_PRECEDENCE);
- if (additionalArgument !== null) buffer.add(", '$additionalArgument'");
- buffer.add(')');
- endExpression(JSPrecedence.CALL_PRECEDENCE);
+ List<js.Expression> arguments = <js.Expression>[];
+ use(node.checkedInput);
+ arguments.add(pop());
+ if (additionalArgument !== null) {
+ arguments.add(new js.StringLiteral("'$additionalArgument'"));
+ }
+ push(new js.Call(new js.Ref(compiler.namer.isolateAccess(helperElement)),
+ arguments));
} else {
- use(node.checkedInput, expectedPrecedence);
+ use(node.checkedInput);
}
}
}
class SsaOptimizedCodeGenerator extends SsaCodeGenerator {
SsaOptimizedCodeGenerator(backend, work, parameters, parameterNames)
- : super(backend, work, parameters, parameterNames) {
+ : super(backend, work, parameterNames) {
// Declare the parameter names only for the optimized version. The
// unoptimized version has different parameters.
parameterNames.forEach((Element element, String name) {
@@ -2718,7 +2303,7 @@ class SsaOptimizedCodeGenerator extends SsaCodeGenerator {
HBasicBlock beginGraph(HGraph graph) => graph.entry;
void endGraph(HGraph graph) {}
- void bailout(HTypeGuard guard, String reason) {
+ js.Statement bailout(HTypeGuard guard, String reason) {
if (maxBailoutParameters === null) {
maxBailoutParameters = 0;
work.guards.forEach((HTypeGuard workGuard) {
@@ -2733,114 +2318,119 @@ class SsaOptimizedCodeGenerator extends SsaCodeGenerator {
HBailoutTarget target = guard.bailoutTarget;
Namer namer = compiler.namer;
Element element = work.element;
- buffer.add('return ');
- if (element.isInstanceMember()) {
- // TODO(ngeoffray): This does not work in case we come from a
- // super call. We must make bailout names unique.
- buffer.add('this.${namer.getBailoutName(element)}');
- } else {
- buffer.add(namer.isolateBailoutAccess(element));
- }
- buffer.add('(${guard.state}');
+ List<js.Expression> arguments = <js.Expression>[];
+ arguments.add(new js.NumberLiteral("${guard.state}"));
// TODO(ngeoffray): try to put a variable at a deterministic
// location, so that multiple bailout calls put the variable at
// the same parameter index.
int i = 0;
for (; i < target.inputs.length; i++) {
assert(guard.inputs.indexOf(target.inputs[i]) >= 0);
- buffer.add(', ');
- use(target.inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE);
+ use(target.inputs[i]);
+ arguments.add(pop());
}
// Make sure we call the bailout method with the number of
// arguments it expects. This avoids having the underlying
// JS engine fill them in for us.
for (; i < maxBailoutParameters; i++) {
- buffer.add(', 0');
+ arguments.add(new js.NumberLiteral('0'));
+ }
+
+ js.Ref bailoutTarget;
Lasse Reichstein Nielsen 2012/08/08 11:16:38 Not a Ref. Perhaps just make a Label class.
floitsch 2012/08/09 16:24:07 It is not a label. It is true, that the isolateBai
+ if (element.isInstanceMember()) {
+ // TODO(ngeoffray): This does not work in case we come from a
+ // super call. We must make bailout names unique.
+ bailoutTarget = new js.Ref('this.${namer.getBailoutName(element)}');
+ } else {
+ bailoutTarget = new js.Ref(namer.isolateBailoutAccess(element));
}
- buffer.add(')');
+ js.Call call = new js.Call(bailoutTarget, arguments);
+ attachLocation(call, guard);
+ return new js.Return(call);
}
void visitTypeGuard(HTypeGuard node) {
- addIndentation();
HInstruction input = node.guarded;
Element indexingBehavior = compiler.jsIndexingBehaviorInterface;
if (node.isInteger()) {
// if (input is !int) bailout
- buffer.add('if (');
checkInt(input, '!==');
- buffer.add(') ');
- bailout(node, 'Not an integer');
+ pushStatement(new js.If.then(pop(), bailout(node, 'Not an integer')),
+ node);
} else if (node.isNumber()) {
// if (input is !num) bailout
- buffer.add('if (');
checkNum(input, '!==');
- buffer.add(') ');
- bailout(node, 'Not a number');
+ pushStatement(new js.If.then(pop(), bailout(node, 'Not a number')), node);
} else if (node.isBoolean()) {
// if (input is !bool) bailout
- buffer.add('if (');
checkBool(input, '!==');
- buffer.add(') ');
- bailout(node, 'Not a boolean');
+ pushStatement(new js.If.then(pop(), bailout(node, 'Not a boolean')),
+ node);
} else if (node.isString()) {
// if (input is !string) bailout
- buffer.add('if (');
checkString(input, '!==');
- buffer.add(') ');
- bailout(node, 'Not a string');
+ pushStatement(new js.If.then(pop(), bailout(node, 'Not a string')), node);
} else if (node.isExtendableArray()) {
// if (input is !Object || input is !Array || input.isFixed) bailout
- buffer.add('if (');
checkObject(input, '!==');
- buffer.add('||');
+ js.Expression objectTest = pop();
checkArray(input, '!==');
- buffer.add('||');
+ js.Expression arrayTest = pop();
checkFixedArray(input);
- buffer.add(') ');
- bailout(node, 'Not an extendable array');
+ js.Expression test = new js.Binary('||', objectTest, arrayTest);
+ test = new js.Binary('||', test, pop());
+ pushStatement(new js.If.then(test,
+ bailout(node, 'Not an extendable array')),
+ node);
} else if (node.isMutableArray()) {
// if (input is !Object
// || ((input is !Array || input.isImmutable)
// && input is !JsIndexingBehavior)) bailout
- buffer.add('if (');
checkObject(input, '!==');
- buffer.add(' || ((');
+ js.Expression objectTest = pop();
checkArray(input, '!==');
- buffer.add(' || ');
+ js.Expression arrayTest = pop();
checkImmutableArray(input);
- buffer.add(') && ');
+ js.Binary notArrayOrImmutable = new js.Binary('||', arrayTest, pop());
checkType(input, indexingBehavior, negative: true);
- buffer.add(')) ');
- bailout(node, 'Not a mutable array');
+ js.Binary notIndexing = new js.Binary('&&', notArrayOrImmutable, pop());
+ pushStatement(new js.If.then(new js.Binary('||', objectTest, notIndexing),
+ bailout(node, 'Not a mutable array')),
+ node);
} else if (node.isReadableArray()) {
// if (input is !Object
// || (input is !Array && input is !JsIndexingBehavior)) bailout
- buffer.add('if (');
checkObject(input, '!==');
- buffer.add(' || (');
+ js.Expression objectTest = pop();
checkArray(input, '!==');
- buffer.add(' && ');
+ js.Expression arrayTest = pop();
checkType(input, indexingBehavior, negative: true);
- buffer.add(')) ');
- bailout(node, 'Not an array');
+ js.Expression notIndexing = new js.Binary('&&', arrayTest, pop());
+ pushStatement(new js.If.then(new js.Binary('||', objectTest, notIndexing),
+ bailout(node, 'Not an array')),
+ node);
} else if (node.isIndexablePrimitive()) {
// if (input is !String
// && (input is !Object
// || (input is !Array && input is !JsIndexingBehavior))) bailout
- buffer.add('if (');
checkString(input, '!==');
- buffer.add(' && (');
+ js.Expression stringTest = pop();
checkObject(input, '!==');
- buffer.add(' || (');
+ js.Expression objectTest = pop();
checkArray(input, '!==');
- buffer.add(' && ');
+ js.Expression arrayTest = pop();
checkType(input, indexingBehavior, negative: true);
- buffer.add('))) ');
- bailout(node, 'Not a string or array');
+ js.Binary notIndexingTest = new js.Binary('&&', arrayTest, pop());
+ js.Binary notObjectOrIndexingTest =
+ new js.Binary('||', objectTest, notIndexingTest);
+ js.Binary condition =
+ new js.Binary('&&', stringTest, notObjectOrIndexingTest);
+ pushStatement(new js.If.then(condition,
+ bailout(node, 'Not a string or array')),
+ node);
} else {
compiler.internalError('Unexpected type guard', instruction: input);
}
- buffer.add(';\n');
}
void visitBailoutTarget(HBailoutTarget target) {
@@ -2848,25 +2438,24 @@ class SsaOptimizedCodeGenerator extends SsaCodeGenerator {
}
void beginLoop(HBasicBlock block) {
- addIndentation();
- HLoopInformation info = block.loopInformation;
- for (LabelElement label in info.labels) {
- writeLabel(label);
- buffer.add(":");
- }
- buffer.add('while (true) {\n');
- indent++;
+ oldContainerStack.add(currentContainer);
+ currentContainer = new js.Block.empty();
}
void endLoop(HBasicBlock block) {
- indent--;
- addIndented('}\n'); // Close 'while' loop.
+ js.Statement body = currentContainer;
+ currentContainer = oldContainerStack.removeLast();
+ body = unwrapStatement(body);
+ js.While loop = new js.While(new js.BoolLiteral(true), body);
+
+ HLoopInformation info = block.loopInformation;
+ attachLocationRange(loop, info.loopBlockInformation.sourcePosition);
+ pushStatement(wrapIntoLabels(loop, info.labels));
}
void handleLoopCondition(HLoopBranch node) {
- buffer.add('if (!');
- use(node.inputs[0], JSPrecedence.PREFIX_PRECEDENCE);
- buffer.add(') break;\n');
+ use(node.inputs[0]);
+ pushStatement(new js.If.then(pop(), new js.Break(null)), node);
}
@@ -2882,8 +2471,10 @@ class SsaOptimizedCodeGenerator extends SsaCodeGenerator {
class SsaUnoptimizedCodeGenerator extends SsaCodeGenerator {
- final CodeBuffer setup;
- final CodeBuffer newParameters;
+ js.Statement setup;
+ js.Switch currentBailoutSwitch;
+ final List<js.Switch> oldBailoutSwitches;
+ final List<js.Param> newParameters;
final List<String> labels;
int labelId = 0;
/**
@@ -2897,9 +2488,10 @@ class SsaUnoptimizedCodeGenerator extends SsaCodeGenerator {
HInstruction savedFirstInstruction;
SsaUnoptimizedCodeGenerator(backend, work, parameters, parameterNames)
- : super(backend, work, parameters, parameterNames),
- setup = new CodeBuffer(),
- newParameters = new CodeBuffer(),
+ : super(backend, work, parameterNames),
+ setup = new js.NOP(),
+ oldBailoutSwitches = <js.Switch>[],
+ newParameters = <js.Param>[],
labels = <String>[],
defaultClauseUsedInBailoutStack = <bool>[];
@@ -2922,7 +2514,7 @@ class SsaUnoptimizedCodeGenerator extends SsaCodeGenerator {
propagator.visitGraph(graph);
// TODO(ngeoffray): We could avoid generating the state at the
// call site for non-complex bailout methods.
- newParameters.add('state');
+ newParameters.add(new js.Param('state'));
if (propagator.hasComplexBailoutTargets) {
// Use generic parameters that will be assigned to
@@ -2930,7 +2522,7 @@ class SsaUnoptimizedCodeGenerator extends SsaCodeGenerator {
for (int i = 0; i < propagator.maxBailoutParameters; i++) {
String name = 'env$i';
declaredVariables.add(name);
- newParameters.add(', $name');
+ newParameters.add(new js.Param(name));
}
startBailoutSwitch();
@@ -2938,7 +2530,7 @@ class SsaUnoptimizedCodeGenerator extends SsaCodeGenerator {
// The setup phase of a bailout function sets up the environment for
// each bailout target. Each bailout target will populate this
// setup phase. It is put at the beginning of the function.
- setup.add(' switch (state) {\n');
+ setup = new js.Switch(new js.Ref('state'), <js.SwitchClause>[]);
return graph.entry;
} else {
// We have a simple bailout target, so we can reuse the names that
@@ -2947,7 +2539,7 @@ class SsaUnoptimizedCodeGenerator extends SsaCodeGenerator {
input = unwrap(input);
String name = variableNames.getName(input);
declaredVariables.add(name);
- newParameters.add(', $name');
+ newParameters.add(new js.Param(name));
}
// We change the first instruction of the first guard to be the
@@ -2973,10 +2565,7 @@ class SsaUnoptimizedCodeGenerator extends SsaCodeGenerator {
void endGraph(HGraph graph) {
if (propagator.hasComplexBailoutTargets) {
- indent--; // Close original case.
- indent--;
- addIndented('}\n'); // Close 'switch'.
- setup.add(' }\n');
+ endBailoutSwitch();
} else {
// Put back the original first instruction of the block.
propagator.firstBailoutTarget.block.first = savedFirstInstruction;
@@ -3007,110 +2596,121 @@ class SsaUnoptimizedCodeGenerator extends SsaCodeGenerator {
void visitBailoutTarget(HBailoutTarget node) {
if (!propagator.hasComplexBailoutTargets) return;
- indent--;
- addIndented('case ${node.state}:\n');
- indent++;
- addIndented('state = 0;\n');
-
- setup.add(' case ${node.state}:\n');
+ js.Block nextBlock = new js.Block.empty();
+ js.Case clause = new js.Case(new js.NumberLiteral('${node.state}'),
+ nextBlock);
+ currentBailoutSwitch.cases.add(clause);
+ currentContainer = nextBlock;
+ pushExpressionAsStatement(new js.Vassign(new js.Ref('state'),
+ new js.NumberLiteral('0')));
+ js.Block setupBlock = new js.Block.empty();
int i = 0;
for (HInstruction input in node.inputs) {
input = unwrap(input);
String name = variableNames.getName(input);
- setup.add(' ');
if (!isVariableDeclared(name)) {
declaredVariables.add(name);
- setup.add('var ');
+ js.Init init = new js.Init(new js.Decl(name), new js.Ref('env$i'));
+ js.Expression varList = new js.VariableDeclarationList(<js.Init>[init]);
+ setupBlock.elements.add(new js.ExpressionStatement(varList));
+ } else {
+ js.Expression assignment =
+ new js.Vassign(new js.Ref(name), new js.Ref('env$i'));
+ setupBlock.elements.add(new js.ExpressionStatement(assignment));
}
- setup.add('$name = env$i;\n');
i++;
}
- setup.add(' break;\n');
+ setupBlock.elements.add(new js.Break(null));
+ js.Case setupClause =
+ new js.Case(new js.NumberLiteral('${node.state}'), setupBlock);
+ (setup as js.Switch).cases.add(setupClause);
}
void startBailoutCase(List<HBailoutTarget> bailouts1,
- List<HBailoutTarget> bailouts2) {
- indent--;
+ [List<HBailoutTarget> bailouts2 = const []]) {
if (!defaultClauseUsedInBailoutStack.last() &&
bailouts1.length + bailouts2.length >= 2) {
- addIndented('default:\n');
+ currentContainer = new js.Block.empty();
+ currentBailoutSwitch.cases.add(new js.Default(currentContainer));
int len = defaultClauseUsedInBailoutStack.length;
defaultClauseUsedInBailoutStack[len - 1] = true;
} else {
- handleBailoutCase(bailouts1);
- handleBailoutCase(bailouts2);
+ _handleBailoutCase(bailouts1);
+ _handleBailoutCase(bailouts2);
+ currentContainer = currentBailoutSwitch.cases.last().body;
}
- indent++;
}
- void handleBailoutCase(List<HBailoutTarget> targets) {
- if (!defaultClauseUsedInBailoutStack.last() && targets.length >= 2) {
- addIndented('default:\n');
- int len = defaultClauseUsedInBailoutStack.length;
- defaultClauseUsedInBailoutStack[len - 1] = true;
- } else {
- for (int i = 0, len = targets.length; i < len; i++) {
- addIndented('case ${targets[i].state}:\n');
- }
+ void _handleBailoutCase(List<HBailoutTarget> targets) {
+ for (int i = 0, len = targets.length; i < len; i++) {
+ js.NumberLiteral expr = new js.NumberLiteral('${targets[i].state}');
+ currentBailoutSwitch.cases.add(new js.Case(expr, new js.Block.empty()));
}
}
void startBailoutSwitch() {
defaultClauseUsedInBailoutStack.add(false);
- addIndented('switch (state) {\n');
- indent++;
- addIndented('case 0:\n');
- indent++;
- }
-
- void endBailoutSwitch() {
- indent--; // Close 'case'.
- indent--;
- addIndented('}\n'); // Close 'switch'.
+ oldBailoutSwitches.add(currentBailoutSwitch);
+ List<js.SwitchClause> cases = <js.SwitchClause>[];
+ js.Block firstBlock = new js.Block.empty();
+ cases.add(new js.Case(new js.NumberLiteral("0"), firstBlock));
+ currentBailoutSwitch = new js.Switch(new js.Ref('state'), cases);
+ pushStatement(currentBailoutSwitch);
+ oldContainerStack.add(currentContainer);
+ currentContainer = firstBlock;
+ }
+
+ js.Switch endBailoutSwitch() {
+ js.Switch result = currentBailoutSwitch;
+ currentBailoutSwitch = oldBailoutSwitches.removeLast();
defaultClauseUsedInBailoutStack.removeLast();
+ currentContainer = oldContainerStack.removeLast();
+ return result;
}
void beginLoop(HBasicBlock block) {
- String newLabel = pushLabel();
+ String loopLabel = pushLabel();
if (block.hasBailoutTargets()) {
- startBailoutCase(block.bailoutTargets, const <HBailoutTarget>[]);
- }
-
- addIndentation();
- HLoopInformation loopInformation = block.loopInformation;
- for (LabelElement label in loopInformation.labels) {
- writeLabel(label);
- buffer.add(":");
+ startBailoutCase(block.bailoutTargets);
}
- buffer.add('$newLabel: while (true) {\n');
- indent++;
-
+ oldContainerStack.add(currentContainer);
+ currentContainer = new js.Block.empty();
if (block.hasBailoutTargets()) {
startBailoutSwitch();
+ HLoopInformation loopInformation = block.loopInformation;
if (loopInformation.target !== null) {
breakAction[loopInformation.target] = (TargetElement target) {
- addIndented("break $newLabel;\n");
+ pushStatement(new js.Break(loopLabel));
};
}
}
}
void endLoop(HBasicBlock block) {
- popLabel();
+ String loopLabel = popLabel();
+
HBasicBlock header = block.isLoopHeader() ? block : block.parentLoopHeader;
+ HLoopInformation info = header.loopInformation;
if (header.hasBailoutTargets()) {
endBailoutSwitch();
- HLoopInformation info = header.loopInformation;
if (info.target != null) breakAction.remove(info.target);
}
- indent--;
- addIndented('}\n'); // Close 'while'.
+
+ js.Statement body = unwrapStatement(currentContainer);
+ currentContainer = oldContainerStack.removeLast();
+
+ js.Statement result = new js.While(new js.BoolLiteral(true), body);
+ attachLocationRange(result, info.loopBlockInformation.sourcePosition);
+ result = new js.Labeled(loopLabel, result);
+ result = wrapIntoLabels(result, info.labels);
+ pushStatement(result);
}
void handleLoopCondition(HLoopBranch node) {
- buffer.add('if (!');
- use(node.inputs[0], JSPrecedence.PREFIX_PRECEDENCE);
- buffer.add(') break ${currentLabel()};\n');
+ use(node.inputs[0]);
+ pushStatement(new js.If.then(new js.Unary('!', pop()),
+ new js.Break(currentLabel())),
+ node);
}
void generateIf(HIf node, HIfBlockInformation info) {
@@ -3119,50 +2719,52 @@ class SsaUnoptimizedCodeGenerator extends SsaCodeGenerator {
bool thenHasGuards = thenGraph.start.hasBailoutTargets();
bool elseHasGuards = elseGraph.start.hasBailoutTargets();
bool hasGuards = thenHasGuards || elseHasGuards;
- if (!hasGuards) return super.generateIf(node, info);
-
- int elseKind = analyzeGraphForCodegen(elseGraph);
- bool emptyElse = elseKind == SsaCodeGenerator.EMPTY;
+ if (!hasGuards) {
+ super.generateIf(node, info);
+ return;
+ }
startBailoutCase(thenGraph.start.bailoutTargets,
- emptyElse ? const <HBailoutTarget>[] : elseGraph.start.bailoutTargets);
+ elseGraph.start.bailoutTargets);
- addIndented('if (');
- int precedence = JSPrecedence.EXPRESSION_PRECEDENCE;
+ use(node.inputs[0]);
+ js.Binary stateEquals0 =
+ new js.Binary('===', new js.Ref('state'), new js.NumberLiteral('0'));
+ js.Expression condition = new js.Binary('&&', stateEquals0, pop());
// TODO(ngeoffray): Put the condition initialization in the
// [setup] buffer.
List<HBailoutTarget> targets = node.thenBlock.bailoutTargets;
for (int i = 0, len = targets.length; i < len; i++) {
- buffer.add('state == ${targets[i].state} || ');
+ js.Ref stateRef = new js.Ref('state');
+ js.Expression targetState = new js.NumberLiteral('${targets[i].state}');
+ js.Binary stateTest = new js.Binary('===', stateRef, targetState);
+ condition = new js.Binary('||', stateTest, condition);
}
- buffer.add('(state == 0 && ');
- precedence = JSPrecedence.BITWISE_OR_PRECEDENCE;
- use(node.inputs[0], precedence);
- buffer.add(')) {\n');
-
- indent++;
+ js.Statement thenBody = new js.Block.empty();
+ js.Block oldContainer = currentContainer;
+ currentContainer = thenBody;
if (thenHasGuards) startBailoutSwitch();
generateStatements(thenGraph);
if (thenHasGuards) endBailoutSwitch();
- indent--;
+ thenBody = unwrapStatement(thenBody);
- if (!emptyElse) {
- addIndented('} else {\n');
- indent++;
- if (elseHasGuards) startBailoutSwitch();
- generateStatements(elseGraph);
- if (elseHasGuards) endBailoutSwitch();
- indent--;
- }
+ js.Statement elseBody = null;
+ elseBody = new js.Block.empty();
+ currentContainer = elseBody;
+ if (elseHasGuards) startBailoutSwitch();
+ generateStatements(elseGraph);
+ if (elseHasGuards) endBailoutSwitch();
+ elseBody = unwrapStatement(elseBody);
- addIndented('}\n');
+ currentContainer = oldContainer;
+ pushStatement(new js.If(condition, thenBody, elseBody), node);
}
void preLabeledBlock(HLabeledBlockInformation labeledBlockInfo) {
if (labeledBlockInfo.body.start.hasBailoutTargets()) {
indent--;
- handleBailoutCase(labeledBlockInfo.body.start.bailoutTargets);
+ startBailoutCase(labeledBlockInfo.body.start.bailoutTargets);
indent++;
}
}

Powered by Google App Engine
This is Rietveld 408576698