001    /*
002     * Copyright 2010-2014 JetBrains s.r.o.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.jetbrains.jet.codegen.optimization.boxing;
018    
019    import org.jetbrains.annotations.NotNull;
020    import org.jetbrains.jet.codegen.optimization.transformer.MethodTransformer;
021    import org.jetbrains.org.objectweb.asm.Opcodes;
022    import org.jetbrains.org.objectweb.asm.tree.*;
023    import org.jetbrains.org.objectweb.asm.tree.analysis.Analyzer;
024    import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue;
025    import org.jetbrains.org.objectweb.asm.tree.analysis.Frame;
026    
027    import java.util.ArrayList;
028    import java.util.List;
029    
030    public class RedundantNullCheckMethodTransformer extends MethodTransformer {
031        public RedundantNullCheckMethodTransformer(MethodTransformer methodTransformer) {
032            super(methodTransformer);
033        }
034    
035        @Override
036        public void transform(@NotNull String internalClassName, @NotNull MethodNode methodNode) {
037    
038            while (removeRedundantNullCheckPass(internalClassName, methodNode)) {
039                //do nothing
040            }
041    
042            super.transform(internalClassName, methodNode);
043        }
044    
045        private static boolean removeRedundantNullCheckPass(@NotNull String internalClassName, @NotNull MethodNode methodNode) {
046            InsnList insnList = methodNode.instructions;
047            Frame<BasicValue>[] frames = analyze(
048                    internalClassName, methodNode,
049                    new BoxingInterpreter(insnList)
050            );
051    
052            List<AbstractInsnNode> insnsToOptimize = new ArrayList<AbstractInsnNode>();
053    
054            for (int i = 0; i < insnList.size(); i++) {
055                Frame<BasicValue> frame = frames[i];
056                AbstractInsnNode insn = insnList.get(i);
057    
058                if ((insn.getOpcode() == Opcodes.IFNULL || insn.getOpcode() == Opcodes.IFNONNULL) &&
059                    frame != null && frame.getStack(frame.getStackSize() - 1) instanceof BoxedBasicValue) {
060    
061                    insnsToOptimize.add(insn);
062                }
063            }
064    
065            for (AbstractInsnNode insn : insnsToOptimize) {
066                if (insn.getPrevious() != null && insn.getPrevious().getOpcode() == Opcodes.DUP) {
067                    insnList.remove(insn.getPrevious());
068                }
069                else {
070                    insnList.insertBefore(insn, new InsnNode(Opcodes.POP));
071                }
072    
073                assert insn.getOpcode() == Opcodes.IFNULL
074                       || insn.getOpcode() == Opcodes.IFNONNULL : "only IFNULL/IFNONNULL are supported";
075    
076                if (insn.getOpcode() == Opcodes.IFNULL) {
077                    insnList.remove(insn);
078                }
079                else {
080                    insnList.set(
081                            insn,
082                            new JumpInsnNode(Opcodes.GOTO, ((JumpInsnNode) insn).label)
083                    );
084                }
085            }
086    
087            return insnsToOptimize.size() > 0;
088        }
089    }