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 }