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    }