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 }