|
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.
|