org.neo4j.codegen.ByteCodeVisitor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of neo4j-codegen Show documentation
Show all versions of neo4j-codegen Show documentation
Simple library for generating code.
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [https://neo4j.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package org.neo4j.codegen;
import static org.objectweb.asm.Type.getType;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
public interface ByteCodeVisitor {
interface Configurable {
void addByteCodeVisitor(ByteCodeVisitor visitor);
}
ByteCodeVisitor DO_NOTHING = (name, bytes) -> {};
void visitByteCode(String name, ByteBuffer bytes);
class Multiplex implements ByteCodeVisitor {
private final ByteCodeVisitor[] visitors;
Multiplex(ByteCodeVisitor[] visitors) {
this.visitors = visitors;
}
@Override
public void visitByteCode(String name, ByteBuffer bytes) {
for (ByteCodeVisitor visitor : visitors) {
visitor.visitByteCode(name, bytes.duplicate());
}
}
}
static Printer printer(PrintWriter out) {
return new Printer() {
@Override
void printf(String format, Object... args) {
out.format(format, args);
}
@Override
void println(CharSequence line) {
out.println(line);
}
};
}
static Printer printer(PrintStream out) {
return new Printer() {
@Override
void printf(String format, Object... args) {
out.format(format, args);
}
@Override
void println(CharSequence line) {
out.println(line);
}
};
}
abstract class Printer extends ClassVisitor implements ByteCodeVisitor, CodeGeneratorOption {
private Printer() {
super(Opcodes.ASM4);
}
@Override
public void applyTo(Object target) {
if (target instanceof Configurable) {
((Configurable) target).addByteCodeVisitor(this);
}
}
abstract void printf(String format, Object... args);
abstract void println(CharSequence line);
@Override
public void visitByteCode(String name, ByteBuffer bytes) {
new ClassReader(bytes.array()).accept(this, 0);
}
@Override
public void visit(
int version, int access, String name, String signature, String superName, String[] interfaces) {
StringBuilder iFaces = new StringBuilder();
String prefix = " implements ";
for (String iFace : interfaces) {
iFaces.append(prefix).append(iFace);
prefix = ", ";
}
printf("%s class %s extends %s%s%n{%n", Modifier.toString(access), name, superName, iFaces);
}
@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
printf(
" %s %s %s%s;%n",
Modifier.toString(access),
getType(desc).getClassName(),
name,
value == null ? "" : (" = " + value));
return super.visitField(access, name, desc, signature, value);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
printf(" %s %s%s%n {%n", Modifier.toString(access), name, desc);
return new MethodVisitor(api) {
int offset;
@Override
public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
StringBuilder frame = new StringBuilder().append(" [FRAME:");
switch (type) {
case Opcodes.F_NEW:
frame.append("NEW");
break;
case Opcodes.F_FULL:
frame.append("FULL");
break;
case Opcodes.F_APPEND:
frame.append("APPEND");
break;
case Opcodes.F_CHOP:
frame.append("CHOP");
break;
case Opcodes.F_SAME:
frame.append("SAME");
break;
case Opcodes.F_SAME1:
frame.append("SAME1");
break;
default:
frame.append(type);
}
frame.append(", ").append(nLocal).append(" locals: [");
String prefix = "";
for (int i = 0; i < nLocal; i++) {
frame.append(prefix);
if (local[i] instanceof String) {
frame.append(local[i]);
} else if (local[i] == Opcodes.TOP) {
frame.append("TOP");
} else if (local[i] == Opcodes.INTEGER) {
frame.append("INTEGER");
} else if (local[i] == Opcodes.FLOAT) {
frame.append("FLOAT");
} else if (local[i] == Opcodes.DOUBLE) {
frame.append("DOUBLE");
} else if (local[i] == Opcodes.LONG) {
frame.append("LONG");
} else if (local[i] == Opcodes.NULL) {
frame.append("NULL");
} else if (local[i] == Opcodes.UNINITIALIZED_THIS) {
frame.append("UNINITIALIZED_THIS");
} else {
frame.append(local[i]);
}
prefix = ", ";
}
frame.append("], ").append(nStack).append(" items on stack: [");
prefix = "";
for (int i = 0; i < nStack; i++) {
frame.append(prefix).append(stack[i]);
prefix = ", ";
}
println(frame.append(']'));
}
@Override
public void visitInsn(int opcode) {
printf(" @%03d: %s%n", offset, opcode(opcode));
offset += 1;
}
@Override
public void visitIntInsn(int opcode, int operand) {
printf(" @%03d: %s %d%n", offset, opcode(opcode), operand);
offset += opcode == Opcodes.SIPUSH ? 3 : 2;
}
@Override
public void visitVarInsn(int opcode, int var) {
printf(" @%03d: %s var:%d%n", offset, opcode(opcode), var);
// guessing the most efficient encoding was used:
if (var <= 0x3) {
offset += 1;
} else if (var <= 0xFF) {
offset += 2;
} else {
offset += 4;
}
}
@Override
public void visitTypeInsn(int opcode, String type) {
printf(" @%03d: %s %s%n", offset, opcode(opcode), type);
offset += 3;
}
@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
printf(" @%03d: %s %s.%s:%s%n", offset, opcode(opcode), owner, name, desc);
offset += 3;
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
printf(" @%03d: %s %s.%s%s%n", offset, opcode(opcode), owner, name, desc);
offset += opcode == Opcodes.INVOKEINTERFACE ? 5 : 3;
}
@Override
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
printf(
" @%03d: InvokeDynamic %s%s / bsm:%s%s%n",
offset, name, desc, bsm, Arrays.toString(bsmArgs));
offset += 5;
}
@Override
public void visitJumpInsn(int opcode, Label label) {
printf(" @%03d: %s %s%n", offset, opcode(opcode), label);
offset += 3; // TODO: how do we tell if a wide (+=5) instruction (GOTO_W=200, JSR_W=201) was used?
// wide instructions get simplified to their basic counterpart, but are used for long jumps
}
@Override
public void visitLabel(Label label) {
printf(" %s:%n", label);
}
@Override
public void visitLdcInsn(Object cst) {
printf(" @%03d: LDC %s%n", offset, cst);
offset += 2; // TODO: how do we tell if the WIDE instruction prefix (+=3) was used?
// we don't know index of the constant in the pool, wide instructions are used for high indexes
}
@Override
public void visitIincInsn(int var, int increment) {
printf(" @%03d: IINC %d += %d%n", offset, var, increment);
// guessing the most efficient encoding was used:
if (var <= 0xFF && increment <= 0xFF) {
offset += 3;
} else {
offset += 6;
}
}
@Override
public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
printf(" @%03d: TABLE_SWITCH(min=%d, max=%d)%n {%n", offset, min, max);
for (int i = 0, val = min; i < labels.length; i++, val++) {
printf(" case %d goto %s%n", val, labels[i]);
}
printf(" default goto %s%n }%n", dflt);
offset += 4 - (offset & 3); // padding bytes, table starts at aligned offset
offset += 12; // default, min, max
offset += 4 * labels.length; // table of offsets
}
@Override
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
printf(" @%03d: LOOKUP_SWITCH%n {%n", offset);
for (int i = 0; i < labels.length; i++) {
printf(" case %d goto %s%n", keys[i], labels[i]);
}
printf(" default goto %s%n }%n", dflt);
offset += 4 - (offset & 3); // padding bytes, table starts at aligned offset
offset += 8; // default, length
offset += 8 * labels.length; // table of key+offset
}
@Override
public void visitMultiANewArrayInsn(String desc, int dims) {
printf(" @%03d: MULTI_ANEW_ARRAY %s, dims:%d%n", offset, desc, dims);
offset += 4;
}
@Override
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
printf(" [try/catch %s start@%s, end@%s, handler@%s]%n", type, start, end, handler);
}
@Override
public void visitLocalVariable(
String name, String desc, String signature, Label start, Label end, int index) {
printf(" [local %s:%s, from %s to %s @offset=%d]%n", name, desc, start, end, index);
}
@Override
public void visitLineNumber(int line, Label start) {
printf(" [line %d @ %s]%n", line, start);
}
@Override
public void visitEnd() {
println(" }");
}
};
}
@Override
public void visitEnd() {
println("}");
}
private static String opcode(int opcode) {
switch (opcode) {
// visitInsn
case Opcodes.NOP:
return "NOP";
case Opcodes.ACONST_NULL:
return "ACONST_NULL";
case Opcodes.ICONST_M1:
return "ICONST_M1";
case Opcodes.ICONST_0:
return "ICONST_0";
case Opcodes.ICONST_1:
return "ICONST_1";
case Opcodes.ICONST_2:
return "ICONST_2";
case Opcodes.ICONST_3:
return "ICONST_3";
case Opcodes.ICONST_4:
return "ICONST_4";
case Opcodes.ICONST_5:
return "ICONST_5";
case Opcodes.LCONST_0:
return "LCONST_0";
case Opcodes.LCONST_1:
return "LCONST_1";
case Opcodes.FCONST_0:
return "FCONST_0";
case Opcodes.FCONST_1:
return "FCONST_1";
case Opcodes.FCONST_2:
return "FCONST_2";
case Opcodes.DCONST_0:
return "DCONST_0";
case Opcodes.DCONST_1:
return "DCONST_1";
case Opcodes.IALOAD:
return "IALOAD";
case Opcodes.LALOAD:
return "LALOAD";
case Opcodes.FALOAD:
return "FALOAD";
case Opcodes.DALOAD:
return "DALOAD";
case Opcodes.AALOAD:
return "AALOAD";
case Opcodes.BALOAD:
return "BALOAD";
case Opcodes.CALOAD:
return "CALOAD";
case Opcodes.SALOAD:
return "SALOAD";
case Opcodes.IASTORE:
return "IASTORE";
case Opcodes.LASTORE:
return "LASTORE";
case Opcodes.FASTORE:
return "FASTORE";
case Opcodes.DASTORE:
return "DASTORE";
case Opcodes.AASTORE:
return "AASTORE";
case Opcodes.BASTORE:
return "BASTORE";
case Opcodes.CASTORE:
return "CASTORE";
case Opcodes.SASTORE:
return "SASTORE";
case Opcodes.POP:
return "POP";
case Opcodes.POP2:
return "POP2";
case Opcodes.DUP:
return "DUP";
case Opcodes.DUP_X1:
return "DUP_X1";
case Opcodes.DUP_X2:
return "DUP_X2";
case Opcodes.DUP2:
return "DUP2";
case Opcodes.DUP2_X1:
return "DUP2_X1";
case Opcodes.DUP2_X2:
return "DUP2_X2";
case Opcodes.SWAP:
return "SWAP";
case Opcodes.IADD:
return "IADD";
case Opcodes.LADD:
return "LADD";
case Opcodes.FADD:
return "FADD";
case Opcodes.DADD:
return "DADD";
case Opcodes.ISUB:
return "ISUB";
case Opcodes.LSUB:
return "LSUB";
case Opcodes.FSUB:
return "FSUB";
case Opcodes.DSUB:
return "DSUB";
case Opcodes.IMUL:
return "IMUL";
case Opcodes.LMUL:
return "LMUL";
case Opcodes.FMUL:
return "FMUL";
case Opcodes.DMUL:
return "DMUL";
case Opcodes.IDIV:
return "IDIV";
case Opcodes.LDIV:
return "LDIV";
case Opcodes.FDIV:
return "FDIV";
case Opcodes.DDIV:
return "DDIV";
case Opcodes.IREM:
return "IREM";
case Opcodes.LREM:
return "LREM";
case Opcodes.FREM:
return "FREM";
case Opcodes.DREM:
return "DREM";
case Opcodes.INEG:
return "INEG";
case Opcodes.LNEG:
return "LNEG";
case Opcodes.FNEG:
return "FNEG";
case Opcodes.DNEG:
return "DNEG";
case Opcodes.ISHL:
return "ISHL";
case Opcodes.LSHL:
return "LSHL";
case Opcodes.ISHR:
return "ISHR";
case Opcodes.LSHR:
return "LSHR";
case Opcodes.IUSHR:
return "IUSHR";
case Opcodes.LUSHR:
return "LUSHR";
case Opcodes.IAND:
return "IAND";
case Opcodes.LAND:
return "LAND";
case Opcodes.IOR:
return "IOR";
case Opcodes.LOR:
return "LOR";
case Opcodes.IXOR:
return "IXOR";
case Opcodes.LXOR:
return "LXOR";
case Opcodes.I2L:
return "I2L";
case Opcodes.I2F:
return "I2F";
case Opcodes.I2D:
return "I2D";
case Opcodes.L2I:
return "L2I";
case Opcodes.L2F:
return "L2F";
case Opcodes.L2D:
return "L2D";
case Opcodes.F2I:
return "F2I";
case Opcodes.F2L:
return "F2L";
case Opcodes.F2D:
return "F2D";
case Opcodes.D2I:
return "D2I";
case Opcodes.D2L:
return "D2L";
case Opcodes.D2F:
return "D2F";
case Opcodes.I2B:
return "I2B";
case Opcodes.I2C:
return "I2C";
case Opcodes.I2S:
return "I2S";
case Opcodes.LCMP:
return "LCMP";
case Opcodes.FCMPL:
return "FCMPL";
case Opcodes.FCMPG:
return "FCMPG";
case Opcodes.DCMPL:
return "DCMPL";
case Opcodes.DCMPG:
return "DCMPG";
case Opcodes.IRETURN:
return "IRETURN";
case Opcodes.LRETURN:
return "LRETURN";
case Opcodes.FRETURN:
return "FRETURN";
case Opcodes.DRETURN:
return "DRETURN";
case Opcodes.ARETURN:
return "ARETURN";
case Opcodes.RETURN:
return "RETURN";
case Opcodes.ARRAYLENGTH:
return "ARRAYLENGTH";
case Opcodes.ATHROW:
return "ATHROW";
case Opcodes.MONITORENTER:
return "MONITORENTER";
case Opcodes.MONITOREXIT:
return "MONITOREXIT";
// visitIntInsn
case Opcodes.BIPUSH:
return "BIPUSH";
case Opcodes.SIPUSH:
return "SIPUSH";
case Opcodes.NEWARRAY:
return "NEWARRAY";
// visitVarInsn
case Opcodes.ILOAD:
return "ILOAD";
case Opcodes.LLOAD:
return "LLOAD";
case Opcodes.FLOAD:
return "FLOAD";
case Opcodes.DLOAD:
return "DLOAD";
case Opcodes.ALOAD:
return "ALOAD";
case Opcodes.ISTORE:
return "ISTORE";
case Opcodes.LSTORE:
return "LSTORE";
case Opcodes.FSTORE:
return "FSTORE";
case Opcodes.DSTORE:
return "DSTORE";
case Opcodes.ASTORE:
return "ASTORE";
case Opcodes.RET:
return "RET";
// visitTypeInsn
case Opcodes.NEW:
return "NEW";
case Opcodes.ANEWARRAY:
return "ANEWARRAY";
case Opcodes.CHECKCAST:
return "CHECKCAST";
case Opcodes.INSTANCEOF:
return "INSTANCEOF";
// visitFieldInsn
case Opcodes.GETSTATIC:
return "GETSTATIC";
case Opcodes.PUTSTATIC:
return "PUTSTATIC";
case Opcodes.GETFIELD:
return "GETFIELD";
case Opcodes.PUTFIELD:
return "PUTFIELD";
// visitMethodInsn
case Opcodes.INVOKEVIRTUAL:
return "INVOKEVIRTUAL";
case Opcodes.INVOKESPECIAL:
return "INVOKESPECIAL";
case Opcodes.INVOKESTATIC:
return "INVOKESTATIC";
case Opcodes.INVOKEINTERFACE:
return "INVOKEINTERFACE";
// visitJumpInsn
case Opcodes.IFEQ:
return "IFEQ";
case Opcodes.IFNE:
return "IFNE";
case Opcodes.IFLT:
return "IFLT";
case Opcodes.IFGE:
return "IFGE";
case Opcodes.IFGT:
return "IFGT";
case Opcodes.IFLE:
return "IFLE";
case Opcodes.IF_ICMPEQ:
return "IF_ICMPEQ";
case Opcodes.IF_ICMPNE:
return "IF_ICMPNE";
case Opcodes.IF_ICMPLT:
return "IF_ICMPLT";
case Opcodes.IF_ICMPGE:
return "IF_ICMPGE";
case Opcodes.IF_ICMPGT:
return "IF_ICMPGT";
case Opcodes.IF_ICMPLE:
return "IF_ICMPLE";
case Opcodes.IF_ACMPEQ:
return "IF_ACMPEQ";
case Opcodes.IF_ACMPNE:
return "IF_ACMPNE";
case Opcodes.GOTO:
return "GOTO";
case Opcodes.JSR:
return "JSR";
case Opcodes.IFNULL:
return "IFNULL";
case Opcodes.IFNONNULL:
return "IFNONNULL";
default:
throw new IllegalArgumentException("unknown opcode: " + opcode);
}
}
}
}