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 <[email protected]> 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 <[email protected]> +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 */ +-@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 _______________________________________________ pkg-java-commits mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-java-commits

