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