Revision: 9903
Author: cromwell...@google.com
Date: Mon Mar 28 09:42:49 2011
Log: Operations like i += d where i is an int and d is a double are
not properly
truncated (narrowed) to the LHS type. This patch forces i += d to be
written as
i = i + d, and applies a narrowing cast as needed, e.g. i = (int)(i + d);
Updated to fix String concat issue:
Review at http://gwt-code-reviews.appspot.com/1385803
Review by: sco...@google.com
http://code.google.com/p/google-web-toolkit/source/detail?r=9903
Added:
/trunk/dev/core/javatests
/trunk/dev/core/test/com/google/gwt/dev/jjs/impl/PostOptimizationCompoundAssignmentNormalizerTest.java
Modified:
/trunk/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
/trunk/dev/core/src/com/google/gwt/dev/jjs/impl/CompoundAssignmentNormalizer.java
/trunk/dev/core/src/com/google/gwt/dev/jjs/impl/PostOptimizationCompoundAssignmentNormalizer.java
=======================================
--- /dev/null
+++ /trunk/dev/core/javatests Mon Mar 28 09:42:49 2011
@@ -0,0 +1,1 @@
+test
=======================================
--- /dev/null
+++
/trunk/dev/core/test/com/google/gwt/dev/jjs/impl/PostOptimizationCompoundAssignmentNormalizerTest.java
Mon Mar 28 09:42:49 2011
@@ -0,0 +1,63 @@
+/*
+ * 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.core.ext.UnableToCompleteException;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JProgram;
+
+/**
+ * Tests {@link PostOptimizationCompoundAssignmentNormalizer}.
+ */
+public class PostOptimizationCompoundAssignmentNormalizerTest
+ extends OptimizerTestBase {
+
+ public void testIntegralFloatCoercion() throws Exception {
+ // long op= float
+ optimize("void", "long x=2L; float d=3; x += d;").into(
+ "long x=2L; float d=3; x = (long)((float)x + d);");
+ // long op= long
+ optimize("void", "long x=2L; long d=3L; x += d;").into(
+ "long x=2L; long d=3L; x = x + d;");
+ // don't touch int op= int
+ optimize("void", "int x=2; int d=3; x += d;").into(
+ "int x=2; int d=3; x += d;");
+ // don't touch, integral types with lhs wider than rhs
+ optimize("void", "int x=2; short d=3; x += d;").into(
+ "int x=2; short d=3; x += d;");
+ // different integral types, but should narrow result
+ optimize("void", "int x=2; short d=3; d += x;").into(
+ "int x=2; short d=3; d = (short)(d + x);");
+ // integral with long, should break up
+ optimize("void", "int x=2; long d=3L; x += d;").into(
+ "int x=2; long d=3L; x = (int)((long)x + d);");
+ // integral with float
+ optimize("void", "int x=2; float d=3.0f; x += d;").into(
+ "int x=2; float d=3.0f; x = (int)(x + d);");
+ // integral with double
+ optimize("void", "int x=2; double d=3.0; x += d;").into(
+ "int x=2; double d=3.0; x = (int)(x + d);");
+ // float and double, don't touch
+ optimize("void", "float x=2; double d=3.0; x += d;").into(
+ "float x=2; double d=3.0; x += d;");
+ }
+
+ protected boolean optimizeMethod(JProgram program, JMethod method) {
+ PostOptimizationCompoundAssignmentNormalizer.exec(program);
+ LongCastNormalizer.exec(program);
+ return true;
+ }
+}
=======================================
---
/trunk/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
Fri Mar 25 12:11:48 2011
+++
/trunk/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
Mon Mar 28 09:42:49 2011
@@ -292,10 +292,10 @@
// (5) "Normalize" the high-level Java tree into a lower-level tree
more
// suited for JavaScript code generation. Don't go reordering these
// willy-nilly because there are some subtle interdependencies.
- LongCastNormalizer.exec(jprogram);
JsoDevirtualizer.exec(jprogram);
CatchBlockNormalizer.exec(jprogram);
PostOptimizationCompoundAssignmentNormalizer.exec(jprogram);
+ LongCastNormalizer.exec(jprogram);
LongEmulationNormalizer.exec(jprogram);
CastNormalizer.exec(jprogram, options.isCastCheckingDisabled());
ArrayNormalizer.exec(jprogram);
=======================================
---
/trunk/dev/core/src/com/google/gwt/dev/jjs/impl/CompoundAssignmentNormalizer.java
Fri May 28 06:43:34 2010
+++
/trunk/dev/core/src/com/google/gwt/dev/jjs/impl/CompoundAssignmentNormalizer.java
Mon Mar 28 09:42:49 2011
@@ -161,8 +161,10 @@
new JMultiExpression(x.getSourceInfo()));
JExpression newLhs = replacer.accept(x.getLhs());
- JBinaryOperation operation = new JBinaryOperation(x.getSourceInfo(),
+ JExpression operation = new JBinaryOperation(x.getSourceInfo(),
newLhs.getType(), op.getNonAssignmentOf(), newLhs, x.getRhs());
+ operation = modifyResultOperation((JBinaryOperation) operation);
+
// newLhs is cloned below because it was used in operation
JBinaryOperation asg = new JBinaryOperation(x.getSourceInfo(),
newLhs.getType(),
JBinaryOperator.ASG, cloner.cloneExpression(newLhs),
@@ -285,6 +287,16 @@
return lhs;
}
+ /**
+ * Decide what expression to return when breaking up a compound
assignment of
+ * the form <code>lhs op= rhs</code>. The breakup creates an expression
of
+ * the form <code>lhs = lhs op rhs</code>, and the right hand side of the
+ * newly created expression is passed to this method.
+ */
+ protected JExpression modifyResultOperation(JBinaryOperation op) {
+ return op;
+ }
+
protected abstract boolean shouldBreakUp(JBinaryOperation x);
protected abstract boolean shouldBreakUp(JPostfixOperation x);
=======================================
---
/trunk/dev/core/src/com/google/gwt/dev/jjs/impl/PostOptimizationCompoundAssignmentNormalizer.java
Fri May 28 06:43:34 2010
+++
/trunk/dev/core/src/com/google/gwt/dev/jjs/impl/PostOptimizationCompoundAssignmentNormalizer.java
Mon Mar 28 09:42:49 2011
@@ -17,10 +17,13 @@
import com.google.gwt.dev.jjs.ast.JBinaryOperation;
import com.google.gwt.dev.jjs.ast.JBinaryOperator;
+import com.google.gwt.dev.jjs.ast.JCastOperation;
+import com.google.gwt.dev.jjs.ast.JExpression;
import com.google.gwt.dev.jjs.ast.JPostfixOperation;
import com.google.gwt.dev.jjs.ast.JPrefixOperation;
import com.google.gwt.dev.jjs.ast.JPrimitiveType;
import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JType;
/**
* Normalize compound assignments as needed after optimization. Integer
division
@@ -34,6 +37,21 @@
protected PostOptimizationCompoundAssignmentNormalizer() {
}
+
+ @Override
+ protected JExpression modifyResultOperation(JBinaryOperation op) {
+ JType lhsType = op.getLhs().getType();
+ JType rhsType = op.getRhs().getType();
+ if (lhsType != rhsType) {
+ // first widen binary op to encompass both sides, then add narrow
cast
+ return new JCastOperation(op.getSourceInfo(), lhsType,
+ new JBinaryOperation(op.getSourceInfo(),
+ widenType(lhsType, rhsType),
+ op.getOp(),
+ op.getLhs(), op.getRhs()));
+ }
+ return op;
+ }
@Override
protected boolean shouldBreakUp(JBinaryOperation x) {
@@ -45,6 +63,19 @@
&& x.getType() != JPrimitiveType.DOUBLE) {
return true;
}
+
+ JType lhsType = x.getLhs().getType();
+ JType rhsType = x.getRhs().getType();
+
+ // don't bother with float op= double since we don't float == double
in JS
+ if (lhsType == JPrimitiveType.FLOAT && rhsType ==
JPrimitiveType.DOUBLE) {
+ return false;
+ }
+ // break up so that result may be coerced to LHS type
+ if (lhsType instanceof JPrimitiveType && rhsType instanceof
JPrimitiveType
+ && widenType(lhsType, rhsType) != lhsType) {
+ return true;
+ }
return false;
}
@@ -63,4 +94,22 @@
}
return false;
}
-}
+
+ /**
+ * Implements
http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#26917
+ */
+ private JType widenType(JType lhsType, JType rhsType) {
+ if (lhsType == JPrimitiveType.DOUBLE ||
+ rhsType == JPrimitiveType.DOUBLE) {
+ return JPrimitiveType.DOUBLE;
+ } else if (lhsType == JPrimitiveType.FLOAT ||
+ rhsType == JPrimitiveType.FLOAT) {
+ return JPrimitiveType.FLOAT;
+ } else if (lhsType == JPrimitiveType.LONG ||
+ rhsType == JPrimitiveType.LONG) {
+ return JPrimitiveType.LONG;
+ } else {
+ return JPrimitiveType.INT;
+ }
+ }
+}
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors