2011-07-11 17:46:00 -04:00
/ *
* Created on Jun 23 , 2011
* /
package org.lwjgl.util.mapped ;
2011-07-12 09:29:04 -04:00
import org.lwjgl.LWJGLUtil ;
import org.objectweb.asm.* ;
import org.objectweb.asm.util.TraceClassVisitor ;
2011-07-12 17:30:48 -04:00
import java.io.* ;
2011-07-11 17:46:00 -04:00
import java.lang.reflect.Field ;
import java.lang.reflect.Modifier ;
import java.nio.ByteBuffer ;
import java.util.HashMap ;
import java.util.Map ;
2011-07-12 17:30:48 -04:00
import java.util.StringTokenizer ;
2011-07-11 17:46:00 -04:00
import static org.objectweb.asm.Opcodes.* ;
/ * *
2011-07-12 09:29:04 -04:00
* This class implements the bytecode transformation that mapped object go through .
* Mapped object classes need to first be registered with the transformer , see { @link # register ( Class ) } .
* < p / >
* The transformer supports some debugging tools , enabled through JVM system properties : < br / >
* org . lwjgl . util . mapped . PrintTiming = true , prints timing information for the transformation step . < br / >
* org . lwjgl . util . mapped . PrintActivity = true , prints activity information . < br / >
* org . lwjgl . util . mapped . PrintBytecode = true , prints the transformed bytecode . [ not working atm ] < br / >
* org . lwjgl . util . Debug must also be set to true for the above to work .
*
2011-07-11 17:46:00 -04:00
* @author Riven
* /
2011-07-12 09:29:04 -04:00
public class MappedObjectTransformer {
2011-07-12 17:30:48 -04:00
static final boolean PRINT_TIMING = LWJGLUtil . DEBUG & & LWJGLUtil . getPrivilegedBoolean ( " org.lwjgl.util.mapped.PrintTiming " ) ;
static final boolean PRINT_ACTIVITY = LWJGLUtil . DEBUG & & LWJGLUtil . getPrivilegedBoolean ( " org.lwjgl.util.mapped.PrintActivity " ) ;
2011-07-13 18:15:25 -04:00
static final boolean PRINT_BYTECODE = LWJGLUtil . DEBUG & & LWJGLUtil . getPrivilegedBoolean ( " org.lwjgl.util.mapped.PrintBytecode " ) ;
2011-07-12 09:29:04 -04:00
2011-07-12 15:29:15 -04:00
static final Map < String , MappedSubtypeInfo > className_to_subtype ;
2011-07-12 09:29:04 -04:00
2011-07-14 12:54:25 -04:00
static final String MAPPED_OBJECT_JVM = jvmClassName ( MappedObject . class ) ;
static final String MAPPED_HELPER_JVM = jvmClassName ( MappedHelper . class ) ;
2011-07-12 09:29:04 -04:00
static {
className_to_subtype = new HashMap < String , MappedSubtypeInfo > ( ) ;
{
// HACK: required for mapped.view++
//
// because the compiler generates:
// => GETFIELD MappedObject.view
// => ICONST_1
// => IADD
// => PUTFIELD MyMappedType.view
//
// instead of:
// => GETFIELD MyMappedType.view
// => ICONST_1
// => IADD
// => PUTFIELD MyMappedType.view
//
2011-07-14 12:54:25 -04:00
MappedSubtypeInfo info = new MappedSubtypeInfo ( MAPPED_OBJECT_JVM , - 1 , - 1 ) ;
2011-07-12 09:29:04 -04:00
className_to_subtype . put ( info . className , info ) ;
}
String vmName = System . getProperty ( " java.vm.name " ) ;
if ( vmName ! = null & & ! vmName . contains ( " Server " ) ) {
2011-07-12 10:21:29 -04:00
System . err . println ( " Warning: " + MappedObject . class . getSimpleName ( ) + " s have inferiour performance on Client VMs, please consider switching to a Server VM. " ) ;
2011-07-12 09:29:04 -04:00
}
}
/ * *
* Registers a class as a mapped object .
* The class must extend { @link MappedObject } and be annotated with { @link MappedField } .
*
* @param type the mapped object class .
* /
public static void register ( Class < ? > type ) {
if ( MappedObjectClassLoader . FORKED )
return ;
MappedType mapped = type . getAnnotation ( MappedType . class ) ;
if ( mapped = = null )
throw new InternalError ( " missing " + MappedType . class . getName ( ) + " annotation " ) ;
2011-07-12 15:29:15 -04:00
if ( type . getEnclosingClass ( ) ! = null & & ! Modifier . isStatic ( type . getModifiers ( ) ) )
throw new InternalError ( " only top-level or static inner classes are allowed " ) ;
2011-07-12 09:29:04 -04:00
String className = jvmClassName ( type ) ;
MappedSubtypeInfo mappedType = new MappedSubtypeInfo ( className , mapped . sizeof ( ) , mapped . align ( ) ) ;
int advancingOffset = 0 ;
for ( Field field : type . getDeclaredFields ( ) ) {
// static fields are never mapped
2011-07-12 15:29:15 -04:00
if ( Modifier . isStatic ( field . getModifiers ( ) ) )
2011-07-12 09:29:04 -04:00
continue ;
// we only support primitives and ByteBuffers
if ( ! field . getType ( ) . isPrimitive ( ) & & field . getType ( ) ! = ByteBuffer . class )
throw new InternalError ( " field ' " + className + " . " + field . getName ( ) + " ' not supported: " + field . getType ( ) ) ;
MappedField meta = field . getAnnotation ( MappedField . class ) ;
if ( meta = = null & & ! mapped . autoGenerateOffsets ( ) )
throw new InternalError ( " field ' " + className + " . " + field . getName ( ) + " ' missing annotation " + MappedField . class . getName ( ) + " : " + className ) ;
// quick hack
long byteOffset = meta = = null ? advancingOffset : meta . byteOffset ( ) ;
long byteLength ;
if ( field . getType ( ) = = long . class | | field . getType ( ) = = double . class )
byteLength = 8 ;
else if ( field . getType ( ) = = int . class | | field . getType ( ) = = float . class )
byteLength = 4 ;
else if ( field . getType ( ) = = char . class | | field . getType ( ) = = short . class )
byteLength = 2 ;
else if ( field . getType ( ) = = byte . class )
byteLength = 1 ;
else if ( field . getType ( ) = = ByteBuffer . class ) {
byteLength = meta . byteLength ( ) ;
if ( byteLength < 0 )
throw new IllegalStateException ( " invalid byte length for mapped ByteBuffer field: " + className + " . " + field . getName ( ) + " [length= " + byteLength + " ] " ) ;
} else
throw new IllegalStateException ( field . getType ( ) . getName ( ) ) ;
if ( field . getType ( ) ! = ByteBuffer . class )
if ( ( advancingOffset % byteLength ) ! = 0 )
throw new IllegalStateException ( " misaligned mapped type: " + className + " . " + field . getName ( ) ) ;
if ( PRINT_ACTIVITY )
LWJGLUtil . log ( MappedObjectTransformer . class . getSimpleName ( ) + " : " + className + " . " + field . getName ( ) + " [type= " + field . getType ( ) . getSimpleName ( ) + " , offset= " + byteOffset + " ] " ) ;
mappedType . fieldToOffset . put ( field . getName ( ) , byteOffset ) ;
mappedType . fieldToLength . put ( field . getName ( ) , byteLength ) ;
advancingOffset + = byteLength ;
}
if ( className_to_subtype . put ( className , mappedType ) ! = null ) {
throw new InternalError ( " duplicate mapped type: " + className ) ;
}
}
2011-07-12 17:30:48 -04:00
static boolean is_currently_computing_frames = false ;
static final String view_constructor_method = " _construct_view_ " ;
2011-07-12 09:29:04 -04:00
static byte [ ] transformFieldAccess ( final String className , byte [ ] bytecode ) {
2011-07-12 17:30:48 -04:00
int flags = ClassWriter . COMPUTE_FRAMES ;
2011-07-12 09:29:04 -04:00
2011-07-12 17:30:48 -04:00
ClassWriter writer = new ClassWriter ( flags ) {
// HACK: prevent user-code static-initialization-blocks to be executed
@Override
protected String getCommonSuperClass ( String a , String b ) {
if ( is_currently_computing_frames )
if ( ! a . startsWith ( " java/ " ) | | ! b . startsWith ( " java/ " ) )
return " java/lang/Object " ;
return super . getCommonSuperClass ( a , b ) ;
}
} ;
2011-07-12 09:29:04 -04:00
ClassAdapter adapter = new ClassAdapter ( writer ) {
@Override
public MethodVisitor visitMethod ( int access , String name , String desc , String signature , String [ ] exceptions ) {
{
MappedSubtypeInfo mappedSubtype = className_to_subtype . get ( className ) ;
2011-07-14 12:54:25 -04:00
if ( mappedSubtype ! = null & & ! mappedSubtype . className . equals ( MAPPED_OBJECT_JVM ) ) {
2011-07-12 09:29:04 -04:00
if ( " <init> " . equals ( name ) ) {
if ( ! " ()V " . equals ( desc ) )
throw new IllegalStateException ( className + " can only have a default constructor, found: " + desc ) ;
MethodVisitor mv = super . visitMethod ( access , name , desc , signature , exceptions ) ;
mv . visitCode ( ) ;
mv . visitVarInsn ( ALOAD , 0 ) ;
2011-07-14 12:54:25 -04:00
mv . visitMethodInsn ( INVOKESPECIAL , MAPPED_OBJECT_JVM , " <init> " , " ()V " ) ;
2011-07-12 09:29:04 -04:00
mv . visitInsn ( RETURN ) ;
mv . visitMaxs ( 1 , 1 ) ;
mv . visitEnd ( ) ;
// put the method body in another method
name = view_constructor_method ;
}
}
}
2011-07-14 19:11:41 -04:00
return new MappedInstanceMethodAdapter ( className , name , desc , super . visitMethod ( access , name , desc , signature , exceptions ) ) ;
2011-07-12 09:29:04 -04:00
}
@Override
public FieldVisitor visitField ( int access , String fieldName , String typeName , String dunno , Object value ) {
// remove redirected fields
MappedSubtypeInfo mappedSubtype = className_to_subtype . get ( className ) ;
if ( mappedSubtype ! = null & & mappedSubtype . fieldToOffset . containsKey ( fieldName ) ) {
if ( PRINT_ACTIVITY )
LWJGLUtil . log ( MappedObjectTransformer . class . getSimpleName ( ) + " : discarding field: " + className + " . " + fieldName + " : " + typeName ) ;
return null ;
}
return super . visitField ( access , fieldName , typeName , dunno , value ) ;
}
} ;
new ClassReader ( bytecode ) . accept ( adapter , 0 ) ;
bytecode = writer . toByteArray ( ) ;
if ( PRINT_BYTECODE )
2011-07-12 17:30:48 -04:00
printBytecode ( bytecode ) ;
2011-07-12 09:29:04 -04:00
return bytecode ;
}
2011-07-12 17:30:48 -04:00
private static void printBytecode ( byte [ ] bytecode ) {
2011-07-12 09:29:04 -04:00
StringWriter sw = new StringWriter ( ) ;
2011-07-12 17:30:48 -04:00
ClassVisitor tracer = new TraceClassVisitor ( new ClassWriter ( 0 ) , new PrintWriter ( sw ) ) ;
2011-07-12 09:29:04 -04:00
new ClassReader ( bytecode ) . accept ( tracer , 0 ) ;
String dump = sw . toString ( ) ;
LWJGLUtil . log ( dump ) ;
}
private static class MappedInstanceMethodAdapter extends MethodAdapter {
private final String className ;
private final String methodName ;
private final String descr ;
MappedInstanceMethodAdapter ( String className , String methodName , String descr , MethodVisitor backing ) {
super ( backing ) ;
this . className = className ;
this . methodName = methodName ;
this . descr = descr ;
}
@Override
public void visitTypeInsn ( int opcode , String typeName ) {
if ( opcode = = NEW & & className_to_subtype . containsKey ( typeName ) ) {
throw new IllegalAccessError ( " must not manually create instances of " + typeName + " in method " + this . className + " . " + this . methodName + this . descr ) ;
}
super . visitTypeInsn ( opcode , typeName ) ;
}
2011-07-12 17:30:48 -04:00
2011-07-12 14:30:56 -04:00
@Override
2011-07-12 17:30:48 -04:00
public void visitMaxs ( int a , int b ) {
try {
is_currently_computing_frames = true ;
super . visitMaxs ( a , b ) ;
} finally {
is_currently_computing_frames = false ;
}
2011-07-12 14:30:56 -04:00
}
2011-07-12 09:29:04 -04:00
@Override
public void visitMethodInsn ( int opcode , String className , String methodName , String signature ) {
2011-07-14 12:54:25 -04:00
if ( opcode = = INVOKESPECIAL & & className . equals ( MAPPED_OBJECT_JVM ) & & " <init> " . equals ( methodName ) & & " ()V " . equals ( signature ) ) {
2011-07-12 09:29:04 -04:00
// stack: instance
visitInsn ( POP ) ;
// stack: -
return ;
}
2011-07-14 12:54:25 -04:00
MappedSubtypeInfo mappedType = className_to_subtype . get ( className ) ;
if ( mappedType ! = null & & visitMappedMethod ( opcode , className , methodName , signature , mappedType ) )
return ;
super . visitMethodInsn ( opcode , className , methodName , signature ) ;
}
private boolean visitMappedMethod ( final int opcode , final String className , final String methodName , final String signature , final MappedSubtypeInfo mappedType ) {
if ( opcode = = INVOKESTATIC ) {
boolean isMapDirectMethod = " map " . equals ( methodName ) & & signature . equals ( " (JI)L " + MAPPED_OBJECT_JVM + " ; " ) ;
boolean isMapBufferMethod = " map " . equals ( methodName ) & & signature . equals ( " (Ljava/nio/ByteBuffer;)L " + MAPPED_OBJECT_JVM + " ; " ) ;
boolean isMallocMethod = " malloc " . equals ( methodName ) & & signature . equals ( " (I)L " + MAPPED_OBJECT_JVM + " ; " ) ;
2011-07-12 09:29:04 -04:00
if ( ( isMapDirectMethod | | isMapBufferMethod ) | | isMallocMethod ) {
if ( isMallocMethod ) {
2011-07-12 17:30:48 -04:00
// stack: count
2011-07-12 09:29:04 -04:00
pushInt ( super . mv , mappedType . sizeof ) ;
// stack: sizeof, count
super . visitInsn ( IMUL ) ;
// stack: bytes
super . visitMethodInsn ( INVOKESTATIC , jvmClassName ( ByteBuffer . class ) , " allocateDirect " , " (I)L " + jvmClassName ( ByteBuffer . class ) + " ; " ) ;
// stack: buffer
} else if ( isMapDirectMethod ) {
// stack: capacity, address
2011-07-14 12:54:25 -04:00
super . visitMethodInsn ( INVOKESTATIC , MAPPED_HELPER_JVM , " newBuffer " , " (JI)L " + jvmClassName ( ByteBuffer . class ) + " ; " ) ;
2011-07-12 09:29:04 -04:00
// stack: buffer
}
// stack: buffer
super . visitTypeInsn ( NEW , className ) ;
// stack: new, buffer
super . visitInsn ( DUP ) ;
// stack: new, new, buffer
super . visitMethodInsn ( INVOKESPECIAL , className , " <init> " , " ()V " ) ;
// stack: new, buffer
super . visitInsn ( DUP_X1 ) ;
// stack: new, buffer, new
super . visitInsn ( SWAP ) ;
// stack: buffer, new, new
pushInt ( super . mv , mappedType . align ) ;
// stack: int, buffer, new, new
pushInt ( super . mv , mappedType . sizeof ) ;
// stack: int, int, buffer, new, new
2011-07-14 12:54:25 -04:00
super . visitMethodInsn ( INVOKESTATIC , MAPPED_HELPER_JVM , " setup " , " (L " + MAPPED_OBJECT_JVM + " ;Ljava/nio/ByteBuffer;II)V " ) ;
2011-07-12 09:29:04 -04:00
// stack: new
2011-07-14 12:54:25 -04:00
return true ;
2011-07-12 09:29:04 -04:00
}
2011-07-14 12:54:25 -04:00
} else if ( opcode = = INVOKEVIRTUAL ) {
if ( " dup " . equals ( methodName ) & & signature . equals ( " ()L " + MAPPED_OBJECT_JVM + " ; " ) ) {
2011-07-12 09:29:04 -04:00
// stack: this
super . visitTypeInsn ( NEW , className ) ;
// stack: new, this
super . visitInsn ( DUP ) ;
// stack: new, new, this
super . visitMethodInsn ( INVOKESPECIAL , className , " <init> " , " ()V " ) ;
// stack: new, this
2011-07-14 12:54:25 -04:00
super . visitMethodInsn ( INVOKESTATIC , MAPPED_HELPER_JVM , " dup " , " (L " + MAPPED_OBJECT_JVM + " ;L " + MAPPED_OBJECT_JVM + " ;)L " + MAPPED_OBJECT_JVM + " ; " ) ;
2011-07-12 09:29:04 -04:00
// stack: new
2011-07-14 12:54:25 -04:00
return true ;
2011-07-12 09:29:04 -04:00
}
2011-07-14 12:54:25 -04:00
if ( " slice " . equals ( methodName ) & & signature . equals ( " ()L " + MAPPED_OBJECT_JVM + " ; " ) ) {
2011-07-12 09:29:04 -04:00
// stack: this
super . visitTypeInsn ( NEW , className ) ;
// stack: new, this
super . visitInsn ( DUP ) ;
// stack: new, new, this
super . visitMethodInsn ( INVOKESPECIAL , className , " <init> " , " ()V " ) ;
// stack: new, this
2011-07-14 12:54:25 -04:00
super . visitMethodInsn ( INVOKESTATIC , MAPPED_HELPER_JVM , " slice " , " (L " + MAPPED_OBJECT_JVM + " ;L " + MAPPED_OBJECT_JVM + " ;)L " + MAPPED_OBJECT_JVM + " ; " ) ;
2011-07-12 09:29:04 -04:00
// stack: new
2011-07-14 12:54:25 -04:00
return true ;
2011-07-12 09:29:04 -04:00
}
//
2011-07-14 12:54:25 -04:00
if ( " runViewConstructor " . equals ( methodName ) & & " ()V " . equals ( signature ) ) {
2011-07-12 09:29:04 -04:00
// stack: this
super . visitInsn ( DUP ) ;
// stack: this, this
super . visitMethodInsn ( INVOKEVIRTUAL , className , view_constructor_method , " ()V " ) ;
// stack: this
2011-07-14 12:54:25 -04:00
return true ;
2011-07-12 09:29:04 -04:00
}
//
2011-07-14 12:54:25 -04:00
if ( " copyTo " . equals ( methodName ) & & signature . equals ( " (L " + MAPPED_OBJECT_JVM + " ;)V " ) ) {
2011-07-12 09:29:04 -04:00
// stack: target, this
pushInt ( super . mv , mappedType . sizeof ) ;
// stack: sizeof, target, this
2011-07-14 12:54:25 -04:00
super . visitMethodInsn ( INVOKESTATIC , MAPPED_HELPER_JVM , " copy " , " (L " + MAPPED_OBJECT_JVM + " ;L " + MAPPED_OBJECT_JVM + " ;I)V " ) ;
2011-07-12 09:29:04 -04:00
// stack: -
2011-07-14 12:54:25 -04:00
return true ;
2011-07-12 09:29:04 -04:00
}
2011-07-14 12:54:25 -04:00
if ( " copyRange " . equals ( methodName ) & & signature . equals ( " (L " + MAPPED_OBJECT_JVM + " ;I)V " ) ) {
2011-07-12 09:29:04 -04:00
// stack: instances, target, this
pushInt ( super . mv , mappedType . sizeof ) ;
// stack: sizeof, instances, target, this
super . visitInsn ( IMUL ) ;
// stack: bytes, target, this
2011-07-14 12:54:25 -04:00
super . visitMethodInsn ( INVOKESTATIC , MAPPED_HELPER_JVM , " copy " , " (L " + MAPPED_OBJECT_JVM + " ;L " + MAPPED_OBJECT_JVM + " ;I)V " ) ;
2011-07-12 09:29:04 -04:00
// stack: -
2011-07-14 12:54:25 -04:00
return true ;
}
if ( " next " . equals ( methodName ) & & " ()V " . equals ( signature ) ) {
// stack: this
pushInt ( super . mv , mappedType . sizeof ) ;
// stack: sizeof, this
super . visitMethodInsn ( INVOKESTATIC , MAPPED_HELPER_JVM , " put_view_next " , " (L " + MAPPED_OBJECT_JVM + " ;I)V " ) ;
// stack: -
return true ;
2011-07-12 09:29:04 -04:00
}
}
2011-07-14 12:54:25 -04:00
return false ;
2011-07-12 09:29:04 -04:00
}
private static void throwAccessErrorOnReadOnlyField ( String className , String fieldName ) {
throw new IllegalAccessError ( " field ' " + className + " . " + fieldName + " ' is final " ) ;
}
@Override
public void visitFieldInsn ( int opcode , String className , String fieldName , String typeName ) {
MappedSubtypeInfo mappedSubtype = className_to_subtype . get ( className ) ;
if ( mappedSubtype = = null ) {
String mappedSetPrefix = jvmClassName ( MappedSet . class ) ;
2011-07-13 18:15:25 -04:00
// MappedSet.view
2011-07-12 09:29:04 -04:00
outer :
if ( " view " . equals ( fieldName ) & & className . startsWith ( mappedSetPrefix ) ) {
if ( opcode = = GETFIELD )
throwAccessErrorOnReadOnlyField ( className , fieldName ) ;
if ( opcode ! = PUTFIELD )
break outer ;
// stack: index, this
if ( false )
break outer ;
else if ( className . equals ( jvmClassName ( MappedSet2 . class ) ) )
2011-07-14 12:54:25 -04:00
super . visitMethodInsn ( INVOKESTATIC , MAPPED_HELPER_JVM , " put_views " , " (L " + jvmClassName ( MappedSet2 . class ) + " ;I)V " ) ;
2011-07-12 09:29:04 -04:00
else if ( className . equals ( jvmClassName ( MappedSet3 . class ) ) )
2011-07-14 12:54:25 -04:00
super . visitMethodInsn ( INVOKESTATIC , MAPPED_HELPER_JVM , " put_views " , " (L " + jvmClassName ( MappedSet3 . class ) + " ;I)V " ) ;
2011-07-12 09:29:04 -04:00
else if ( className . equals ( jvmClassName ( MappedSet4 . class ) ) )
2011-07-14 12:54:25 -04:00
super . visitMethodInsn ( INVOKESTATIC , MAPPED_HELPER_JVM , " put_views " , " (L " + jvmClassName ( MappedSet4 . class ) + " ;I)V " ) ;
2011-07-12 09:29:04 -04:00
else
break outer ;
// stack: -
return ;
}
// early out
super . visitFieldInsn ( opcode , className , fieldName , typeName ) ;
return ;
}
if ( " SIZEOF " . equals ( fieldName ) ) {
if ( ! " I " . equals ( typeName ) )
throw new IllegalStateException ( ) ;
if ( opcode = = GETSTATIC ) {
pushInt ( super . mv , mappedSubtype . sizeof ) ;
return ;
}
if ( opcode = = PUTSTATIC ) {
throwAccessErrorOnReadOnlyField ( className , fieldName ) ;
}
}
if ( " view " . equals ( fieldName ) ) {
if ( ! " I " . equals ( typeName ) )
throw new IllegalStateException ( ) ;
if ( opcode = = GETFIELD ) {
// stack: instance
2011-07-13 18:15:25 -04:00
pushInt ( super . mv , mappedSubtype . sizeof ) ;
// stack: sizeof, instance
2011-07-14 12:54:25 -04:00
super . visitMethodInsn ( INVOKESTATIC , MAPPED_HELPER_JVM , " get_view " , " (L " + MAPPED_OBJECT_JVM + " ;I)I " ) ;
2011-07-13 18:15:25 -04:00
// stack: view
2011-07-12 09:29:04 -04:00
return ;
}
if ( opcode = = PUTFIELD ) {
2011-07-13 18:15:25 -04:00
// stack: view, instance
pushInt ( super . mv , mappedSubtype . sizeof ) ;
// stack: sizeof, view, instance
2011-07-14 12:54:25 -04:00
super . visitMethodInsn ( INVOKESTATIC , MAPPED_HELPER_JVM , " put_view " , " (L " + MAPPED_OBJECT_JVM + " ;II)V " ) ;
2011-07-13 18:15:25 -04:00
// stack: -
2011-07-12 09:29:04 -04:00
return ;
}
}
2011-07-13 18:15:25 -04:00
if ( " align " . equals ( fieldName ) | | " sizeof " . equals ( fieldName ) ) {
2011-07-12 09:29:04 -04:00
if ( ! " I " . equals ( typeName ) )
throw new IllegalStateException ( ) ;
if ( opcode = = GETFIELD ) {
// stack: instance
super . visitInsn ( POP ) ;
// stack: -
2011-07-13 18:15:25 -04:00
if ( " sizeof " . equals ( fieldName ) )
pushInt ( super . mv , mappedSubtype . sizeof ) ;
else if ( " align " . equals ( fieldName ) )
pushInt ( super . mv , mappedSubtype . align ) ;
2011-07-12 09:29:04 -04:00
// stack: int
return ;
}
if ( opcode = = PUTFIELD ) {
throwAccessErrorOnReadOnlyField ( className , fieldName ) ;
}
}
if ( " baseAddress " . equals ( fieldName ) | | " viewAddress " . equals ( fieldName ) ) {
if ( ! " J " . equals ( typeName ) )
throw new IllegalStateException ( ) ;
if ( opcode = = GETFIELD ) {
// do not change a thing
}
if ( opcode = = PUTFIELD ) {
throwAccessErrorOnReadOnlyField ( className , fieldName ) ;
}
}
Long fieldOffset = mappedSubtype . fieldToOffset . get ( fieldName ) ;
if ( fieldOffset = = null ) {
// early out
super . visitFieldInsn ( opcode , className , fieldName , typeName ) ;
return ;
}
2011-07-13 18:15:25 -04:00
// now we're going to transform ByteBuffer-typed field access
2011-07-12 09:29:04 -04:00
if ( typeName . equals ( " L " + jvmClassName ( ByteBuffer . class ) + " ; " ) ) {
if ( opcode = = PUTFIELD ) {
throwAccessErrorOnReadOnlyField ( className , fieldName ) ;
}
if ( opcode = = GETFIELD ) {
Long fieldLength = mappedSubtype . fieldToLength . get ( fieldName ) ;
2011-07-12 14:30:56 -04:00
// stack: ref
2011-07-12 09:29:04 -04:00
super . visitFieldInsn ( GETFIELD , mappedSubtype . className , " viewAddress " , " J " ) ;
2011-07-12 14:30:56 -04:00
// stack: long
2011-07-12 09:29:04 -04:00
super . visitLdcInsn ( fieldOffset ) ;
2011-07-12 14:30:56 -04:00
// stack: long, long
2011-07-12 09:29:04 -04:00
super . visitInsn ( LADD ) ;
2011-07-12 14:30:56 -04:00
// stack: long
2011-07-12 09:29:04 -04:00
super . visitLdcInsn ( fieldLength ) ;
2011-07-12 14:30:56 -04:00
// stack: long, long
2011-07-12 09:29:04 -04:00
super . visitInsn ( L2I ) ;
2011-07-12 14:30:56 -04:00
// stack: int, long
2011-07-14 12:54:25 -04:00
super . visitMethodInsn ( INVOKESTATIC , MAPPED_HELPER_JVM , " newBuffer " , " (JI)L " + jvmClassName ( ByteBuffer . class ) + " ; " ) ;
2011-07-12 14:30:56 -04:00
// stack: buffer
2011-07-12 09:29:04 -04:00
return ;
}
}
2011-07-13 18:15:25 -04:00
// we're now going to transform the field access
2011-07-12 09:29:04 -04:00
if ( opcode = = PUTFIELD ) {
2011-07-12 17:30:48 -04:00
// stack: value, ref
2011-07-12 09:29:04 -04:00
super . visitInsn ( SWAP ) ;
2011-07-12 14:30:56 -04:00
// stack: ref, value
2011-07-12 09:29:04 -04:00
super . visitFieldInsn ( GETFIELD , mappedSubtype . className , " viewAddress " , " J " ) ;
2011-07-13 18:15:25 -04:00
// stack: viewAddr, value
2011-07-12 09:29:04 -04:00
super . visitLdcInsn ( fieldOffset ) ;
2011-07-13 18:15:25 -04:00
// stack: offset, viewAddr, value
2011-07-12 09:29:04 -04:00
super . visitInsn ( LADD ) ;
2011-07-13 18:15:25 -04:00
// stack: fieldAddr, value
2011-07-14 12:54:25 -04:00
super . visitMethodInsn ( INVOKESTATIC , MAPPED_HELPER_JVM , typeName . toLowerCase ( ) + " put " , " ( " + typeName + " J)V " ) ;
2011-07-12 14:30:56 -04:00
// stack -
2011-07-13 18:15:25 -04:00
2011-07-12 09:29:04 -04:00
return ;
}
if ( opcode = = GETFIELD ) {
2011-07-12 17:30:48 -04:00
// stack: ref
2011-07-12 09:29:04 -04:00
super . visitFieldInsn ( GETFIELD , mappedSubtype . className , " viewAddress " , " J " ) ;
2011-07-13 18:15:25 -04:00
// stack: viewAddr
2011-07-12 09:29:04 -04:00
super . visitLdcInsn ( fieldOffset ) ;
2011-07-13 18:15:25 -04:00
// stack: fieldOffset, viewAddr
2011-07-12 09:29:04 -04:00
super . visitInsn ( LADD ) ;
2011-07-13 18:15:25 -04:00
// stack: fieldAddr
2011-07-14 12:54:25 -04:00
super . visitMethodInsn ( INVOKESTATIC , MAPPED_HELPER_JVM , typeName . toLowerCase ( ) + " get " , " (J) " + typeName ) ;
2011-07-12 14:30:56 -04:00
// stack: value
2011-07-13 18:15:25 -04:00
2011-07-12 09:29:04 -04:00
return ;
}
// original field access
super . visitFieldInsn ( opcode , className , fieldName , typeName ) ;
return ;
}
}
2011-07-12 15:29:15 -04:00
static void pushInt ( MethodVisitor mv , int value ) {
2011-07-12 09:29:04 -04:00
if ( value = = - 1 )
mv . visitInsn ( ICONST_M1 ) ;
else if ( value = = 0 )
mv . visitInsn ( ICONST_0 ) ;
else if ( value = = 1 )
mv . visitInsn ( ICONST_1 ) ;
else if ( value = = 2 )
mv . visitInsn ( ICONST_2 ) ;
else if ( value = = 3 )
mv . visitInsn ( ICONST_3 ) ;
else if ( value = = 4 )
mv . visitInsn ( ICONST_4 ) ;
else if ( value = = 5 )
mv . visitInsn ( ICONST_5 ) ;
else if ( value > = Byte . MIN_VALUE & & value < = Byte . MAX_VALUE )
mv . visitIntInsn ( BIPUSH , value ) ;
else if ( value > = Short . MIN_VALUE & & value < = Short . MAX_VALUE )
mv . visitIntInsn ( SIPUSH , value ) ;
else
2011-07-13 18:15:25 -04:00
mv . visitLdcInsn ( value ) ;
2011-07-12 09:29:04 -04:00
}
2011-07-12 15:29:15 -04:00
static String jvmClassName ( Class < ? > type ) {
2011-07-12 09:29:04 -04:00
return type . getName ( ) . replace ( '.' , '/' ) ;
}
private static class MappedSubtypeInfo {
public final String className ;
public int sizeof ;
public int align ;
public Map < String , Long > fieldToOffset ;
public Map < String , Long > fieldToLength ;
MappedSubtypeInfo ( String className , int sizeof , int align ) {
this . className = className ;
this . sizeof = sizeof ;
this . align = align ;
this . fieldToOffset = new HashMap < String , Long > ( ) ;
this . fieldToLength = new HashMap < String , Long > ( ) ;
}
}
2011-07-11 17:46:00 -04:00
2011-07-12 17:30:48 -04:00
public static String exportConfiguration ( ) {
StringBuilder sb = new StringBuilder ( ) ;
for ( MappedSubtypeInfo info : className_to_subtype . values ( ) ) {
sb . append ( " class \ t " + info . className + " \ t " + info . sizeof + " \ t " + info . align + " \ r \ n " ) ;
for ( String fieldName : info . fieldToOffset . keySet ( ) ) {
sb . append ( " field \ t " + info . className + " \ t " + fieldName + " \ t " + info . fieldToOffset . get ( fieldName ) + " \ t " + info . fieldToLength . get ( fieldName ) + " \ r \ n " ) ;
}
}
className_to_subtype . clear ( ) ;
return sb . toString ( ) ;
}
public static void importConfigation ( String input ) {
className_to_subtype . clear ( ) ;
try {
BufferedReader br = new BufferedReader ( new StringReader ( input ) ) ;
while ( true ) {
String line = br . readLine ( ) ;
if ( line = = null )
break ;
if ( ( line = line . trim ( ) ) . isEmpty ( ) )
continue ;
StringTokenizer st = new StringTokenizer ( line , " \ t " ) ;
String type = st . nextToken ( ) ;
if ( type . equals ( " class " ) ) {
String className = st . nextToken ( ) ;
int sizeof = Integer . parseInt ( st . nextToken ( ) ) ;
int align = Integer . parseInt ( st . nextToken ( ) ) ;
className_to_subtype . put ( className , new MappedSubtypeInfo ( className , sizeof , align ) ) ;
} else if ( type . equals ( " field " ) ) {
MappedObjectTransformer . MappedSubtypeInfo info = className_to_subtype . get ( st . nextToken ( ) ) ;
String methodName = st . nextToken ( ) ;
int off = Integer . parseInt ( st . nextToken ( ) ) ;
int len = Integer . parseInt ( st . nextToken ( ) ) ;
info . fieldToOffset . put ( methodName , Long . valueOf ( off ) ) ;
info . fieldToLength . put ( methodName , Long . valueOf ( len ) ) ;
} else {
throw new IllegalStateException ( type ) ;
}
}
} catch ( IOException exc ) {
throw new IllegalStateException ( " never happens " ) ;
}
}
2011-07-11 17:46:00 -04:00
}