001 /*
002 * Copyright 2010-2016 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;
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.org.objectweb.asm.MethodVisitor;
023 import org.jetbrains.org.objectweb.asm.Opcodes;
024 import org.jetbrains.org.objectweb.asm.tree.LocalVariableNode;
025 import org.jetbrains.org.objectweb.asm.tree.MethodNode;
026 import org.jetbrains.org.objectweb.asm.util.Textifier;
027 import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor;
028
029 import java.util.ArrayList;
030 import java.util.List;
031
032 public abstract class TransformationMethodVisitor extends MethodVisitor {
033
034 private final MethodNode methodNode;
035 private final MethodVisitor delegate;
036
037 public TransformationMethodVisitor(
038 @NotNull MethodVisitor delegate,
039 int access,
040 @NotNull String name,
041 @NotNull String desc,
042 @Nullable String signature,
043 @Nullable String[] exceptions
044 ) {
045 super(Opcodes.ASM5);
046 this.delegate = delegate;
047 this.methodNode = new MethodNode(access, name, desc, signature, exceptions);
048 this.methodNode.localVariables = new ArrayList<LocalVariableNode>(5);
049 this.mv = InlineCodegenUtil.wrapWithMaxLocalCalc(methodNode);
050 }
051
052 @Override
053 public void visitEnd() {
054 // force mv to calculate maxStack/maxLocals in case it didn't yet done
055 if (methodNode.maxLocals <= 0 || methodNode.maxStack <= 0) {
056 mv.visitMaxs(-1, -1);
057 }
058
059 super.visitEnd();
060
061 try {
062 if (shouldBeTransformed(methodNode)) {
063 performTransformations(methodNode);
064 }
065
066 methodNode.accept(new EndIgnoringMethodVisitorDecorator(Opcodes.ASM5, delegate));
067
068
069 // In case of empty instructions list MethodNode.accept doesn't call visitLocalVariables of delegate
070 // So we just do it here
071 if (methodNode.instructions.size() == 0
072 // MethodNode does not create a list of variables for abstract methods, so we would get NPE in accept() instead
073 && (!(delegate instanceof MethodNode) || (methodNode.access & Opcodes.ACC_ABSTRACT) == 0)
074 ) {
075 List<LocalVariableNode> localVariables = methodNode.localVariables;
076 // visits local variables
077 int n = localVariables == null ? 0 : localVariables.size();
078 for (int i = 0; i < n; ++i) {
079 localVariables.get(i).accept(delegate);
080 }
081 }
082
083 delegate.visitEnd();
084 }
085 catch (Throwable t) {
086 throw new CompilationException("Couldn't transform method node: " + InlineCodegenUtil.getNodeText(methodNode), t, null);
087 }
088 }
089
090 protected abstract void performTransformations(@NotNull MethodNode methodNode);
091
092 /**
093 * You can use it when you need to ignore visit end
094 */
095 private static class EndIgnoringMethodVisitorDecorator extends MethodVisitor {
096 public EndIgnoringMethodVisitorDecorator(int api, @NotNull MethodVisitor mv) {
097 super(api, mv);
098 }
099
100 @Override
101 public void visitEnd() {
102
103 }
104 }
105
106 @Nullable
107 public TraceMethodVisitor getTraceMethodVisitorIfPossible() {
108 TraceMethodVisitor traceMethodVisitor = new TraceMethodVisitor(new Textifier());
109 try {
110 methodNode.accept(traceMethodVisitor);
111 }
112 catch (Throwable e) {
113 return null;
114 }
115
116 return traceMethodVisitor;
117 }
118
119 private static boolean shouldBeTransformed(@NotNull MethodNode node) {
120 return node.instructions.size() > 0;
121 }
122 }