| /* |
| * 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.JCastOperation; |
| import com.google.gwt.dev.jjs.ast.JClassType; |
| import com.google.gwt.dev.jjs.ast.JExpression; |
| import com.google.gwt.dev.jjs.ast.JModVisitor; |
| 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.util.log.speedtracer.CompilerEventType; |
| import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger; |
| import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event; |
| |
| /** |
| * Most autoboxing is handled by {@link GenerateJavaAST}. The only cases it does |
| * not handle are <code>++</code>, <code>--</code>, and compound assignment |
| * operations (<code>+=</code>, etc.) when applied to a boxed type. This class |
| * fixes such cases in two steps. First, an internal subclass of |
| * {@link CompoundAssignmentNormalizer} simplifies such expressions to a simple |
| * assignment expression. Second, this visitor replaces an assignment to an |
| * unboxing method (<code>unbox(x) = unbox(x) + 1</code>) with an assignment to |
| * the underlying box (<code>x = box(unbox(x) + 1)</code>). |
| * |
| * <p> |
| * Update: GenerateJavaAST can also leave invalid AST structures of the form |
| * <code>(Foo) x = foo</code> due to the way generics are handled. This can |
| * happen when assigning into a field of a generic type. We'll go ahead and |
| * resolve that case here as well. |
| * </p> |
| */ |
| public class FixAssignmentsToUnboxOrCast extends JModVisitor { |
| /** |
| * Normalize compound assignments where the lhs is an unbox operation. |
| */ |
| private static class CompoundAssignmentsToUnboxOrCastNormalizer |
| extends CompoundAssignmentNormalizer { |
| private final AutoboxUtils autoboxUtils; |
| |
| protected CompoundAssignmentsToUnboxOrCastNormalizer(JProgram program) { |
| autoboxUtils = new AutoboxUtils(program); |
| } |
| |
| /** |
| * If the lhs is an unbox operation, then return the box rather than the |
| * original value. |
| */ |
| @Override |
| protected JExpression expressionToReturn(JExpression lhs) { |
| JExpression boxed = autoboxUtils.undoUnbox(lhs); |
| if (boxed != null) { |
| return boxed; |
| } |
| return lhs; |
| } |
| |
| @Override |
| protected boolean shouldBreakUp(JBinaryOperation x) { |
| return isUnboxOrCastExpression(x.getLhs()); |
| } |
| |
| @Override |
| protected boolean shouldBreakUp(JPostfixOperation x) { |
| return isUnboxOrCastExpression(x.getArg()); |
| } |
| |
| @Override |
| protected boolean shouldBreakUp(JPrefixOperation x) { |
| return isUnboxOrCastExpression(x.getArg()); |
| } |
| |
| private boolean isUnboxOrCastExpression(JExpression x) { |
| return (autoboxUtils.undoUnbox(x) != null) || x instanceof JCastOperation; |
| } |
| } |
| |
| public static void exec(JProgram program) { |
| Event fixAssignmentToUnboxEvent = |
| SpeedTracerLogger.start(CompilerEventType.FIX_ASSIGNMENT_TO_UNBOX); |
| new CompoundAssignmentsToUnboxOrCastNormalizer(program).accept(program); |
| new FixAssignmentsToUnboxOrCast(program).accept(program); |
| fixAssignmentToUnboxEvent.end(); |
| } |
| |
| private final AutoboxUtils autoboxUtils; |
| |
| private FixAssignmentsToUnboxOrCast(JProgram program) { |
| this.autoboxUtils = new AutoboxUtils(program); |
| } |
| |
| private JBinaryOperation maybeFixLhsCast(JBinaryOperation x) { |
| if (!(x.getLhs() instanceof JCastOperation)) { |
| return x; |
| } |
| // Assignment-to-cast-operation, e.g. |
| // (Foo) x = foo -> x = foo |
| // (Foo) x += foo -> x += foo |
| JCastOperation cast = (JCastOperation) x.getLhs(); |
| return new JBinaryOperation(x.getSourceInfo(), x.getType(), x.getOp(), cast.getExpr(), |
| x.getRhs()); |
| } |
| |
| private JBinaryOperation maybeUndoBox(JBinaryOperation x) { |
| JExpression lhs = x.getLhs(); |
| JExpression boxed = autoboxUtils.undoUnbox(lhs); |
| if (boxed == null) { |
| return x; |
| } |
| |
| // Assignment-to-unbox, e.g. |
| // unbox(x) = foo -> x = box(foo) |
| JClassType boxedType = (JClassType) boxed.getType(); |
| return new JBinaryOperation(x.getSourceInfo(), boxedType, x.getOp(), boxed, |
| autoboxUtils.box(x.getRhs(), (JPrimitiveType) lhs.getType())); |
| } |
| |
| @Override |
| public void endVisit(JBinaryOperation x, Context ctx) { |
| if (!x.isAssignment()) { |
| return; |
| } |
| |
| JBinaryOperation result = maybeFixLhsCast(maybeUndoBox(x)); |
| |
| if (result != x) { |
| ctx.replaceMe(result); |
| } |
| } |
| } |