Chromium Code Reviews| 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++; |
| } |
| } |