[lwjgl] 20/35: Add no-asm-support.patch
Markus Koschany
apo at moszumanska.debian.org
Sat Apr 9 20:38:50 UTC 2016
This is an automated email from the git hooks/post-receive script.
apo pushed a commit to branch master
in repository lwjgl.
commit 6bd7f8d65bf1201b87cb049b2d8de041a429caa1
Author: Markus Koschany <apo at debian.org>
Date: Sat Apr 9 20:29:08 2016 +0200
Add no-asm-support.patch
---
debian/patches/no-asm-support.patch | 2480 +++++++++++++++++++++++++++++++++++
debian/patches/series | 1 +
2 files changed, 2481 insertions(+)
diff --git a/debian/patches/no-asm-support.patch b/debian/patches/no-asm-support.patch
new file mode 100644
index 0000000..ee44c23
--- /dev/null
+++ b/debian/patches/no-asm-support.patch
@@ -0,0 +1,2480 @@
+From: Markus Koschany <apo at debian.org>
+Date: Sat, 9 Apr 2016 20:28:48 +0200
+Subject: no asm support
+
+---
+ .../org/lwjgl/test/mapped/TestMappedObject.java | 87 --
+ .../test/opengl/sprites/SpriteShootoutMapped.java | 838 -------------
+ .../lwjgl/util/mapped/MappedObjectClassLoader.java | 193 ---
+ .../lwjgl/util/mapped/MappedObjectTransformer.java | 1319 --------------------
+ 4 files changed, 2437 deletions(-)
+ delete mode 100644 src/java/org/lwjgl/test/mapped/TestMappedObject.java
+ delete mode 100644 src/java/org/lwjgl/test/opengl/sprites/SpriteShootoutMapped.java
+ delete mode 100644 src/java/org/lwjgl/util/mapped/MappedObjectClassLoader.java
+ delete mode 100644 src/java/org/lwjgl/util/mapped/MappedObjectTransformer.java
+
+diff --git a/src/java/org/lwjgl/test/mapped/TestMappedObject.java b/src/java/org/lwjgl/test/mapped/TestMappedObject.java
+deleted file mode 100644
+index 0ae560e..0000000
+--- a/src/java/org/lwjgl/test/mapped/TestMappedObject.java
++++ /dev/null
+@@ -1,87 +0,0 @@
+-/*
+- * 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.test.mapped;
+-
+-import org.lwjgl.util.mapped.MappedObjectClassLoader;
+-import org.lwjgl.util.mapped.MappedObjectTransformer;
+-
+-/** @author Riven */
+- at SuppressWarnings("static-access")
+-public class TestMappedObject {
+-
+- static {
+- boolean assertsEnabled = false;
+- assert assertsEnabled = true; // Intentional side effect!!!
+- if ( !assertsEnabled )
+- throw new RuntimeException("Asserts must be enabled for this test.");
+- }
+-
+- public static void main(String[] args) throws Exception {
+- MappedObjectTransformer.register(MappedFloat.class);
+- MappedObjectTransformer.register(MappedVec2.class);
+- MappedObjectTransformer.register(MappedVec3.class);
+- MappedObjectTransformer.register(MappedSomething.class);
+- MappedObjectTransformer.register(MappedObjectTests3.Xyz.class);
+- MappedObjectTransformer.register(MappedObjectTests4.MappedPointer.class);
+- MappedObjectTransformer.register(MappedObjectTests4.MappedCacheLinePadded.class);
+- MappedObjectTransformer.register(MappedObjectTests4.MappedFieldCacheLinePadded.class);
+-
+- if ( MappedObjectClassLoader.fork(TestMappedObject.class, args) ) {
+- return;
+- }
+-
+- MappedObjectTests1.testViewField();
+-
+- MappedObjectTests2.testFields();
+-
+- // MappedObjectBench.benchmarkMapped();
+- // MappedObjectBench.benchmarkInstances();
+- // MappedObjectBench.benchmarkIndirectArray();
+- // MappedObjectBench.benchmarkDirectArray();
+- // MappedObjectBench.benchmarkUnsafe();
+-
+- MappedObjectTests3.testMappedBuffer();
+- MappedObjectTests3.testForeach();
+- MappedObjectTests3.testConstructor();
+- MappedObjectTests3.testMappedSet();
+-
+- MappedObjectTests4.testLocalView();
+- //MappedObjectTests4.testLWJGL();
+- MappedObjectTests4.testPointer();
+- MappedObjectTests4.testCacheLineAlignment();
+- MappedObjectTests4.testCacheLinePadding();
+- MappedObjectTests4.testCacheLinePaddingPOJO();
+-
+- System.out.println("done");
+- }
+-
+-}
+\ No newline at end of file
+diff --git a/src/java/org/lwjgl/test/opengl/sprites/SpriteShootoutMapped.java b/src/java/org/lwjgl/test/opengl/sprites/SpriteShootoutMapped.java
+deleted file mode 100644
+index 83805d1..0000000
+--- a/src/java/org/lwjgl/test/opengl/sprites/SpriteShootoutMapped.java
++++ /dev/null
+@@ -1,838 +0,0 @@
+-/*
+- * 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.test.opengl.sprites;
+-
+-import org.lwjgl.BufferUtils;
+-import org.lwjgl.LWJGLException;
+-import org.lwjgl.Sys;
+-import org.lwjgl.input.Keyboard;
+-import org.lwjgl.input.Mouse;
+-import org.lwjgl.opengl.*;
+-import org.lwjgl.util.mapped.MappedObject;
+-import org.lwjgl.util.mapped.MappedObjectClassLoader;
+-import org.lwjgl.util.mapped.MappedObjectTransformer;
+-import org.lwjgl.util.mapped.MappedType;
+-
+-import java.awt.image.BufferedImage;
+-import java.awt.image.Raster;
+-import java.io.IOException;
+-import java.nio.ByteBuffer;
+-import java.util.Random;
+-import javax.imageio.ImageIO;
+-
+-import static org.lwjgl.opengl.EXTTransformFeedback.*;
+-import static org.lwjgl.opengl.GL11.*;
+-import static org.lwjgl.opengl.GL12.*;
+-import static org.lwjgl.opengl.GL15.*;
+-import static org.lwjgl.opengl.GL20.*;
+-import static org.lwjgl.opengl.GL30.*;
+-
+-/**
+- * Sprite rendering demo. Three implementations are supported:
+- * a) CPU animation + BufferData VBO update.
+- * b) CPU animation + MapBufferRange VBO update.
+- * c) GPU animation using transform feedback with a vertex shader.
+- *
+- * @author Spasi
+- * @since 18/3/2011
+- */
+-public final class SpriteShootoutMapped {
+-
+- static final int SCREEN_WIDTH = 800;
+- static final int SCREEN_HEIGHT = 600;
+-
+- private static final int ANIMATION_TICKS = 60;
+-
+- private boolean run = true;
+- private boolean render = true;
+- private boolean colorMask = true;
+- private boolean animate = true;
+- private boolean smooth;
+- private boolean vsync;
+-
+- int ballSize = 42;
+- int ballCount = 100 * 1000;
+-
+- private SpriteRenderer renderer;
+-
+- // OpenGL stuff
+- private int texID;
+- private int texBigID;
+- private int texSmallID;
+-
+- long animateTime;
+-
+- private SpriteShootoutMapped() {
+- }
+-
+- public static void main(String[] args) {
+- MappedObjectTransformer.register(Pixel4b.class);
+- MappedObjectTransformer.register(Pixel3b.class);
+- MappedObjectTransformer.register(Sprite.class);
+- MappedObjectTransformer.register(SpriteRender.class);
+-
+- if ( MappedObjectClassLoader.fork(SpriteShootoutMapped.class, args) )
+- return;
+-
+- try {
+- new SpriteShootoutMapped().start();
+- } catch (LWJGLException e) {
+- e.printStackTrace();
+- }
+- }
+-
+- private void start() throws LWJGLException {
+- try {
+- initGL();
+-
+- final ContextCapabilities caps = GLContext.getCapabilities();
+- if ( !true && (caps.OpenGL30 || caps.GL_EXT_transform_feedback) )
+- renderer = new SpriteRendererTF();
+- else if ( true && caps.GL_ARB_map_buffer_range )
+- renderer = new SpriteRendererMapped();
+- else
+- renderer = new SpriteRendererPlain();
+-
+- updateBalls(ballCount);
+- run();
+- } catch (Throwable t) {
+- t.printStackTrace();
+- } finally {
+- destroy();
+- }
+- }
+-
+- private void initGL() throws LWJGLException {
+- Display.setLocation((Display.getDisplayMode().getWidth() - SCREEN_WIDTH) / 2,
+- (Display.getDisplayMode().getHeight() - SCREEN_HEIGHT) / 2);
+- Display.setDisplayMode(new DisplayMode(SCREEN_WIDTH, SCREEN_HEIGHT));
+- Display.setTitle("Sprite Shootout");
+- Display.create();
+- //Display.create(new PixelFormat(), new ContextAttribs(4, 1).withProfileCompatibility(true).withDebug(true));
+- //AMDDebugOutput.glDebugMessageCallbackAMD(new AMDDebugOutputCallback());
+-
+- if ( !GLContext.getCapabilities().OpenGL20 )
+- throw new RuntimeException("OpenGL 2.0 is required for this demo.");
+-
+- // Setup viewport
+-
+- glMatrixMode(GL_PROJECTION);
+- glLoadIdentity();
+- glOrtho(0, SCREEN_WIDTH, 0, SCREEN_HEIGHT, -1.0, 1.0);
+-
+- glMatrixMode(GL_MODELVIEW);
+- glLoadIdentity();
+- glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
+-
+- glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
+-
+- // Create textures
+-
+- try {
+- texSmallID = createTexture("res/ball_sm.png");
+- texBigID = createTexture("res/ball.png");
+- } catch (IOException e) {
+- e.printStackTrace();
+- System.exit(-1);
+- }
+- texID = texBigID;
+-
+- // Setup rendering state
+-
+- glEnable(GL_BLEND);
+- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+-
+- glEnable(GL_ALPHA_TEST);
+- glAlphaFunc(GL_GREATER, 0.0f);
+-
+- glColorMask(colorMask, colorMask, colorMask, false);
+- glDepthMask(false);
+- glDisable(GL_DEPTH_TEST);
+-
+- // Setup geometry
+-
+- glEnableClientState(GL_VERTEX_ARRAY);
+-
+- Util.checkGLError();
+- }
+-
+- private static int createTexture(final String path) throws IOException {
+- final BufferedImage img = ImageIO.read(SpriteShootoutMapped.class.getClassLoader().getResource(path));
+-
+- final int w = img.getWidth();
+- final int h = img.getHeight();
+-
+- final ByteBuffer buffer = readImage(img);
+-
+- final int texID = glGenTextures();
+-
+- glBindTexture(GL_TEXTURE_2D, texID);
+- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, buffer);
+-
+- return texID;
+- }
+-
+- public static class Pixel4b extends MappedObject {
+-
+- public byte r, g, b, a;
+-
+- }
+-
+- @MappedType(align = 3)
+- public static class Pixel3b extends MappedObject {
+-
+- public byte r, g, b;
+-
+- }
+-
+- private static ByteBuffer readImage(final BufferedImage img) throws IOException {
+- final Raster raster = img.getRaster();
+-
+- final int bands = raster.getNumBands();
+-
+- final int w = img.getWidth();
+- final int h = img.getHeight();
+-
+- final int count = w * h;
+-
+- final byte[] pixels = new byte[count * bands];
+- raster.getDataElements(0, 0, w, h, pixels);
+-
+- if ( bands == 4 ) {
+- Pixel4b p = Pixel4b.malloc(count);
+-
+- int b = 0;
+- for ( int i = 0; i < count; i++, b += 4 ) {
+- // Pre-multiply alpha
+- final float a = unpackUByte01(pixels[b + 3]);
+-
+- p.view = i;
+- p.r = packUByte01(unpackUByte01(pixels[b + 2]) * a);
+- p.g = packUByte01(unpackUByte01(pixels[b + 1]) * a);
+- p.b = packUByte01(unpackUByte01(pixels[b + 0]) * a);
+- p.a = pixels[b + 3];
+- }
+-
+- return p.backingByteBuffer();
+- } else if ( bands == 3 ) {
+- Pixel3b p = Pixel3b.malloc(count);
+-
+- int b = 0;
+- for ( int i = 0; i < count; i++, b += 3 ) {
+- p.view = i;
+- p.r = pixels[b + 2];
+- p.g = pixels[b + 1];
+- p.b = pixels[b + 0];
+- }
+-
+- return p.backingByteBuffer();
+- } else {
+- ByteBuffer p = BufferUtils.createByteBuffer(count * bands);
+- p.put(pixels, 0, p.capacity());
+- p.flip();
+- return p;
+- }
+-
+- }
+-
+- private static float unpackUByte01(final byte x) {
+- return (x & 0xFF) / 255.0f;
+- }
+-
+- private static byte packUByte01(final float x) {
+- return (byte)(x * 255.0f);
+- }
+-
+- private void updateBalls(final int count) {
+- System.out.println("NUMBER OF BALLS: " + count);
+- renderer.updateBalls(ballCount);
+- }
+-
+- private void run() {
+- long startTime = System.currentTimeMillis() + 5000;
+- long fps = 0;
+-
+- long time = Sys.getTime();
+- final int ticksPerUpdate = (int)(Sys.getTimerResolution() / ANIMATION_TICKS);
+-
+- renderer.render(false, true, 0);
+-
+- while ( run ) {
+- Display.processMessages();
+- handleInput();
+-
+- glClear(GL_COLOR_BUFFER_BIT);
+-
+- final long currTime = Sys.getTime();
+- final int delta = (int)(currTime - time);
+- if ( smooth || delta >= ticksPerUpdate ) {
+- renderer.render(render, animate, delta);
+- time = currTime;
+- } else
+- renderer.render(render, false, 0);
+-
+- Display.update(false);
+- //Display.sync(60);
+-
+- if ( startTime > System.currentTimeMillis() ) {
+- fps++;
+- } else {
+- long timeUsed = 5000 + (startTime - System.currentTimeMillis());
+- startTime = System.currentTimeMillis() + 5000;
+- System.out.println("FPS: " + (Math.round(fps / (timeUsed / 1000.0) * 10) / 10.0) + ", Balls: " + ballCount);
+- System.out.println("Animation: " + animateTime / fps);
+- animateTime = 0;
+- fps = 0;
+- }
+- }
+- }
+-
+- private void handleInput() {
+- if ( Display.isCloseRequested() )
+- run = false;
+-
+- while ( Keyboard.next() ) {
+- if ( Keyboard.getEventKeyState() )
+- continue;
+-
+- switch ( Keyboard.getEventKey() ) {
+- case Keyboard.KEY_1:
+- case Keyboard.KEY_2:
+- case Keyboard.KEY_3:
+- case Keyboard.KEY_4:
+- case Keyboard.KEY_5:
+- case Keyboard.KEY_6:
+- case Keyboard.KEY_7:
+- case Keyboard.KEY_8:
+- case Keyboard.KEY_9:
+- case Keyboard.KEY_0:
+- ballCount = 1 << (Keyboard.getEventKey() - Keyboard.KEY_1);
+- updateBalls(ballCount);
+- break;
+- case Keyboard.KEY_ADD:
+- case Keyboard.KEY_SUBTRACT:
+- int mult;
+- if ( Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT) )
+- mult = 1000;
+- else if ( Keyboard.isKeyDown(Keyboard.KEY_LMENU) || Keyboard.isKeyDown(Keyboard.KEY_RMENU) )
+- mult = 100;
+- else if ( Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || Keyboard.isKeyDown(Keyboard.KEY_RCONTROL) )
+- mult = 10;
+- else
+- mult = 1;
+- if ( Keyboard.getEventKey() == Keyboard.KEY_SUBTRACT )
+- mult = -mult;
+- ballCount += mult * 100;
+- if ( ballCount <= 0 )
+- ballCount = 1;
+- updateBalls(ballCount);
+- break;
+- case Keyboard.KEY_ESCAPE:
+- run = false;
+- break;
+- case Keyboard.KEY_A:
+- animate = !animate;
+- System.out.println("Animation is now " + (animate ? "on" : "off") + ".");
+- break;
+- case Keyboard.KEY_C:
+- colorMask = !colorMask;
+- glColorMask(colorMask, colorMask, colorMask, false);
+- System.out.println("Color mask is now " + (colorMask ? "on" : "off") + ".");
+- // Disable alpha test when color mask is off, else we get no benefit.
+- if ( colorMask ) {
+- glEnable(GL_BLEND);
+- glEnable(GL_ALPHA_TEST);
+- } else {
+- glDisable(GL_BLEND);
+- glDisable(GL_ALPHA_TEST);
+- }
+- break;
+- case Keyboard.KEY_R:
+- render = !render;
+- System.out.println("Rendering is now " + (render ? "on" : "off") + ".");
+- break;
+- case Keyboard.KEY_S:
+- smooth = !smooth;
+- System.out.println("Smooth animation is now " + (smooth ? "on" : "off") + ".");
+- break;
+- case Keyboard.KEY_T:
+- if ( texID == texBigID ) {
+- texID = texSmallID;
+- ballSize = 16;
+- } else {
+- texID = texBigID;
+- ballSize = 42;
+- }
+- renderer.updateBallSize();
+- glBindTexture(GL_TEXTURE_2D, texID);
+- System.out.println("Now using the " + (texID == texBigID ? "big" : "small") + " texture.");
+- break;
+- case Keyboard.KEY_V:
+- vsync = !vsync;
+- Display.setVSyncEnabled(vsync);
+- System.out.println("VSYNC is now " + (vsync ? "enabled" : "disabled") + ".");
+- break;
+- }
+- }
+-
+- while ( Mouse.next() ) ;
+- }
+-
+- private void destroy() {
+- Display.destroy();
+- }
+-
+- public static class Sprite extends MappedObject {
+-
+- public float dx, x;
+- public float dy, y;
+-
+- }
+-
+- public static class SpriteRender extends MappedObject {
+-
+- public float x, y;
+-
+- }
+-
+- private abstract class SpriteRenderer {
+-
+- protected Sprite sprites;
+-
+- protected int spriteCount;
+-
+- protected int vshID;
+- protected int progID;
+-
+- protected void createProgram() {
+- final int fshID = glCreateShader(GL_FRAGMENT_SHADER);
+- glShaderSource(fshID, "uniform sampler2D COLOR_MAP;\n" +
+- "void main(void) {\n" +
+- " gl_FragColor = texture2D(COLOR_MAP, gl_PointCoord);\n" +
+- "}");
+- glCompileShader(fshID);
+- if ( glGetShaderi(fshID, GL_COMPILE_STATUS) == GL_FALSE ) {
+- System.out.println(glGetShaderInfoLog(fshID, glGetShaderi(fshID, GL_INFO_LOG_LENGTH)));
+- throw new RuntimeException("Failed to compile fragment shader.");
+- }
+-
+- progID = glCreateProgram();
+- glAttachShader(progID, vshID);
+- glAttachShader(progID, fshID);
+- glLinkProgram(progID);
+- if ( glGetProgrami(progID, GL_LINK_STATUS) == GL_FALSE ) {
+- System.out.println(glGetProgramInfoLog(progID, glGetProgrami(progID, GL_INFO_LOG_LENGTH)));
+- throw new RuntimeException("Failed to link shader program.");
+- }
+-
+- glUseProgram(progID);
+- glUniform1i(glGetUniformLocation(progID, "COLOR_MAP"), 0);
+-
+- updateBallSize();
+-
+- glEnableClientState(GL_VERTEX_ARRAY);
+- }
+-
+- public void updateBallSize() {
+- glPointSize(ballSize);
+- }
+-
+- public abstract void updateBalls(int count);
+-
+- protected abstract void render(boolean render, boolean animate, int delta);
+-
+- }
+-
+- private abstract class SpriteRendererBatched extends SpriteRenderer {
+-
+- protected static final int BALLS_PER_BATCH = 10 * 1000;
+-
+- SpriteRendererBatched() {
+- vshID = glCreateShader(GL_VERTEX_SHADER);
+- glShaderSource(vshID, "void main(void) {\n" +
+- " gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" +
+- "}");
+- glCompileShader(vshID);
+- if ( glGetShaderi(vshID, GL_COMPILE_STATUS) == GL_FALSE ) {
+- System.out.println(glGetShaderInfoLog(vshID, glGetShaderi(vshID, GL_INFO_LOG_LENGTH)));
+- throw new RuntimeException("Failed to compile vertex shader.");
+- }
+-
+- createProgram();
+- }
+-
+- public void updateBalls(final int count) {
+- final Random random = new Random();
+-
+- final Sprite newSprites = Sprite.malloc(count);
+- if ( sprites != null ) {
+- sprites.view = 0;
+- sprites.copyRange(newSprites, Math.min(count, spriteCount));
+- }
+-
+- if ( count > spriteCount ) {
+- for ( int i = spriteCount; i < count; i++ ) {
+- newSprites.view = i;
+-
+- newSprites.x = (int)(random.nextFloat() * (SCREEN_WIDTH - ballSize) + ballSize * 0.5f);
+- newSprites.y = (int)(random.nextFloat() * (SCREEN_HEIGHT - ballSize) + ballSize * 0.5f);
+- newSprites.dx = random.nextFloat() * 0.4f - 0.2f;
+- newSprites.dy = random.nextFloat() * 0.4f - 0.2f;
+- }
+- }
+-
+- sprites = newSprites;
+- spriteCount = count;
+- }
+-
+- protected void animate(
+- final Sprite sprite,
+- final SpriteRender spriteRender,
+- final int ballSize, final int ballIndex, final int batchSize, final int delta
+- ) {
+- final float ballRadius = ballSize * 0.5f;
+- final float boundW = SCREEN_WIDTH - ballRadius;
+- final float boundH = SCREEN_HEIGHT - ballRadius;
+-
+- final Sprite[] sprites = sprite.asArray();
+- final SpriteRender[] spritesRender = spriteRender.asArray();
+- for ( int b = ballIndex, r = 0, len = (ballIndex + batchSize); b < len; b++, r++ ) {
+- float dx = sprites[b].dx;
+- float x = sprites[b].x;
+-
+- x += dx * delta;
+- if ( x < ballRadius ) {
+- x = ballRadius;
+- dx = -dx;
+- } else if ( x > boundW ) {
+- x = boundW;
+- dx = -dx;
+- }
+-
+- sprites[b].dx = dx;
+- sprites[b].x = x;
+- spritesRender[r].x = x;
+-
+- float dy = sprites[b].dy;
+- float y = sprites[b].y;
+-
+- y += dy * delta;
+- if ( y < ballRadius ) {
+- y = ballRadius;
+- dy = -dy;
+- } else if ( y > boundH ) {
+- y = boundH;
+- dy = -dy;
+- }
+-
+- sprites[b].dy = dy;
+- sprites[b].y = y;
+- spritesRender[r].y = y;
+- }
+- }
+-
+- }
+-
+- private class SpriteRendererPlain extends SpriteRendererBatched {
+-
+- private final int DATA_PER_BATCH = BALLS_PER_BATCH * 2 * 4; // balls * 2 floats * 4 bytes
+-
+- protected int[] animVBO;
+-
+- private SpriteRender spritesRender;
+-
+- SpriteRendererPlain() {
+- System.out.println("Shootout Implementation: CPU animation & BufferData");
+- spritesRender = SpriteRender.malloc(BALLS_PER_BATCH);
+- }
+-
+- public void updateBalls(final int count) {
+- super.updateBalls(count);
+-
+- final int batchCount = count / BALLS_PER_BATCH + (count % BALLS_PER_BATCH == 0 ? 0 : 1);
+- if ( animVBO != null && batchCount == animVBO.length )
+- return;
+-
+- final int[] newAnimVBO = new int[batchCount];
+- if ( animVBO != null ) {
+- System.arraycopy(animVBO, 0, newAnimVBO, 0, Math.min(animVBO.length, newAnimVBO.length));
+- for ( int i = newAnimVBO.length; i < animVBO.length; i++ )
+- glDeleteBuffers(animVBO[i]);
+- }
+- for ( int i = animVBO == null ? 0 : animVBO.length; i < newAnimVBO.length; i++ ) {
+- newAnimVBO[i] = glGenBuffers();
+- glBindBuffer(GL_ARRAY_BUFFER, newAnimVBO[i]);
+- }
+-
+- animVBO = newAnimVBO;
+- }
+-
+- public void render(final boolean render, final boolean animate, final int delta) {
+- int batchSize = Math.min(ballCount, BALLS_PER_BATCH);
+- int ballIndex = 0;
+- int batchIndex = 0;
+- while ( ballIndex < ballCount ) {
+- glBindBuffer(GL_ARRAY_BUFFER, animVBO[batchIndex]);
+-
+- if ( animate )
+- animate(ballIndex, batchSize, delta);
+-
+- if ( render ) {
+- glVertexPointer(2, GL_FLOAT, 0, 0);
+- glDrawArrays(GL_POINTS, 0, batchSize);
+- }
+-
+- ballIndex += batchSize;
+- batchSize = Math.min(ballCount - ballIndex, BALLS_PER_BATCH);
+- batchIndex++;
+- }
+- }
+-
+- private void animate(final int ballIndex, final int batchSize, final int delta) {
+- animate(
+- sprites, spritesRender,
+- ballSize, ballIndex, batchSize, delta
+- );
+-
+- glBufferData(GL_ARRAY_BUFFER, DATA_PER_BATCH, GL_STREAM_DRAW);
+- glBufferSubData(GL_ARRAY_BUFFER, 0, spritesRender.backingByteBuffer());
+- }
+-
+- }
+-
+- private class SpriteRendererMapped extends SpriteRendererBatched {
+-
+- private StreamVBO animVBO;
+-
+- SpriteRendererMapped() {
+- System.out.println("Shootout Implementation: CPU animation & MapBufferRange");
+- }
+-
+- public void updateBalls(final int count) {
+- super.updateBalls(count);
+-
+- if ( animVBO != null )
+- animVBO.destroy();
+-
+- animVBO = new StreamVBO(GL_ARRAY_BUFFER, ballCount * (2 * 4));
+- }
+-
+- public void render(final boolean render, final boolean animate, final int delta) {
+- int batchSize = Math.min(ballCount, BALLS_PER_BATCH);
+- int ballIndex = 0;
+- while ( ballIndex < ballCount ) {
+- if ( animate ) {
+- final ByteBuffer buffer = animVBO.map(batchSize * (2 * 4));
+-
+- long t0 = System.nanoTime();
+- animate(sprites, SpriteRender.<SpriteRender>map(buffer), ballSize, ballIndex, batchSize, delta);
+- long t1 = System.nanoTime();
+-
+- animateTime += t1 - t0;
+-
+- animVBO.unmap();
+- }
+-
+- if ( render ) {
+- glVertexPointer(2, GL_FLOAT, 0, ballIndex * (2 * 4));
+- glDrawArrays(GL_POINTS, 0, batchSize);
+- }
+-
+- ballIndex += batchSize;
+- batchSize = Math.min(ballCount - ballIndex, BALLS_PER_BATCH);
+- }
+- }
+-
+- }
+-
+- private class SpriteRendererTF extends SpriteRenderer {
+-
+- private int progIDTF;
+- private int ballSizeLoc;
+- private int deltaLoc;
+-
+- private int[] tfVBO = new int[2];
+- private int currVBO;
+-
+- SpriteRendererTF() {
+- System.out.println("Shootout Implementation: TF GPU animation");
+-
+- // Transform-feedback program
+-
+- final int vshID = glCreateShader(GL_VERTEX_SHADER);
+- glShaderSource(vshID, "#version 130\n" +
+- "const float WIDTH = " + SCREEN_WIDTH + ";\n" +
+- "const float HEIGHT = " + SCREEN_HEIGHT + ";\n" +
+- "uniform float ballSize;\n" + // ballSize / 2
+- "uniform float delta;\n" +
+- "void main(void) {\n" +
+- " vec4 anim = gl_Vertex;\n" +
+- " anim.xy = anim.xy + anim.zw * delta;\n" +
+- " vec2 animC = clamp(anim.xy, vec2(ballSize), vec2(WIDTH - ballSize, HEIGHT - ballSize));\n" +
+- " if ( anim.x != animC.x ) anim.z = -anim.z;\n" +
+- " if ( anim.y != animC.y ) anim.w = -anim.w;\n" +
+- " gl_Position = vec4(animC, anim.zw);\n" +
+- "}");
+- glCompileShader(vshID);
+- if ( glGetShaderi(vshID, GL_COMPILE_STATUS) == GL_FALSE ) {
+- System.out.println(glGetShaderInfoLog(vshID, glGetShaderi(vshID, GL_INFO_LOG_LENGTH)));
+- throw new RuntimeException("Failed to compile vertex shader.");
+- }
+-
+- progIDTF = glCreateProgram();
+- glAttachShader(progIDTF, vshID);
+- glTransformFeedbackVaryings(progIDTF, new CharSequence[] { "gl_Position" }, GL_SEPARATE_ATTRIBS);
+- glLinkProgram(progIDTF);
+- if ( glGetProgrami(progIDTF, GL_LINK_STATUS) == GL_FALSE ) {
+- System.out.println(glGetProgramInfoLog(progIDTF, glGetProgrami(progIDTF, GL_INFO_LOG_LENGTH)));
+- throw new RuntimeException("Failed to link shader program.");
+- }
+-
+- glUseProgram(progIDTF);
+-
+- ballSizeLoc = glGetUniformLocation(progIDTF, "ballSize");
+- deltaLoc = glGetUniformLocation(progIDTF, "delta");
+-
+- glUniform1f(ballSizeLoc, ballSize * 0.5f);
+-
+- // -----------------
+-
+- this.vshID = glCreateShader(GL_VERTEX_SHADER);
+- glShaderSource(this.vshID, "void main(void) {\n" +
+- " gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" +
+- "}");
+- glCompileShader(this.vshID);
+- if ( glGetShaderi(this.vshID, GL_COMPILE_STATUS) == GL_FALSE ) {
+- System.out.println(glGetShaderInfoLog(this.vshID, glGetShaderi(this.vshID, GL_INFO_LOG_LENGTH)));
+- throw new RuntimeException("Failed to compile vertex shader.");
+- }
+-
+- createProgram();
+- }
+-
+- public void updateBallSize() {
+- glUseProgram(progIDTF);
+- glUniform1f(ballSizeLoc, ballSize * 0.5f);
+-
+- glUseProgram(progID);
+- super.updateBallSize();
+- }
+-
+- private void doUpdateBalls(final int count) {
+- final Random random = new Random();
+-
+- final Sprite newSprites = Sprite.malloc(count);
+- if ( sprites != null ) {
+- sprites.view = 0;
+- sprites.copyRange(newSprites, Math.min(count, spriteCount));
+- }
+-
+- if ( count > spriteCount ) {
+- for ( int i = spriteCount; i < count; i++ ) {
+- newSprites.view = i;
+-
+- newSprites.x = (int)(random.nextFloat() * (SCREEN_WIDTH - ballSize) + ballSize * 0.5f);
+- newSprites.y = (int)(random.nextFloat() * (SCREEN_HEIGHT - ballSize) + ballSize * 0.5f);
+- newSprites.dx = random.nextFloat() * 0.4f - 0.2f;
+- newSprites.dy = random.nextFloat() * 0.4f - 0.2f;
+- }
+- }
+-
+- sprites = newSprites;
+- spriteCount = count;
+- }
+-
+- public void updateBalls(final int count) {
+- if ( tfVBO[0] != 0 ) {
+- // Fetch current animation state
+- glGetBufferSubData(GL_TRANSFORM_FEEDBACK_BUFFER, 0, sprites.backingByteBuffer());
+- }
+-
+- doUpdateBalls(count);
+-
+- if ( tfVBO[0] != 0 ) {
+- for ( int i = 0; i < tfVBO.length; i++ )
+- glDeleteBuffers(tfVBO[i]);
+- }
+-
+- for ( int i = 0; i < tfVBO.length; i++ ) {
+- tfVBO[i] = glGenBuffers();
+- glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, tfVBO[i]);
+- glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sprites.backingByteBuffer(), GL_STATIC_DRAW);
+- }
+-
+- glBindBuffer(GL_ARRAY_BUFFER, tfVBO[0]);
+- glVertexPointer(2, GL_FLOAT, (4 * 4), 0);
+- }
+-
+- public void render(final boolean render, final boolean animate, final int delta) {
+- if ( animate ) {
+- glUseProgram(progIDTF);
+- glUniform1f(deltaLoc, delta);
+-
+- final int vbo = currVBO;
+- currVBO = 1 - currVBO;
+-
+- glBindBuffer(GL_ARRAY_BUFFER, tfVBO[vbo]);
+- glVertexPointer(4, GL_FLOAT, 0, 0);
+-
+- glEnable(GL_RASTERIZER_DISCARD);
+- if ( GLContext.getCapabilities().OpenGL30 ) {
+- glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, tfVBO[1 - vbo]);
+-
+- glBeginTransformFeedback(GL_POINTS);
+- glDrawArrays(GL_POINTS, 0, ballCount);
+- glEndTransformFeedback();
+- } else {
+- glBindBufferBaseEXT(GL_TRANSFORM_FEEDBACK_BUFFER_EXT, 0, tfVBO[1 - vbo]);
+-
+- glBeginTransformFeedbackEXT(GL_POINTS);
+- glDrawArrays(GL_POINTS, 0, ballCount);
+- glEndTransformFeedbackEXT();
+- }
+- glDisable(GL_RASTERIZER_DISCARD);
+-
+- glUseProgram(progID);
+- glVertexPointer(2, GL_FLOAT, (4 * 4), 0);
+- }
+-
+- if ( render )
+- glDrawArrays(GL_POINTS, 0, ballCount);
+- }
+-
+- }
+-
+-}
+\ No newline at end of file
+diff --git a/src/java/org/lwjgl/util/mapped/MappedObjectClassLoader.java b/src/java/org/lwjgl/util/mapped/MappedObjectClassLoader.java
+deleted file mode 100644
+index 463823f..0000000
+--- a/src/java/org/lwjgl/util/mapped/MappedObjectClassLoader.java
++++ /dev/null
+@@ -1,193 +0,0 @@
+-/*
+- * 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 org.lwjgl.LWJGLUtil;
+-
+-import java.io.IOException;
+-import java.io.InputStream;
+-import java.lang.reflect.InvocationTargetException;
+-import java.lang.reflect.Method;
+-import java.net.URLClassLoader;
+-
+-/**
+- * This classloader is responsible for applying the bytecode transformation to mapped objects.
+- * The transformation can either be applied using a Java agent, or with the convenient {@link #fork} method.
+- *
+- * @author Riven
+- */
+-public class MappedObjectClassLoader extends URLClassLoader {
+-
+- static final String MAPPEDOBJECT_PACKAGE_PREFIX = MappedObjectClassLoader.class.getPackage().getName() + ".";
+-
+- static boolean FORKED;
+-
+- /**
+- * Forks the specified class containing a main method, passing the specified arguments. See
+- * {@link org.lwjgl.test.mapped.TestMappedObject} for example usage.
+- *
+- * @param mainClass the class containing the main method
+- * @param args the arguments to pass
+- *
+- * @return true if the fork was successful.
+- */
+- public static boolean fork(Class<?> mainClass, String[] args) {
+- if ( FORKED ) {
+- return false;
+- }
+-
+- FORKED = true;
+-
+- try {
+- MappedObjectClassLoader loader = new MappedObjectClassLoader(mainClass);
+- loader.loadMappedObject();
+-
+- Class<?> replacedMainClass = loader.loadClass(mainClass.getName());
+- Method mainMethod = replacedMainClass.getMethod("main", String[].class);
+- mainMethod.invoke(null, new Object[] { args });
+- } catch (InvocationTargetException exc) {
+- Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), exc.getCause());
+- } catch (Throwable cause) {
+- throw new Error("failed to fork", cause);
+- }
+-
+- return true;
+- }
+-
+- private MappedObjectClassLoader(Class<?> mainClass) {
+- super(((URLClassLoader)mainClass.getClassLoader()).getURLs());
+- }
+-
+- protected synchronized Class<?> loadMappedObject() throws ClassNotFoundException {
+- final String name = MappedObject.class.getName();
+- String className = name.replace('.', '/');
+-
+- byte[] bytecode = readStream(this.getResourceAsStream(className.concat(".class")));
+-
+- long t0 = System.nanoTime();
+- bytecode = MappedObjectTransformer.transformMappedObject(bytecode);
+- long t1 = System.nanoTime();
+- total_time_transforming += (t1 - t0);
+-
+- if ( MappedObjectTransformer.PRINT_ACTIVITY )
+- printActivity(className, t0, t1);
+-
+- Class<?> clazz = super.defineClass(name, bytecode, 0, bytecode.length);
+- resolveClass(clazz);
+- return clazz;
+- }
+-
+- private static long total_time_transforming;
+-
+- @Override
+- protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+- if ( name.startsWith("java.")
+- || name.startsWith("javax.")
+- || name.startsWith("sun.")
+- || name.startsWith("sunw.")
+- || name.startsWith("org.objectweb.asm.")
+- )
+- return super.loadClass(name, resolve);
+-
+- final String className = name.replace('.', '/');
+- final boolean inThisPackage = name.startsWith(MAPPEDOBJECT_PACKAGE_PREFIX);
+-
+- if ( inThisPackage && (
+- name.equals(MappedObjectClassLoader.class.getName())
+- || name.equals((MappedObjectTransformer.class.getName()))
+- || name.equals((CacheUtil.class.getName()))
+- ) )
+- return super.loadClass(name, resolve);
+-
+- 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 ( !(inThisPackage && name.substring(MAPPEDOBJECT_PACKAGE_PREFIX.length()).indexOf('.') == -1) ) {
+- long t0 = System.nanoTime();
+- final byte[] newBytecode = MappedObjectTransformer.transformMappedAPI(className, bytecode);
+- long t1 = System.nanoTime();
+-
+- total_time_transforming += (t1 - t0);
+-
+- if ( bytecode != newBytecode ) {
+- bytecode = newBytecode;
+- if ( MappedObjectTransformer.PRINT_ACTIVITY )
+- printActivity(className, t0, t1);
+- }
+- }
+-
+- Class<?> clazz = super.defineClass(name, bytecode, 0, bytecode.length);
+- if ( resolve )
+- resolveClass(clazz);
+- 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;
+- try {
+- while ( true ) {
+- if ( bytecode.length == len )
+- bytecode = copyOf(bytecode, len * 2);
+- int got = in.read(bytecode, len, bytecode.length - len);
+- if ( got == -1 )
+- break;
+- len += got;
+- }
+- } catch (IOException exc) {
+- // stop!
+- } finally {
+- try {
+- in.close();
+- } catch (IOException exc) {
+- // ignore...
+- }
+- }
+- return copyOf(bytecode, len);
+- }
+-
+- private static byte[] copyOf(byte[] original, int newLength) {
+- byte[] copy = new byte[newLength];
+- System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
+- return copy;
+- }
+-
+-}
+\ No newline at end of file
+diff --git a/src/java/org/lwjgl/util/mapped/MappedObjectTransformer.java b/src/java/org/lwjgl/util/mapped/MappedObjectTransformer.java
+deleted file mode 100644
+index c34c14e..0000000
+--- a/src/java/org/lwjgl/util/mapped/MappedObjectTransformer.java
++++ /dev/null
+@@ -1,1319 +0,0 @@
+-/*
+- * 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 org.lwjgl.BufferUtils;
+-import org.lwjgl.LWJGLUtil;
+-import org.lwjgl.MemoryUtil;
+-import org.objectweb.asm.*;
+-import org.objectweb.asm.tree.*;
+-import org.objectweb.asm.tree.analysis.*;
+-import org.objectweb.asm.tree.analysis.Frame;
+-import org.objectweb.asm.util.TraceClassVisitor;
+-
+-import java.io.PrintWriter;
+-import java.io.StringWriter;
+-import java.lang.reflect.Field;
+-import java.lang.reflect.Modifier;
+-import java.nio.Buffer;
+-import java.nio.ByteBuffer;
+-import java.util.HashMap;
+-import java.util.Map;
+-
+-import static org.objectweb.asm.ClassWriter.*;
+-import static org.objectweb.asm.Opcodes.*;
+-
+-/**
+- * This class implements the bytecode transformation that mapped object go through.
+- * Mapped object classes need to first be registered with the transformer, see {@link #register(Class)}.
+- * <p/>
+- * The transformer supports some debugging tools, enabled through JVM system properties:<br/>
+- * org.lwjgl.util.mapped.PrintTiming=true, prints timing information for the transformation step.<br/>
+- * org.lwjgl.util.mapped.PrintActivity=true, prints activity information.<br/>
+- * org.lwjgl.util.mapped.PrintBytecode=true, prints the transformed bytecode.<br/>
+- * org.lwjgl.util.Debug must also be set to true for the above to work.
+- *
+- * @author Riven
+- */
+-public class MappedObjectTransformer {
+-
+- 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;
+-
+- static final String MAPPED_OBJECT_JVM = jvmClassName(MappedObject.class);
+- static final String MAPPED_HELPER_JVM = jvmClassName(MappedHelper.class);
+-
+- static final String MAPPEDSET_PREFIX = jvmClassName(MappedSet.class);
+- static final String MAPPED_SET2_JVM = jvmClassName(MappedSet2.class);
+- static final String MAPPED_SET3_JVM = jvmClassName(MappedSet3.class);
+- static final String MAPPED_SET4_JVM = jvmClassName(MappedSet4.class);
+-
+- static final String CACHE_LINE_PAD_JVM = "L" + jvmClassName(CacheLinePad.class) + ";";
+-
+- // Public methods
+- static final String VIEWADDRESS_METHOD_NAME = "getViewAddress";
+- static final String NEXT_METHOD_NAME = "next";
+- static final String ALIGN_METHOD_NAME = "getAlign";
+- static final String SIZEOF_METHOD_NAME = "getSizeof";
+- static final String CAPACITY_METHOD_NAME = "capacity"; // Used for .asArray().length
+-
+- // Internal methods
+- static final String VIEW_CONSTRUCTOR_NAME = "constructView$LWJGL"; // Used by runViewConstructor
+-
+- static final Map<Integer, String> OPCODE_TO_NAME = new HashMap<Integer, String>();
+- static final Map<Integer, String> INSNTYPE_TO_NAME = new HashMap<Integer, String>();
+-
+- static boolean is_currently_computing_frames;
+-
+- static {
+- getClassEnums(Opcodes.class, OPCODE_TO_NAME, "V1_", "ACC_", "T_", "F_", "MH_");
+- getClassEnums(AbstractInsnNode.class, INSNTYPE_TO_NAME);
+-
+- className_to_subtype = new HashMap<String, MappedSubtypeInfo>();
+-
+- {
+- // HACK: required for mapped.view++
+- //
+- // because the compiler generates:
+- // => GETFIELD MappedObject.view
+- // => ICONST_1
+- // => IADD
+- // => PUTFIELD MyMappedType.view
+- //
+- // instead of:
+- // => GETFIELD MyMappedType.view
+- // => ICONST_1
+- // => IADD
+- // => PUTFIELD MyMappedType.view
+- //
+- className_to_subtype.put(MAPPED_OBJECT_JVM, new MappedSubtypeInfo(MAPPED_OBJECT_JVM, null, -1, -1, -1, false));
+- }
+-
+- final String vmName = System.getProperty("java.vm.name");
+- if ( vmName != null && !vmName.contains("Server") ) {
+- System.err.println("Warning: " + MappedObject.class.getSimpleName() + "s have inferiour performance on Client VMs, please consider switching to a Server VM.");
+- }
+- }
+-
+- /**
+- * Registers a class as a mapped object.
+- * The class must extend {@link org.lwjgl.util.mapped.MappedObject} and be annotated with {@link org.lwjgl.util.mapped.MappedField}.
+- *
+- * @param type the mapped object class.
+- */
+- public static void register(Class<? extends MappedObject> type) {
+- if ( MappedObjectClassLoader.FORKED )
+- return;
+-
+- final MappedType mapped = type.getAnnotation(MappedType.class);
+-
+- if ( mapped != null && 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 String className = jvmClassName(type);
+- final Map<String, FieldInfo> fields = new HashMap<String, FieldInfo>();
+-
+- long sizeof = 0;
+- for ( Field field : type.getDeclaredFields() ) {
+- FieldInfo fieldInfo = registerField(mapped == null || mapped.autoGenerateOffsets(), className, sizeof, field);
+- if ( fieldInfo == null )
+- continue;
+-
+- fields.put(field.getName(), fieldInfo);
+-
+- sizeof = Math.max(sizeof, fieldInfo.offset + fieldInfo.lengthPadded);
+- }
+-
+- int align = 4;
+- int padding = 0;
+- boolean cacheLinePadded = false;
+-
+- if ( mapped != null ) {
+- align = mapped.align();
+- if ( mapped.cacheLinePadding() ) {
+- if ( mapped.padding() != 0 )
+- throw new ClassFormatError("Mapped type padding cannot be specified together with cacheLinePadding.");
+-
+- final int cacheLineMod = (int)(sizeof % CacheUtil.getCacheLineSize());
+- if ( cacheLineMod != 0 )
+- padding = CacheUtil.getCacheLineSize() - cacheLineMod;
+-
+- cacheLinePadded = true;
+- } else
+- padding = mapped.padding();
+- }
+-
+- sizeof += padding;
+-
+- final MappedSubtypeInfo mappedType = new MappedSubtypeInfo(className, fields, (int)sizeof, align, padding, cacheLinePadded);
+- if ( className_to_subtype.put(className, mappedType) != null )
+- throw new InternalError("duplicate mapped type: " + mappedType.className);
+- }
+-
+- private static FieldInfo registerField(final boolean autoGenerateOffsets, final String className, long advancingOffset, final Field field) {
+- if ( Modifier.isStatic(field.getModifiers()) ) // static fields are never mapped
+- return null;
+-
+- // we only support primitives and ByteBuffers
+- if ( !field.getType().isPrimitive() && field.getType() != ByteBuffer.class )
+- throw new ClassFormatError("field '" + className + "." + field.getName() + "' not supported: " + field.getType());
+-
+- MappedField meta = field.getAnnotation(MappedField.class);
+- if ( meta == null && !autoGenerateOffsets )
+- 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. @Pointer field found: " + className + "." + field.getName() + ": " + field.getType());
+-
+- if ( Modifier.isVolatile(field.getModifiers()) && (pointer != null || field.getType() == ByteBuffer.class) )
+- throw new ClassFormatError("The volatile keyword is not supported for @Pointer or ByteBuffer fields. Volatile field found: " + className + "." + field.getName() + ": " + field.getType());
+-
+- // quick hack
+- long byteLength;
+- 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;
+- else if ( field.getType() == char.class || field.getType() == short.class )
+- byteLength = 2;
+- else if ( field.getType() == byte.class )
+- byteLength = 1;
+- else if ( field.getType() == ByteBuffer.class ) {
+- byteLength = meta.byteLength();
+- if ( byteLength < 0 )
+- throw new IllegalStateException("invalid byte length for mapped ByteBuffer field: " + className + "." + field.getName() + " [length=" + byteLength + "]");
+- } else
+- throw new ClassFormatError(field.getType().getName());
+-
+- if ( field.getType() != ByteBuffer.class && (advancingOffset % byteLength) != 0 )
+- throw new IllegalStateException("misaligned mapped type: " + className + "." + field.getName());
+-
+- CacheLinePad pad = field.getAnnotation(CacheLinePad.class);
+-
+- long byteOffset = advancingOffset;
+- if ( meta != null && meta.byteOffset() != -1 ) {
+- if ( meta.byteOffset() < 0 )
+- throw new ClassFormatError("Invalid field byte offset: " + className + "." + field.getName() + " [byteOffset=" + meta.byteOffset() + "]");
+- if ( pad != null )
+- throw new ClassFormatError("A field byte offset cannot be specified together with cache-line padding: " + className + "." + field.getName());
+-
+- byteOffset = meta.byteOffset();
+- }
+-
+- long byteLengthPadded = byteLength;
+- if ( pad != null ) {
+- // Pad before
+- if ( pad.before() && byteOffset % CacheUtil.getCacheLineSize() != 0 )
+- byteOffset += CacheUtil.getCacheLineSize() - (byteOffset & (CacheUtil.getCacheLineSize() - 1));
+-
+- // Pad after
+- if ( pad.after() && (byteOffset + byteLength) % CacheUtil.getCacheLineSize() != 0 )
+- byteLengthPadded += CacheUtil.getCacheLineSize() - (byteOffset + byteLength) % CacheUtil.getCacheLineSize();
+-
+- assert !pad.before() || (byteOffset % CacheUtil.getCacheLineSize() == 0);
+- assert !pad.after() || ((byteOffset + byteLengthPadded) % CacheUtil.getCacheLineSize() == 0);
+- }
+-
+- if ( PRINT_ACTIVITY )
+- LWJGLUtil.log(MappedObjectTransformer.class.getSimpleName() + ": " + className + "." + field.getName() + " [type=" + field.getType().getSimpleName() + ", offset=" + byteOffset + "]");
+-
+- return new FieldInfo(byteOffset, byteLength, byteLengthPadded, Type.getType(field.getType()), Modifier.isVolatile(field.getModifiers()), pointer != null);
+- }
+-
+- /** Removes final from methods that will be overriden by subclasses. */
+- static byte[] transformMappedObject(byte[] bytecode) {
+- final ClassWriter cw = new ClassWriter(0);
+-
+- ClassVisitor cv = new ClassAdapter(cw) {
+-
+- private final String[] DEFINALIZE_LIST = {
+- VIEWADDRESS_METHOD_NAME,
+- NEXT_METHOD_NAME,
+- ALIGN_METHOD_NAME,
+- SIZEOF_METHOD_NAME,
+- CAPACITY_METHOD_NAME,
+- };
+-
+- public MethodVisitor visitMethod(int access, final String name, final String desc, final String signature, final String[] exceptions) {
+- for ( String method : DEFINALIZE_LIST ) {
+- if ( name.equals(method) ) {
+- access &= ~ACC_FINAL;
+- break;
+- }
+- }
+- return super.visitMethod(access, name, desc, signature, exceptions);
+- }
+- };
+-
+- new ClassReader(bytecode).accept(cv, 0);
+- return cw.toByteArray();
+- }
+-
+- static byte[] transformMappedAPI(final String className, byte[] bytecode) {
+- final ClassWriter cw = new ClassWriter(COMPUTE_FRAMES) {
+-
+- @Override
+- protected String getCommonSuperClass(String a, String b) {
+- // HACK: prevent user-code static-initialization-blocks to be executed
+- if ( is_currently_computing_frames && !a.startsWith("java/") || !b.startsWith("java/") )
+- return "java/lang/Object";
+-
+- return super.getCommonSuperClass(a, b);
+- }
+-
+- };
+-
+- 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);
+-
+- return bytecode;
+- }
+-
+- private static ClassAdapter getMethodGenAdapter(final String className, final ClassVisitor cv) {
+- return new ClassAdapter(cv) {
+-
+- @Override
+- public void visitEnd() {
+- final MappedSubtypeInfo mappedSubtype = className_to_subtype.get(className);
+-
+- generateViewAddressGetter();
+- generateCapacity();
+- generateAlignGetter(mappedSubtype);
+- generateSizeofGetter();
+- generateNext();
+-
+- for ( String fieldName : mappedSubtype.fields.keySet() ) {
+- final FieldInfo field = mappedSubtype.fields.get(fieldName);
+-
+- if ( field.type.getDescriptor().length() > 1 ) { // ByteBuffer, getter only
+- generateByteBufferGetter(fieldName, field);
+- } else {
+- generateFieldGetter(fieldName, field);
+- generateFieldSetter(fieldName, field);
+- }
+- }
+-
+- super.visitEnd();
+- }
+-
+- private void generateViewAddressGetter() {
+- MethodVisitor mv = super.visitMethod(ACC_PUBLIC, VIEWADDRESS_METHOD_NAME, "(I)J", null, null);
+- mv.visitCode();
+- mv.visitVarInsn(ALOAD, 0);
+- mv.visitFieldInsn(GETFIELD, MAPPED_OBJECT_JVM, "baseAddress", "J");
+- mv.visitVarInsn(ILOAD, 1);
+- mv.visitFieldInsn(GETSTATIC, className, "SIZEOF", "I");
+- mv.visitInsn(IMUL);
+- mv.visitInsn(I2L);
+- mv.visitInsn(LADD);
+- if ( MappedObject.CHECKS ) {
+- mv.visitInsn(DUP2);
+- mv.visitVarInsn(ALOAD, 0);
+- mv.visitMethodInsn(INVOKESTATIC, MAPPED_HELPER_JVM, "checkAddress", "(JL" + MAPPED_OBJECT_JVM + ";)V");
+- }
+- mv.visitInsn(LRETURN);
+- mv.visitMaxs(3, 2);
+- mv.visitEnd();
+- }
+-
+- private void generateCapacity() {
+- // return (backingByteBuffer().capacity() + (int)(MemoryUtil.getAddress0(backingByteBuffer()) - baseAddress)) / SIZEOF;
+- MethodVisitor mv = super.visitMethod(ACC_PUBLIC, CAPACITY_METHOD_NAME, "()I", null, null);
+- mv.visitCode();
+- mv.visitVarInsn(ALOAD, 0);
+- mv.visitMethodInsn(INVOKEVIRTUAL, MAPPED_OBJECT_JVM, "backingByteBuffer", "()L" + jvmClassName(ByteBuffer.class) + ";");
+- mv.visitInsn(DUP);
+- mv.visitMethodInsn(INVOKEVIRTUAL, jvmClassName(ByteBuffer.class), "capacity", "()I");
+- mv.visitInsn(SWAP);
+- mv.visitMethodInsn(INVOKESTATIC, jvmClassName(MemoryUtil.class), "getAddress0", "(L" + jvmClassName(Buffer.class) + ";)J");
+- mv.visitVarInsn(ALOAD, 0);
+- mv.visitFieldInsn(GETFIELD, MAPPED_OBJECT_JVM, "baseAddress", "J");
+- mv.visitInsn(LSUB);
+- mv.visitInsn(L2I);
+- mv.visitInsn(IADD);
+- mv.visitFieldInsn(GETSTATIC, className, "SIZEOF", "I");
+- mv.visitInsn(IDIV);
+- mv.visitInsn(IRETURN);
+- mv.visitMaxs(3, 1);
+- mv.visitEnd();
+- }
+-
+- private void generateAlignGetter(final MappedSubtypeInfo mappedSubtype) {
+- MethodVisitor mv = super.visitMethod(ACC_PUBLIC, ALIGN_METHOD_NAME, "()I", null, null);
+- mv.visitCode();
+- visitIntNode(mv, mappedSubtype.sizeof);
+- mv.visitInsn(IRETURN);
+- mv.visitMaxs(1, 1);
+- mv.visitEnd();
+- }
+-
+- private void generateSizeofGetter() {
+- MethodVisitor mv = super.visitMethod(ACC_PUBLIC, SIZEOF_METHOD_NAME, "()I", null, null);
+- mv.visitCode();
+- mv.visitFieldInsn(GETSTATIC, className, "SIZEOF", "I");
+- mv.visitInsn(IRETURN);
+- mv.visitMaxs(1, 1);
+- mv.visitEnd();
+- }
+-
+- private void generateNext() {
+- MethodVisitor mv = super.visitMethod(ACC_PUBLIC, NEXT_METHOD_NAME, "()V", null, null);
+- mv.visitCode();
+- mv.visitVarInsn(ALOAD, 0);
+- mv.visitInsn(DUP);
+- mv.visitFieldInsn(GETFIELD, MAPPED_OBJECT_JVM, "viewAddress", "J");
+- mv.visitFieldInsn(GETSTATIC, className, "SIZEOF", "I");
+- mv.visitInsn(I2L);
+- mv.visitInsn(LADD);
+- mv.visitMethodInsn(INVOKEVIRTUAL, className, "setViewAddress", "(J)V");
+- mv.visitInsn(RETURN);
+- mv.visitMaxs(3, 1);
+- mv.visitEnd();
+- }
+-
+- 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, (int)field.offset);
+- mv.visitInsn(I2L);
+- mv.visitInsn(LADD);
+- visitIntNode(mv, (int)field.length);
+- mv.visitMethodInsn(INVOKESTATIC, MAPPED_HELPER_JVM, "newBuffer", "(JI)L" + jvmClassName(ByteBuffer.class) + ";");
+- mv.visitInsn(ARETURN);
+- mv.visitMaxs(3, 2);
+- mv.visitEnd();
+- }
+-
+- 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.getAccessType() + "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 ( field.type.getSort() ) {
+- case Type.BOOLEAN:
+- case Type.CHAR:
+- case Type.BYTE:
+- case Type.SHORT:
+- case Type.INT:
+- load = ILOAD;
+- break;
+- case Type.FLOAT:
+- load = FLOAD;
+- break;
+- case Type.LONG:
+- load = LLOAD;
+- break;
+- case Type.DOUBLE:
+- load = DLOAD;
+- break;
+- }
+- mv.visitVarInsn(load, 2);
+- mv.visitVarInsn(ALOAD, 0);
+- mv.visitVarInsn(ILOAD, 1);
+- 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.getAccessType() + "put", "(" + field.type.getDescriptor() + "J)V");
+- mv.visitInsn(RETURN);
+- mv.visitMaxs(4, 4);
+- mv.visitEnd();
+- }
+-
+- };
+- }
+-
+- private static class TransformationAdapter extends ClassAdapter {
+-
+- 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;
+- }
+-
+- if ( (access & ACC_STATIC) == 0 ) {
+- return new FieldNode(access, name, desc, signature, value) {
+- public void visitEnd() {
+- if ( visibleAnnotations == null ) { // early-out
+- accept(cv);
+- return;
+- }
+-
+- boolean before = false;
+- boolean after = false;
+- int byteLength = 0;
+- for ( AnnotationNode pad : visibleAnnotations ) {
+- if ( CACHE_LINE_PAD_JVM.equals(pad.desc) ) {
+- if ( "J".equals(desc) || "D".equals(desc) )
+- byteLength = 8;
+- else if ( "I".equals(desc) || "F".equals(desc) )
+- byteLength = 4;
+- else if ( "S".equals(desc) || "C".equals(desc) )
+- byteLength = 2;
+- else if ( "B".equals(desc) || "Z".equals(desc) )
+- byteLength = 1;
+- else
+- throw new ClassFormatError("The @CacheLinePad annotation cannot be used on non-primitive fields: " + className + "." + name);
+-
+- transformed = true;
+-
+- after = true;
+- if ( pad.values != null ) {
+- for ( int i = 0; i < pad.values.size(); i += 2 ) {
+- final boolean value = pad.values.get(i + 1).equals(Boolean.TRUE);
+- if ( "before".equals(pad.values.get(i)) )
+- before = value;
+- else
+- after = value;
+- }
+- }
+- break;
+- }
+- }
+-
+- /*
+- We make the fields public to force the JVM to keep the fields in the object.
+- Instead of using only longs or integers, we use the same type as the original
+- field. That's because modern JVMs usually reorder fields by type:
+- longs, then doubles, then integers, then booleans, etc. This way it's more
+- likely that the padding will work as expected.
+- */
+-
+- if ( before ) {
+- final int count = CacheUtil.getCacheLineSize() / byteLength - 1;
+- for ( int i = count; i >= 1; i-- )
+- cv.visitField(access | ACC_PUBLIC | ACC_SYNTHETIC, name + "$PAD_" + i, desc, signature, null);
+- }
+-
+- accept(cv);
+-
+- if ( after ) {
+- final int count = CacheUtil.getCacheLineSize() / byteLength - 1;
+- for ( int i = 1; i <= count; i++ )
+- cv.visitField(access | ACC_PUBLIC | ACC_SYNTHETIC, name + "$PAD" + i, desc, signature, null);
+- }
+- }
+- };
+- } else
+- return super.visitField(access, name, desc, signature, value);
+- }
+-
+- @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);
+- 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) {
+-
+- /** 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 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);
+- }
+-
+- @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 {
+- 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();
+- }
+-
+- private void transformMethod(final Frame<BasicValue>[] frames) {
+- final InsnList instructions = this.instructions;
+-
+- final Map<Integer, MappedSubtypeInfo> arrayVars = new HashMap<Integer, MappedSubtypeInfo>();
+-
+- /*
+- 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;
+- }
+- }
+- }
+- };
+- }
+- }
+-
+- static int transformMethodCall(final InsnList instructions, int i, final Map<AbstractInsnNode, Frame<BasicValue>> frameMap, final MethodInsnNode methodInsn, final MappedSubtypeInfo mappedType, final Map<Integer, MappedSubtypeInfo> arrayVars) {
+- switch ( methodInsn.getOpcode() ) {
+- case INVOKEVIRTUAL:
+- if ( "asArray".equals(methodInsn.name) && methodInsn.desc.equals("()[L" + MAPPED_OBJECT_JVM + ";") ) {
+- // Go forward and store the local variable index.
+- // We only allow this pattern: INVOKEVIRTUAL -> CHECKCAST -> ASTORE.
+- // We remove the first two and store the target MappedSubtype in the ASTORE variable
+- AbstractInsnNode nextInstruction;
+- checkInsnAfterIsArray(nextInstruction = methodInsn.getNext(), CHECKCAST);
+- checkInsnAfterIsArray(nextInstruction = nextInstruction.getNext(), ASTORE);
+-
+- final Frame<BasicValue> frame = frameMap.get(nextInstruction);
+- final String targetType = frame.getStack(frame.getStackSize() - 1).getType().getElementType().getInternalName();
+- if ( !methodInsn.owner.equals(targetType) ) {
+- /*
+- This may happen with the current API, like so:
+- MappedA foo = MappedA.malloc(...);
+- MappedB[] cursor = foo.asArray();
+- We have to parameterize MappedObject to avoid this.
+- */
+- throw new ClassCastException("Source: " + methodInsn.owner + " - Target: " + targetType);
+- }
+-
+- final VarInsnNode varInstruction = (VarInsnNode)nextInstruction;
+-
+- arrayVars.put(varInstruction.var, mappedType);
+-
+- instructions.remove(methodInsn.getNext()); // Remove CHECKCAST
+- instructions.remove(methodInsn); // Remove INVOKEVIRTUAL
+- }
+-
+- if ( "dup".equals(methodInsn.name) && methodInsn.desc.equals("()L" + MAPPED_OBJECT_JVM + ";") ) {
+- i = replace(instructions, i, methodInsn, generateDupInstructions(methodInsn));
+- break;
+- }
+-
+- if ( "slice".equals(methodInsn.name) && methodInsn.desc.equals("()L" + MAPPED_OBJECT_JVM + ";") ) {
+- i = replace(instructions, i, methodInsn, generateSliceInstructions(methodInsn));
+- break;
+- }
+-
+- if ( "runViewConstructor".equals(methodInsn.name) && "()V".equals(methodInsn.desc) ) {
+- i = replace(instructions, i, methodInsn, generateRunViewConstructorInstructions(methodInsn));
+- break;
+- }
+-
+- if ( "copyTo".equals(methodInsn.name) && methodInsn.desc.equals("(L" + MAPPED_OBJECT_JVM + ";)V") ) {
+- i = replace(instructions, i, methodInsn, generateCopyToInstructions(mappedType));
+- break;
+- }
+-
+- if ( "copyRange".equals(methodInsn.name) && methodInsn.desc.equals("(L" + MAPPED_OBJECT_JVM + ";I)V") ) {
+- i = replace(instructions, i, methodInsn, generateCopyRangeInstructions(mappedType));
+- break;
+- }
+-
+- break;
+- case INVOKESPECIAL:
+- // super() in VIEW_CONSTRUCTOR_NAME, remove
+- if ( methodInsn.owner.equals(MAPPED_OBJECT_JVM) && "<init>".equals(methodInsn.name) && "()V".equals(methodInsn.desc) ) {
+- instructions.remove(methodInsn.getPrevious()); // ALOAD
+- instructions.remove(methodInsn); // INVOKESPECIAL
+-
+- i -= 2;
+- }
+- break;
+- case INVOKESTATIC:
+- boolean isMapDirectMethod = "map".equals(methodInsn.name) && methodInsn.desc.equals("(JI)L" + MAPPED_OBJECT_JVM + ";");
+- boolean isMapBufferMethod = "map".equals(methodInsn.name) && methodInsn.desc.equals("(Ljava/nio/ByteBuffer;)L" + MAPPED_OBJECT_JVM + ";");
+- boolean isMallocMethod = "malloc".equals(methodInsn.name) && methodInsn.desc.equals("(I)L" + MAPPED_OBJECT_JVM + ";");
+-
+- if ( (isMapDirectMethod || isMapBufferMethod) || isMallocMethod )
+- i = replace(instructions, i, methodInsn, generateMapInstructions(mappedType, methodInsn.owner, isMapDirectMethod, isMallocMethod));
+- break;
+- }
+-
+- return i;
+- }
+-
+- private static InsnList generateCopyRangeInstructions(final MappedSubtypeInfo mappedType) {
+- final InsnList list = new InsnList();
+-
+- // stack: instances, target, this
+- list.add(getIntNode(mappedType.sizeof));
+- // stack: sizeof, instances, target, this
+- list.add(new InsnNode(IMUL));
+- // stack: bytes, target, this
+- list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, "copy", "(L" + MAPPED_OBJECT_JVM + ";L" + MAPPED_OBJECT_JVM + ";I)V"));
+- // stack: -
+-
+- return list;
+- }
+-
+- private static InsnList generateCopyToInstructions(final MappedSubtypeInfo mappedType) {
+- final InsnList list = new InsnList();
+-
+- // stack: target, this
+- list.add(getIntNode(mappedType.sizeof - mappedType.padding));
+- // stack: sizeof, target, this
+- list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, "copy", "(L" + MAPPED_OBJECT_JVM + ";L" + MAPPED_OBJECT_JVM + ";I)V"));
+- // stack: -
+-
+- return list;
+- }
+-
+- private static InsnList generateRunViewConstructorInstructions(final MethodInsnNode methodInsn) {
+- final InsnList list = new InsnList();
+-
+- // stack: this
+- list.add(new InsnNode(DUP));
+- // stack: this, this
+- list.add(new MethodInsnNode(INVOKEVIRTUAL, methodInsn.owner, VIEW_CONSTRUCTOR_NAME, "()V"));
+- // stack: this
+-
+- return list;
+- }
+-
+- private static InsnList generateSliceInstructions(final MethodInsnNode methodInsn) {
+- final InsnList list = new InsnList();
+-
+- // stack: this
+- list.add(new TypeInsnNode(NEW, methodInsn.owner));
+- // stack: new, this
+- list.add(new InsnNode(DUP));
+- // stack: new, new, this
+- list.add(new MethodInsnNode(INVOKESPECIAL, methodInsn.owner, "<init>", "()V"));
+- // stack: new, this
+- list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, "slice", "(L" + MAPPED_OBJECT_JVM + ";L" + MAPPED_OBJECT_JVM + ";)L" + MAPPED_OBJECT_JVM + ";"));
+- // stack: new
+-
+- return list;
+- }
+-
+- private static InsnList generateDupInstructions(final MethodInsnNode methodInsn) {
+- final InsnList list = new InsnList();
+-
+- // stack: this
+- list.add(new TypeInsnNode(NEW, methodInsn.owner));
+- // stack: new, this
+- list.add(new InsnNode(DUP));
+- // stack: new, new, this
+- list.add(new MethodInsnNode(INVOKESPECIAL, methodInsn.owner, "<init>", "()V"));
+- // stack: new, this
+- list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, "dup", "(L" + MAPPED_OBJECT_JVM + ";L" + MAPPED_OBJECT_JVM + ";)L" + MAPPED_OBJECT_JVM + ";"));
+- // stack: new
+-
+- return list;
+- }
+-
+- private static InsnList generateMapInstructions(final MappedSubtypeInfo mappedType, final String className, final boolean mapDirectMethod, final boolean mallocMethod) {
+- final InsnList trg = new InsnList();
+-
+- if ( mallocMethod ) {
+- // stack: count
+- trg.add(getIntNode(mappedType.sizeof));
+- // stack: sizeof, count
+- trg.add(new InsnNode(IMUL));
+- // stack: bytes
+- trg.add(new MethodInsnNode(INVOKESTATIC, mappedType.cacheLinePadded ? jvmClassName(CacheUtil.class) : jvmClassName(BufferUtils.class), "createByteBuffer", "(I)L" + jvmClassName(ByteBuffer.class) + ";"));
+- // stack: buffer
+- } else if ( mapDirectMethod ) {
+- // stack: capacity, address
+- trg.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, "newBuffer", "(JI)L" + jvmClassName(ByteBuffer.class) + ";"));
+- // stack: buffer
+- }
+-
+- // stack: buffer
+- trg.add(new TypeInsnNode(NEW, className));
+- // stack: new, buffer
+- trg.add(new InsnNode(DUP));
+- // stack: new, new, buffer
+- trg.add(new MethodInsnNode(INVOKESPECIAL, className, "<init>", "()V"));
+- // stack: new, buffer
+- trg.add(new InsnNode(DUP_X1));
+- // stack: new, buffer, new
+- trg.add(new InsnNode(SWAP));
+- // stack: buffer, new, new
+- trg.add(getIntNode(mappedType.align));
+- // stack: int, buffer, new, new
+- trg.add(getIntNode(mappedType.sizeof));
+- // stack: int, int, buffer, new, new
+- trg.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, "setup", "(L" + MAPPED_OBJECT_JVM + ";Ljava/nio/ByteBuffer;II)V"));
+- // stack: new
+-
+- return trg;
+- }
+-
+- static InsnList transformFieldAccess(final FieldInsnNode fieldInsn) {
+- final MappedSubtypeInfo mappedSubtype;
+- mappedSubtype = className_to_subtype.get(fieldInsn.owner);
+- if ( mappedSubtype == null ) { // early out
+- // MappedSet.view
+- outer:
+- if ( "view".equals(fieldInsn.name) && fieldInsn.owner.startsWith(MAPPEDSET_PREFIX) )
+- return generateSetViewInstructions(fieldInsn);
+-
+- return null; // early out
+- }
+-
+- if ( "SIZEOF".equals(fieldInsn.name) )
+- return generateSIZEOFInstructions(fieldInsn, mappedSubtype);
+-
+- if ( "view".equals(fieldInsn.name) )
+- return generateViewInstructions(fieldInsn, mappedSubtype);
+-
+- if ( "baseAddress".equals(fieldInsn.name) || "viewAddress".equals(fieldInsn.name) ) {
+- return generateAddressInstructions(fieldInsn);
+- }
+-
+- 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, field.offset);
+-
+- // we're now going to transform the field access
+- return generateFieldInstructions(fieldInsn, field);
+- }
+-
+- private static InsnList generateSetViewInstructions(final FieldInsnNode fieldInsn) {
+- if ( fieldInsn.getOpcode() == GETFIELD )
+- throwAccessErrorOnReadOnlyField(fieldInsn.owner, fieldInsn.name);
+- if ( fieldInsn.getOpcode() != PUTFIELD )
+- throw new InternalError();
+-
+- final InsnList list = new InsnList();
+-
+- // stack: index, this
+- if ( MAPPED_SET2_JVM.equals(fieldInsn.owner) )
+- list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, "put_views", "(L" + MAPPED_SET2_JVM + ";I)V"));
+- else if ( MAPPED_SET3_JVM.equals(fieldInsn.owner) )
+- list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, "put_views", "(L" + MAPPED_SET3_JVM + ";I)V"));
+- else if ( MAPPED_SET4_JVM.equals(fieldInsn.owner) )
+- list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, "put_views", "(L" + MAPPED_SET4_JVM + ";I)V"));
+- else
+- throw new InternalError();
+- // stack: -
+-
+- return list;
+- }
+-
+- private static InsnList generateSIZEOFInstructions(final FieldInsnNode fieldInsn, final MappedSubtypeInfo mappedSubtype) {
+- if ( !"I".equals(fieldInsn.desc) )
+- throw new InternalError();
+-
+- final InsnList list = new InsnList();
+-
+- if ( fieldInsn.getOpcode() == GETSTATIC ) {
+- list.add(getIntNode(mappedSubtype.sizeof));
+- return list;
+- }
+-
+- if ( fieldInsn.getOpcode() == PUTSTATIC )
+- throwAccessErrorOnReadOnlyField(fieldInsn.owner, fieldInsn.name);
+-
+- throw new InternalError();
+- }
+-
+- private static InsnList generateViewInstructions(final FieldInsnNode fieldInsn, final MappedSubtypeInfo mappedSubtype) {
+- if ( !"I".equals(fieldInsn.desc) )
+- throw new InternalError();
+-
+- final InsnList list = new InsnList();
+-
+- if ( fieldInsn.getOpcode() == GETFIELD ) {
+- if ( mappedSubtype.sizeof_shift != 0 ) {
+- // stack: instance
+- list.add(getIntNode(mappedSubtype.sizeof_shift));
+- // stack: sizeof, instance
+- list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, "get_view_shift", "(L" + MAPPED_OBJECT_JVM + ";I)I"));
+- // stack: view
+- } else {
+- // stack: instance
+- list.add(getIntNode(mappedSubtype.sizeof));
+- // stack: sizeof, instance
+- list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, "get_view", "(L" + MAPPED_OBJECT_JVM + ";I)I"));
+- // stack: view
+- }
+- return list;
+- }
+-
+- if ( fieldInsn.getOpcode() == PUTFIELD ) {
+- if ( mappedSubtype.sizeof_shift != 0 ) {
+- // stack: view, instance
+- list.add(getIntNode(mappedSubtype.sizeof_shift));
+- // stack: sizeof, view, instance
+- list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, "put_view_shift", "(L" + MAPPED_OBJECT_JVM + ";II)V"));
+- // stack: -
+- } else {
+- // stack: view, instance
+- list.add(getIntNode(mappedSubtype.sizeof));
+- // stack: sizeof, view, instance
+- list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, "put_view", "(L" + MAPPED_OBJECT_JVM + ";II)V"));
+- // stack: -
+- }
+- return list;
+- }
+-
+- throw new InternalError();
+- }
+-
+- private static InsnList generateAddressInstructions(final FieldInsnNode fieldInsn) {
+- if ( !"J".equals(fieldInsn.desc) )
+- throw new IllegalStateException();
+-
+- if ( fieldInsn.getOpcode() == GETFIELD ) // do not change a thing
+- return null;
+-
+- if ( fieldInsn.getOpcode() == PUTFIELD )
+- throwAccessErrorOnReadOnlyField(fieldInsn.owner, fieldInsn.name);
+-
+- throw new InternalError();
+- }
+-
+- 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 InsnList list = new InsnList();
+-
+- // stack: ref
+- list.add(new FieldInsnNode(GETFIELD, mappedSubtype.className, "viewAddress", "J"));
+- // stack: long
+- list.add(new LdcInsnNode(fieldOffset));
+- // stack: long, long
+- list.add(new InsnNode(LADD));
+- // stack: long
+- list.add(new LdcInsnNode(mappedSubtype.fields.get(fieldInsn.name).length));
+- // stack: long, long
+- list.add(new InsnNode(L2I));
+- // stack: int, long
+- list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, "newBuffer", "(JI)L" + jvmClassName(ByteBuffer.class) + ";"));
+- // stack: buffer
+-
+- return list;
+- }
+-
+- throw new InternalError();
+- }
+-
+- private static InsnList generateFieldInstructions(final FieldInsnNode fieldInsn, final FieldInfo field) {
+- final InsnList list = new InsnList();
+-
+- if ( fieldInsn.getOpcode() == PUTFIELD ) {
+- // stack: value, ref
+- list.add(getIntNode((int)field.offset));
+- // stack: fieldOffset, value, ref
+- list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, field.getAccessType() + "put", "(L" + MAPPED_OBJECT_JVM + ";" + fieldInsn.desc + "I)V"));
+- // stack -
+- return list;
+- }
+-
+- if ( fieldInsn.getOpcode() == GETFIELD ) {
+- // stack: ref
+- list.add(getIntNode((int)field.offset));
+- // stack: fieldOffset, ref
+- list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, field.getAccessType() + "get", "(L" + MAPPED_OBJECT_JVM + ";I)" + fieldInsn.desc));
+- // stack: -
+- return list;
+- }
+-
+- throw new InternalError();
+- }
+-
+- static int transformArrayAccess(final InsnList instructions, int i, final Map<AbstractInsnNode, Frame<BasicValue>> frameMap, final VarInsnNode loadInsn, final MappedSubtypeInfo mappedSubtype, final int var) {
+- // We need to go forward in time to find how we use the array var
+- final int loadStackSize = frameMap.get(loadInsn).getStackSize() + 1;
+-
+- AbstractInsnNode nextInsn = loadInsn;
+-
+- while ( true ) {
+- nextInsn = nextInsn.getNext();
+- if ( nextInsn == null )
+- throw new InternalError();
+-
+- Frame<BasicValue> frame = frameMap.get(nextInsn);
+- if ( frame == null )
+- continue;
+-
+- int stackSize = frame.getStackSize();
+-
+- if ( stackSize == loadStackSize + 1 && nextInsn.getOpcode() == AALOAD ) {
+- final AbstractInsnNode aaLoadInsn = nextInsn;
+-
+- while ( true ) {
+- nextInsn = nextInsn.getNext();
+- if ( nextInsn == null )
+- break;
+-
+- frame = frameMap.get(nextInsn);
+- if ( frame == null )
+- continue;
+- stackSize = frame.getStackSize();
+-
+- if ( stackSize == loadStackSize + 1 && nextInsn.getOpcode() == PUTFIELD ) {
+- final FieldInsnNode fieldInsn = (FieldInsnNode)nextInsn;
+-
+- // stack: value, view, ref
+- instructions.insert(nextInsn, new MethodInsnNode(INVOKESTATIC, mappedSubtype.className, setterName(fieldInsn.name), "(L" + mappedSubtype.className + ";I" + fieldInsn.desc + ")V"));
+- // stack: -
+- instructions.remove(nextInsn);
+-
+- break;
+- } else if ( stackSize == loadStackSize && nextInsn.getOpcode() == GETFIELD ) {
+- final FieldInsnNode fieldInsn = (FieldInsnNode)nextInsn;
+-
+- // stack: view, ref
+- instructions.insert(nextInsn, new MethodInsnNode(INVOKESTATIC, mappedSubtype.className, getterName(fieldInsn.name), "(L" + mappedSubtype.className + ";I)" + fieldInsn.desc));
+- // stack: value
+- instructions.remove(nextInsn);
+-
+- break;
+- } else if ( stackSize == loadStackSize && nextInsn.getOpcode() == DUP && nextInsn.getNext().getOpcode() == GETFIELD ) {
+- // May happen with operator+assignment (e.g. cursor[i].value += 10)
+- final FieldInsnNode fieldInsn = (FieldInsnNode)nextInsn.getNext();
+-
+- final MethodInsnNode getter = new MethodInsnNode(INVOKESTATIC, mappedSubtype.className, getterName(fieldInsn.name), "(L" + mappedSubtype.className + ";I)" + fieldInsn.desc);
+-
+- // stack: view, ref
+- instructions.insert(nextInsn, new InsnNode(DUP2));
+- // stack: view, ref, view, ref
+- instructions.insert(nextInsn.getNext(), getter);
+- // stack: value, view, ref
+-
+- instructions.remove(nextInsn);
+- instructions.remove(fieldInsn);
+-
+- nextInsn = getter;
+- continue;
+- } else if ( stackSize < loadStackSize )
+- throw new ClassFormatError("Invalid " + mappedSubtype.className + " view array usage detected: " + getOpcodeName(nextInsn));
+- }
+-
+- instructions.remove(aaLoadInsn);
+-
+- return i;
+- } else if ( stackSize == loadStackSize && nextInsn.getOpcode() == ARRAYLENGTH ) {
+- if ( LWJGLUtil.DEBUG && loadInsn.getNext() != nextInsn )
+- throw new InternalError();
+-
+- instructions.remove(nextInsn);
+- loadInsn.var = var;
+- instructions.insert(loadInsn, new MethodInsnNode(INVOKEVIRTUAL, mappedSubtype.className, CAPACITY_METHOD_NAME, "()I"));
+-
+- return i + 1;
+- } else if ( stackSize < loadStackSize ) // Consumed by something other than AALOAD or ARRAYLENGTH
+- throw new ClassFormatError("Invalid " + mappedSubtype.className + " view array usage detected: " + getOpcodeName(nextInsn));
+- }
+- }
+-
+- private static class FieldInfo {
+-
+- final long offset;
+- final long length;
+- final long lengthPadded;
+- final Type type;
+- final boolean isVolatile;
+- final boolean isPointer;
+-
+- FieldInfo(final long offset, final long length, final long lengthPadded, final Type type, final boolean isVolatile, final boolean isPointer) {
+- this.offset = offset;
+- this.length = length;
+- this.lengthPadded = lengthPadded;
+- this.type = type;
+- this.isVolatile = isVolatile;
+- this.isPointer = isPointer;
+- }
+-
+- String getAccessType() {
+- return isPointer ? "a" : type.getDescriptor().toLowerCase() + (isVolatile ? "v" : "");
+- }
+-
+- }
+-
+- private static class MappedSubtypeInfo {
+-
+- final String className;
+-
+- final int sizeof;
+- final int sizeof_shift;
+- final int align;
+- final int padding;
+- final boolean cacheLinePadded;
+-
+- final Map<String, FieldInfo> fields;
+-
+- MappedSubtypeInfo(String className, Map<String, FieldInfo> fields, int sizeof, int align, int padding, final boolean cacheLinePadded) {
+- 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.padding = padding;
+- this.cacheLinePadded = cacheLinePadded;
+-
+- this.fields = fields;
+- }
+-
+- private static int getPoT(int value) {
+- int pot = -1;
+- while ( value > 0 ) {
+- pot++;
+- value >>= 1;
+- }
+- return pot;
+- }
+-
+- }
+-
+- // -------------------------------------------------------
+- // -------------------[ MACROS & UTILS ]------------------
+- // -------------------------------------------------------
+-
+- private static void getClassEnums(final Class clazz, final Map<Integer, String> map, final String... prefixFilters) {
+- try {
+- OUTER:
+- for ( Field field : clazz.getFields() ) {
+- if ( !Modifier.isStatic(field.getModifiers()) || field.getType() != int.class )
+- continue;
+-
+- for ( String filter : prefixFilters ) {
+- if ( field.getName().startsWith(filter) )
+- continue OUTER;
+- }
+-
+- if ( map.put((Integer)field.get(null), field.getName()) != null )
+- throw new IllegalStateException();
+- }
+- } catch (Exception e) {
+- e.printStackTrace();
+- }
+- }
+-
+- static String getOpcodeName(final AbstractInsnNode insn) {
+- final String op = OPCODE_TO_NAME.get(insn.getOpcode());
+- return INSNTYPE_TO_NAME.get(insn.getType()) + ": " + insn.getOpcode() + (op == null ? "" : " [" + OPCODE_TO_NAME.get(insn.getOpcode()) + "]");
+- }
+-
+- static String jvmClassName(Class<?> type) {
+- return type.getName().replace('.', '/');
+- }
+-
+- static String getterName(final String fieldName) {
+- return "get$" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1) + "$LWJGL";
+- }
+-
+- static String setterName(final String fieldName) {
+- return "set$" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1) + "$LWJGL";
+- }
+-
+- private static void checkInsnAfterIsArray(final AbstractInsnNode instruction, final int opcode) {
+- if ( instruction == null )
+- throw new ClassFormatError("Unexpected end of instructions after .asArray() method.");
+-
+- if ( instruction.getOpcode() != opcode )
+- throw new ClassFormatError("The result of .asArray() must be stored to a local variable. Found: " + getOpcodeName(instruction));
+- }
+-
+- static AbstractInsnNode getIntNode(final int value) {
+- if ( value <= 5 && -1 <= value )
+- return new InsnNode(ICONST_M1 + value + 1);
+-
+- if ( value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE )
+- return new IntInsnNode(BIPUSH, value);
+-
+- if ( value >= Short.MIN_VALUE && value <= Short.MAX_VALUE )
+- return new IntInsnNode(SIPUSH, value);
+-
+- return new LdcInsnNode(value);
+- }
+-
+- static void visitIntNode(final MethodVisitor mv, final int value) {
+- if ( value <= 5 && -1 <= value )
+- mv.visitInsn(ICONST_M1 + value + 1);
+- else if ( value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE )
+- mv.visitIntInsn(BIPUSH, value);
+- else if ( value >= Short.MIN_VALUE && value <= Short.MAX_VALUE )
+- mv.visitIntInsn(SIPUSH, value);
+- else
+- mv.visitLdcInsn(value);
+- }
+-
+- /** Replace an instruction with a list of instructions. */
+- static int replace(final InsnList instructions, final int i, final AbstractInsnNode location, final InsnList list) {
+- final int size = list.size();
+-
+- instructions.insert(location, list);
+- instructions.remove(location);
+-
+- return i + (size - 1);
+- }
+-
+- private static void throwAccessErrorOnReadOnlyField(String className, String fieldName) {
+- throw new IllegalAccessError("The " + className + "." + fieldName + " field is final.");
+- }
+-
+- private static void printBytecode(byte[] bytecode) {
+- StringWriter sw = new StringWriter();
+- ClassVisitor tracer = new TraceClassVisitor(new ClassWriter(0), new PrintWriter(sw));
+- new ClassReader(bytecode).accept(tracer, 0);
+- String dump = sw.toString();
+-
+- LWJGLUtil.log(dump);
+- }
+-
+-}
+\ No newline at end of file
diff --git a/debian/patches/series b/debian/patches/series
index 0b1e96c..fde7631 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -4,3 +4,4 @@ allarchs.patch
ppc64el.patch
systemjinput.patch
build-failure.patch
+no-asm-support.patch
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/lwjgl.git
More information about the pkg-java-commits
mailing list