From: <sp...@us...> - 2011-07-23 22:02:08
|
Revision: 3599 http://java-game-lib.svn.sourceforge.net/java-game-lib/?rev=3599&view=rev Author: spasi Date: 2011-07-23 22:02:01 +0000 (Sat, 23 Jul 2011) Log Message: ----------- Removed sizeof from @MappedType, it's calculated automatically now. Added padding to @MappedType, defaults to 0. Added support for @Pointer long fields for easier interaction with pointer data. Modified Paths: -------------- trunk/LWJGL/src/java/org/lwjgl/LWJGLUtil.java trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedFloat.java trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedObjectTests3.java trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedObjectTests4.java trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedSomething.java trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedVec2.java trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedVec3.java trunk/LWJGL/src/java/org/lwjgl/test/mapped/TestMappedObject.java trunk/LWJGL/src/java/org/lwjgl/test/opengl/sprites/SpriteShootoutMapped.java trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedHelper.java trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedObjectClassLoader.java trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedObjectTransformer.java trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedType.java Added Paths: ----------- trunk/LWJGL/src/java/org/lwjgl/util/mapped/Pointer.java Modified: trunk/LWJGL/src/java/org/lwjgl/LWJGLUtil.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/LWJGLUtil.java 2011-07-22 20:09:01 UTC (rev 3598) +++ trunk/LWJGL/src/java/org/lwjgl/LWJGLUtil.java 2011-07-23 22:02:01 UTC (rev 3599) @@ -462,7 +462,7 @@ * * @param msg Message to print */ - public static void log(String msg) { + public static void log(CharSequence msg) { if (DEBUG) { System.err.println("[LWJGL] " + msg); } Modified: trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedFloat.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedFloat.java 2011-07-22 20:09:01 UTC (rev 3598) +++ trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedFloat.java 2011-07-23 22:02:01 UTC (rev 3599) @@ -35,7 +35,7 @@ import org.lwjgl.util.mapped.MappedType; /** @author Riven */ -@MappedType(sizeof = 4) +@MappedType public class MappedFloat extends MappedObject { public MappedFloat() { Modified: trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedObjectTests3.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedObjectTests3.java 2011-07-22 20:09:01 UTC (rev 3598) +++ trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedObjectTests3.java 2011-07-23 22:02:01 UTC (rev 3599) @@ -82,7 +82,7 @@ System.out.println("current.view=" + some.view + ", not " + elementCount + ", as you might expect"); } - @MappedType(sizeof = 12) + @MappedType public static class Xyz extends MappedObject { int x, y, z; Modified: trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedObjectTests4.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedObjectTests4.java 2011-07-22 20:09:01 UTC (rev 3598) +++ trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedObjectTests4.java 2011-07-23 22:02:01 UTC (rev 3599) @@ -32,7 +32,11 @@ package org.lwjgl.test.mapped; import org.lwjgl.MemoryUtil; +import org.lwjgl.PointerBuffer; import org.lwjgl.opengl.Display; +import org.lwjgl.util.mapped.MappedObject; +import org.lwjgl.util.mapped.MappedType; +import org.lwjgl.util.mapped.Pointer; import java.io.File; import java.nio.ByteBuffer; @@ -112,4 +116,35 @@ } } + @MappedType + public static class MappedPointer extends MappedObject { + + int foo; + @Pointer long pointer; + int bar; + + } + + public static void testPointer() { + MappedPointer data = MappedPointer.malloc(100); + + assert (data.backingByteBuffer().capacity() == 100 * (4 + 4 + PointerBuffer.getPointerSize())); + + for ( int i = 0; i < 100; i++ ) { + data.view = i; + + data.foo = i; + data.pointer = i * 1000; + data.bar = i * 2; + } + + for ( int i = 0; i < 100; i++ ) { + data.view = i; + + assert (data.foo == i); + assert (data.pointer == i * 1000); + assert (data.bar == i * 2); + } + } + } \ No newline at end of file Modified: trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedSomething.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedSomething.java 2011-07-22 20:09:01 UTC (rev 3598) +++ trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedSomething.java 2011-07-23 22:02:01 UTC (rev 3599) @@ -38,7 +38,7 @@ import java.nio.ByteBuffer; /** @author Riven */ -@MappedType(sizeof = 64) +@MappedType public class MappedSomething extends MappedObject { @MappedField(byteOffset = 0) Modified: trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedVec2.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedVec2.java 2011-07-22 20:09:01 UTC (rev 3598) +++ trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedVec2.java 2011-07-23 22:02:01 UTC (rev 3599) @@ -35,7 +35,7 @@ import org.lwjgl.util.mapped.MappedType; /** @author Riven */ -@MappedType(sizeof = 8) +@MappedType public class MappedVec2 extends MappedObject { public float x; Modified: trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedVec3.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedVec3.java 2011-07-22 20:09:01 UTC (rev 3598) +++ trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedVec3.java 2011-07-23 22:02:01 UTC (rev 3599) @@ -35,7 +35,7 @@ import org.lwjgl.util.mapped.MappedType; /** @author Riven */ -@MappedType(sizeof = 12) +@MappedType public class MappedVec3 extends MappedObject { public float x; Modified: trunk/LWJGL/src/java/org/lwjgl/test/mapped/TestMappedObject.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/test/mapped/TestMappedObject.java 2011-07-22 20:09:01 UTC (rev 3598) +++ trunk/LWJGL/src/java/org/lwjgl/test/mapped/TestMappedObject.java 2011-07-23 22:02:01 UTC (rev 3599) @@ -51,6 +51,7 @@ MappedObjectTransformer.register(MappedVec3.class); MappedObjectTransformer.register(MappedSomething.class); MappedObjectTransformer.register(MappedObjectTests3.Xyz.class); + MappedObjectTransformer.register(MappedObjectTests4.MappedPointer.class); if ( MappedObjectClassLoader.fork(TestMappedObject.class, args) ) { return; @@ -72,10 +73,9 @@ MappedObjectTests3.testMappedSet(); MappedObjectTests4.testLocalView(); - //MappedObjectTests4.testLWJGL(); + MappedObjectTests4.testPointer(); - System.out.println("done"); } Modified: trunk/LWJGL/src/java/org/lwjgl/test/opengl/sprites/SpriteShootoutMapped.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/test/opengl/sprites/SpriteShootoutMapped.java 2011-07-22 20:09:01 UTC (rev 3598) +++ trunk/LWJGL/src/java/org/lwjgl/test/opengl/sprites/SpriteShootoutMapped.java 2011-07-23 22:02:01 UTC (rev 3599) @@ -204,14 +204,14 @@ return texID; } - @MappedType(sizeof = 4) + @MappedType public static class Pixel4b extends MappedObject { public byte r, g, b, a; } - @MappedType(sizeof = 3, align = 3) + @MappedType(align = 3) public static class Pixel3b extends MappedObject { public byte r, g, b; @@ -413,15 +413,15 @@ Display.destroy(); } - @MappedType(sizeof = 4 * 4) + @MappedType public static class Sprite extends MappedObject { - public float x, y; - public float dx, dy; + public float x, dx; + public float y, dy; } - @MappedType(sizeof = 2 * 4) + @MappedType public static class SpriteRender extends MappedObject { public float x, y; @@ -536,12 +536,15 @@ x += dx * delta; if ( x < ballRadius ) { x = ballRadius; - sprites[b].dx = -dx; + dx = -dx; } else if ( x > boundW ) { x = boundW; - sprites[b].dx = -dx; + dx = -dx; } + sprites[b].x = x; + sprites[b].dx = dx; + spritesRender[r].x = x; float y = sprites[b].y; float dy = sprites[b].dy; @@ -549,14 +552,14 @@ y += dy * delta; if ( y < ballRadius ) { y = ballRadius; - sprites[b].dy = -dy; + dy = -dy; } else if ( y > boundH ) { y = boundH; - sprites[b].dy = -dy; + dy = -dy; } + sprites[b].y = y; - - spritesRender[r].x = x; + sprites[b].dy = dy; spritesRender[r].y = y; } } Modified: trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedHelper.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedHelper.java 2011-07-22 20:09:01 UTC (rev 3598) +++ trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedHelper.java 2011-07-23 22:02:01 UTC (rev 3599) @@ -239,6 +239,24 @@ return MappedObjectUnsafe.INSTANCE.getLong(mapped.viewAddress + fieldOffset); } + // address + + public static void aput(long value, long addr) { + MappedObjectUnsafe.INSTANCE.putAddress(addr, value); + } + + public static void aput(MappedObject mapped, long value, int fieldOffset) { + MappedObjectUnsafe.INSTANCE.putAddress(mapped.viewAddress + fieldOffset, value); + } + + public static long aget(long addr) { + return MappedObjectUnsafe.INSTANCE.getAddress(addr); + } + + public static long aget(MappedObject mapped, int fieldOffset) { + return MappedObjectUnsafe.INSTANCE.getAddress(mapped.viewAddress + fieldOffset); + } + // double public static void dput(double value, long addr) { Modified: trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedObjectClassLoader.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedObjectClassLoader.java 2011-07-22 20:09:01 UTC (rev 3598) +++ trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedObjectClassLoader.java 2011-07-23 22:02:01 UTC (rev 3599) @@ -96,9 +96,6 @@ final String name = MappedObject.class.getName(); String className = name.replace('.', '/'); - if ( MappedObjectTransformer.PRINT_ACTIVITY ) - LWJGLUtil.log(MappedObjectClassLoader.class.getSimpleName() + ": " + className); - byte[] bytecode = readStream(this.getResourceAsStream(className.concat(".class"))); long t0 = System.nanoTime(); @@ -106,8 +103,8 @@ long t1 = System.nanoTime(); total_time_transforming += (t1 - t0); - if ( MappedObjectTransformer.PRINT_TIMING ) - LWJGLUtil.log("transforming " + className + " took " + (t1 - t0) / 1000 + " micros (total: " + (total_time_transforming / 1000 / 1000) + "ms)"); + if ( MappedObjectTransformer.PRINT_ACTIVITY ) + printActivity(className, t0, t1); Class<?> clazz = super.defineClass(name, bytecode, 0, bytecode.length); resolveClass(clazz); @@ -128,25 +125,29 @@ if ( name.startsWith("sunw.") ) return super.loadClass(name, resolve); - if ( name.equals(MappedObjectClassLoader.class.getName()) ) + if ( name.startsWith("org.objectweb.asm.") ) return super.loadClass(name, resolve); + if ( name.equals(MappedObjectClassLoader.class.getName()) || name.equals((MappedObjectTransformer.class.getName())) ) + return super.loadClass(name, resolve); + String className = name.replace('.', '/'); - if ( MappedObjectTransformer.PRINT_ACTIVITY ) - LWJGLUtil.log(MappedObjectClassLoader.class.getSimpleName() + ": " + className); - byte[] bytecode = readStream(this.getResourceAsStream(className.concat(".class"))); // Classes in this package do not get transformed, but need to go through here because we have transformed MappedObject. if ( !(name.startsWith(MAPPEDOBJECT_PACKAGE_PREFIX) && name.substring(MAPPEDOBJECT_PACKAGE_PREFIX.length()).indexOf('.') == -1) ) { long t0 = System.nanoTime(); - bytecode = MappedObjectTransformer.transformMappedAPI(className, bytecode); + final byte[] newBytecode = MappedObjectTransformer.transformMappedAPI(className, bytecode); long t1 = System.nanoTime(); total_time_transforming += (t1 - t0); - if ( MappedObjectTransformer.PRINT_TIMING ) - LWJGLUtil.log("transforming " + className + " took " + (t1 - t0) / 1000 + " micros (total: " + (total_time_transforming / 1000 / 1000) + "ms)"); + + if ( bytecode != newBytecode ) { + bytecode = newBytecode; + if ( MappedObjectTransformer.PRINT_ACTIVITY ) + printActivity(className, t0, t1); + } } Class<?> clazz = super.defineClass(name, bytecode, 0, bytecode.length); @@ -155,6 +156,15 @@ return clazz; } + private static void printActivity(final String className, final long t0, final long t1) { + final StringBuilder msg = new StringBuilder(MappedObjectClassLoader.class.getSimpleName() + ": " + className); + + if ( MappedObjectTransformer.PRINT_TIMING ) + msg.append("\n\ttransforming took " + (t1 - t0) / 1000 + " micros (total: " + (total_time_transforming / 1000 / 1000) + "ms)"); + + LWJGLUtil.log(msg); + } + private static byte[] readStream(InputStream in) { byte[] bytecode = new byte[256]; int len = 0; Modified: trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedObjectTransformer.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedObjectTransformer.java 2011-07-22 20:09:01 UTC (rev 3598) +++ trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedObjectTransformer.java 2011-07-23 22:02:01 UTC (rev 3599) @@ -63,8 +63,8 @@ */ public class MappedObjectTransformer { - 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"); + static final boolean PRINT_TIMING = PRINT_ACTIVITY && LWJGLUtil.getPrivilegedBoolean("org.lwjgl.util.mapped.PrintTiming"); static final boolean PRINT_BYTECODE = LWJGLUtil.DEBUG && LWJGLUtil.getPrivilegedBoolean("org.lwjgl.util.mapped.PrintBytecode"); static final Map<String, MappedSubtypeInfo> className_to_subtype; @@ -113,7 +113,7 @@ // => IADD // => PUTFIELD MyMappedType.view // - className_to_subtype.put(MAPPED_OBJECT_JVM, new MappedSubtypeInfo(MAPPED_OBJECT_JVM, -1, -1)); + className_to_subtype.put(MAPPED_OBJECT_JVM, new MappedSubtypeInfo(MAPPED_OBJECT_JVM, null, -1, -1)); } final String vmName = System.getProperty("java.vm.name"); @@ -129,39 +129,66 @@ * @param type the mapped object class. */ public static void register(Class<?> type) { + if ( MappedObjectClassLoader.FORKED ) + return; + final MappedType mapped = type.getAnnotation(MappedType.class); if ( mapped == null ) - throw new InternalError("missing " + MappedType.class.getName() + " annotation"); + throw new ClassFormatError("missing " + MappedType.class.getName() + " annotation"); + if ( mapped.padding() < 0 ) + throw new ClassFormatError("Invalid mapped type padding: " + mapped.padding()); + if ( type.getEnclosingClass() != null && !Modifier.isStatic(type.getModifiers()) ) throw new InternalError("only top-level or static inner classes are allowed"); - final MappedSubtypeInfo mappedType = new MappedSubtypeInfo(jvmClassName(type), mapped.sizeof(), mapped.align()); + final String className = jvmClassName(type); + final Map<String, FieldInfo> fields = new HashMap<String, FieldInfo>(); int advancingOffset = 0; - for ( Field field : type.getDeclaredFields() ) - advancingOffset += registerField(mapped, mappedType.className, mappedType, advancingOffset, field); + long sizeof = 0; + for ( Field field : type.getDeclaredFields() ) { + FieldInfo fieldInfo = registerField(mapped, className, advancingOffset, field); + if ( fieldInfo == null ) + continue; - if ( className_to_subtype.put(mappedType.className, mappedType) != null ) + fields.put(field.getName(), fieldInfo); + + advancingOffset += fieldInfo.length; + sizeof = Math.max(sizeof, fieldInfo.offset + fieldInfo.length); + } + + sizeof += mapped.padding(); + + final MappedSubtypeInfo mappedType = new MappedSubtypeInfo(className, fields, (int)sizeof, mapped.align()); + if ( className_to_subtype.put(className, mappedType) != null ) throw new InternalError("duplicate mapped type: " + mappedType.className); } - private static int registerField(final MappedType mapped, final String className, final MappedSubtypeInfo mappedType, int advancingOffset, final Field field) { + private static FieldInfo registerField(final MappedType mapped, final String className, int advancingOffset, final Field field) { if ( Modifier.isStatic(field.getModifiers()) ) // static fields are never mapped - return 0; + return null; // 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()); + throw new ClassFormatError("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); + throw new ClassFormatError("field '" + className + "." + field.getName() + "' missing annotation " + MappedField.class.getName() + ": " + className); + Pointer pointer = field.getAnnotation(Pointer.class); + if ( pointer != null && field.getType() != long.class ) + throw new ClassFormatError("The @Pointer annotation can only be used on long fields. Field found: " + className + "." + field.getName() + ": " + field.getType()); // quick hack long byteOffset = meta == null ? advancingOffset : meta.byteOffset(); long byteLength; - if ( field.getType() == long.class || field.getType() == double.class ) + 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 ) byteLength = 8; else if ( field.getType() == int.class || field.getType() == float.class ) byteLength = 4; @@ -174,7 +201,7 @@ if ( byteLength < 0 ) throw new IllegalStateException("invalid byte length for mapped ByteBuffer field: " + className + "." + field.getName() + " [length=" + byteLength + "]"); } else - throw new InternalError(field.getType().getName()); + throw new ClassFormatError(field.getType().getName()); if ( field.getType() != ByteBuffer.class && (advancingOffset % byteLength) != 0 ) throw new IllegalStateException("misaligned mapped type: " + className + "." + field.getName()); @@ -182,11 +209,7 @@ 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); - mappedType.fieldToType.put(field.getName(), Type.getType(field.getType())); - - return (int)byteLength; + return new FieldInfo(byteOffset, byteLength, Type.getType(field.getType()), pointer != null); } /** Removes final from methods that will be overriden by subclasses. */ @@ -231,13 +254,18 @@ }; - ClassVisitor cv = getTransformationAdapter(className, cw); + final TransformationAdapter ta = new TransformationAdapter(cw, className); + + ClassVisitor cv = ta; if ( className_to_subtype.containsKey(className) ) // Do a first pass to generate address getters cv = getMethodGenAdapter(className, cv); new ClassReader(bytecode).accept(cv, ClassReader.SKIP_FRAMES); + + if ( !ta.transformed ) + return bytecode; + bytecode = cw.toByteArray(); - if ( PRINT_BYTECODE ) printBytecode(bytecode); @@ -257,14 +285,14 @@ generateSizeofGetter(); generateNext(); - for ( String fieldName : mappedSubtype.fieldToOffset.keySet() ) { - final Type type = mappedSubtype.fieldToType.get(fieldName); + for ( String fieldName : mappedSubtype.fields.keySet() ) { + final FieldInfo field = mappedSubtype.fields.get(fieldName); - if ( type.getDescriptor().length() > 1 ) { // ByteBuffer, getter only - generateByteBufferGetter(mappedSubtype, fieldName, type); + if ( field.type.getDescriptor().length() > 1 ) { // ByteBuffer, getter only + generateByteBufferGetter(fieldName, field); } else { - generateFieldGetter(mappedSubtype, fieldName, type); - generateFieldSetter(mappedSubtype, fieldName, type); + generateFieldGetter(fieldName, field); + generateFieldSetter(fieldName, field); } } @@ -342,42 +370,42 @@ mv.visitEnd(); } - private void generateByteBufferGetter(final MappedSubtypeInfo mappedSubtype, final String fieldName, final Type type) { - MethodVisitor mv = super.visitMethod(ACC_PUBLIC | ACC_STATIC, getterName(fieldName), "(L" + className + ";I)" + type.getDescriptor(), null, null); + 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); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ILOAD, 1); mv.visitMethodInsn(INVOKEVIRTUAL, className, VIEWADDRESS_METHOD_NAME, "(I)J"); - visitIntNode(mv, mappedSubtype.fieldToOffset.get(fieldName).intValue()); + visitIntNode(mv, (int)field.offset); mv.visitInsn(I2L); mv.visitInsn(LADD); - visitIntNode(mv, mappedSubtype.fieldToLength.get(fieldName).intValue()); + visitIntNode(mv, (int)field.length); mv.visitMethodInsn(INVOKESTATIC, MAPPED_HELPER_JVM, "newBuffer", "(JI)L" + jvmClassName(ByteBuffer.class) + ";"); mv.visitInsn(ARETURN); - mv.visitMaxs(4, 2); + mv.visitMaxs(3, 2); mv.visitEnd(); } - private void generateFieldGetter(final MappedSubtypeInfo mappedSubtype, final String fieldName, final Type type) { - MethodVisitor mv = super.visitMethod(ACC_PUBLIC | ACC_STATIC, getterName(fieldName), "(L" + className + ";I)" + type.getDescriptor(), null, null); + 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); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ILOAD, 1); mv.visitMethodInsn(INVOKEVIRTUAL, className, VIEWADDRESS_METHOD_NAME, "(I)J"); - visitIntNode(mv, mappedSubtype.fieldToOffset.get(fieldName).intValue()); + visitIntNode(mv, (int)field.offset); mv.visitInsn(I2L); mv.visitInsn(LADD); - mv.visitMethodInsn(INVOKESTATIC, MAPPED_HELPER_JVM, type.getDescriptor().toLowerCase() + "get", "(J)" + type.getDescriptor()); - mv.visitInsn(type.getOpcode(IRETURN)); + mv.visitMethodInsn(INVOKESTATIC, MAPPED_HELPER_JVM, field.isPointer ? "a" : field.type.getDescriptor().toLowerCase() + "get", "(J)" + field.type.getDescriptor()); + mv.visitInsn(field.type.getOpcode(IRETURN)); mv.visitMaxs(3, 2); mv.visitEnd(); } - private void generateFieldSetter(final MappedSubtypeInfo mappedSubtype, final String fieldName, final Type type) { - MethodVisitor mv = super.visitMethod(ACC_PUBLIC | ACC_STATIC, setterName(fieldName), "(L" + className + ";I" + type.getDescriptor() + ")V", null, null); + 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); mv.visitCode(); int load = 0; - switch ( type.getSort() ) { + switch ( field.type.getSort() ) { case Type.BOOLEAN: case Type.CHAR: case Type.BYTE: @@ -399,156 +427,164 @@ mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ILOAD, 1); mv.visitMethodInsn(INVOKEVIRTUAL, className, VIEWADDRESS_METHOD_NAME, "(I)J"); - visitIntNode(mv, mappedSubtype.fieldToOffset.get(fieldName).intValue()); + visitIntNode(mv, (int)field.offset); mv.visitInsn(I2L); mv.visitInsn(LADD); - mv.visitMethodInsn(INVOKESTATIC, MAPPED_HELPER_JVM, type.getDescriptor().toLowerCase() + "put", "(" + type.getDescriptor() + "J)V"); + mv.visitMethodInsn(INVOKESTATIC, MAPPED_HELPER_JVM, field.isPointer ? "a" : field.type.getDescriptor().toLowerCase() + "put", "(" + field.type.getDescriptor() + "J)V"); mv.visitInsn(RETURN); - mv.visitMaxs(4, 3); + mv.visitMaxs(4, 4); mv.visitEnd(); } }; } - private static ClassAdapter getTransformationAdapter(final String className, final ClassWriter cw) { - return new ClassAdapter(cw) { + private static class TransformationAdapter extends ClassAdapter { - @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.fieldToOffset.containsKey(name) ) { - if ( PRINT_ACTIVITY ) - LWJGLUtil.log(MappedObjectTransformer.class.getSimpleName() + ": discarding field: " + className + "." + name + ":" + desc); - return null; - } + final String className; - return super.visitField(access, name, desc, signature, value); + 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) ) { + if ( PRINT_ACTIVITY ) + LWJGLUtil.log(MappedObjectTransformer.class.getSimpleName() + ": discarding field: " + className + "." + name + ":" + desc); + return null; } - @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); + return super.visitField(access, name, desc, signature, value); + } - 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); + @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); - // put the method body in another method - name = VIEW_CONSTRUCTOR_NAME; - } + 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); + + // 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) { + final MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); + return new MethodNode(access, name, desc, signature, exceptions) { - /** 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; + /** 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; - @Override - public void visitMaxs(int a, int b) { - try { - is_currently_computing_frames = true; - super.visitMaxs(a, b); - } finally { - is_currently_computing_frames = false; - } + @Override + public void visitMaxs(int a, int b) { + try { + is_currently_computing_frames = true; + super.visitMaxs(a, b); + } finally { + is_currently_computing_frames = false; } + } - @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; + @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; - super.visitFieldInsn(opcode, owner, name, desc); - } + super.visitFieldInsn(opcode, owner, name, desc); + } - @Override - public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) { - if ( className_to_subtype.containsKey(owner) ) - needsTransformation = true; + @Override + public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) { + if ( className_to_subtype.containsKey(owner) ) + needsTransformation = true; - super.visitMethodInsn(opcode, owner, name, desc); - } + super.visitMethodInsn(opcode, owner, name, desc); + } - @Override - public void visitEnd() { - if ( needsTransformation ) { // Early-out for methods that do not touch a mapped object. - //System.err.println("\nTRANSFORMING: " + className + "." + name + desc); - try { - transformMethod(analyse()); - } catch (Exception e) { - throw new RuntimeException(e); - } + @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); } - - // Pass the instruction stream to the adapter's MethodVisitor - accept(mv); } - private Frame<BasicValue>[] analyse() throws AnalyzerException { - final Analyzer<BasicValue> a = new Analyzer<BasicValue>(new SimpleVerifier()); - a.analyze(className, this); - return a.getFrames(); - } + // Pass the instruction stream to the adapter's MethodVisitor + accept(mv); + } - private void transformMethod(final Frame<BasicValue>[] frames) { - final InsnList instructions = this.instructions; + private Frame<BasicValue>[] analyse() throws AnalyzerException { + final Analyzer<BasicValue> a = new Analyzer<BasicValue>(new SimpleVerifier()); + a.analyze(className, this); + return a.getFrames(); + } - final Map<Integer, MappedSubtypeInfo> arrayVars = new HashMap<Integer, MappedSubtypeInfo>(); + private void transformMethod(final Frame<BasicValue>[] frames) { + final InsnList instructions = this.instructions; - /* - 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? - */ - 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]); + final Map<Integer, MappedSubtypeInfo> arrayVars = new HashMap<Integer, MappedSubtypeInfo>(); - for ( int i = 0; i < instructions.size(); i++ ) { // f is a separate cursor for frames - final AbstractInsnNode instruction = instructions.get(i); + /* + 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? + */ + 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]); - //System.out.println("MAIN LOOP #" + i + " - " + getOpcodeName(instruction)); + for ( int i = 0; i < instructions.size(); i++ ) { // f is a separate cursor for frames + final AbstractInsnNode instruction = instructions.get(i); - 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; + //System.out.println("MAIN LOOP #" + i + " - " + getOpcodeName(instruction)); - final InsnList list = transformFieldAccess(fieldInsn); - if ( list != null ) - i = replace(instructions, i, instruction, list); + 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; - 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; - } + 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; } } - }; - } - }; + } + }; + } } 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) { @@ -760,16 +796,16 @@ return generateAddressInstructions(fieldInsn); } - final Long fieldOffset = mappedSubtype.fieldToOffset.get(fieldInsn.name); - if ( fieldOffset == null ) // early out + final FieldInfo field = mappedSubtype.fields.get(fieldInsn.name); + if ( field == null ) // early out return null; // now we're going to transform ByteBuffer-typed field access if ( fieldInsn.desc.equals("L" + jvmClassName(ByteBuffer.class) + ";") ) - return generateByteBufferInstructions(fieldInsn, mappedSubtype, fieldOffset); + return generateByteBufferInstructions(fieldInsn, mappedSubtype, field.offset); // we're now going to transform the field access - return generateFieldInstructions(fieldInsn, fieldOffset); + return generateFieldInstructions(fieldInsn, field); } private static InsnList generateSetViewInstructions(final FieldInsnNode fieldInsn) { @@ -867,13 +903,11 @@ throw new InternalError(); } - private static InsnList generateByteBufferInstructions(final FieldInsnNode fieldInsn, final MappedSubtypeInfo mappedSubtype, final Long fieldOffset) { + private static InsnList generateByteBufferInstructions(final FieldInsnNode fieldInsn, final MappedSubtypeInfo mappedSubtype, final long fieldOffset) { if ( fieldInsn.getOpcode() == PUTFIELD ) throwAccessErrorOnReadOnlyField(fieldInsn.owner, fieldInsn.name); if ( fieldInsn.getOpcode() == GETFIELD ) { - final Long fieldLength = mappedSubtype.fieldToLength.get(fieldInsn.name); - final InsnList list = new InsnList(); // stack: ref @@ -883,7 +917,7 @@ // stack: long, long list.add(new InsnNode(LADD)); // stack: long - list.add(new LdcInsnNode(fieldLength)); + list.add(new LdcInsnNode(mappedSubtype.fields.get(fieldInsn.name).length)); // stack: long, long list.add(new InsnNode(L2I)); // stack: int, long @@ -896,23 +930,25 @@ throw new InternalError(); } - private static InsnList generateFieldInstructions(final FieldInsnNode fieldInsn, final Long fieldOffset) { + private static InsnList generateFieldInstructions(final FieldInsnNode fieldInsn, final FieldInfo field) { final InsnList list = new InsnList(); + final String dataType = field.isPointer ? "a" : fieldInsn.desc.toLowerCase(); + if ( fieldInsn.getOpcode() == PUTFIELD ) { // stack: value, ref - list.add(getIntNode(fieldOffset.intValue())); + list.add(getIntNode((int)field.offset)); // stack: fieldOffset, value, ref - list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, fieldInsn.desc.toLowerCase() + "put", "(L" + MAPPED_OBJECT_JVM + ";" + fieldInsn.desc + "I)V")); + list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, dataType + "put", "(L" + MAPPED_OBJECT_JVM + ";" + fieldInsn.desc + "I)V")); // stack - return list; } if ( fieldInsn.getOpcode() == GETFIELD ) { // stack: ref - list.add(getIntNode(fieldOffset.intValue())); + list.add(getIntNode((int)field.offset)); // stack: fieldOffset, ref - list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, fieldInsn.desc.toLowerCase() + "get", "(L" + MAPPED_OBJECT_JVM + ";I)" + fieldInsn.desc)); + list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, dataType + "get", "(L" + MAPPED_OBJECT_JVM + ";I)" + fieldInsn.desc)); // stack: - return list; } @@ -1006,29 +1042,43 @@ } } + private static class FieldInfo { + + final long offset; + final long length; + final Type type; + final boolean isPointer; + + FieldInfo(final long offset, final long length, final Type type, final boolean isPointer) { + this.offset = offset; + this.length = length; + this.type = type; + this.isPointer = isPointer; + } + + } + private static class MappedSubtypeInfo { - public final String className; + final String className; - public int sizeof; - public int sizeof_shift; - public int align; + final int sizeof; + final int sizeof_shift; + final int align; - public Map<String, Long> fieldToOffset; - public Map<String, Long> fieldToLength; - public Map<String, Type> fieldToType; + final Map<String, FieldInfo> fields; - MappedSubtypeInfo(String className, int sizeof, int align) { + MappedSubtypeInfo(String className, Map<String, FieldInfo> fields, int sizeof, int align) { this.className = className; this.sizeof = sizeof; if ( ((sizeof - 1) & sizeof) == 0 ) this.sizeof_shift = getPoT(sizeof); + else + this.sizeof_shift = 0; this.align = align; - this.fieldToOffset = new HashMap<String, Long>(); - this.fieldToLength = new HashMap<String, Long>(); - this.fieldToType = new HashMap<String, Type>(); + this.fields = fields; } private static int getPoT(int value) { Modified: trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedType.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedType.java 2011-07-22 20:09:01 UTC (rev 3598) +++ trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedType.java 2011-07-23 22:02:01 UTC (rev 3599) @@ -60,11 +60,12 @@ public @interface MappedType { /** - * The total size of the mapped object, in bytes. + * The number of bytes to add to the total byte size. + * SIZEOF will be calculated as <code>SIZEOF = max(field_offset + field_length) + padding</code> * - * @return the byte size + * @return the padding amount */ - int sizeof(); + int padding() default 0; /** * The mapped data memory alignment, in bytes. Added: trunk/LWJGL/src/java/org/lwjgl/util/mapped/Pointer.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/util/mapped/Pointer.java (rev 0) +++ trunk/LWJGL/src/java/org/lwjgl/util/mapped/Pointer.java 2011-07-23 22:02:01 UTC (rev 3599) @@ -0,0 +1,50 @@ +/* + * 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. + */ +package org.lwjgl.util.mapped; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation can be used on long fields of {@link MappedType} classes, + * to specify that the long value should be interpreted as a pointer. This + * will determine the actual byte size of the field at runtime (4 or 8 bytes). + * + * @author Spasi + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Pointer { + +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |