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 145761d9419a02556bf47bbec9a9c78c541fdad4..a2a8a23f75d353af9f38633673eddbe35912f1f9 100644 |
| --- a/lib/compiler/implementation/ssa/codegen.dart |
| +++ b/lib/compiler/implementation/ssa/codegen.dart |
| @@ -273,15 +273,83 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { |
| } |
| } |
| - void withPrecedence(int precedence, void action()) { |
| + /** |
| + * Adds parenthes around the code generated by [body] if necessary. |
| + * The precedence level of the body is assumed to be [precedence]. |
| + */ |
| + void parenthesize(int precedence, void body()) { |
| int oldPrecedence = expectedPrecedence; |
| beginExpression(precedence); |
| + // Raise the expected precedence-level to what the body expects. |
| expectedPrecedence = precedence; |
| - action(); |
| + body(); |
| expectedPrecedence = oldPrecedence; |
| endExpression(precedence); |
| } |
| + void withPrecedence(int precedence, void action()) { |
| + int oldPrecedence = expectedPrecedence; |
| + expectedPrecedence = precedence; |
| + action(); |
| + expectedPrecedence = oldPrecedence; |
| + } |
| + |
| + /** |
| + * Generates code for a JavaScript binary operator. |
| + * The code is parenthesized if necessary, and the expected |
| + * precedence level of the left- and right-hand sides is set, |
| + * based on the operator. |
| + */ |
| + void binary(String operator, void lhs(), void rhs()) { |
|
Lasse Reichstein Nielsen
2012/06/29 11:34:56
Could be called "infix". Could take operator as se
|
| + JSBinaryOperatorPrecedence op = JSPrecedence.binary[operator]; |
| + beginExpression(op.precedence); |
| + withPrecedence(op.left, lhs); |
| + buffer.add(' '); |
| + buffer.add(operator); |
| + buffer.add(' '); |
| + withPrecedence(op.right, rhs); |
| + endExpression(op.precedence); |
| + } |
| + |
| + /** |
| + * Generate code for a JavaScript postfix operator (only -- and ++ |
| + * exist). |
| + */ |
| + void PostfixExpression(String operator, void body()) { |
| + // Corresponds to a JavaScript production of the form (ES5 11.3): |
| + // PostfixExpression ::= LeftHandSideExpression <operator> |
| + beginExpression(JSPrecedence.POSTFIX_PRECEDENCE); |
| + withPrecedence(JSPrecedence.CALL_PRECEDENCE, body); |
| + buffer.add(operator); |
| + endExpression(JSPrecedence.PSOTFIX_PRECEDENCE); |
| + } |
| + |
| + /** |
| + * Generate code for a JavaScript prefix operator. |
|
Lasse Reichstein Nielsen
2012/06/29 11:34:56
Should add comment that the operator isn't checked
|
| + */ |
| + void prefix(String operator, void body()) { |
| + // Corresponds to a JavaScript production of the form (ES5 11.4): |
| + // UnaryExpression ::= <operator> UnaryExpression |
| + beginExpression(JSPrecedence.PREFIX_PRECEDENCE); |
| + buffer.add(operator); |
| + withPrecedence(JSPrecedence.PREFIX_PRECEDENCE, body); |
| + endExpression(JSPrecedence.PREFIX_PRECEDENCE); |
| + } |
| + |
| + void conditional(void condition(), [void ifTrue(), void ifFalse()]) { |
| + beginExpression(JSPrecedence.CONDITIONAL_PRECEDENCE); |
| + withPrecedence(JSPrecedence.LOGICAL_OR_PRECEDENCE, condition); |
| + buffer.add(" ? "); |
| + withPrecedence(JSPrecedence.ASSIGNMENT_PRECEDENCE, ifTrue); |
| + buffer.add(" : "); |
| + withPrecedence(JSPrecedence.ASSIGNMENT_PRECEDENCE, ifFalse); |
| + endExpression(JSPrecedence.CONDITIONAL_PRECEDENCE); |
| + } |
| + |
| + void literal(String text) { |
| + buffer.add(text); |
| + } |
| + |
| void preGenerateMethod(HGraph graph) { |
| new SsaInstructionMerger(generateAtUseSite).visitGraph(graph); |
| new SsaConditionMerger(generateAtUseSite, |
| @@ -570,22 +638,14 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { |
| rightIsOne = (numConstant.value == 1); |
| } |
| if (binaryInstruction is HAdd && rightIsOne) { |
| - beginExpression(JSPrecedence.PREFIX_PRECEDENCE); |
| - buffer.add('++'); |
| - declareVariable(name); |
| - endExpression(JSPrecedence.PREFIX_PRECEDENCE); |
| + prefix('++', () => declareVariable(name)); |
| } else if (binaryInstruction is HSubtract && rightIsOne) { |
| - beginExpression(JSPrecedence.PREFIX_PRECEDENCE); |
| - buffer.add('--'); |
| - declareVariable(name); |
| - endExpression(JSPrecedence.PREFIX_PRECEDENCE); |
| + prefix("--", () => declareVariable(name)); |
| } else { |
| var operation = binaryInstruction.operation.name; |
| - beginExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE); |
| - declareVariable(name); |
| - buffer.add(' ${operation}= '); |
| - use(right, JSPrecedence.ASSIGNMENT_PRECEDENCE); |
| - endExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE); |
| + binary('$operation=', |
| + () => declareVariable(name), |
| + () => use(right, expectedPrecedence)); |
| } |
| return true; |
| } |
| @@ -615,10 +675,10 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { |
| var name = variableNames.getName(instruction); |
| if (!handleSimpleUpdateDefinition(instruction, name) |
| && !handleTypeConversion(instruction, name)) { |
| - withPrecedence(JSPrecedence.ASSIGNMENT_PRECEDENCE, () { |
| + binary("=", (){ |
|
floitsch
2012/06/29 11:55:32
But each closure on one line.
Lasse Reichstein Nielsen
2012/06/29 14:20:13
Done.
|
| declareInstruction(instruction); |
| - buffer.add(" = "); |
| - visit(instruction, JSPrecedence.ASSIGNMENT_PRECEDENCE); |
| + }, (){ |
| + visit(instruction, expectedPrecedence); |
| }); |
| } |
| } else { |
| @@ -1165,12 +1225,9 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { |
| 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); |
| + binary(op, |
| + () => use(node.left, expectedPrecedence), |
|
floitsch
2012/06/29 11:55:32
indentation.
Lasse Reichstein Nielsen
2012/06/29 14:20:13
Done.
|
| + () => use(node.right, expectedPrecedence)); |
| } else { |
| visitInvokeStatic(node); |
| } |
| @@ -1180,13 +1237,9 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { |
| // shift operator to achieve this. |
| visitBitInvokeBinary(HBinaryBitOp node, String 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); |
| + binary(">>>", |
| + () => visitInvokeBinary(node, op), |
|
floitsch
2012/06/29 11:55:32
indendantion.
Lasse Reichstein Nielsen
2012/06/29 14:20:13
Done everywhere *cross fingers*.
|
| + () => literal("0")); |
| } else { |
| visitInvokeBinary(node, op); |
| } |
| @@ -1194,10 +1247,7 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { |
| 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); |
| + prefix(op, () => use(node.operand, expectedPrecedence)); |
| } else { |
| visitInvokeStatic(node); |
| } |
| @@ -1206,14 +1256,10 @@ 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) { |
| - 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); |
| + if (node.builtin && requiresUintConversion(node)){ |
| + binary(">>>", |
| + () => visitInvokeUnary(node, op), |
| + () => literal("0")); |
| } else { |
| visitInvokeUnary(node, op); |
| } |
| @@ -1225,40 +1271,29 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { |
| if (leftType.canBeNull() && rightType.canBeNull()) { |
| if (left.isConstantNull() || right.isConstantNull() || |
| (leftType.isPrimitive() && leftType == rightType)) { |
| - beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); |
| - use(left, JSPrecedence.EQUALITY_PRECEDENCE); |
| - buffer.add(' == '); |
| - use(right, JSPrecedence.RELATIONAL_PRECEDENCE); |
| - endExpression(JSPrecedence.EQUALITY_PRECEDENCE); |
| + binary("==", |
| + () => use(left, expectedPrecedence), |
| + () => use(right, expectedPrecedence)); |
| } 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); |
| - }); |
| + conditional(() { |
| + binary("==", |
| + () => use(left, expectedPrecedence), |
|
Lasse Reichstein Nielsen
2012/06/29 11:34:56
Maybe I should add a "delayedUse(x) => () => use(x
floitsch
2012/06/29 11:55:32
I don't think that's necessary.
floitsch
2012/06/29 11:55:32
I would define the closures first:
Function condit
Lasse Reichstein Nielsen
2012/06/29 14:20:13
Done.
|
| + () => buffer.add("null")); |
|
Lasse Reichstein Nielsen
2012/06/29 11:34:56
should be "literal".
|
| + }, ifTrue: () { |
| + binary("==", |
| + () => use(right, expectedPrecedence), |
| + () => literal("null")); |
| + }, ifFalse: () { |
| + binary("===", |
| + () => use(left, expectedPrecedence), |
| + () => use(right, expectedPrecedence)); |
| }); |
| } |
| } else { |
| - beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); |
| - use(left, JSPrecedence.EQUALITY_PRECEDENCE); |
| - buffer.add(' === '); |
| - use(right, JSPrecedence.RELATIONAL_PRECEDENCE); |
| - endExpression(JSPrecedence.EQUALITY_PRECEDENCE); |
| + binary("===", |
| + () => use(left, expectedPrecedence), |
| + () => use(right, expectedPrecedence)); |
| } |
| } |
| @@ -1538,18 +1573,9 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { |
| 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); |
| + binary("&&", condition, () => visitExpression(toVisit)); |
| buffer.add(";\n"); |
| } |
| @@ -1595,8 +1621,8 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { |
| switch (elseKind) { |
| case EMPTY: |
| if (thenKind == ONE_EXPRESSION) { |
| - int precedence = operatorPrecedence.left; |
| - generateAnd(thenGraph, () { use(node.inputs[0], precedence); }); |
| + generateAnd(thenGraph, () { use(node.inputs[0], |
|
floitsch
2012/06/29 11:55:32
move the closure in one line.
Lasse Reichstein Nielsen
2012/06/29 14:20:13
Done.
|
| + expectedPrecedence); }); |
| } else { |
| emitIf(); |
| visitWithoutIndent(thenGraph); |
| @@ -1852,19 +1878,19 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { |
| node.value.guaranteedType); |
| } |
| String name = compiler.namer.getName(node.element); |
| - 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) { |
| - world.registerFieldSetter(node.element.name, type); |
| - backend.updateFieldIntegerSetters(node.element, |
| - node.value.isInteger()); |
| - } |
| - buffer.add(' = '); |
| - use(node.value, JSPrecedence.ASSIGNMENT_PRECEDENCE); |
| - endExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE); |
| + binary("=", () { |
| + use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE); |
| + buffer.add('.'); |
| + buffer.add(name); |
| + Type type = node.receiver.propagatedType.computeType(compiler); |
| + if (type != null) { |
| + world.registerFieldSetter(node.element.name, type); |
| + backend.updateFieldIntegerSetters(node.element, |
| + node.value.isInteger()); |
| + } |
| + }, () { |
| + use(node.value, expectedPrecedence); |
| + }); |
| } |
| visitLocalGet(HLocalGet node) { |
| @@ -1872,9 +1898,9 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { |
| } |
| visitLocalSet(HLocalSet node) { |
| - declareInstruction(node.receiver); |
| - buffer.add(' = '); |
| - use(node.value, JSPrecedence.ASSIGNMENT_PRECEDENCE); |
| + binary("=", |
| + () => declareInstruction(node.receiver), |
| + () => use(node.value, expectedPrecedence)); |
| } |
| visitForeign(HForeign node) { |
| @@ -1998,10 +2024,9 @@ 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); |
| + binary("!==", |
| + () => use(input.inputs[0], JSPrecedence.EQUALITY_PRECEDENCE), |
| + () => literal("true")); |
| } else if (isBuiltinRelational(input) && |
| isGenerateAtUseSite(input) && |
| input.inputs[0].propagatedType.isUseful() && |
| @@ -2024,10 +2049,7 @@ 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); |
| + prefix("!", () => use(input, JSPrecedence.PREFIX_PRECEDENCE)); |
| } |
| } |
| @@ -2051,29 +2073,24 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { |
| use(node.inputs[0], expectedPrecedence); |
| } 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); |
| + binary(operation, () { |
| + if (operation == '||') { |
| + if (input is HNot) { |
| + use(input.inputs[0], expectedPrecedence); |
| + } else { |
| + generateNot(input); |
| + } |
| } else { |
| - generateNot(input); |
| + use(input, expectedPrecedence); |
| } |
| - } else { |
| - use(input, operatorPrecedence.left); |
| - } |
| - buffer.add(" $operation "); |
| - use(node.inputs[0], operatorPrecedence.right); |
| - endExpression(operatorPrecedence.precedence); |
| + }, () { |
| + use(node.inputs[0], expectedPrecedence); |
| + }); |
| } 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); |
| + conditional( |
| + () => use(input, expectedPrecedence), |
| + ifTrue: () => use(node.inputs[0], expectedPrecedence), |
| + ifFalse: () => use(node.inputs[1], expectedPrecedence)); |
| } |
| } |
| @@ -2113,15 +2130,22 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { |
| // completely. |
| assert(node.staticChecks != HBoundsCheck.ALWAYS_TRUE); |
| if (node.staticChecks != HBoundsCheck.ALWAYS_FALSE) { |
| + void checkUpperBound() { |
| + binary(">=", |
| + () => use(node.index, expectedPrecedence), |
| + () => use(node.length, expectedPrecedence)); |
| + } |
| buffer.add('if ('); |
| if (node.staticChecks != HBoundsCheck.ALWAYS_ABOVE_ZERO) { |
| assert(node.staticChecks == HBoundsCheck.FULL_CHECK); |
| - use(node.index, JSPrecedence.RELATIONAL_PRECEDENCE); |
| - buffer.add(' < 0 || '); |
| + binary("||", |
| + () => binary("<", |
| + () => use(node.index, expectedPrecedence), |
| + () => literal("0")), |
| + checkUpperBound); |
| + } else { |
| + checkUpperBound(); |
| } |
| - use(node.index, JSPrecedence.RELATIONAL_PRECEDENCE); |
| - buffer.add(' >= '); |
| - use(node.length, JSPrecedence.SHIFT_PRECEDENCE); |
| buffer.add(") "); |
| } |
| generateThrowWithHelper('ioore', node.index); |
| @@ -2183,17 +2207,14 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { |
| } else if (isEmptyString(node.right)) { |
| useStringified(node.left, expectedPrecedence); |
| } 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); |
| + // safe to make it left associative by omitting parentheses. |
| + bool useAdditivePrecedence = node.right is HStringConcat; |
| + binary("+", |
| + () => useStringified(node.left, expectedPrecedence), |
| + () => useStringified(node.right, |
| + useAdditivePrecedence ? JSPrecedence.ADDITIVE_PRECEDENCE |
| + : expectedPrecedence)); |
| } |
| } |
| @@ -2289,11 +2310,9 @@ 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); |
| + binary('+', |
| + () => use(node.inputs[1], expectedPrecedence), |
| + () => use(node.inputs[2], expectedPrecedence)); |
| } else { |
| beginExpression(JSPrecedence.CALL_PRECEDENCE); |
| use(node.inputs[1], JSPrecedence.MEMBER_PRECEDENCE); |
| @@ -2314,20 +2333,21 @@ 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); |
| + binary(cmp, |
| + () => use(input, expectedPrecedence), |
| + () => binary("|", |
| + () => use(input, expectedPrecedence), |
| + () => literal("0"))); |
| + } |
| + |
| + void checkJSType(HInstruction input, String cmp, String type) { |
| + binary(cmp, |
| + () => prefix("typeof ", () => use(input, expectedPrecedence)), |
| + () => literal("'$type'")); |
| } |
| 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); |
| + checkJSType(input, cmp, "number"); |
| } |
| void checkDouble(HInstruction input, String cmp) { |
| @@ -2335,123 +2355,99 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { |
| } |
| 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); |
| + checkJSType(input, cmp, "string"); |
| } |
| 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); |
| + checkJSType(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); |
| - }); |
| + binary("&&", |
| + () => checkJSType(input, "===", "object"), |
| + () => binary("!==", |
| + () => use(input, expectedPrecedence), |
| + () => literal("null"))); |
| } 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); |
| - }); |
| + binary("||", |
| + () => checkJSType(input, "!==", "object"), |
| + () => binary("===", |
| + () => use(input, expectedPrecedence), |
| + () => literal("null"))); |
| } |
| } |
| void checkArray(HInstruction input, String cmp) { |
| - beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); |
| - use(input, JSPrecedence.MEMBER_PRECEDENCE); |
| - buffer.add('.constructor $cmp Array'); |
| - endExpression(JSPrecedence.EQUALITY_PRECEDENCE); |
| + binary(cmp, |
| + () { |
| + use(input, JSPrecedence.MEMBER_PRECEDENCE); |
| + buffer.add('.constructor'); |
| + }, |
| + () => literal("Array")); |
| } |
| void checkImmutableArray(HInstruction input) { |
| - beginExpression(JSPrecedence.PREFIX_PRECEDENCE); |
| - buffer.add('!!'); |
| - use(input, JSPrecedence.MEMBER_PRECEDENCE); |
| - buffer.add('.immutable\$list'); |
| - endExpression(JSPrecedence.PREFIX_PRECEDENCE); |
| + prefix("!!", () { |
| + use(input, JSPrecedence.MEMBER_PRECEDENCE); |
| + buffer.add('.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); |
| + prefix("!!" , () { |
| + use(input, JSPrecedence.MEMBER_PRECEDENCE); |
| + buffer.add('.fixed\$length'); |
| + }); |
| } |
| void checkFixedArray(HInstruction input) { |
| - beginExpression(JSPrecedence.PREFIX_PRECEDENCE); |
| - use(input, JSPrecedence.MEMBER_PRECEDENCE); |
| - buffer.add('.fixed\$length'); |
| - endExpression(JSPrecedence.PREFIX_PRECEDENCE); |
| + parenthesize(JSPrecedence.MEMBER_PRECEDENCE, () { |
| + use(input, JSPrecedence.MEMBER_PRECEDENCE); |
| + buffer.add('.fixed\$length'); |
| + }); |
| } |
| void checkNull(HInstruction input) { |
| - beginExpression(JSPrecedence.EQUALITY_PRECEDENCE); |
| - use(input, JSPrecedence.EQUALITY_PRECEDENCE); |
| - buffer.add(" == null"); |
| - endExpression(JSPrecedence.EQUALITY_PRECEDENCE); |
| + binary("==", |
| + () => use(input, expectedPrecedence), |
| + () => literal("null")); |
| } |
| 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); |
| - }); |
| + binary("||", |
| + () => checkJSType(input, "===", "function"), |
| + () => binary('&&', |
| + () => checkObject(input, '==='), |
| + () => checkType(input, element))); |
| } |
| void checkType(HInstruction input, Element element, [bool negative = false]) { |
| world.registerIsCheck(element); |
| bool requiresNativeIsCheck = |
| backend.emitter.nativeEmitter.requiresNativeIsCheck(element); |
| + void body() { |
| + assert(JSPrecedence.CALL_PRECEDENCE == JSPrecedence.MEMBER_PRECEDENCE); |
| + parenthesize(JSPrecedence.CALL_PRECEDENCE, () { |
| + use(input, JSPrecedence.MEMBER_PRECEDENCE); |
| + buffer.add('.'); |
| + buffer.add(compiler.namer.operatorIs(element)); |
| + if (requiresNativeIsCheck) buffer.add('()'); |
| + }); |
| + } |
| if (!requiresNativeIsCheck) { |
| if (negative) { |
| - buffer.add('!'); |
| + prefix("!", body); |
| } else { |
| - buffer.add('!!'); |
| + prefix("!!", body); |
| } |
| } else if (negative) { |
| - buffer.add('!'); |
| + prefix("!", body); |
| + } else { |
| + body(); |
| } |
| - use(input, JSPrecedence.MEMBER_PRECEDENCE); |
| - buffer.add('.'); |
| - buffer.add(compiler.namer.operatorIs(element)); |
| - if (requiresNativeIsCheck) buffer.add('()'); |
| } |
| void handleStringSupertypeCheck(HInstruction input, Element element) { |
| @@ -2459,15 +2455,11 @@ 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); |
| - }); |
| - }); |
| + binary("||", |
| + () => checkString(input, '==='), |
| + () => binary("&&", |
| + () => checkObject(input, '==='), |
| + () => checkType(input, element))); |
| } |
| void handleListOrSupertypeCheck(HInstruction input, Element element) { |
| @@ -2475,16 +2467,11 @@ 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); |
| - checkArray(input, '==='); |
| - buffer.add(' || '); |
| - checkType(input, element); |
| - buffer.add(')'); |
| - endExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE); |
| - endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE); |
| + binary("&&", |
| + () => checkObject(input, '==='), |
| + () => binary("||", |
| + () => checkArray(input, '==='), |
| + () => checkType(input, element))); |
| } |
| void visitIs(HIs node) { |
| @@ -2499,66 +2486,70 @@ 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'); |
| - } else if (element == compiler.stringClass) { |
| - checkString(input, '==='); |
| - } else if (element == compiler.doubleClass) { |
| - checkDouble(input, '==='); |
| - } else if (element == compiler.numClass) { |
| - checkNum(input, '==='); |
| - } else if (element == compiler.boolClass) { |
| - checkBool(input, '==='); |
| - } else if (element == compiler.functionClass) { |
| - checkFunction(input, element); |
| - } else if (element == compiler.intClass) { |
| - beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE); |
| - checkNum(input, '==='); |
| - buffer.add(' && '); |
| - checkInt(input, '==='); |
| - endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE); |
| - } else if (Elements.isStringSupertype(element, compiler)) { |
| - handleStringSupertypeCheck(input, element); |
| - } else if (element === compiler.listClass |
| - || Elements.isListSupertype(element, compiler)) { |
| - handleListOrSupertypeCheck(input, element); |
| - } else if (input.propagatedType.canBePrimitive() |
| - || input.propagatedType.canBeNull()) { |
| - beginExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE); |
| - checkObject(input, '==='); |
| - buffer.add(' && '); |
| - checkType(input, element); |
| - endExpression(JSPrecedence.LOGICAL_AND_PRECEDENCE); |
| - } else { |
| - checkType(input, element); |
| - } |
| - if (compiler.codegenWorld.rti.hasTypeArguments(type)) { |
| - InterfaceType interfaceType = type; |
| - ClassElement cls = type.element; |
| - Link<Type> arguments = interfaceType.arguments; |
| - buffer.add(' && '); |
| - checkObject(node.typeInfoCall, '==='); |
| - 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); |
| - }); |
| + void plainTypeCheck() { |
| + 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. |
| + literal('true'); |
| + } else if (element == compiler.stringClass) { |
| + checkString(input, '==='); |
| + } else if (element == compiler.doubleClass) { |
| + checkDouble(input, '==='); |
| + } else if (element == compiler.numClass) { |
| + checkNum(input, '==='); |
| + } else if (element == compiler.boolClass) { |
| + checkBool(input, '==='); |
| + } else if (element == compiler.functionClass) { |
| + checkFunction(input, element); |
| + } else if (element == compiler.intClass) { |
| + binary("&&", |
| + () => checkNum(input, '==='), |
| + () => checkInt(input, '===')); |
| + } else if (Elements.isStringSupertype(element, compiler)) { |
| + handleStringSupertypeCheck(input, element); |
| + } else if (element === compiler.listClass |
| + || Elements.isListSupertype(element, compiler)) { |
| + handleListOrSupertypeCheck(input, element); |
| + } else if (input.propagatedType.canBePrimitive() |
| + || input.propagatedType.canBeNull()) { |
| + binary("&&", |
| + () => checkObject(input, '==='), |
| + () => checkType(input, element)); |
| + } else { |
| + checkType(input, element); |
| + } |
| + } |
| + |
| + void typeArgumentCheck() { |
| + if (compiler.codegenWorld.rti.hasTypeArguments(type)) { |
| + InterfaceType interfaceType = type; |
| + ClassElement cls = type.element; |
| + Link<Type> arguments = interfaceType.arguments; |
| + binary("&&", plainTypeCheck, () { |
| + var base = () => checkObject(node.typeInfoCall, '==='); |
| + // Do left-fold on elements with [base] as initial value. |
| + cls.typeParameters.forEach((name, _) { |
| + Type argument = arguments.head; |
| + // TODO(lrn): Should we advance arguments here? |
| + // What if there aren't any? |
| + var oldBase = base; |
| + base = () => binary("&&", oldBase, () { |
| + parenthesize(JSPrecedence.ASSIGNMENT_PRECEDENCE, () { |
| + use(node.typeInfoCall, JSPrecedence.MEMBER_PRECEDENCE); |
| + buffer.add(".${name.slowToString()} === '${argument}'"); |
| + }); |
| + }); |
| + }); |
| + base(); |
| + }); |
| + } else { |
| + plainTypeCheck(); |
| + } |
| } |
| if (node.nullOk) { |
| - expectedPrecedence = oldPrecedence; |
| - endExpression(JSPrecedence.LOGICAL_OR_PRECEDENCE); |
| + binary("||", () => checkNull(input), typeArgumentCheck); |
| + } else { |
| + typeArgumentCheck(); |
| } |
| } |
| @@ -2752,11 +2743,11 @@ class SsaOptimizedCodeGenerator extends SsaCodeGenerator { |
| } else if (node.isExtendableArray()) { |
| // if (input is !Object || input is !Array || input.isFixed) bailout |
| buffer.add('if ('); |
| - checkObject(input, '!=='); |
| - buffer.add('||'); |
| - checkArray(input, '!=='); |
| - buffer.add('||'); |
| - checkFixedArray(input); |
| + binary("||", |
| + binary("||", |
| + () => checkObject(input, '!=='), |
| + () => checkArray(input, '!==')), |
| + () => checkFixedArray(input)); |
| buffer.add(') '); |
| bailout(node, 'Not an extendable array'); |
| } else if (node.isMutableArray()) { |
| @@ -2764,39 +2755,39 @@ class SsaOptimizedCodeGenerator extends SsaCodeGenerator { |
| // || ((input is !Array || input.isImmutable) |
| // && input is !JsIndexingBehavior)) bailout |
| buffer.add('if ('); |
| - checkObject(input, '!=='); |
| - buffer.add(' || (('); |
| - checkArray(input, '!=='); |
| - buffer.add(' || '); |
| - checkImmutableArray(input); |
| - buffer.add(') && '); |
| - checkType(input, indexingBehavior, negative: true); |
| - buffer.add(')) '); |
| + binary("||", |
| + () => checkObject(input, '!=='), |
| + () => binary("&&", |
| + () => binary("||", |
| + () => checkArray(input, '!=='), |
| + () => checkImmutableArray(input)), |
| + () => checkType(input, indexingBehavior, negative: true))); |
| + buffer.add(") "); |
| bailout(node, 'Not a mutable array'); |
| } else if (node.isReadableArray()) { |
| // if (input is !Object |
| // || (input is !Array && input is !JsIndexingBehavior)) bailout |
| buffer.add('if ('); |
| - checkObject(input, '!=='); |
| - buffer.add(' || ('); |
| - checkArray(input, '!=='); |
| - buffer.add(' && '); |
| - checkType(input, indexingBehavior, negative: true); |
| - buffer.add(')) '); |
| + binary("||", |
| + () => checkObject(input, '!=='), |
| + () => binary("&&", |
| + () => checkArray(input, '!=='), |
| + () => checkType(input, indexingBehavior, negative: true))); |
| + buffer.add(') '); |
| bailout(node, 'Not an array'); |
| } 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(' && ('); |
| - checkObject(input, '!=='); |
| - buffer.add(' || ('); |
| - checkArray(input, '!=='); |
| - buffer.add(' && '); |
| - checkType(input, indexingBehavior, negative: true); |
| - buffer.add('))) '); |
| + binary("&&", |
| + () => checkString(input, '!=='), |
| + () => binary("||", |
| + () => checkObject(input, '!=='), |
| + () => binary("&&", |
| + () => checkArray(input, '!=='), |
| + () => checkType(input, indexingBehavior, negative: true)))); |
| + buffer.add(') '); |
| bailout(node, 'Not a string or array'); |
| } else { |
| compiler.internalError('Unexpected type guard', instruction: input); |
| @@ -3062,15 +3053,33 @@ class SsaUnoptimizedCodeGenerator extends SsaCodeGenerator { |
| int precedence = JSPrecedence.EXPRESSION_PRECEDENCE; |
| // TODO(ngeoffray): Put the condition initialization in the |
| // [setup] buffer. |
| + |
| + void stateTest() { |
| + binary("&&", |
| + () => binary("==", () => literal("state"), () => literal("0")), |
| + () => use(node.inputs[0], expectedPrecedence)); |
| + } |
| + |
| List<HTypeGuard> guards = node.thenBlock.guards; |
| - for (int i = 0, len = guards.length; i < len; i++) { |
| - buffer.add('state == ${guards[i].state} || '); |
| + if (guards.length > 0) { |
| + // Fold guards from the left using '||'. |
| + Function buildGuard(int i) => () { |
| + binary("==", |
| + () => literal("state"), |
| + () => literal("${guards[i].state}")); |
| + } |
| + Function guard = buildGuard(0); |
| + for (int i = 1, len = guards.length; i < len; i++) { |
| + int index = i; |
| + Function oldGuard = guard; |
| + guard = () => binary("||", oldGuard, buildGuard(index)); |
| + } |
| + binary("||", guard, stateTest); |
| + } else { |
| + stateTest(); |
| } |
| - buffer.add('(state == 0 && '); |
| - precedence = JSPrecedence.BITWISE_OR_PRECEDENCE; |
| - use(node.inputs[0], precedence); |
| - buffer.add(')) {\n'); |
| + buffer.add(') {\n'); |
| indent++; |
| if (thenHasGuards) startBailoutSwitch(); |