| 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..e166566a22a53b24078e8171f9842244cbd40d45 100644
|
| --- a/lib/compiler/implementation/ssa/codegen.dart
|
| +++ b/lib/compiler/implementation/ssa/codegen.dart
|
| @@ -273,15 +273,87 @@ 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()) {
|
| + 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.
|
| + * There are no validation on the operator text. If it is a "word"
|
| + * operator (e.g., "typeof" or "void") the caller should put in a
|
| + * trailing space if necessary to delimit it from the following
|
| + * expression.
|
| + */
|
| + 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 +642,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,11 +679,9 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
|
| 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);
|
| - });
|
| + binary("=",
|
| + () => declareInstruction(instruction),
|
| + () => visit(instruction, expectedPrecedence));
|
| }
|
| } else {
|
| visit(instruction, expectedPrecedence);
|
| @@ -1165,12 +1227,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),
|
| + () => use(node.right, expectedPrecedence));
|
| } else {
|
| visitInvokeStatic(node);
|
| }
|
| @@ -1180,13 +1239,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),
|
| + () => literal("0"));
|
| } else {
|
| visitInvokeBinary(node, op);
|
| }
|
| @@ -1194,10 +1249,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 +1258,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 +1273,32 @@ 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);
|
| - });
|
| - });
|
| + void condition() {
|
| + binary("==",
|
| + () => use(left, expectedPrecedence),
|
| + () => literal("null"));
|
| + }
|
| + void ifTrue() {
|
| + binary("==",
|
| + () => use(right, expectedPrecedence),
|
| + () => literal("null"));
|
| + }
|
| + void ifFalse() {
|
| + binary("===",
|
| + () => use(left, expectedPrecedence),
|
| + () => use(right, expectedPrecedence));
|
| + }
|
| + conditional(condition, ifTrue, ifFalse);
|
| }
|
| } 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 +1578,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 +1626,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], expectedPrecedence));
|
| } else {
|
| emitIf();
|
| visitWithoutIndent(thenGraph);
|
| @@ -1852,19 +1883,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 +1903,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 +2029,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 +2054,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 +2078,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 +2135,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 +2212,15 @@ 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 +2316,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 +2339,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 +2361,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 +2461,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 +2473,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 +2492,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 +2749,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 +2761,43 @@ 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 +3063,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();
|
|
|