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 [http://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 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;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Objects;
import static org.objectweb.asm.Type.getType;
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( Objects.toString( 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 );
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy