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 }