Convert TransformationMethodVisitor: step 2

This commit is contained in:
Alexey Tsvetkov
2018-12-07 19:21:01 +03:00
parent 56b72c96eb
commit 82eb7c17e6
@@ -14,111 +14,96 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.codegen; package org.jetbrains.kotlin.codegen
import org.jetbrains.annotations.NotNull; import org.jetbrains.org.objectweb.asm.MethodVisitor
import org.jetbrains.annotations.Nullable; import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.MethodVisitor; import org.jetbrains.org.objectweb.asm.tree.MethodNode
import org.jetbrains.org.objectweb.asm.Opcodes; import org.jetbrains.org.objectweb.asm.util.Textifier
import org.jetbrains.org.objectweb.asm.tree.LocalVariableNode; import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor
import org.jetbrains.org.objectweb.asm.tree.MethodNode;
import org.jetbrains.org.objectweb.asm.util.Textifier;
import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor;
import java.util.ArrayList; import java.util.ArrayList
import java.util.List;
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.getNodeText; import org.jetbrains.kotlin.codegen.inline.nodeText
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.wrapWithMaxLocalCalc; import org.jetbrains.kotlin.codegen.inline.wrapWithMaxLocalCalc
public abstract class TransformationMethodVisitor extends MethodVisitor { abstract class TransformationMethodVisitor(
private val delegate: MethodVisitor,
access: Int,
name: String,
desc: String,
signature: String?,
exceptions: Array<String>?
) : MethodVisitor(Opcodes.ASM5) {
private final MethodNode methodNode; private val methodNode = MethodNode(access, name, desc, signature, exceptions).apply {
private final MethodVisitor delegate; localVariables = ArrayList(5)
public TransformationMethodVisitor(
@NotNull MethodVisitor delegate,
int access,
@NotNull String name,
@NotNull String desc,
@Nullable String signature,
@Nullable String[] exceptions
) {
super(Opcodes.ASM5);
this.delegate = delegate;
this.methodNode = new MethodNode(access, name, desc, signature, exceptions);
this.methodNode.localVariables = new ArrayList<>(5);
this.mv = wrapWithMaxLocalCalc(methodNode);
} }
@Override val traceMethodVisitorIfPossible: TraceMethodVisitor?
public void visitEnd() { get() {
// force mv to calculate maxStack/maxLocals in case it didn't yet done val traceMethodVisitor = TraceMethodVisitor(Textifier())
if (methodNode.maxLocals <= 0 || methodNode.maxStack <= 0) { try {
mv.visitMaxs(-1, -1); methodNode.accept(traceMethodVisitor)
} catch (e: Throwable) {
return null
}
return traceMethodVisitor
} }
super.visitEnd(); init {
mv = wrapWithMaxLocalCalc(methodNode)
}
override fun visitEnd() {
// force mv to calculate maxStack/maxLocals in case it didn't yet done
if (methodNode.maxLocals <= 0 || methodNode.maxStack <= 0) {
mv.visitMaxs(-1, -1)
}
super.visitEnd()
try { try {
if (shouldBeTransformed(methodNode)) { if (shouldBeTransformed(methodNode)) {
performTransformations(methodNode); performTransformations(methodNode)
} }
methodNode.accept(new EndIgnoringMethodVisitorDecorator(Opcodes.ASM5, delegate)); methodNode.accept(EndIgnoringMethodVisitorDecorator(Opcodes.ASM5, delegate))
// In case of empty instructions list MethodNode.accept doesn't call visitLocalVariables of delegate // In case of empty instructions list MethodNode.accept doesn't call visitLocalVariables of delegate
// So we just do it here // So we just do it here
if (methodNode.instructions.size() == 0 if (methodNode.instructions.size() == 0
// MethodNode does not create a list of variables for abstract methods, so we would get NPE in accept() instead // MethodNode does not create a list of variables for abstract methods, so we would get NPE in accept() instead
&& (!(delegate instanceof MethodNode) || methodNode.localVariables != null) && (delegate !is MethodNode || methodNode.localVariables != null)
) { ) {
List<LocalVariableNode> localVariables = methodNode.localVariables; val localVariables = methodNode.localVariables
// visits local variables // visits local variables
int n = localVariables == null ? 0 : localVariables.size(); val n = localVariables?.size ?: 0
for (int i = 0; i < n; ++i) { for (i in 0 until n) {
localVariables.get(i).accept(delegate); localVariables!![i].accept(delegate)
} }
} }
delegate.visitEnd(); delegate.visitEnd()
} } catch (t: Throwable) {
catch (Throwable t) { throw CompilationException("Couldn't transform method node:\n" + methodNode.nodeText, t, null)
throw new CompilationException("Couldn't transform method node:\n" + getNodeText(methodNode), t, null);
} }
} }
protected abstract void performTransformations(@NotNull MethodNode methodNode); protected abstract fun performTransformations(methodNode: MethodNode)
/** /**
* You can use it when you need to ignore visit end * You can use it when you need to ignore visit end
*/ */
private static class EndIgnoringMethodVisitorDecorator extends MethodVisitor { private class EndIgnoringMethodVisitorDecorator(api: Int, mv: MethodVisitor) : MethodVisitor(api, mv) {
public EndIgnoringMethodVisitorDecorator(int api, @NotNull MethodVisitor mv) {
super(api, mv);
}
@Override
public void visitEnd() {
override fun visitEnd() {
} }
} }
@Nullable private fun shouldBeTransformed(node: MethodNode): Boolean {
public TraceMethodVisitor getTraceMethodVisitorIfPossible() { return node.instructions.size() > 0
TraceMethodVisitor traceMethodVisitor = new TraceMethodVisitor(new Textifier());
try {
methodNode.accept(traceMethodVisitor);
}
catch (Throwable e) {
return null;
}
return traceMethodVisitor;
}
private static boolean shouldBeTransformed(@NotNull MethodNode node) {
return node.instructions.size() > 0;
} }
} }