| /* |
| * 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.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 FixAssignmentToUnbox extends JModVisitor { |
| /** |
| * Normalize compound assignments where the lhs is an unbox operation. |
| */ |
| private static class CompoundAssignmentToUnboxNormalizer extends CompoundAssignmentNormalizer { |
| private final AutoboxUtils autoboxUtils; |
| |
| protected CompoundAssignmentToUnboxNormalizer(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 isUnboxExpression(x.getLhs()); |
| } |
| |
| @Override |
| protected boolean shouldBreakUp(JPostfixOperation x) { |
| return isUnboxExpression(x.getArg()); |
| } |
| |
| @Override |
| protected boolean shouldBreakUp(JPrefixOperation x) { |
| return isUnboxExpression(x.getArg()); |
| } |
| |
| private boolean isUnboxExpression(JExpression x) { |
| return (autoboxUtils.undoUnbox(x) != null); |
| } |
| } |
| |
| public static void exec(JProgram program) { |
| Event fixAssignmentToUnboxEvent = |
| SpeedTracerLogger.start(CompilerEventType.FIX_ASSIGNMENT_TO_UNBOX); |
| new CompoundAssignmentToUnboxNormalizer(program).accept(program); |
| new FixAssignmentToUnbox(program).accept(program); |
| fixAssignmentToUnboxEvent.end(); |
| } |
| |
| private final AutoboxUtils autoboxUtils; |
| |
| private FixAssignmentToUnbox(JProgram program) { |
| this.autoboxUtils = new AutoboxUtils(program); |
| } |
| |
| @Override |
| public void endVisit(JBinaryOperation x, Context ctx) { |
| if (x.getOp() != JBinaryOperator.ASG) { |
| return; |
| } |
| |
| JExpression lhs = x.getLhs(); |
| JExpression boxed = autoboxUtils.undoUnbox(lhs); |
| if (boxed != null) { |
| // Assignment-to-unbox, e.g. |
| // unbox(x) = foo -> x = box(foo) |
| JClassType boxedType = (JClassType) boxed.getType(); |
| ctx.replaceMe(new JBinaryOperation(x.getSourceInfo(), boxedType, JBinaryOperator.ASG, boxed, |
| autoboxUtils.box(x.getRhs(), (JPrimitiveType) lhs.getType()))); |
| return; |
| } |
| |
| if (lhs instanceof JCastOperation) { |
| // Assignment-to-cast-operation, e.g. |
| // (Foo) x = foo -> x = foo |
| JCastOperation cast = (JCastOperation) lhs; |
| JBinaryOperation newAsg = |
| new JBinaryOperation(x.getSourceInfo(), x.getType(), JBinaryOperator.ASG, cast.getExpr(), |
| x.getRhs()); |
| ctx.replaceMe(newAsg); |
| } |
| } |
| } |