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