All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.dev.jjs.impl.EqualityNormalizer Maven / Gradle / Ivy

/*
 * Copyright 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.gwt.dev.jjs.impl;

import com.google.gwt.dev.jjs.ast.Context;
import com.google.gwt.dev.jjs.ast.JBinaryOperation;
import com.google.gwt.dev.jjs.ast.JBinaryOperator;
import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JExpression;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JMethodCall;
import com.google.gwt.dev.jjs.ast.JModVisitor;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JReferenceType;
import com.google.gwt.dev.jjs.ast.JType;

/**
 * 

* Rewrite Java == so that it will execute correctly in JavaScript. * After this pass, Java's == is considered equivalent to * JavaScript's ===. *

*

* Whenever possible, a Java == is replaced by a JavaScript * ==. This is shorter than ===, and it avoids any * complication due to GWT treating both null and * undefined as a valid translation of a Java null. *

*

* However, whenever something that may be a String is compared to something * that may not be a String, use ===. A Java object * compared to a string should always yield false, but that's not true when * comparing in JavaScript using ==. The cases where * === must be used are: *

*
    *
  • One or both sides have unknown String status.
  • *
  • One side is definitely String and one side is definitely ! * String.
    * TODO: This case could be optimized as * (a == null) & (b == null).
  • *
*

* Since null !== undefined, it is also necessary to normalize * null vs. undefined if it's possible for one side to * be null and the other to be undefined. *

*/ public class EqualityNormalizer { /** * Breaks apart certain complex assignments. */ private class BreakupAssignOpsVisitor extends JModVisitor { @Override public void endVisit(JBinaryOperation x, Context ctx) { JBinaryOperator op = x.getOp(); if (op != JBinaryOperator.EQ && op != JBinaryOperator.NEQ) { return; } JExpression lhs = x.getLhs(); JExpression rhs = x.getRhs(); JType lhsType = lhs.getType(); JType rhsType = rhs.getType(); if (!(lhsType instanceof JReferenceType)) { assert !(rhsType instanceof JReferenceType); return; } StringStatus lhsStatus = getStringStatus((JReferenceType) lhsType); StringStatus rhsStatus = getStringStatus((JReferenceType) rhsType); int strat = COMPARISON_STRAT[lhsStatus.getIndex()][rhsStatus.getIndex()]; switch (strat) { case STRAT_TRIPLE: { if (canBeNull(lhs) && canBeNull(rhs)) { /* * If it's possible for one side to be null and the other side * undefined, then mask both sides. */ lhs = maskUndefined(lhs); rhs = maskUndefined(rhs); } JBinaryOperation binOp = new JBinaryOperation(x.getSourceInfo(), x.getType(), x.getOp(), lhs, rhs); ctx.replaceMe(binOp); break; } case STRAT_DOUBLE: { boolean lhsNullLit = lhs == program.getLiteralNull(); boolean rhsNullLit = rhs == program.getLiteralNull(); if ((lhsNullLit && rhsStatus == StringStatus.NOTSTRING) || (rhsNullLit && lhsStatus == StringStatus.NOTSTRING)) { /* * If either side is a null literal and the other is non-String, * replace with a null-check. */ String methodName; if (op == JBinaryOperator.EQ) { methodName = "Cast.isNull"; } else { methodName = "Cast.isNotNull"; } JMethod isNullMethod = program.getIndexedMethod(methodName); JMethodCall call = new JMethodCall(x.getSourceInfo(), null, isNullMethod); call.addArg(lhsNullLit ? rhs : lhs); ctx.replaceMe(call); } else { // Replace with a call to Cast.jsEquals, which does a == internally. String methodName; if (op == JBinaryOperator.EQ) { methodName = "Cast.jsEquals"; } else { methodName = "Cast.jsNotEquals"; } JMethod eqMethod = program.getIndexedMethod(methodName); JMethodCall call = new JMethodCall(x.getSourceInfo(), null, eqMethod); call.addArgs(lhs, rhs); ctx.replaceMe(call); } break; } } } private StringStatus getStringStatus(JReferenceType type) { JClassType stringType = program.getTypeJavaLangString(); if (type == program.getTypeNull()) { return StringStatus.NULL; } else if (program.typeOracle.canTriviallyCast(type, stringType)) { return StringStatus.STRING; } else if (program.typeOracle.canTheoreticallyCast(type, stringType)) { return StringStatus.UNKNOWN; } else { return StringStatus.NOTSTRING; } } private JExpression maskUndefined(JExpression lhs) { assert ((JReferenceType) lhs.getType()).canBeNull(); JMethod maskMethod = program.getIndexedMethod("Cast.maskUndefined"); JMethodCall lhsCall = new JMethodCall(lhs.getSourceInfo(), null, maskMethod, lhs.getType()); lhsCall.addArg(lhs); return lhsCall; } } /** * Represents what we know about an operand type in terms of its type and * null status. */ private enum StringStatus { NOTSTRING(2), NULL(3), STRING(1), UNKNOWN(0); private int index; StringStatus(int index) { this.index = index; } public int getIndex() { return index; } } /** * A map of the combinations where each comparison strategy should be used. */ private static int[][] COMPARISON_STRAT = { // ..U..S.!S..N {1, 1, 1, 0,}, // UNKNOWN {1, 0, 1, 0,}, // STRING {1, 1, 0, 0,}, // NOTSTRING {0, 0, 0, 0,}, // NULL }; /** * The comparison strategy of using ==. */ private static final int STRAT_DOUBLE = 0; /** * The comparison strategy of using ===. */ private static final int STRAT_TRIPLE = 1; public static void exec(JProgram program) { new EqualityNormalizer(program).execImpl(); } private static boolean canBeNull(JExpression x) { return ((JReferenceType) x.getType()).canBeNull(); } private final JProgram program; private EqualityNormalizer(JProgram program) { this.program = program; } private void execImpl() { BreakupAssignOpsVisitor breaker = new BreakupAssignOpsVisitor(); breaker.accept(program); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy