2011-07-11 17:46:00 -04:00
/ *
2011-07-21 22:01:56 -04:00
* Copyright ( c ) 2002 - 2011 LWJGL Project
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are
* met :
*
* * Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
*
* * Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
*
* * Neither the name of ' LWJGL ' nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* " AS IS " AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED
* TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL ,
* EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO ,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR
* PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING
* NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
2011-07-11 17:46:00 -04:00
* /
package org.lwjgl.util.mapped ;
2011-08-10 12:05:46 -04:00
import org.lwjgl.BufferUtils ;
2011-07-12 09:29:04 -04:00
import org.lwjgl.LWJGLUtil ;
2011-08-02 19:45:19 -04:00
import org.lwjgl.MemoryUtil ;
2011-07-12 09:29:04 -04:00
import org.objectweb.asm.* ;
2011-07-21 22:01:56 -04:00
import org.objectweb.asm.tree.* ;
import org.objectweb.asm.tree.analysis.* ;
2011-07-22 16:09:01 -04:00
import org.objectweb.asm.tree.analysis.Frame ;
2011-07-12 09:29:04 -04:00
import org.objectweb.asm.util.TraceClassVisitor ;
2011-07-21 22:01:56 -04:00
import java.io.PrintWriter ;
import java.io.StringWriter ;
2011-07-11 17:46:00 -04:00
import java.lang.reflect.Field ;
import java.lang.reflect.Modifier ;
2011-08-02 19:45:19 -04:00
import java.nio.Buffer ;
2011-07-11 17:46:00 -04:00
import java.nio.ByteBuffer ;
import java.util.HashMap ;
import java.util.Map ;
2011-07-21 22:01:56 -04:00
import static org.objectweb.asm.ClassWriter.* ;
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 / >
2011-07-21 22:01:56 -04:00
* org . lwjgl . util . mapped . PrintBytecode = true , prints the transformed bytecode . < br / >
2011-07-12 09:29:04 -04:00
* 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_ACTIVITY = LWJGLUtil . DEBUG & & LWJGLUtil . getPrivilegedBoolean ( " org.lwjgl.util.mapped.PrintActivity " ) ;
2011-07-23 18:02:01 -04:00
static final boolean PRINT_TIMING = PRINT_ACTIVITY & & LWJGLUtil . getPrivilegedBoolean ( " org.lwjgl.util.mapped.PrintTiming " ) ;
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-21 22:01:56 -04:00
static final String MAPPEDSET_PREFIX = jvmClassName ( MappedSet . class ) ;
static final String MAPPED_SET2_JVM = jvmClassName ( MappedSet2 . class ) ;
static final String MAPPED_SET3_JVM = jvmClassName ( MappedSet3 . class ) ;
static final String MAPPED_SET4_JVM = jvmClassName ( MappedSet4 . class ) ;
2011-08-10 12:05:46 -04:00
static final String CACHE_LINE_PAD_JVM = " L " + jvmClassName ( CacheLinePad . class ) + " ; " ;
2011-07-22 16:09:01 -04:00
// Public methods
static final String VIEWADDRESS_METHOD_NAME = " getViewAddress " ;
static final String NEXT_METHOD_NAME = " next " ;
static final String ALIGN_METHOD_NAME = " getAlign " ;
static final String SIZEOF_METHOD_NAME = " getSizeof " ;
2011-08-02 19:45:19 -04:00
static final String CAPACITY_METHOD_NAME = " capacity " ; // Used for .asArray().length
2011-07-22 16:09:01 -04:00
// Internal methods
static final String VIEW_CONSTRUCTOR_NAME = " constructView$LWJGL " ; // Used by runViewConstructor
2011-07-21 22:01:56 -04:00
static final Map < Integer , String > OPCODE_TO_NAME = new HashMap < Integer , String > ( ) ;
static final Map < Integer , String > INSNTYPE_TO_NAME = new HashMap < Integer , String > ( ) ;
static boolean is_currently_computing_frames ;
2011-07-12 09:29:04 -04:00
static {
2011-07-21 22:01:56 -04:00
getClassEnums ( Opcodes . class , OPCODE_TO_NAME , " V1_ " , " ACC_ " , " T_ " , " F_ " , " MH_ " ) ;
getClassEnums ( AbstractInsnNode . class , INSNTYPE_TO_NAME ) ;
2011-07-12 09:29:04 -04:00
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-08-10 12:05:46 -04:00
className_to_subtype . put ( MAPPED_OBJECT_JVM , new MappedSubtypeInfo ( MAPPED_OBJECT_JVM , null , - 1 , - 1 , - 1 , false ) ) ;
2011-07-12 09:29:04 -04:00
}
2011-07-21 22:01:56 -04:00
final String vmName = System . getProperty ( " java.vm.name " ) ;
2011-07-12 09:29:04 -04:00
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 .
2011-07-21 22:01:56 -04:00
* The class must extend { @link org . lwjgl . util . mapped . MappedObject } and be annotated with { @link org . lwjgl . util . mapped . MappedField } .
2011-07-12 09:29:04 -04:00
*
* @param type the mapped object class .
* /
2011-07-24 05:38:46 -04:00
public static void register ( Class < ? extends MappedObject > type ) {
2011-07-23 18:02:01 -04:00
if ( MappedObjectClassLoader . FORKED )
return ;
2011-07-21 22:01:56 -04:00
final MappedType mapped = type . getAnnotation ( MappedType . class ) ;
2011-07-23 18:02:01 -04:00
2011-07-24 05:38:46 -04:00
if ( mapped ! = null & & mapped . padding ( ) < 0 )
2011-07-23 18:02:01 -04:00
throw new ClassFormatError ( " Invalid mapped type padding: " + mapped . padding ( ) ) ;
2011-07-12 09:29:04 -04:00
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-23 18:02:01 -04:00
final String className = jvmClassName ( type ) ;
final Map < String , FieldInfo > fields = new HashMap < String , FieldInfo > ( ) ;
2011-07-12 09:29:04 -04:00
2011-07-23 18:02:01 -04:00
long sizeof = 0 ;
for ( Field field : type . getDeclaredFields ( ) ) {
2011-08-10 12:05:46 -04:00
FieldInfo fieldInfo = registerField ( mapped = = null | | mapped . autoGenerateOffsets ( ) , className , sizeof , field ) ;
2011-07-23 18:02:01 -04:00
if ( fieldInfo = = null )
continue ;
fields . put ( field . getName ( ) , fieldInfo ) ;
2011-07-12 09:29:04 -04:00
2011-08-10 12:05:46 -04:00
sizeof = Math . max ( sizeof , fieldInfo . offset + fieldInfo . lengthPadded ) ;
2011-07-23 18:02:01 -04:00
}
2011-08-10 12:05:46 -04:00
int align = 4 ;
int padding = 0 ;
boolean cacheLinePadded = false ;
if ( mapped ! = null ) {
align = mapped . align ( ) ;
if ( mapped . cacheLinePadding ( ) ) {
if ( mapped . padding ( ) ! = 0 )
throw new ClassFormatError ( " Mapped type padding cannot be specified together with cacheLinePadding. " ) ;
final int cacheLineMod = ( int ) ( sizeof % CacheUtil . getCacheLineSize ( ) ) ;
if ( cacheLineMod ! = 0 )
padding = CacheUtil . getCacheLineSize ( ) - cacheLineMod ;
cacheLinePadded = true ;
} else
padding = mapped . padding ( ) ;
}
2011-07-23 18:02:01 -04:00
2011-07-24 05:38:46 -04:00
sizeof + = padding ;
2011-08-10 12:05:46 -04:00
final MappedSubtypeInfo mappedType = new MappedSubtypeInfo ( className , fields , ( int ) sizeof , align , padding , cacheLinePadded ) ;
2011-07-23 18:02:01 -04:00
if ( className_to_subtype . put ( className , mappedType ) ! = null )
2011-07-21 22:01:56 -04:00
throw new InternalError ( " duplicate mapped type: " + mappedType . className ) ;
2011-07-12 09:29:04 -04:00
}
2011-08-10 12:05:46 -04:00
private static FieldInfo registerField ( final boolean autoGenerateOffsets , final String className , long advancingOffset , final Field field ) {
2011-07-21 22:01:56 -04:00
if ( Modifier . isStatic ( field . getModifiers ( ) ) ) // static fields are never mapped
2011-07-23 18:02:01 -04:00
return null ;
2011-07-21 22:01:56 -04:00
// we only support primitives and ByteBuffers
if ( ! field . getType ( ) . isPrimitive ( ) & & field . getType ( ) ! = ByteBuffer . class )
2011-07-23 18:02:01 -04:00
throw new ClassFormatError ( " field ' " + className + " . " + field . getName ( ) + " ' not supported: " + field . getType ( ) ) ;
2011-07-21 22:01:56 -04:00
MappedField meta = field . getAnnotation ( MappedField . class ) ;
2011-07-24 05:38:46 -04:00
if ( meta = = null & & ! autoGenerateOffsets )
2011-07-23 18:02:01 -04:00
throw new ClassFormatError ( " field ' " + className + " . " + field . getName ( ) + " ' missing annotation " + MappedField . class . getName ( ) + " : " + className ) ;
2011-07-21 22:01:56 -04:00
2011-07-23 18:02:01 -04:00
Pointer pointer = field . getAnnotation ( Pointer . class ) ;
if ( pointer ! = null & & field . getType ( ) ! = long . class )
2011-07-23 18:28:57 -04:00
throw new ClassFormatError ( " The @Pointer annotation can only be used on long fields. @Pointer field found: " + className + " . " + field . getName ( ) + " : " + field . getType ( ) ) ;
if ( Modifier . isVolatile ( field . getModifiers ( ) ) & & ( pointer ! = null | | field . getType ( ) = = ByteBuffer . class ) )
throw new ClassFormatError ( " The volatile keyword is not supported for @Pointer or ByteBuffer fields. Volatile field found: " + className + " . " + field . getName ( ) + " : " + field . getType ( ) ) ;
2011-07-21 22:01:56 -04:00
// quick hack
long byteLength ;
2011-07-23 18:02:01 -04:00
if ( field . getType ( ) = = long . class | | field . getType ( ) = = double . class ) {
if ( pointer = = null )
byteLength = 8 ;
else
byteLength = MappedObjectUnsafe . INSTANCE . addressSize ( ) ;
} else if ( field . getType ( ) = = double . class )
2011-07-21 22:01:56 -04:00
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
2011-07-23 18:02:01 -04:00
throw new ClassFormatError ( field . getType ( ) . getName ( ) ) ;
2011-07-21 22:01:56 -04:00
if ( field . getType ( ) ! = ByteBuffer . class & & ( advancingOffset % byteLength ) ! = 0 )
throw new IllegalStateException ( " misaligned mapped type: " + className + " . " + field . getName ( ) ) ;
2011-08-10 12:05:46 -04:00
CacheLinePad pad = field . getAnnotation ( CacheLinePad . class ) ;
long byteOffset = advancingOffset ;
if ( meta ! = null & & meta . byteOffset ( ) ! = - 1 ) {
if ( meta . byteOffset ( ) < 0 )
throw new ClassFormatError ( " Invalid field byte offset: " + className + " . " + field . getName ( ) + " [byteOffset= " + meta . byteOffset ( ) + " ] " ) ;
if ( pad ! = null )
throw new ClassFormatError ( " A field byte offset cannot be specified together with cache-line padding: " + className + " . " + field . getName ( ) ) ;
byteOffset = meta . byteOffset ( ) ;
}
long byteLengthPadded = byteLength ;
if ( pad ! = null ) {
// Pad before
if ( pad . before ( ) & & byteOffset % CacheUtil . getCacheLineSize ( ) ! = 0 )
byteOffset + = CacheUtil . getCacheLineSize ( ) - ( byteOffset & ( CacheUtil . getCacheLineSize ( ) - 1 ) ) ;
// Pad after
if ( pad . after ( ) & & ( byteOffset + byteLength ) % CacheUtil . getCacheLineSize ( ) ! = 0 )
byteLengthPadded + = CacheUtil . getCacheLineSize ( ) - ( byteOffset + byteLength ) % CacheUtil . getCacheLineSize ( ) ;
assert ! pad . before ( ) | | ( byteOffset % CacheUtil . getCacheLineSize ( ) = = 0 ) ;
assert ! pad . after ( ) | | ( ( byteOffset + byteLengthPadded ) % CacheUtil . getCacheLineSize ( ) = = 0 ) ;
}
2022-09-12 20:44:41 -04:00
if ( PRINT_ACTIVITY ) {
long byteOffset1 = byteOffset ;
LWJGLUtil . logger ( ) . log ( ( ) - > MappedObjectTransformer . class . getSimpleName ( ) + " : " + className + '.' + field . getName ( ) + " [type= " + field . getType ( ) . getSimpleName ( ) + " , offset= " + byteOffset1 + ']' ) ;
}
2011-07-21 22:01:56 -04:00
2011-08-10 12:05:46 -04:00
return new FieldInfo ( byteOffset , byteLength , byteLengthPadded , Type . getType ( field . getType ( ) ) , Modifier . isVolatile ( field . getModifiers ( ) ) , pointer ! = null ) ;
2011-07-21 22:01:56 -04:00
}
2011-07-12 09:29:04 -04:00
2011-07-22 16:09:01 -04:00
/** Removes final from methods that will be overriden by subclasses. */
static byte [ ] transformMappedObject ( byte [ ] bytecode ) {
final ClassWriter cw = new ClassWriter ( 0 ) ;
ClassVisitor cv = new ClassAdapter ( cw ) {
private final String [ ] DEFINALIZE_LIST = {
VIEWADDRESS_METHOD_NAME ,
NEXT_METHOD_NAME ,
ALIGN_METHOD_NAME ,
SIZEOF_METHOD_NAME ,
2011-08-02 19:45:19 -04:00
CAPACITY_METHOD_NAME ,
2011-07-22 16:09:01 -04:00
} ;
public MethodVisitor visitMethod ( int access , final String name , final String desc , final String signature , final String [ ] exceptions ) {
for ( String method : DEFINALIZE_LIST ) {
if ( name . equals ( method ) ) {
access & = ~ ACC_FINAL ;
break ;
}
}
return super . visitMethod ( access , name , desc , signature , exceptions ) ;
}
} ;
new ClassReader ( bytecode ) . accept ( cv , 0 ) ;
return cw . toByteArray ( ) ;
}
2011-07-21 22:01:56 -04:00
static byte [ ] transformMappedAPI ( final String className , byte [ ] bytecode ) {
final ClassWriter cw = new ClassWriter ( COMPUTE_FRAMES ) {
2011-07-12 17:30:48 -04:00
@Override
protected String getCommonSuperClass ( String a , String b ) {
2011-07-21 22:01:56 -04:00
// HACK: prevent user-code static-initialization-blocks to be executed
if ( is_currently_computing_frames & & ! a . startsWith ( " java/ " ) | | ! b . startsWith ( " java/ " ) )
return " java/lang/Object " ;
2011-07-12 17:30:48 -04:00
return super . getCommonSuperClass ( a , b ) ;
}
2011-07-21 22:01:56 -04:00
2011-07-12 17:30:48 -04:00
} ;
2011-07-12 09:29:04 -04:00
2011-07-23 18:02:01 -04:00
final TransformationAdapter ta = new TransformationAdapter ( cw , className ) ;
ClassVisitor cv = ta ;
2011-07-21 22:01:56 -04:00
if ( className_to_subtype . containsKey ( className ) ) // Do a first pass to generate address getters
cv = getMethodGenAdapter ( className , cv ) ;
2011-07-22 16:09:01 -04:00
new ClassReader ( bytecode ) . accept ( cv , ClassReader . SKIP_FRAMES ) ;
2011-07-21 22:01:56 -04:00
2011-07-23 18:02:01 -04:00
if ( ! ta . transformed )
return bytecode ;
bytecode = cw . toByteArray ( ) ;
2022-09-12 20:44:41 -04:00
if ( PRINT_BYTECODE ) {
2011-07-21 22:01:56 -04:00
printBytecode ( bytecode ) ;
2022-09-12 20:44:41 -04:00
}
2011-07-21 22:01:56 -04:00
return bytecode ;
}
private static ClassAdapter getMethodGenAdapter ( final String className , final ClassVisitor cv ) {
return new ClassAdapter ( cv ) {
2011-07-12 09:29:04 -04:00
@Override
2011-07-21 22:01:56 -04:00
public void visitEnd ( ) {
2011-07-22 16:09:01 -04:00
final MappedSubtypeInfo mappedSubtype = className_to_subtype . get ( className ) ;
2011-07-21 22:01:56 -04:00
generateViewAddressGetter ( ) ;
2011-08-02 19:45:19 -04:00
generateCapacity ( ) ;
2011-07-22 16:09:01 -04:00
generateAlignGetter ( mappedSubtype ) ;
generateSizeofGetter ( ) ;
generateNext ( ) ;
2011-07-21 22:01:56 -04:00
2011-07-23 18:02:01 -04:00
for ( String fieldName : mappedSubtype . fields . keySet ( ) ) {
final FieldInfo field = mappedSubtype . fields . get ( fieldName ) ;
2011-07-21 22:01:56 -04:00
2011-07-23 18:02:01 -04:00
if ( field . type . getDescriptor ( ) . length ( ) > 1 ) { // ByteBuffer, getter only
generateByteBufferGetter ( fieldName , field ) ;
2011-07-21 22:01:56 -04:00
} else {
2011-07-23 18:02:01 -04:00
generateFieldGetter ( fieldName , field ) ;
generateFieldSetter ( fieldName , field ) ;
2011-07-12 09:29:04 -04:00
}
}
2011-07-21 22:01:56 -04:00
super . visitEnd ( ) ;
2011-07-12 09:29:04 -04:00
}
2011-07-21 22:01:56 -04:00
private void generateViewAddressGetter ( ) {
2011-07-22 16:09:01 -04:00
MethodVisitor mv = super . visitMethod ( ACC_PUBLIC , VIEWADDRESS_METHOD_NAME , " (I)J " , null , null ) ;
2011-07-21 22:01:56 -04:00
mv . visitCode ( ) ;
mv . visitVarInsn ( ALOAD , 0 ) ;
mv . visitFieldInsn ( GETFIELD , MAPPED_OBJECT_JVM , " baseAddress " , " J " ) ;
mv . visitVarInsn ( ILOAD , 1 ) ;
mv . visitFieldInsn ( GETSTATIC , className , " SIZEOF " , " I " ) ;
mv . visitInsn ( IMUL ) ;
mv . visitInsn ( I2L ) ;
mv . visitInsn ( LADD ) ;
if ( MappedObject . CHECKS ) {
2011-08-10 12:05:46 -04:00
mv . visitInsn ( DUP2 ) ;
2011-07-21 22:01:56 -04:00
mv . visitVarInsn ( ALOAD , 0 ) ;
2011-08-10 12:05:46 -04:00
mv . visitMethodInsn ( INVOKESTATIC , MAPPED_HELPER_JVM , " checkAddress " , " (JL " + MAPPED_OBJECT_JVM + " ;)V " ) ;
2011-07-21 22:01:56 -04:00
}
mv . visitInsn ( LRETURN ) ;
2011-08-10 12:05:46 -04:00
mv . visitMaxs ( 3 , 2 ) ;
2011-07-21 22:01:56 -04:00
mv . visitEnd ( ) ;
}
2011-08-02 19:45:19 -04:00
private void generateCapacity ( ) {
// return (backingByteBuffer().capacity() + (int)(MemoryUtil.getAddress0(backingByteBuffer()) - baseAddress)) / SIZEOF;
MethodVisitor mv = super . visitMethod ( ACC_PUBLIC , CAPACITY_METHOD_NAME , " ()I " , null , null ) ;
2011-07-21 22:01:56 -04:00
mv . visitCode ( ) ;
mv . visitVarInsn ( ALOAD , 0 ) ;
2011-07-22 16:09:01 -04:00
mv . visitMethodInsn ( INVOKEVIRTUAL , MAPPED_OBJECT_JVM , " backingByteBuffer " , " ()L " + jvmClassName ( ByteBuffer . class ) + " ; " ) ;
2011-08-02 19:45:19 -04:00
mv . visitInsn ( DUP ) ;
2011-07-21 22:01:56 -04:00
mv . visitMethodInsn ( INVOKEVIRTUAL , jvmClassName ( ByteBuffer . class ) , " capacity " , " ()I " ) ;
2011-08-02 19:45:19 -04:00
mv . visitInsn ( SWAP ) ;
mv . visitMethodInsn ( INVOKESTATIC , jvmClassName ( MemoryUtil . class ) , " getAddress0 " , " (L " + jvmClassName ( Buffer . class ) + " ;)J " ) ;
mv . visitVarInsn ( ALOAD , 0 ) ;
mv . visitFieldInsn ( GETFIELD , MAPPED_OBJECT_JVM , " baseAddress " , " J " ) ;
mv . visitInsn ( LSUB ) ;
mv . visitInsn ( L2I ) ;
mv . visitInsn ( IADD ) ;
2011-07-21 22:01:56 -04:00
mv . visitFieldInsn ( GETSTATIC , className , " SIZEOF " , " I " ) ;
mv . visitInsn ( IDIV ) ;
mv . visitInsn ( IRETURN ) ;
2011-08-02 19:45:19 -04:00
mv . visitMaxs ( 3 , 1 ) ;
2011-07-21 22:01:56 -04:00
mv . visitEnd ( ) ;
}
2011-07-22 16:09:01 -04:00
private void generateAlignGetter ( final MappedSubtypeInfo mappedSubtype ) {
MethodVisitor mv = super . visitMethod ( ACC_PUBLIC , ALIGN_METHOD_NAME , " ()I " , null , null ) ;
mv . visitCode ( ) ;
visitIntNode ( mv , mappedSubtype . sizeof ) ;
mv . visitInsn ( IRETURN ) ;
mv . visitMaxs ( 1 , 1 ) ;
mv . visitEnd ( ) ;
}
private void generateSizeofGetter ( ) {
MethodVisitor mv = super . visitMethod ( ACC_PUBLIC , SIZEOF_METHOD_NAME , " ()I " , null , null ) ;
mv . visitCode ( ) ;
mv . visitFieldInsn ( GETSTATIC , className , " SIZEOF " , " I " ) ;
mv . visitInsn ( IRETURN ) ;
mv . visitMaxs ( 1 , 1 ) ;
mv . visitEnd ( ) ;
}
private void generateNext ( ) {
MethodVisitor mv = super . visitMethod ( ACC_PUBLIC , NEXT_METHOD_NAME , " ()V " , null , null ) ;
mv . visitCode ( ) ;
mv . visitVarInsn ( ALOAD , 0 ) ;
mv . visitInsn ( DUP ) ;
mv . visitFieldInsn ( GETFIELD , MAPPED_OBJECT_JVM , " viewAddress " , " J " ) ;
mv . visitFieldInsn ( GETSTATIC , className , " SIZEOF " , " I " ) ;
mv . visitInsn ( I2L ) ;
mv . visitInsn ( LADD ) ;
mv . visitMethodInsn ( INVOKEVIRTUAL , className , " setViewAddress " , " (J)V " ) ;
mv . visitInsn ( RETURN ) ;
mv . visitMaxs ( 3 , 1 ) ;
mv . visitEnd ( ) ;
}
2011-07-23 18:02:01 -04:00
private void generateByteBufferGetter ( final String fieldName , final FieldInfo field ) {
MethodVisitor mv = super . visitMethod ( ACC_PUBLIC | ACC_STATIC , getterName ( fieldName ) , " (L " + className + " ;I) " + field . type . getDescriptor ( ) , null , null ) ;
2011-07-21 22:01:56 -04:00
mv . visitCode ( ) ;
mv . visitVarInsn ( ALOAD , 0 ) ;
mv . visitVarInsn ( ILOAD , 1 ) ;
2011-07-22 16:09:01 -04:00
mv . visitMethodInsn ( INVOKEVIRTUAL , className , VIEWADDRESS_METHOD_NAME , " (I)J " ) ;
2011-07-23 18:02:01 -04:00
visitIntNode ( mv , ( int ) field . offset ) ;
2011-07-21 22:01:56 -04:00
mv . visitInsn ( I2L ) ;
mv . visitInsn ( LADD ) ;
2011-07-23 18:02:01 -04:00
visitIntNode ( mv , ( int ) field . length ) ;
2011-07-21 22:01:56 -04:00
mv . visitMethodInsn ( INVOKESTATIC , MAPPED_HELPER_JVM , " newBuffer " , " (JI)L " + jvmClassName ( ByteBuffer . class ) + " ; " ) ;
mv . visitInsn ( ARETURN ) ;
2011-07-23 18:02:01 -04:00
mv . visitMaxs ( 3 , 2 ) ;
2011-07-21 22:01:56 -04:00
mv . visitEnd ( ) ;
}
2011-07-23 18:02:01 -04:00
private void generateFieldGetter ( final String fieldName , final FieldInfo field ) {
MethodVisitor mv = super . visitMethod ( ACC_PUBLIC | ACC_STATIC , getterName ( fieldName ) , " (L " + className + " ;I) " + field . type . getDescriptor ( ) , null , null ) ;
2011-07-21 22:01:56 -04:00
mv . visitCode ( ) ;
mv . visitVarInsn ( ALOAD , 0 ) ;
mv . visitVarInsn ( ILOAD , 1 ) ;
2011-07-22 16:09:01 -04:00
mv . visitMethodInsn ( INVOKEVIRTUAL , className , VIEWADDRESS_METHOD_NAME , " (I)J " ) ;
2011-07-23 18:02:01 -04:00
visitIntNode ( mv , ( int ) field . offset ) ;
2011-07-21 22:01:56 -04:00
mv . visitInsn ( I2L ) ;
mv . visitInsn ( LADD ) ;
2011-07-23 18:28:57 -04:00
mv . visitMethodInsn ( INVOKESTATIC , MAPPED_HELPER_JVM , field . getAccessType ( ) + " get " , " (J) " + field . type . getDescriptor ( ) ) ;
2011-07-23 18:02:01 -04:00
mv . visitInsn ( field . type . getOpcode ( IRETURN ) ) ;
2011-07-22 16:09:01 -04:00
mv . visitMaxs ( 3 , 2 ) ;
2011-07-21 22:01:56 -04:00
mv . visitEnd ( ) ;
}
2011-07-23 18:02:01 -04:00
private void generateFieldSetter ( final String fieldName , final FieldInfo field ) {
MethodVisitor mv = super . visitMethod ( ACC_PUBLIC | ACC_STATIC , setterName ( fieldName ) , " (L " + className + " ;I " + field . type . getDescriptor ( ) + " )V " , null , null ) ;
2011-07-21 22:01:56 -04:00
mv . visitCode ( ) ;
int load = 0 ;
2011-07-23 18:02:01 -04:00
switch ( field . type . getSort ( ) ) {
2011-07-21 22:01:56 -04:00
case Type . BOOLEAN :
case Type . CHAR :
case Type . BYTE :
case Type . SHORT :
case Type . INT :
load = ILOAD ;
break ;
case Type . FLOAT :
load = FLOAD ;
break ;
case Type . LONG :
load = LLOAD ;
break ;
case Type . DOUBLE :
load = DLOAD ;
break ;
}
mv . visitVarInsn ( load , 2 ) ;
mv . visitVarInsn ( ALOAD , 0 ) ;
mv . visitVarInsn ( ILOAD , 1 ) ;
2011-07-22 16:09:01 -04:00
mv . visitMethodInsn ( INVOKEVIRTUAL , className , VIEWADDRESS_METHOD_NAME , " (I)J " ) ;
2011-07-23 18:02:01 -04:00
visitIntNode ( mv , ( int ) field . offset ) ;
2011-07-21 22:01:56 -04:00
mv . visitInsn ( I2L ) ;
mv . visitInsn ( LADD ) ;
2011-07-23 18:28:57 -04:00
mv . visitMethodInsn ( INVOKESTATIC , MAPPED_HELPER_JVM , field . getAccessType ( ) + " put " , " ( " + field . type . getDescriptor ( ) + " J)V " ) ;
2011-07-21 22:01:56 -04:00
mv . visitInsn ( RETURN ) ;
2011-07-23 18:02:01 -04:00
mv . visitMaxs ( 4 , 4 ) ;
2011-07-21 22:01:56 -04:00
mv . visitEnd ( ) ;
}
} ;
}
2011-07-23 18:02:01 -04:00
private static class TransformationAdapter extends ClassAdapter {
2011-07-21 22:01:56 -04:00
2011-07-23 18:02:01 -04:00
final String className ;
2011-07-12 09:29:04 -04:00
2011-07-23 18:02:01 -04:00
boolean transformed ;
TransformationAdapter ( final ClassVisitor cv , final String className ) {
super ( cv ) ;
this . className = className ;
}
@Override
public FieldVisitor visitField ( final int access , final String name , final String desc , final String signature , final Object value ) {
// remove redirected fields
final MappedSubtypeInfo mappedSubtype = className_to_subtype . get ( className ) ;
if ( mappedSubtype ! = null & & mappedSubtype . fields . containsKey ( name ) ) {
2022-09-12 20:44:41 -04:00
if ( PRINT_ACTIVITY ) {
LWJGLUtil . logger ( ) . log ( ( ) - > MappedObjectTransformer . class . getSimpleName ( ) + " : discarding field: " + className + '.' + name + ':' + desc ) ;
}
2011-07-23 18:02:01 -04:00
return null ;
2011-07-21 22:01:56 -04:00
}
2011-08-10 12:05:46 -04:00
if ( ( access & ACC_STATIC ) = = 0 ) {
return new FieldNode ( access , name , desc , signature , value ) {
public void visitEnd ( ) {
if ( visibleAnnotations = = null ) { // early-out
accept ( cv ) ;
return ;
}
boolean before = false ;
boolean after = false ;
int byteLength = 0 ;
for ( AnnotationNode pad : visibleAnnotations ) {
if ( CACHE_LINE_PAD_JVM . equals ( pad . desc ) ) {
if ( " J " . equals ( desc ) | | " D " . equals ( desc ) )
byteLength = 8 ;
else if ( " I " . equals ( desc ) | | " F " . equals ( desc ) )
byteLength = 4 ;
else if ( " S " . equals ( desc ) | | " C " . equals ( desc ) )
byteLength = 2 ;
else if ( " B " . equals ( desc ) | | " Z " . equals ( desc ) )
byteLength = 1 ;
else
throw new ClassFormatError ( " The @CacheLinePad annotation cannot be used on non-primitive fields: " + className + " . " + name ) ;
transformed = true ;
after = true ;
if ( pad . values ! = null ) {
for ( int i = 0 ; i < pad . values . size ( ) ; i + = 2 ) {
final boolean value = pad . values . get ( i + 1 ) . equals ( Boolean . TRUE ) ;
if ( " before " . equals ( pad . values . get ( i ) ) )
before = value ;
else
after = value ;
}
}
break ;
}
}
/ *
We make the fields public to force the JVM to keep the fields in the object .
Instead of using only longs or integers , we use the same type as the original
field . That ' s because modern JVMs usually reorder fields by type :
longs , then doubles , then integers , then booleans , etc . This way it ' s more
likely that the padding will work as expected .
* /
if ( before ) {
final int count = CacheUtil . getCacheLineSize ( ) / byteLength - 1 ;
for ( int i = count ; i > = 1 ; i - - )
cv . visitField ( access | ACC_PUBLIC | ACC_SYNTHETIC , name + " $PAD_ " + i , desc , signature , null ) ;
}
accept ( cv ) ;
if ( after ) {
final int count = CacheUtil . getCacheLineSize ( ) / byteLength - 1 ;
for ( int i = 1 ; i < = count ; i + + )
cv . visitField ( access | ACC_PUBLIC | ACC_SYNTHETIC , name + " $PAD " + i , desc , signature , null ) ;
}
}
} ;
} else
return super . visitField ( access , name , desc , signature , value ) ;
2011-07-23 18:02:01 -04:00
}
2011-07-21 22:01:56 -04:00
2011-07-23 18:02:01 -04:00
@Override
public MethodVisitor visitMethod ( final int access , String name , final String desc , final String signature , final String [ ] exceptions ) {
// Move MappedSubtype constructors to another method
if ( " <init> " . equals ( name ) ) {
final MappedSubtypeInfo mappedSubtype = className_to_subtype . get ( className ) ;
if ( mappedSubtype ! = null ) {
if ( ! " ()V " . equals ( desc ) )
throw new ClassFormatError ( className + " can only have a default constructor, found: " + desc ) ;
2011-07-21 22:01:56 -04:00
2011-07-23 18:02:01 -04:00
final MethodVisitor mv = super . visitMethod ( access , name , desc , signature , exceptions ) ;
mv . visitVarInsn ( ALOAD , 0 ) ;
mv . visitMethodInsn ( INVOKESPECIAL , MAPPED_OBJECT_JVM , " <init> " , " ()V " ) ;
mv . visitInsn ( RETURN ) ;
mv . visitMaxs ( 0 , 0 ) ;
2011-07-21 22:01:56 -04:00
2011-07-23 18:02:01 -04:00
// put the method body in another method
name = VIEW_CONSTRUCTOR_NAME ;
}
}
final MethodVisitor mv = super . visitMethod ( access , name , desc , signature , exceptions ) ;
return new MethodNode ( access , name , desc , signature , exceptions ) {
2011-07-21 22:01:56 -04:00
2011-07-23 18:02:01 -04:00
/ * * When true , the method has touched a mapped object and needs to be transformed . We track this
* so we can skip the expensive frame analysis and tree API usage . * /
boolean needsTransformation ;
2011-07-21 22:01:56 -04:00
2011-07-23 18:02:01 -04:00
@Override
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-21 22:01:56 -04:00
}
2011-07-23 18:02:01 -04:00
}
2011-07-21 22:01:56 -04:00
2011-07-23 18:02:01 -04:00
@Override
public void visitFieldInsn ( final int opcode , final String owner , final String name , final String desc ) {
if ( className_to_subtype . containsKey ( owner ) | | owner . startsWith ( MAPPEDSET_PREFIX ) )
needsTransformation = true ;
2011-07-21 22:01:56 -04:00
2011-07-23 18:02:01 -04:00
super . visitFieldInsn ( opcode , owner , name , desc ) ;
}
2011-07-21 22:01:56 -04:00
2011-07-23 18:02:01 -04:00
@Override
public void visitMethodInsn ( final int opcode , final String owner , final String name , final String desc ) {
if ( className_to_subtype . containsKey ( owner ) )
needsTransformation = true ;
2011-07-21 22:01:56 -04:00
2011-07-23 18:02:01 -04:00
super . visitMethodInsn ( opcode , owner , name , desc ) ;
}
2011-07-21 22:01:56 -04:00
2011-07-23 18:02:01 -04:00
@Override
public void visitEnd ( ) {
if ( needsTransformation ) { // Early-out for methods that do not touch a mapped object.
//System.err.println("\nTRANSFORMING: " + className + "." + name + desc);
transformed = true ;
try {
transformMethod ( analyse ( ) ) ;
} catch ( Exception e ) {
throw new RuntimeException ( e ) ;
}
2011-07-21 22:01:56 -04:00
}
2011-07-23 18:02:01 -04:00
// Pass the instruction stream to the adapter's MethodVisitor
accept ( mv ) ;
}
2011-07-21 22:01:56 -04:00
2011-07-23 18:02:01 -04:00
private Frame < BasicValue > [ ] analyse ( ) throws AnalyzerException {
final Analyzer < BasicValue > a = new Analyzer < BasicValue > ( new SimpleVerifier ( ) ) ;
a . analyze ( className , this ) ;
return a . getFrames ( ) ;
}
2011-07-21 22:01:56 -04:00
2011-07-23 18:02:01 -04:00
private void transformMethod ( final Frame < BasicValue > [ ] frames ) {
final InsnList instructions = this . instructions ;
final Map < Integer , MappedSubtypeInfo > arrayVars = new HashMap < Integer , MappedSubtypeInfo > ( ) ;
/ *
2011-07-29 07:30:14 -04:00
We need this map because we insert / remove instructions from the stream and we need a way
to match each original instruction with the corresponding frame .
TODO : Can we keep track of everything more efficiently without a map ?
* /
2011-07-23 18:02:01 -04:00
final Map < AbstractInsnNode , Frame < BasicValue > > frameMap = new HashMap < AbstractInsnNode , Frame < BasicValue > > ( ) ;
for ( int i = 0 ; i < frames . length ; i + + )
frameMap . put ( instructions . get ( i ) , frames [ i ] ) ;
for ( int i = 0 ; i < instructions . size ( ) ; i + + ) { // f is a separate cursor for frames
final AbstractInsnNode instruction = instructions . get ( i ) ;
//System.out.println("MAIN LOOP #" + i + " - " + getOpcodeName(instruction));
switch ( instruction . getType ( ) ) {
case AbstractInsnNode . VAR_INSN :
if ( instruction . getOpcode ( ) = = ALOAD ) {
VarInsnNode varInsn = ( VarInsnNode ) instruction ;
final MappedSubtypeInfo mappedSubtype = arrayVars . get ( varInsn . var ) ;
if ( mappedSubtype ! = null )
i = transformArrayAccess ( instructions , i , frameMap , varInsn , mappedSubtype , varInsn . var ) ;
}
break ;
case AbstractInsnNode . FIELD_INSN :
FieldInsnNode fieldInsn = ( FieldInsnNode ) instruction ;
final InsnList list = transformFieldAccess ( fieldInsn ) ;
if ( list ! = null )
i = replace ( instructions , i , instruction , list ) ;
break ;
case AbstractInsnNode . METHOD_INSN :
MethodInsnNode methodInsn = ( MethodInsnNode ) instruction ;
final MappedSubtypeInfo mappedType = className_to_subtype . get ( methodInsn . owner ) ;
if ( mappedType ! = null )
i = transformMethodCall ( instructions , i , frameMap , methodInsn , mappedType , arrayVars ) ;
break ;
2011-07-21 22:01:56 -04:00
}
}
2011-07-23 18:02:01 -04:00
}
} ;
}
2011-07-21 22:01:56 -04:00
}
2011-07-12 09:29:04 -04:00
2011-07-21 22:01:56 -04:00
static int transformMethodCall ( final InsnList instructions , int i , final Map < AbstractInsnNode , Frame < BasicValue > > frameMap , final MethodInsnNode methodInsn , final MappedSubtypeInfo mappedType , final Map < Integer , MappedSubtypeInfo > arrayVars ) {
switch ( methodInsn . getOpcode ( ) ) {
case INVOKEVIRTUAL :
if ( " asArray " . equals ( methodInsn . name ) & & methodInsn . desc . equals ( " ()[L " + MAPPED_OBJECT_JVM + " ; " ) ) {
// Go forward and store the local variable index.
// We only allow this pattern: INVOKEVIRTUAL -> CHECKCAST -> ASTORE.
// We remove the first two and store the target MappedSubtype in the ASTORE variable
AbstractInsnNode nextInstruction ;
checkInsnAfterIsArray ( nextInstruction = methodInsn . getNext ( ) , CHECKCAST ) ;
checkInsnAfterIsArray ( nextInstruction = nextInstruction . getNext ( ) , ASTORE ) ;
final Frame < BasicValue > frame = frameMap . get ( nextInstruction ) ;
final String targetType = frame . getStack ( frame . getStackSize ( ) - 1 ) . getType ( ) . getElementType ( ) . getInternalName ( ) ;
if ( ! methodInsn . owner . equals ( targetType ) ) {
/ *
This may happen with the current API , like so :
MappedA foo = MappedA . malloc ( . . . ) ;
MappedB [ ] cursor = foo . asArray ( ) ;
We have to parameterize MappedObject to avoid this .
* /
throw new ClassCastException ( " Source: " + methodInsn . owner + " - Target: " + targetType ) ;
}
2011-07-12 09:29:04 -04:00
2011-07-21 22:01:56 -04:00
final VarInsnNode varInstruction = ( VarInsnNode ) nextInstruction ;
2011-07-12 09:29:04 -04:00
2011-07-21 22:01:56 -04:00
arrayVars . put ( varInstruction . var , mappedType ) ;
instructions . remove ( methodInsn . getNext ( ) ) ; // Remove CHECKCAST
instructions . remove ( methodInsn ) ; // Remove INVOKEVIRTUAL
}
if ( " dup " . equals ( methodInsn . name ) & & methodInsn . desc . equals ( " ()L " + MAPPED_OBJECT_JVM + " ; " ) ) {
i = replace ( instructions , i , methodInsn , generateDupInstructions ( methodInsn ) ) ;
break ;
}
if ( " slice " . equals ( methodInsn . name ) & & methodInsn . desc . equals ( " ()L " + MAPPED_OBJECT_JVM + " ; " ) ) {
i = replace ( instructions , i , methodInsn , generateSliceInstructions ( methodInsn ) ) ;
break ;
}
if ( " runViewConstructor " . equals ( methodInsn . name ) & & " ()V " . equals ( methodInsn . desc ) ) {
i = replace ( instructions , i , methodInsn , generateRunViewConstructorInstructions ( methodInsn ) ) ;
break ;
}
if ( " copyTo " . equals ( methodInsn . name ) & & methodInsn . desc . equals ( " (L " + MAPPED_OBJECT_JVM + " ;)V " ) ) {
i = replace ( instructions , i , methodInsn , generateCopyToInstructions ( mappedType ) ) ;
break ;
}
if ( " copyRange " . equals ( methodInsn . name ) & & methodInsn . desc . equals ( " (L " + MAPPED_OBJECT_JVM + " ;I)V " ) ) {
i = replace ( instructions , i , methodInsn , generateCopyRangeInstructions ( mappedType ) ) ;
break ;
}
break ;
case INVOKESPECIAL :
// super() in VIEW_CONSTRUCTOR_NAME, remove
if ( methodInsn . owner . equals ( MAPPED_OBJECT_JVM ) & & " <init> " . equals ( methodInsn . name ) & & " ()V " . equals ( methodInsn . desc ) ) {
instructions . remove ( methodInsn . getPrevious ( ) ) ; // ALOAD
instructions . remove ( methodInsn ) ; // INVOKESPECIAL
i - = 2 ;
}
break ;
case INVOKESTATIC :
boolean isMapDirectMethod = " map " . equals ( methodInsn . name ) & & methodInsn . desc . equals ( " (JI)L " + MAPPED_OBJECT_JVM + " ; " ) ;
boolean isMapBufferMethod = " map " . equals ( methodInsn . name ) & & methodInsn . desc . equals ( " (Ljava/nio/ByteBuffer;)L " + MAPPED_OBJECT_JVM + " ; " ) ;
boolean isMallocMethod = " malloc " . equals ( methodInsn . name ) & & methodInsn . desc . equals ( " (I)L " + MAPPED_OBJECT_JVM + " ; " ) ;
if ( ( isMapDirectMethod | | isMapBufferMethod ) | | isMallocMethod )
i = replace ( instructions , i , methodInsn , generateMapInstructions ( mappedType , methodInsn . owner , isMapDirectMethod , isMallocMethod ) ) ;
break ;
}
return i ;
2011-07-12 09:29:04 -04:00
}
2011-07-21 22:01:56 -04:00
private static InsnList generateCopyRangeInstructions ( final MappedSubtypeInfo mappedType ) {
final InsnList list = new InsnList ( ) ;
2011-07-12 09:29:04 -04:00
2011-07-21 22:01:56 -04:00
// stack: instances, target, this
list . add ( getIntNode ( mappedType . sizeof ) ) ;
// stack: sizeof, instances, target, this
list . add ( new InsnNode ( IMUL ) ) ;
// stack: bytes, target, this
list . add ( new MethodInsnNode ( INVOKESTATIC , MAPPED_HELPER_JVM , " copy " , " (L " + MAPPED_OBJECT_JVM + " ;L " + MAPPED_OBJECT_JVM + " ;I)V " ) ) ;
// stack: -
2011-07-12 09:29:04 -04:00
2011-07-21 22:01:56 -04:00
return list ;
}
private static InsnList generateCopyToInstructions ( final MappedSubtypeInfo mappedType ) {
final InsnList list = new InsnList ( ) ;
// stack: target, this
2011-07-24 05:38:46 -04:00
list . add ( getIntNode ( mappedType . sizeof - mappedType . padding ) ) ;
2011-07-21 22:01:56 -04:00
// stack: sizeof, target, this
list . add ( new MethodInsnNode ( INVOKESTATIC , MAPPED_HELPER_JVM , " copy " , " (L " + MAPPED_OBJECT_JVM + " ;L " + MAPPED_OBJECT_JVM + " ;I)V " ) ) ;
// stack: -
return list ;
}
private static InsnList generateRunViewConstructorInstructions ( final MethodInsnNode methodInsn ) {
final InsnList list = new InsnList ( ) ;
// stack: this
list . add ( new InsnNode ( DUP ) ) ;
// stack: this, this
list . add ( new MethodInsnNode ( INVOKEVIRTUAL , methodInsn . owner , VIEW_CONSTRUCTOR_NAME , " ()V " ) ) ;
// stack: this
return list ;
}
private static InsnList generateSliceInstructions ( final MethodInsnNode methodInsn ) {
final InsnList list = new InsnList ( ) ;
// stack: this
list . add ( new TypeInsnNode ( NEW , methodInsn . owner ) ) ;
// stack: new, this
list . add ( new InsnNode ( DUP ) ) ;
// stack: new, new, this
list . add ( new MethodInsnNode ( INVOKESPECIAL , methodInsn . owner , " <init> " , " ()V " ) ) ;
// stack: new, this
list . add ( new MethodInsnNode ( INVOKESTATIC , MAPPED_HELPER_JVM , " slice " , " (L " + MAPPED_OBJECT_JVM + " ;L " + MAPPED_OBJECT_JVM + " ;)L " + MAPPED_OBJECT_JVM + " ; " ) ) ;
// stack: new
return list ;
}
private static InsnList generateDupInstructions ( final MethodInsnNode methodInsn ) {
final InsnList list = new InsnList ( ) ;
// stack: this
list . add ( new TypeInsnNode ( NEW , methodInsn . owner ) ) ;
// stack: new, this
list . add ( new InsnNode ( DUP ) ) ;
// stack: new, new, this
list . add ( new MethodInsnNode ( INVOKESPECIAL , methodInsn . owner , " <init> " , " ()V " ) ) ;
// stack: new, this
list . add ( new MethodInsnNode ( INVOKESTATIC , MAPPED_HELPER_JVM , " dup " , " (L " + MAPPED_OBJECT_JVM + " ;L " + MAPPED_OBJECT_JVM + " ;)L " + MAPPED_OBJECT_JVM + " ; " ) ) ;
// stack: new
return list ;
}
private static InsnList generateMapInstructions ( final MappedSubtypeInfo mappedType , final String className , final boolean mapDirectMethod , final boolean mallocMethod ) {
final InsnList trg = new InsnList ( ) ;
if ( mallocMethod ) {
// stack: count
trg . add ( getIntNode ( mappedType . sizeof ) ) ;
// stack: sizeof, count
trg . add ( new InsnNode ( IMUL ) ) ;
// stack: bytes
2011-08-10 12:05:46 -04:00
trg . add ( new MethodInsnNode ( INVOKESTATIC , mappedType . cacheLinePadded ? jvmClassName ( CacheUtil . class ) : jvmClassName ( BufferUtils . class ) , " createByteBuffer " , " (I)L " + jvmClassName ( ByteBuffer . class ) + " ; " ) ) ;
2011-07-21 22:01:56 -04:00
// stack: buffer
} else if ( mapDirectMethod ) {
// stack: capacity, address
trg . add ( new MethodInsnNode ( INVOKESTATIC , MAPPED_HELPER_JVM , " newBuffer " , " (JI)L " + jvmClassName ( ByteBuffer . class ) + " ; " ) ) ;
// stack: buffer
2011-07-12 09:29:04 -04:00
}
2011-07-21 22:01:56 -04:00
// stack: buffer
trg . add ( new TypeInsnNode ( NEW , className ) ) ;
// stack: new, buffer
trg . add ( new InsnNode ( DUP ) ) ;
// stack: new, new, buffer
trg . add ( new MethodInsnNode ( INVOKESPECIAL , className , " <init> " , " ()V " ) ) ;
// stack: new, buffer
trg . add ( new InsnNode ( DUP_X1 ) ) ;
// stack: new, buffer, new
trg . add ( new InsnNode ( SWAP ) ) ;
// stack: buffer, new, new
trg . add ( getIntNode ( mappedType . align ) ) ;
// stack: int, buffer, new, new
trg . add ( getIntNode ( mappedType . sizeof ) ) ;
// stack: int, int, buffer, new, new
trg . add ( new MethodInsnNode ( INVOKESTATIC , MAPPED_HELPER_JVM , " setup " , " (L " + MAPPED_OBJECT_JVM + " ;Ljava/nio/ByteBuffer;II)V " ) ) ;
// stack: new
return trg ;
}
2011-07-12 09:29:04 -04:00
2011-07-21 22:01:56 -04:00
static InsnList transformFieldAccess ( final FieldInsnNode fieldInsn ) {
final MappedSubtypeInfo mappedSubtype ;
mappedSubtype = className_to_subtype . get ( fieldInsn . owner ) ;
if ( mappedSubtype = = null ) { // early out
// MappedSet.view
outer :
if ( " view " . equals ( fieldInsn . name ) & & fieldInsn . owner . startsWith ( MAPPEDSET_PREFIX ) )
return generateSetViewInstructions ( fieldInsn ) ;
return null ; // early out
2011-07-12 09:29:04 -04:00
}
2011-07-12 17:30:48 -04:00
2011-07-21 22:01:56 -04:00
if ( " SIZEOF " . equals ( fieldInsn . name ) )
return generateSIZEOFInstructions ( fieldInsn , mappedSubtype ) ;
2011-07-12 17:30:48 -04:00
2011-07-21 22:01:56 -04:00
if ( " view " . equals ( fieldInsn . name ) )
return generateViewInstructions ( fieldInsn , mappedSubtype ) ;
if ( " baseAddress " . equals ( fieldInsn . name ) | | " viewAddress " . equals ( fieldInsn . name ) ) {
return generateAddressInstructions ( fieldInsn ) ;
2011-07-12 14:30:56 -04:00
}
2011-07-12 09:29:04 -04:00
2011-07-23 18:02:01 -04:00
final FieldInfo field = mappedSubtype . fields . get ( fieldInsn . name ) ;
if ( field = = null ) // early out
2011-07-21 22:01:56 -04:00
return null ;
// now we're going to transform ByteBuffer-typed field access
if ( fieldInsn . desc . equals ( " L " + jvmClassName ( ByteBuffer . class ) + " ; " ) )
2011-07-23 18:02:01 -04:00
return generateByteBufferInstructions ( fieldInsn , mappedSubtype , field . offset ) ;
2011-07-21 22:01:56 -04:00
// we're now going to transform the field access
2011-07-23 18:02:01 -04:00
return generateFieldInstructions ( fieldInsn , field ) ;
2011-07-21 22:01:56 -04:00
}
private static InsnList generateSetViewInstructions ( final FieldInsnNode fieldInsn ) {
if ( fieldInsn . getOpcode ( ) = = GETFIELD )
throwAccessErrorOnReadOnlyField ( fieldInsn . owner , fieldInsn . name ) ;
if ( fieldInsn . getOpcode ( ) ! = PUTFIELD )
throw new InternalError ( ) ;
final InsnList list = new InsnList ( ) ;
// stack: index, this
if ( MAPPED_SET2_JVM . equals ( fieldInsn . owner ) )
list . add ( new MethodInsnNode ( INVOKESTATIC , MAPPED_HELPER_JVM , " put_views " , " (L " + MAPPED_SET2_JVM + " ;I)V " ) ) ;
else if ( MAPPED_SET3_JVM . equals ( fieldInsn . owner ) )
list . add ( new MethodInsnNode ( INVOKESTATIC , MAPPED_HELPER_JVM , " put_views " , " (L " + MAPPED_SET3_JVM + " ;I)V " ) ) ;
else if ( MAPPED_SET4_JVM . equals ( fieldInsn . owner ) )
list . add ( new MethodInsnNode ( INVOKESTATIC , MAPPED_HELPER_JVM , " put_views " , " (L " + MAPPED_SET4_JVM + " ;I)V " ) ) ;
else
throw new InternalError ( ) ;
// stack: -
return list ;
}
private static InsnList generateSIZEOFInstructions ( final FieldInsnNode fieldInsn , final MappedSubtypeInfo mappedSubtype ) {
if ( ! " I " . equals ( fieldInsn . desc ) )
throw new InternalError ( ) ;
final InsnList list = new InsnList ( ) ;
if ( fieldInsn . getOpcode ( ) = = GETSTATIC ) {
list . add ( getIntNode ( mappedSubtype . sizeof ) ) ;
return list ;
}
if ( fieldInsn . getOpcode ( ) = = PUTSTATIC )
throwAccessErrorOnReadOnlyField ( fieldInsn . owner , fieldInsn . name ) ;
throw new InternalError ( ) ;
}
private static InsnList generateViewInstructions ( final FieldInsnNode fieldInsn , final MappedSubtypeInfo mappedSubtype ) {
if ( ! " I " . equals ( fieldInsn . desc ) )
throw new InternalError ( ) ;
final InsnList list = new InsnList ( ) ;
if ( fieldInsn . getOpcode ( ) = = GETFIELD ) {
if ( mappedSubtype . sizeof_shift ! = 0 ) {
2011-07-12 09:29:04 -04:00
// stack: instance
2011-07-21 22:01:56 -04:00
list . add ( getIntNode ( mappedSubtype . sizeof_shift ) ) ;
// stack: sizeof, instance
list . add ( new MethodInsnNode ( INVOKESTATIC , MAPPED_HELPER_JVM , " get_view_shift " , " (L " + MAPPED_OBJECT_JVM + " ;I)I " ) ) ;
// stack: view
} else {
// stack: instance
list . add ( getIntNode ( mappedSubtype . sizeof ) ) ;
// stack: sizeof, instance
list . add ( new MethodInsnNode ( INVOKESTATIC , MAPPED_HELPER_JVM , " get_view " , " (L " + MAPPED_OBJECT_JVM + " ;I)I " ) ) ;
// stack: view
}
return list ;
}
if ( fieldInsn . getOpcode ( ) = = PUTFIELD ) {
if ( mappedSubtype . sizeof_shift ! = 0 ) {
// stack: view, instance
list . add ( getIntNode ( mappedSubtype . sizeof_shift ) ) ;
// stack: sizeof, view, instance
list . add ( new MethodInsnNode ( INVOKESTATIC , MAPPED_HELPER_JVM , " put_view_shift " , " (L " + MAPPED_OBJECT_JVM + " ;II)V " ) ) ;
// stack: -
} else {
// stack: view, instance
list . add ( getIntNode ( mappedSubtype . sizeof ) ) ;
// stack: sizeof, view, instance
list . add ( new MethodInsnNode ( INVOKESTATIC , MAPPED_HELPER_JVM , " put_view " , " (L " + MAPPED_OBJECT_JVM + " ;II)V " ) ) ;
2011-07-12 09:29:04 -04:00
// stack: -
}
2011-07-21 22:01:56 -04:00
return list ;
}
2011-07-12 09:29:04 -04:00
2011-07-21 22:01:56 -04:00
throw new InternalError ( ) ;
}
2011-07-14 12:54:25 -04:00
2011-07-21 22:01:56 -04:00
private static InsnList generateAddressInstructions ( final FieldInsnNode fieldInsn ) {
if ( ! " J " . equals ( fieldInsn . desc ) )
throw new IllegalStateException ( ) ;
2011-07-12 09:29:04 -04:00
2011-07-21 22:01:56 -04:00
if ( fieldInsn . getOpcode ( ) = = GETFIELD ) // do not change a thing
return null ;
2011-07-12 09:29:04 -04:00
2011-07-21 22:01:56 -04:00
if ( fieldInsn . getOpcode ( ) = = PUTFIELD )
throwAccessErrorOnReadOnlyField ( fieldInsn . owner , fieldInsn . name ) ;
2011-07-12 09:29:04 -04:00
2011-07-21 22:01:56 -04:00
throw new InternalError ( ) ;
}
2011-07-12 09:29:04 -04:00
2011-07-23 18:02:01 -04:00
private static InsnList generateByteBufferInstructions ( final FieldInsnNode fieldInsn , final MappedSubtypeInfo mappedSubtype , final long fieldOffset ) {
2011-07-21 22:01:56 -04:00
if ( fieldInsn . getOpcode ( ) = = PUTFIELD )
throwAccessErrorOnReadOnlyField ( fieldInsn . owner , fieldInsn . name ) ;
if ( fieldInsn . getOpcode ( ) = = GETFIELD ) {
final InsnList list = new InsnList ( ) ;
// stack: ref
list . add ( new FieldInsnNode ( GETFIELD , mappedSubtype . className , " viewAddress " , " J " ) ) ;
// stack: long
list . add ( new LdcInsnNode ( fieldOffset ) ) ;
// stack: long, long
list . add ( new InsnNode ( LADD ) ) ;
// stack: long
2011-07-23 18:02:01 -04:00
list . add ( new LdcInsnNode ( mappedSubtype . fields . get ( fieldInsn . name ) . length ) ) ;
2011-07-21 22:01:56 -04:00
// stack: long, long
list . add ( new InsnNode ( L2I ) ) ;
// stack: int, long
list . add ( new MethodInsnNode ( INVOKESTATIC , MAPPED_HELPER_JVM , " newBuffer " , " (JI)L " + jvmClassName ( ByteBuffer . class ) + " ; " ) ) ;
// stack: buffer
return list ;
}
2011-07-12 09:29:04 -04:00
2011-07-21 22:01:56 -04:00
throw new InternalError ( ) ;
}
2011-07-14 12:54:25 -04:00
2011-07-23 18:02:01 -04:00
private static InsnList generateFieldInstructions ( final FieldInsnNode fieldInsn , final FieldInfo field ) {
2011-07-21 22:01:56 -04:00
final InsnList list = new InsnList ( ) ;
2011-07-12 09:29:04 -04:00
2011-07-21 22:01:56 -04:00
if ( fieldInsn . getOpcode ( ) = = PUTFIELD ) {
// stack: value, ref
2011-07-23 18:02:01 -04:00
list . add ( getIntNode ( ( int ) field . offset ) ) ;
2011-07-21 22:01:56 -04:00
// stack: fieldOffset, value, ref
2011-07-23 18:28:57 -04:00
list . add ( new MethodInsnNode ( INVOKESTATIC , MAPPED_HELPER_JVM , field . getAccessType ( ) + " put " , " (L " + MAPPED_OBJECT_JVM + " ; " + fieldInsn . desc + " I)V " ) ) ;
2011-07-21 22:01:56 -04:00
// stack -
return list ;
2011-07-12 09:29:04 -04:00
}
2011-07-21 22:01:56 -04:00
if ( fieldInsn . getOpcode ( ) = = GETFIELD ) {
// stack: ref
2011-07-23 18:02:01 -04:00
list . add ( getIntNode ( ( int ) field . offset ) ) ;
2011-07-21 22:01:56 -04:00
// stack: fieldOffset, ref
2011-07-23 18:28:57 -04:00
list . add ( new MethodInsnNode ( INVOKESTATIC , MAPPED_HELPER_JVM , field . getAccessType ( ) + " get " , " (L " + MAPPED_OBJECT_JVM + " ;I) " + fieldInsn . desc ) ) ;
2011-07-21 22:01:56 -04:00
// stack: -
return list ;
2011-07-12 09:29:04 -04:00
}
2011-07-21 22:01:56 -04:00
throw new InternalError ( ) ;
}
2011-07-12 09:29:04 -04:00
2011-07-21 22:01:56 -04:00
static int transformArrayAccess ( final InsnList instructions , int i , final Map < AbstractInsnNode , Frame < BasicValue > > frameMap , final VarInsnNode loadInsn , final MappedSubtypeInfo mappedSubtype , final int var ) {
// We need to go forward in time to find how we use the array var
final int loadStackSize = frameMap . get ( loadInsn ) . getStackSize ( ) + 1 ;
2011-07-12 09:29:04 -04:00
2011-07-21 22:01:56 -04:00
AbstractInsnNode nextInsn = loadInsn ;
2011-07-12 09:29:04 -04:00
2011-07-21 22:01:56 -04:00
while ( true ) {
nextInsn = nextInsn . getNext ( ) ;
if ( nextInsn = = null )
throw new InternalError ( ) ;
2011-07-12 09:29:04 -04:00
2011-07-21 22:01:56 -04:00
Frame < BasicValue > frame = frameMap . get ( nextInsn ) ;
if ( frame = = null )
continue ;
2011-07-12 09:29:04 -04:00
2011-07-21 22:01:56 -04:00
int stackSize = frame . getStackSize ( ) ;
2011-07-12 09:29:04 -04:00
2011-07-21 22:01:56 -04:00
if ( stackSize = = loadStackSize + 1 & & nextInsn . getOpcode ( ) = = AALOAD ) {
final AbstractInsnNode aaLoadInsn = nextInsn ;
2011-07-12 09:29:04 -04:00
2011-07-21 22:01:56 -04:00
while ( true ) {
nextInsn = nextInsn . getNext ( ) ;
if ( nextInsn = = null )
break ;
2011-07-12 09:29:04 -04:00
2011-07-21 22:01:56 -04:00
frame = frameMap . get ( nextInsn ) ;
if ( frame = = null )
continue ;
stackSize = frame . getStackSize ( ) ;
2011-07-12 09:29:04 -04:00
2011-07-21 22:01:56 -04:00
if ( stackSize = = loadStackSize + 1 & & nextInsn . getOpcode ( ) = = PUTFIELD ) {
final FieldInsnNode fieldInsn = ( FieldInsnNode ) nextInsn ;
2011-07-12 09:29:04 -04:00
2011-07-21 22:01:56 -04:00
// stack: value, view, ref
instructions . insert ( nextInsn , new MethodInsnNode ( INVOKESTATIC , mappedSubtype . className , setterName ( fieldInsn . name ) , " (L " + mappedSubtype . className + " ;I " + fieldInsn . desc + " )V " ) ) ;
// stack: -
instructions . remove ( nextInsn ) ;
2011-07-12 09:29:04 -04:00
2011-07-21 22:01:56 -04:00
break ;
} else if ( stackSize = = loadStackSize & & nextInsn . getOpcode ( ) = = GETFIELD ) {
final FieldInsnNode fieldInsn = ( FieldInsnNode ) nextInsn ;
2011-07-13 18:15:25 -04:00
2011-07-21 22:01:56 -04:00
// stack: view, ref
instructions . insert ( nextInsn , new MethodInsnNode ( INVOKESTATIC , mappedSubtype . className , getterName ( fieldInsn . name ) , " (L " + mappedSubtype . className + " ;I) " + fieldInsn . desc ) ) ;
// stack: value
instructions . remove ( nextInsn ) ;
break ;
} else if ( stackSize = = loadStackSize & & nextInsn . getOpcode ( ) = = DUP & & nextInsn . getNext ( ) . getOpcode ( ) = = GETFIELD ) {
// May happen with operator+assignment (e.g. cursor[i].value += 10)
final FieldInsnNode fieldInsn = ( FieldInsnNode ) nextInsn . getNext ( ) ;
final MethodInsnNode getter = new MethodInsnNode ( INVOKESTATIC , mappedSubtype . className , getterName ( fieldInsn . name ) , " (L " + mappedSubtype . className + " ;I) " + fieldInsn . desc ) ;
// stack: view, ref
instructions . insert ( nextInsn , new InsnNode ( DUP2 ) ) ;
// stack: view, ref, view, ref
instructions . insert ( nextInsn . getNext ( ) , getter ) ;
// stack: value, view, ref
instructions . remove ( nextInsn ) ;
instructions . remove ( fieldInsn ) ;
nextInsn = getter ;
continue ;
} else if ( stackSize < loadStackSize )
2011-07-22 16:09:01 -04:00
throw new ClassFormatError ( " Invalid " + mappedSubtype . className + " view array usage detected: " + getOpcodeName ( nextInsn ) ) ;
2011-07-12 09:29:04 -04:00
}
2011-07-21 22:01:56 -04:00
instructions . remove ( aaLoadInsn ) ;
2011-07-12 09:29:04 -04:00
2011-07-21 22:01:56 -04:00
return i ;
} else if ( stackSize = = loadStackSize & & nextInsn . getOpcode ( ) = = ARRAYLENGTH ) {
if ( LWJGLUtil . DEBUG & & loadInsn . getNext ( ) ! = nextInsn )
throw new InternalError ( ) ;
2011-07-12 09:29:04 -04:00
2011-07-21 22:01:56 -04:00
instructions . remove ( nextInsn ) ;
loadInsn . var = var ;
2011-08-02 19:45:19 -04:00
instructions . insert ( loadInsn , new MethodInsnNode ( INVOKEVIRTUAL , mappedSubtype . className , CAPACITY_METHOD_NAME , " ()I " ) ) ;
2011-07-12 09:29:04 -04:00
2011-07-21 22:01:56 -04:00
return i + 1 ;
} else if ( stackSize < loadStackSize ) // Consumed by something other than AALOAD or ARRAYLENGTH
throw new ClassFormatError ( " Invalid " + mappedSubtype . className + " view array usage detected: " + getOpcodeName ( nextInsn ) ) ;
}
2011-07-12 09:29:04 -04:00
}
2011-07-23 18:02:01 -04:00
private static class FieldInfo {
final long offset ;
final long length ;
2011-08-10 12:05:46 -04:00
final long lengthPadded ;
2011-07-23 18:02:01 -04:00
final Type type ;
2011-07-23 18:28:57 -04:00
final boolean isVolatile ;
2011-07-23 18:02:01 -04:00
final boolean isPointer ;
2011-08-10 12:05:46 -04:00
FieldInfo ( final long offset , final long length , final long lengthPadded , final Type type , final boolean isVolatile , final boolean isPointer ) {
2011-07-23 18:02:01 -04:00
this . offset = offset ;
this . length = length ;
2011-08-10 12:05:46 -04:00
this . lengthPadded = lengthPadded ;
2011-07-23 18:02:01 -04:00
this . type = type ;
2011-07-23 18:28:57 -04:00
this . isVolatile = isVolatile ;
2011-07-23 18:02:01 -04:00
this . isPointer = isPointer ;
}
2011-07-23 18:28:57 -04:00
String getAccessType ( ) {
return isPointer ? " a " : type . getDescriptor ( ) . toLowerCase ( ) + ( isVolatile ? " v " : " " ) ;
}
2011-07-23 18:02:01 -04:00
}
2011-07-12 09:29:04 -04:00
private static class MappedSubtypeInfo {
2011-07-23 18:02:01 -04:00
final String className ;
2011-07-12 09:29:04 -04:00
2011-08-10 12:05:46 -04:00
final int sizeof ;
final int sizeof_shift ;
final int align ;
final int padding ;
final boolean cacheLinePadded ;
2011-07-12 09:29:04 -04:00
2011-07-23 18:02:01 -04:00
final Map < String , FieldInfo > fields ;
2011-07-12 09:29:04 -04:00
2011-08-10 12:05:46 -04:00
MappedSubtypeInfo ( String className , Map < String , FieldInfo > fields , int sizeof , int align , int padding , final boolean cacheLinePadded ) {
2011-07-12 09:29:04 -04:00
this . className = className ;
this . sizeof = sizeof ;
2011-07-21 22:01:56 -04:00
if ( ( ( sizeof - 1 ) & sizeof ) = = 0 )
this . sizeof_shift = getPoT ( sizeof ) ;
2011-07-23 18:02:01 -04:00
else
this . sizeof_shift = 0 ;
2011-07-12 09:29:04 -04:00
this . align = align ;
2011-07-24 05:38:46 -04:00
this . padding = padding ;
2011-08-10 12:05:46 -04:00
this . cacheLinePadded = cacheLinePadded ;
2011-07-12 09:29:04 -04:00
2011-07-23 18:02:01 -04:00
this . fields = fields ;
2011-07-12 09:29:04 -04:00
}
2011-07-12 17:30:48 -04:00
2011-07-21 22:01:56 -04:00
private static int getPoT ( int value ) {
int pot = - 1 ;
while ( value > 0 ) {
pot + + ;
value > > = 1 ;
2011-07-12 17:30:48 -04:00
}
2011-07-21 22:01:56 -04:00
return pot ;
2011-07-12 17:30:48 -04:00
}
}
2011-07-21 22:01:56 -04:00
// -------------------------------------------------------
// -------------------[ MACROS & UTILS ]------------------
// -------------------------------------------------------
2011-07-12 17:30:48 -04:00
2011-07-21 22:01:56 -04:00
private static void getClassEnums ( final Class clazz , final Map < Integer , String > map , final String . . . prefixFilters ) {
2011-07-12 17:30:48 -04:00
try {
2011-07-21 22:01:56 -04:00
OUTER :
for ( Field field : clazz . getFields ( ) ) {
if ( ! Modifier . isStatic ( field . getModifiers ( ) ) | | field . getType ( ) ! = int . class )
2011-07-12 17:30:48 -04:00
continue ;
2011-07-21 22:01:56 -04:00
for ( String filter : prefixFilters ) {
if ( field . getName ( ) . startsWith ( filter ) )
continue OUTER ;
2011-07-12 17:30:48 -04:00
}
2011-07-21 22:01:56 -04:00
if ( map . put ( ( Integer ) field . get ( null ) , field . getName ( ) ) ! = null )
throw new IllegalStateException ( ) ;
2011-07-12 17:30:48 -04:00
}
2011-07-21 22:01:56 -04:00
} catch ( Exception e ) {
e . printStackTrace ( ) ;
2011-07-12 17:30:48 -04:00
}
}
2011-07-21 22:01:56 -04:00
static String getOpcodeName ( final AbstractInsnNode insn ) {
final String op = OPCODE_TO_NAME . get ( insn . getOpcode ( ) ) ;
return INSNTYPE_TO_NAME . get ( insn . getType ( ) ) + " : " + insn . getOpcode ( ) + ( op = = null ? " " : " [ " + OPCODE_TO_NAME . get ( insn . getOpcode ( ) ) + " ] " ) ;
}
static String jvmClassName ( Class < ? > type ) {
return type . getName ( ) . replace ( '.' , '/' ) ;
}
static String getterName ( final String fieldName ) {
return " get$ " + Character . toUpperCase ( fieldName . charAt ( 0 ) ) + fieldName . substring ( 1 ) + " $LWJGL " ;
}
static String setterName ( final String fieldName ) {
return " set$ " + Character . toUpperCase ( fieldName . charAt ( 0 ) ) + fieldName . substring ( 1 ) + " $LWJGL " ;
}
private static void checkInsnAfterIsArray ( final AbstractInsnNode instruction , final int opcode ) {
if ( instruction = = null )
throw new ClassFormatError ( " Unexpected end of instructions after .asArray() method. " ) ;
if ( instruction . getOpcode ( ) ! = opcode )
throw new ClassFormatError ( " The result of .asArray() must be stored to a local variable. Found: " + getOpcodeName ( instruction ) ) ;
}
static AbstractInsnNode getIntNode ( final int value ) {
if ( value < = 5 & & - 1 < = value )
return new InsnNode ( ICONST_M1 + value + 1 ) ;
if ( value > = Byte . MIN_VALUE & & value < = Byte . MAX_VALUE )
return new IntInsnNode ( BIPUSH , value ) ;
if ( value > = Short . MIN_VALUE & & value < = Short . MAX_VALUE )
return new IntInsnNode ( SIPUSH , value ) ;
return new LdcInsnNode ( value ) ;
}
static void visitIntNode ( final MethodVisitor mv , final int value ) {
if ( value < = 5 & & - 1 < = value )
mv . visitInsn ( ICONST_M1 + value + 1 ) ;
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
mv . visitLdcInsn ( value ) ;
}
/** Replace an instruction with a list of instructions. */
static int replace ( final InsnList instructions , final int i , final AbstractInsnNode location , final InsnList list ) {
final int size = list . size ( ) ;
instructions . insert ( location , list ) ;
instructions . remove ( location ) ;
return i + ( size - 1 ) ;
}
private static void throwAccessErrorOnReadOnlyField ( String className , String fieldName ) {
throw new IllegalAccessError ( " The " + className + " . " + fieldName + " field is final. " ) ;
}
private static void printBytecode ( byte [ ] bytecode ) {
StringWriter sw = new StringWriter ( ) ;
ClassVisitor tracer = new TraceClassVisitor ( new ClassWriter ( 0 ) , new PrintWriter ( sw ) ) ;
new ClassReader ( bytecode ) . accept ( tracer , 0 ) ;
2022-09-12 20:44:41 -04:00
LWJGLUtil . logger ( ) . log ( sw : : toString ) ;
2011-07-21 22:01:56 -04:00
}
2022-09-12 20:44:41 -04:00
}