diff --git a/pom.xml b/pom.xml
index b0d1f98..c6795ae 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
4.0.0
org.lwjglx
lwjglx-debug
- 1.0.5
+ 1.0.6
LWJGLX/debug
Debug LWJGL3 OpenGL applications
2017
diff --git a/src/org/lwjglx/debug/Agent.java b/src/org/lwjglx/debug/Agent.java
index 3b0a01a..5883ec6 100644
--- a/src/org/lwjglx/debug/Agent.java
+++ b/src/org/lwjglx/debug/Agent.java
@@ -188,31 +188,33 @@ public void visitInvokeDynamicInsn(String dynamicname, String descriptor, Handle
Object secondArgument = bootstrapMethodArguments[1];
if (secondArgument instanceof Handle) {
Handle h = (Handle) secondArgument;
- String owner = h.getOwner();
- String name = h.getName();
- String desc = h.getDesc();
- if (owner.startsWith("org/lwjgl/") && !excluded(owner, name)) {
- String key = owner + "." + name + desc;
- InterceptedCall call = calls.get(key);
- if (call == null) {
- /* Resolve declaring class */
- String resolvedOwner = resolveOwner(owner, name, desc);
- /* Rewrite a GLnnC call to GLnn to be able to intercept the call */
- if (resolvedOwner.matches(".*/GL(\\d\\d)C$")) {
- resolvedOwner = resolvedOwner.substring(0, resolvedOwner.length() - 1);
+ if (h.getTag() == H_INVOKESTATIC) {
+ String owner = h.getOwner();
+ String name = h.getName();
+ String desc = h.getDesc();
+ if (owner.startsWith("org/lwjgl/") && !excluded(owner, name)) {
+ String key = owner + "." + name + desc;
+ InterceptedCall call = calls.get(key);
+ if (call == null) {
+ /* Resolve declaring class */
+ String resolvedOwner = resolveOwner(owner, name, desc);
+ /* Rewrite a GLnnC call to GLnn to be able to intercept the call */
+ if (resolvedOwner.matches(".*/GL(\\d\\d)C$")) {
+ resolvedOwner = resolvedOwner.substring(0, resolvedOwner.length() - 1);
+ }
+ call = new InterceptedCall(owner, resolvedOwner, name, desc);
+ String methodName;
+ methodName = name + call.index;
+ call.generatedMethodName = methodName;
+ calls.put(key, call);
}
- call = new InterceptedCall(owner, resolvedOwner, name, desc);
- String methodName;
- methodName = name + call.index;
- call.generatedMethodName = methodName;
- calls.put(key, call);
+ Log.maxLineNumberLength = Math.max(Log.maxLineNumberLength, (int) (Math.log10(lastLineNumber) + 1));
+ // modify invokedynamic handle
+ Handle newHandle = new Handle(H_INVOKESTATIC, proxyName, call.generatedMethodName, call.desc, false);
+ bootstrapMethodArguments[1] = newHandle;
+ modifications.needsProxyClass = true;
}
- Log.maxLineNumberLength = Math.max(Log.maxLineNumberLength, (int) (Math.log10(lastLineNumber) + 1));
- // modify invokedynamic handle
- Handle newHandle = new Handle(H_INVOKESTATIC, proxyName, call.generatedMethodName, call.desc, false);
- bootstrapMethodArguments[1] = newHandle;
- modifications.needsProxyClass = true;
- }
+ }
}
}
super.visitInvokeDynamicInsn(dynamicname, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);
diff --git a/src/org/lwjglx/debug/InterceptClassGenerator.java b/src/org/lwjglx/debug/InterceptClassGenerator.java
index 95cf4cb..410a128 100644
--- a/src/org/lwjglx/debug/InterceptClassGenerator.java
+++ b/src/org/lwjglx/debug/InterceptClassGenerator.java
@@ -438,6 +438,23 @@ public static Class> generate(ClassLoader classLoader, String proxyInternalNam
}
mv.visitMaxs(-1, -1);
mv.visitEnd();
+
+ if (TRACE.enabled) {
+ MethodVisitor mvOverload = cw.visitMethod(ACC_PUBLIC | ACC_STATIC | ACC_SYNTHETIC, call.generatedMethodName, call.desc, null, null);
+ mvOverload.visitCode();
+ Type[] paramTypes = Type.getArgumentTypes(call.desc);
+ int var = 0;
+ for (Type paramType : paramTypes) {
+ mvOverload.visitVarInsn(paramType.getOpcode(ILOAD), var);
+ var += paramType.getSize();
+ }
+ Util.ldcI(mvOverload, -1);
+ mvOverload.visitMethodInsn(INVOKESTATIC, proxyInternalName, call.generatedMethodName, effectiveDesc, false);
+ Type retType = Type.getReturnType(call.desc);
+ mvOverload.visitInsn(retType.getOpcode(IRETURN));
+ mvOverload.visitMaxs(-1, -1);
+ mvOverload.visitEnd();
+ }
}
cw.visitEnd();
byte[] arr = cw.toByteArray();
diff --git a/src/org/lwjglx/debug/org/lwjgl/opengl/CGL.java b/src/org/lwjglx/debug/org/lwjgl/opengl/CGL.java
new file mode 100644
index 0000000..6b48c82
--- /dev/null
+++ b/src/org/lwjglx/debug/org/lwjgl/opengl/CGL.java
@@ -0,0 +1,23 @@
+package org.lwjglx.debug.org.lwjgl.opengl;
+
+import static org.lwjglx.debug.org.lwjgl.opengl.Context.*;
+
+public class CGL {
+
+ public static int CGLSetCurrentContext(long context) {
+ int result = org.lwjgl.opengl.CGL.CGLSetCurrentContext(context);
+ if (result == org.lwjgl.opengl.CGL.kCGLNoError) {
+ if (context == 0L) {
+ CURRENT_CONTEXT.remove();
+ } else {
+ Context ctx = CONTEXTS.get(context);
+ if (ctx == null) {
+ Context.create(context, 0L);
+ ctx = CONTEXTS.get(context);
+ }
+ CURRENT_CONTEXT.set(ctx);
+ }
+ }
+ return result;
+ }
+}
diff --git a/src/org/lwjglx/debug/org/lwjgl/opengl/Context.java b/src/org/lwjglx/debug/org/lwjgl/opengl/Context.java
index c88a568..2131d62 100644
--- a/src/org/lwjglx/debug/org/lwjgl/opengl/Context.java
+++ b/src/org/lwjglx/debug/org/lwjgl/opengl/Context.java
@@ -158,6 +158,16 @@ public static Context currentContext() {
return ctx;
}
+ public static Context createCurrent() {
+ org.lwjglx.debug.Log.warn("No OpenGL context has been made current through recognized API methods. Created a fallback context.", new Throwable(), 3);
+ Context ctx = new Context();
+ ctx.counter = CONTEXT_COUNTER.getAndIncrement();
+ ctx.shareGroup = new ShareGroup();
+ ctx.shareGroup.contexts.add(ctx);
+ CURRENT_CONTEXT.set(ctx);
+ return ctx;
+ }
+
public static void create(long window, long share) {
Context ctx = new Context();
ctx.window = window;
diff --git a/src/org/lwjglx/debug/org/lwjgl/opengl/GL.java b/src/org/lwjglx/debug/org/lwjgl/opengl/GL.java
index c405316..79983a4 100644
--- a/src/org/lwjglx/debug/org/lwjgl/opengl/GL.java
+++ b/src/org/lwjglx/debug/org/lwjgl/opengl/GL.java
@@ -34,7 +34,10 @@ public static org.lwjgl.opengl.GLCapabilities createCapabilities() {
if (Properties.VALIDATE.enabled) {
callback = GLUtil.setupDebugMessageCallback();
}
- Context context = Context.currentContext();
+ Context context = CURRENT_CONTEXT.get();
+ if (context == null) {
+ context = Context.createCurrent();
+ }
context.caps = caps;
context.debugCallback = callback;
int GL_MAX_VERTEX_ATTRIBS = 16;
@@ -53,7 +56,10 @@ public static org.lwjgl.opengl.GLCapabilities createCapabilities(boolean forward
if (Properties.VALIDATE.enabled) {
callback = GLUtil.setupDebugMessageCallback();
}
- Context context = Context.currentContext();
+ Context context = CURRENT_CONTEXT.get();
+ if (context == null) {
+ context = Context.createCurrent();
+ }
context.caps = caps;
context.debugCallback = callback;
int GL_MAX_VERTEX_ATTRIBS = 16;
@@ -69,6 +75,9 @@ public static org.lwjgl.opengl.GLCapabilities createCapabilities(boolean forward
public static void setCapabilities(org.lwjgl.opengl.GLCapabilities caps) {
org.lwjgl.opengl.GL.setCapabilities(caps);
Context context = CURRENT_CONTEXT.get();
+ if (context == null && caps != null) {
+ context = Context.createCurrent();
+ }
if (context != null) {
/* Can happen when calling setCapabilities(null) after glfwDestroyWindow()/glfwMakeContextCurrent(0L) */
context.caps = caps;
diff --git a/src/org/lwjglx/debug/org/lwjgl/opengl/GLX.java b/src/org/lwjglx/debug/org/lwjgl/opengl/GLX.java
new file mode 100644
index 0000000..740bbc6
--- /dev/null
+++ b/src/org/lwjglx/debug/org/lwjgl/opengl/GLX.java
@@ -0,0 +1,23 @@
+package org.lwjglx.debug.org.lwjgl.opengl;
+
+import static org.lwjglx.debug.org.lwjgl.opengl.Context.*;
+
+public class GLX {
+
+ public static boolean glXMakeCurrent(long display, long drawable, long ctx) {
+ boolean result = org.lwjgl.opengl.GLX.glXMakeCurrent(display, drawable, ctx);
+ if (result) {
+ if (ctx == 0L) {
+ CURRENT_CONTEXT.remove();
+ } else {
+ Context context = CONTEXTS.get(ctx);
+ if (context == null) {
+ Context.create(ctx, 0L);
+ context = CONTEXTS.get(ctx);
+ }
+ CURRENT_CONTEXT.set(context);
+ }
+ }
+ return result;
+ }
+}
diff --git a/src/org/lwjglx/debug/org/lwjgl/opengl/GLX13.java b/src/org/lwjglx/debug/org/lwjgl/opengl/GLX13.java
new file mode 100644
index 0000000..3003314
--- /dev/null
+++ b/src/org/lwjglx/debug/org/lwjgl/opengl/GLX13.java
@@ -0,0 +1,23 @@
+package org.lwjglx.debug.org.lwjgl.opengl;
+
+import static org.lwjglx.debug.org.lwjgl.opengl.Context.*;
+
+public class GLX13 {
+
+ public static boolean glXMakeContextCurrent(long display, long draw, long read, long ctx) {
+ boolean result = org.lwjgl.opengl.GLX13.glXMakeContextCurrent(display, draw, read, ctx);
+ if (result) {
+ if (ctx == 0L) {
+ CURRENT_CONTEXT.remove();
+ } else {
+ Context context = CONTEXTS.get(ctx);
+ if (context == null) {
+ Context.create(ctx, 0L);
+ context = CONTEXTS.get(ctx);
+ }
+ CURRENT_CONTEXT.set(context);
+ }
+ }
+ return result;
+ }
+}
diff --git a/src/org/lwjglx/debug/org/lwjgl/opengl/WGL.java b/src/org/lwjglx/debug/org/lwjgl/opengl/WGL.java
new file mode 100644
index 0000000..f1fb6de
--- /dev/null
+++ b/src/org/lwjglx/debug/org/lwjgl/opengl/WGL.java
@@ -0,0 +1,23 @@
+package org.lwjglx.debug.org.lwjgl.opengl;
+
+import static org.lwjglx.debug.org.lwjgl.opengl.Context.*;
+
+public class WGL {
+
+ public static boolean wglMakeCurrent(java.nio.IntBuffer buffer, long hdc, long hglrc) {
+ boolean result = org.lwjgl.opengl.WGL.wglMakeCurrent(buffer, hdc, hglrc);
+ if (result) {
+ if (hglrc == 0L) {
+ CURRENT_CONTEXT.remove();
+ } else {
+ Context context = CONTEXTS.get(hglrc);
+ if (context == null) {
+ Context.create(hglrc, 0L);
+ context = CONTEXTS.get(hglrc);
+ }
+ CURRENT_CONTEXT.set(context);
+ }
+ }
+ return result;
+ }
+}
diff --git a/test/test/DebugIT.java b/test/test/DebugIT.java
index 27859c5..318fcc4 100644
--- a/test/test/DebugIT.java
+++ b/test/test/DebugIT.java
@@ -29,6 +29,11 @@
import org.lwjgl.system.*;
import org.lwjglx.debug.Properties;
import org.lwjglx.debug.org.lwjgl.opengl.Context;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
public class DebugIT {
@@ -40,6 +45,7 @@ public class DebugIT {
private static final Set CORE_PROFILE_TESTS = new HashSet<>(Arrays.asList(
"testMethodReferences",
+ "testMethodReferencesWithTraceEnabled",
"testNoVertexAttribPointerInCustomVAO",
"testNoVertexAttribPointerInCustomVAOWithIndicesBuffer",
"testBindVAOFromSharedContext",
@@ -254,6 +260,131 @@ public void testMethodReferences() {
glEnableVertexAttribArray(3);
}
+ private static class CustomClassLoader extends ClassLoader {
+ CustomClassLoader(ClassLoader parent) {
+ super(parent);
+ }
+ public Class> define(String name, byte[] b) {
+ return defineClass(name, b, 0, b.length);
+ }
+ }
+
+ @Test
+ public void testMethodReferencesWithTraceEnabled() throws Exception {
+ boolean oldTrace = Properties.TRACE.enabled;
+ Properties.TRACE.enabled = true;
+ try {
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+ cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, "test/DynamicTraceTestClass", null, "java/lang/Object", null);
+
+ MethodVisitor ctor = cw.visitMethod(Opcodes.ACC_PUBLIC, "", "()V", null, null);
+ ctor.visitCode();
+ ctor.visitVarInsn(Opcodes.ALOAD, 0);
+ ctor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "", "()V", false);
+ ctor.visitInsn(Opcodes.RETURN);
+ ctor.visitMaxs(-1, -1);
+ ctor.visitEnd();
+
+ MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "getSupplier", "()Ljava/util/function/Supplier;", null, null);
+ mv.visitCode();
+
+ Handle bootstrapMethod = new Handle(
+ Opcodes.H_INVOKESTATIC,
+ "java/lang/invoke/LambdaMetafactory",
+ "metafactory",
+ "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;",
+ false
+ );
+
+ mv.visitInvokeDynamicInsn(
+ "get",
+ "()Ljava/util/function/Supplier;",
+ bootstrapMethod,
+ Type.getType("()Ljava/lang/Object;"),
+ new Handle(
+ Opcodes.H_INVOKESTATIC,
+ "org/lwjgl/opengl/GL32C",
+ "glGenVertexArrays",
+ "()I",
+ false
+ ),
+ Type.getType("()Ljava/lang/Integer;")
+ );
+ mv.visitInsn(Opcodes.ARETURN);
+ mv.visitMaxs(-1, -1);
+ mv.visitEnd();
+ cw.visitEnd();
+ byte[] bytes = cw.toByteArray();
+
+ CustomClassLoader loader = new CustomClassLoader(getClass().getClassLoader());
+ Class> clazz = loader.define("test.DynamicTraceTestClass", bytes);
+ java.lang.reflect.Method getSupplier = clazz.getMethod("getSupplier");
+ @SuppressWarnings("unchecked")
+ Supplier supplier = (Supplier) getSupplier.invoke(null);
+
+ window = glfwCreateWindow(800, 600, "", 0L, 0L);
+ glfwMakeContextCurrent(window);
+ createCapabilities();
+ glBindVertexArray(supplier.get());
+ glEnableVertexAttribArray(3);
+ } finally {
+ Properties.TRACE.enabled = oldTrace;
+ }
+ }
+
+ @Test
+ public void testInstanceMethodReferenceNotIntercepted() {
+ long monitor = glfwGetPrimaryMonitor();
+ if (monitor == 0L) {
+ return;
+ }
+ org.lwjgl.glfw.GLFWVidMode.Buffer modes = glfwGetVideoModes(monitor);
+ if (modes == null) {
+ return;
+ }
+ java.util.function.Function> streamFunc = org.lwjgl.glfw.GLFWVidMode.Buffer::stream;
+ assertNotNull(streamFunc.apply(modes));
+ }
+
+ @Test
+ public void testFallbackContextCreation() {
+ window = glfwCreateWindow(800, 600, "", 0L, 0L);
+ glfwMakeContextCurrent(window);
+
+ org.lwjglx.debug.org.lwjgl.opengl.Context.CURRENT_CONTEXT.remove();
+
+ createCapabilities();
+
+ assertNotNull(org.lwjglx.debug.org.lwjgl.opengl.Context.CURRENT_CONTEXT.get(), "Fallback context should be created automatically");
+ }
+
+ @Test
+ public void testMacOSCGLContextInterception() {
+ if (!isMac) {
+ return;
+ }
+ try {
+ window = glfwCreateWindow(800, 600, "", 0L, 0L);
+ glfwMakeContextCurrent(window);
+ createCapabilities();
+
+ long cglCtx = org.lwjgl.glfw.GLFWNativeNSGL.glfwGetNSGLContext(window);
+ if (cglCtx == 0L) {
+ return;
+ }
+
+ org.lwjglx.debug.org.lwjgl.opengl.Context.CURRENT_CONTEXT.remove();
+
+ int result = org.lwjgl.opengl.CGL.CGLSetCurrentContext(cglCtx);
+ if (result == org.lwjgl.opengl.CGL.kCGLNoError) {
+ assertNotNull(org.lwjglx.debug.org.lwjgl.opengl.Context.CURRENT_CONTEXT.get());
+ assertEquals(cglCtx, org.lwjglx.debug.org.lwjgl.opengl.Context.CURRENT_CONTEXT.get().window);
+ }
+ } catch (UnsatisfiedLinkError e) {
+ // Ignored if CGL native library is not loaded
+ }
+ }
+
@Test
public void testNoVertexAttribPointerInCustomVAO() {
window = glfwCreateWindow(800, 600, "", 0L, 0L);