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