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