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 }