mirror of
https://github.com/shadowfacts/lwjgl2-arm64.git
synced 2026-04-05 14:35:58 +00:00
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.
This commit is contained in:
parent
896e363979
commit
d0cb1f8c90
14 changed files with 388 additions and 221 deletions
|
|
@ -462,7 +462,7 @@ public class LWJGLUtil {
|
|||
*
|
||||
* @param msg Message to print
|
||||
*/
|
||||
public static void log(String msg) {
|
||||
public static void log(CharSequence msg) {
|
||||
if (DEBUG) {
|
||||
System.err.println("[LWJGL] " + msg);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ import org.lwjgl.util.mapped.MappedObject;
|
|||
import org.lwjgl.util.mapped.MappedType;
|
||||
|
||||
/** @author Riven */
|
||||
@MappedType(sizeof = 4)
|
||||
@MappedType
|
||||
public class MappedFloat extends MappedObject {
|
||||
|
||||
public MappedFloat() {
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ public class MappedObjectTests3 {
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -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 @@ public class MappedObjectTests4 {
|
|||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -38,7 +38,7 @@ import org.lwjgl.util.mapped.MappedType;
|
|||
import java.nio.ByteBuffer;
|
||||
|
||||
/** @author Riven */
|
||||
@MappedType(sizeof = 64)
|
||||
@MappedType
|
||||
public class MappedSomething extends MappedObject {
|
||||
|
||||
@MappedField(byteOffset = 0)
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ import org.lwjgl.util.mapped.MappedObject;
|
|||
import org.lwjgl.util.mapped.MappedType;
|
||||
|
||||
/** @author Riven */
|
||||
@MappedType(sizeof = 8)
|
||||
@MappedType
|
||||
public class MappedVec2 extends MappedObject {
|
||||
|
||||
public float x;
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ import org.lwjgl.util.mapped.MappedObject;
|
|||
import org.lwjgl.util.mapped.MappedType;
|
||||
|
||||
/** @author Riven */
|
||||
@MappedType(sizeof = 12)
|
||||
@MappedType
|
||||
public class MappedVec3 extends MappedObject {
|
||||
|
||||
public float x;
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ public class TestMappedObject {
|
|||
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,9 +73,8 @@ public class TestMappedObject {
|
|||
MappedObjectTests3.testMappedSet();
|
||||
|
||||
MappedObjectTests4.testLocalView();
|
||||
|
||||
//MappedObjectTests4.testLWJGL();
|
||||
|
||||
MappedObjectTests4.testPointer();
|
||||
|
||||
System.out.println("done");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -204,14 +204,14 @@ public final class SpriteShootoutMapped {
|
|||
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 @@ public final class SpriteShootoutMapped {
|
|||
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 @@ public final class SpriteShootoutMapped {
|
|||
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 @@ public final class SpriteShootoutMapped {
|
|||
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].y = y;
|
||||
sprites[b].dy = dy;
|
||||
spritesRender[r].y = y;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -239,6 +239,24 @@ public class MappedHelper {
|
|||
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) {
|
||||
|
|
|
|||
|
|
@ -96,9 +96,6 @@ public class MappedObjectClassLoader extends URLClassLoader {
|
|||
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 @@ public class MappedObjectClassLoader extends URLClassLoader {
|
|||
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 @@ public class MappedObjectClassLoader extends URLClassLoader {
|
|||
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 @@ public class MappedObjectClassLoader extends URLClassLoader {
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -63,8 +63,8 @@ import static org.objectweb.asm.Opcodes.*;
|
|||
*/
|
||||
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 @@ public class MappedObjectTransformer {
|
|||
// => 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 @@ public class MappedObjectTransformer {
|
|||
* @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 @@ public class MappedObjectTransformer {
|
|||
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 @@ public class MappedObjectTransformer {
|
|||
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 @@ public class MappedObjectTransformer {
|
|||
|
||||
};
|
||||
|
||||
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);
|
||||
bytecode = cw.toByteArray();
|
||||
|
||||
if ( !ta.transformed )
|
||||
return bytecode;
|
||||
|
||||
bytecode = cw.toByteArray();
|
||||
if ( PRINT_BYTECODE )
|
||||
printBytecode(bytecode);
|
||||
|
||||
|
|
@ -257,14 +285,14 @@ public class MappedObjectTransformer {
|
|||
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 @@ public class MappedObjectTransformer {
|
|||
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.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);
|
||||
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());
|
||||
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.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 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, (int)field.offset);
|
||||
mv.visitInsn(I2L);
|
||||
mv.visitInsn(LADD);
|
||||
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 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 @@ public class MappedObjectTransformer {
|
|||
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 String className;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return super.visitField(access, name, desc, signature, value);
|
||||
}
|
||||
|
||||
@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 && mappedSubtype.fieldToOffset.containsKey(name) ) {
|
||||
if ( PRINT_ACTIVITY )
|
||||
LWJGLUtil.log(MappedObjectTransformer.class.getSimpleName() + ": discarding field: " + className + "." + name + ":" + desc);
|
||||
return null;
|
||||
}
|
||||
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);
|
||||
|
||||
// put the method body in another method
|
||||
name = VIEW_CONSTRUCTOR_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
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);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, MAPPED_OBJECT_JVM, "<init>", "()V");
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
/** 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;
|
||||
|
||||
// put the method body in another method
|
||||
name = VIEW_CONSTRUCTOR_NAME;
|
||||
@Override
|
||||
public void visitMaxs(int a, int b) {
|
||||
try {
|
||||
is_currently_computing_frames = true;
|
||||
super.visitMaxs(a, b);
|
||||
} finally {
|
||||
is_currently_computing_frames = false;
|
||||
}
|
||||
}
|
||||
|
||||
final MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
|
||||
return new MethodNode(access, name, desc, signature, exceptions) {
|
||||
@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;
|
||||
|
||||
/** 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;
|
||||
super.visitFieldInsn(opcode, owner, name, desc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMaxs(int a, int b) {
|
||||
@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);
|
||||
}
|
||||
|
||||
@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 {
|
||||
is_currently_computing_frames = true;
|
||||
super.visitMaxs(a, b);
|
||||
} finally {
|
||||
is_currently_computing_frames = false;
|
||||
transformMethod(analyse());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
// Pass the instruction stream to the adapter's MethodVisitor
|
||||
accept(mv);
|
||||
}
|
||||
|
||||
super.visitFieldInsn(opcode, owner, name, desc);
|
||||
}
|
||||
private Frame<BasicValue>[] analyse() throws AnalyzerException {
|
||||
final Analyzer<BasicValue> a = new Analyzer<BasicValue>(new SimpleVerifier());
|
||||
a.analyze(className, this);
|
||||
return a.getFrames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) {
|
||||
if ( className_to_subtype.containsKey(owner) )
|
||||
needsTransformation = true;
|
||||
private void transformMethod(final Frame<BasicValue>[] frames) {
|
||||
final InsnList instructions = this.instructions;
|
||||
|
||||
super.visitMethodInsn(opcode, owner, name, desc);
|
||||
}
|
||||
final Map<Integer, MappedSubtypeInfo> arrayVars = new HashMap<Integer, MappedSubtypeInfo>();
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
/*
|
||||
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]);
|
||||
|
||||
// Pass the instruction stream to the adapter's MethodVisitor
|
||||
accept(mv);
|
||||
}
|
||||
for ( int i = 0; i < instructions.size(); i++ ) { // f is a separate cursor for frames
|
||||
final AbstractInsnNode instruction = instructions.get(i);
|
||||
|
||||
private Frame<BasicValue>[] analyse() throws AnalyzerException {
|
||||
final Analyzer<BasicValue> a = new Analyzer<BasicValue>(new SimpleVerifier());
|
||||
a.analyze(className, this);
|
||||
return a.getFrames();
|
||||
}
|
||||
//System.out.println("MAIN LOOP #" + i + " - " + getOpcodeName(instruction));
|
||||
|
||||
private void transformMethod(final Frame<BasicValue>[] frames) {
|
||||
final InsnList instructions = this.instructions;
|
||||
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 Map<Integer, MappedSubtypeInfo> arrayVars = new HashMap<Integer, MappedSubtypeInfo>();
|
||||
final InsnList list = transformFieldAccess(fieldInsn);
|
||||
if ( list != null )
|
||||
i = replace(instructions, i, instruction, list);
|
||||
|
||||
/*
|
||||
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]);
|
||||
|
||||
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;
|
||||
}
|
||||
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 @@ public class MappedObjectTransformer {
|
|||
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 @@ public class MappedObjectTransformer {
|
|||
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 @@ public class MappedObjectTransformer {
|
|||
// 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 @@ public class MappedObjectTransformer {
|
|||
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 @@ public class MappedObjectTransformer {
|
|||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -60,11 +60,12 @@ import java.lang.annotation.Target;
|
|||
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.
|
||||
|
|
|
|||
50
src/java/org/lwjgl/util/mapped/Pointer.java
Normal file
50
src/java/org/lwjgl/util/mapped/Pointer.java
Normal file
|
|
@ -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 {
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue