diff --git a/debian/changelog b/debian/changelog index e621e9d..39aa244 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,7 +1,7 @@ lwjgl (2.9.3+dfsg-4) UNRELEASED; urgency=medium * Team upload. - * Simplified the OS X patch + * Simplified the OS X and ASM patches * Standards-Version updated to 4.2.1 * Use salsa.debian.org Vcs-* URLs diff --git a/debian/patches/no-asm-support.patch b/debian/patches/no-asm-support.patch index a5e3f8d..e259b76 100644 --- a/debian/patches/no-asm-support.patch +++ b/debian/patches/no-asm-support.patch @@ -1,2456 +1,17 @@ Description: No asm support. We don't support the optional asm.jar because LWJGL is not compatible with libasm-java. Author: Markus Koschany Forwarded: not-needed ---- 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 ---- 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.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 ---- 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 ---- 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)}. -- *

-- * The transformer supports some debugging tools, enabled through JVM system properties:
-- * org.lwjgl.util.mapped.PrintTiming=true, prints timing information for the transformation step.
-- * org.lwjgl.util.mapped.PrintActivity=true, prints activity information.
-- * org.lwjgl.util.mapped.PrintBytecode=true, prints the transformed bytecode.
-- * 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 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 OPCODE_TO_NAME = new HashMap(); -- static final Map INSNTYPE_TO_NAME = new HashMap(); -- -- 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(); -- -- { -- // 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 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 fields = new HashMap(); -- -- 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 ( "".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, "", "()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[] analyse() throws AnalyzerException { -- final Analyzer a = new Analyzer(new SimpleVerifier()); -- a.analyze(className, this); -- return a.getFrames(); -- } -- -- private void transformMethod(final Frame[] frames) { -- final InsnList instructions = this.instructions; -- -- final Map arrayVars = new HashMap(); -- -- /* -- 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> frameMap = new HashMap>(); -- 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> frameMap, final MethodInsnNode methodInsn, final MappedSubtypeInfo mappedType, final Map 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 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) && "".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, "", "()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, "", "()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, "", "()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> 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 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 fields; -- -- MappedSubtypeInfo(String className, Map 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 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 +--- a/build.xml ++++ b/build.xml +@@ -476,8 +476,10 @@ + + + ++ ++ + +- ++ + + +