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;
018    
019    import org.jetbrains.annotations.NotNull;
020    import org.jetbrains.annotations.Nullable;
021    import org.jetbrains.jet.codegen.inline.InlineCodegenUtil;
022    import org.jetbrains.jet.codegen.optimization.boxing.RedundantBoxingMethodTransformer;
023    import org.jetbrains.jet.codegen.optimization.boxing.RedundantNullCheckMethodTransformer;
024    import org.jetbrains.jet.codegen.optimization.transformer.MethodTransformer;
025    import org.jetbrains.org.objectweb.asm.MethodVisitor;
026    import org.jetbrains.org.objectweb.asm.Opcodes;
027    import org.jetbrains.org.objectweb.asm.tree.LocalVariableNode;
028    import org.jetbrains.org.objectweb.asm.tree.MethodNode;
029    import org.jetbrains.org.objectweb.asm.util.Textifier;
030    import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor;
031    
032    import java.util.ArrayList;
033    import java.util.List;
034    
035    public class OptimizationMethodVisitor extends MethodVisitor {
036        private static final int MAX_INSTRUCTIONS_SIZE_TO_OPTIMIZE = 5000;
037        private static final MethodTransformer MAIN_METHOD_TRANSFORMER = new RedundantNullCheckMethodTransformer(
038                new RedundantBoxingMethodTransformer(null)
039        );
040    
041        private final MethodNode methodNode;
042        private final MethodVisitor delegate;
043    
044        public OptimizationMethodVisitor(
045                @NotNull MethodVisitor delegate,
046                int access,
047                @NotNull String name,
048                @NotNull String desc,
049                @Nullable String signature,
050                @Nullable String[] exceptions
051        ) {
052            super(Opcodes.ASM5);
053            this.delegate = delegate;
054            this.methodNode = new MethodNode(access, name, desc, signature, exceptions);
055            this.methodNode.localVariables = new ArrayList<LocalVariableNode>(5);
056            this.mv = InlineCodegenUtil.wrapWithMaxLocalCalc(methodNode);
057        }
058    
059        @Override
060        public void visitEnd() {
061            // force mv to calculate maxStack/maxLocals in case it didn't yet done
062            if (methodNode.maxLocals <= 0 || methodNode.maxStack <= 0) {
063                mv.visitMaxs(-1, -1);
064            }
065    
066            super.visitEnd();
067    
068            if (methodNode.instructions.size() > 0 &&
069                methodNode.instructions.size() <= MAX_INSTRUCTIONS_SIZE_TO_OPTIMIZE) {
070                MAIN_METHOD_TRANSFORMER.transform("fake", methodNode);
071            }
072    
073            methodNode.accept(new EndIgnoringMethodVisitorDecorator(Opcodes.ASM5, delegate));
074    
075    
076            // In case of empty instructions list MethodNode.accept doesn't call visitLocalVariables of delegate
077            // So we just do it here
078            if (methodNode.instructions.size() == 0) {
079                List<LocalVariableNode> localVariables = methodNode.localVariables;
080                // visits local variables
081                int n = localVariables == null ? 0 : localVariables.size();
082                for (int i = 0; i < n; ++i) {
083                    localVariables.get(i).accept(delegate);
084                }
085            }
086    
087            delegate.visitEnd();
088        }
089    
090        /**
091         * You can use it when you need to ignore visit end
092         */
093        private static class EndIgnoringMethodVisitorDecorator extends MethodVisitor {
094            public EndIgnoringMethodVisitorDecorator(int api, @NotNull MethodVisitor mv) {
095                super(api, mv);
096            }
097    
098            @Override
099            public void visitEnd() {
100    
101            }
102        }
103    
104        @Nullable
105        public TraceMethodVisitor getTraceMethodVisitorIfPossible() {
106            TraceMethodVisitor traceMethodVisitor = new TraceMethodVisitor(new Textifier());
107            try {
108                methodNode.accept(traceMethodVisitor);
109            }
110            catch (Throwable e) {
111                return null;
112            }
113    
114            return traceMethodVisitor;
115        }
116    }