Files
Spine_lab/tools/live/src/GeomProbe6.java
T

178 lines
6.7 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import java.lang.instrument.Instrumentation;
import java.lang.reflect.*;
import java.net.URLClassLoader;
/**
* 只读探针 6:把面板的**整棵控件树**连坐标打出来(含 stage 坐标),
* 用来定位"列表里的内容被画到面板外"。
*
* 取值方法来自字节码(javap -p -c):
* dl.c_()F=getX dl.d_()F=getY dl.L()F=getWidth dl.I()F=getHeight
* dl.D()Ldo; = getParent do.D()Lhq; = getChildren hq extends fI(Array),size 字段在 fI 里
* ⚠️ et(Table) 自己也声明了 `int L()` / `int I()`(列数/行数),所以必须**按返回类型 float 过滤**。
*/
public final class GeomProbe6 {
static Instrumentation INST;
static Object STATE;
static Class<?> HQL, ARR, AO;
public static void agentmain(String args, Instrumentation inst) {
INST = inst;
try { run(); } catch (Throwable e) { log("失败: " + e); e.printStackTrace(System.err); }
}
static void log(String m) { System.err.println("[geom6] " + m); }
static void run() throws Exception {
for (Class<?> c : INST.getAllLoadedClasses()) {
if (!c.getName().equals("com.spineview.ObjectPanel")) continue;
try {
Field f = c.getDeclaredField("LAST_STATE");
f.setAccessible(true);
Object v = f.get(null);
if (v != null) STATE = v;
} catch (Throwable ignored) { }
}
if (STATE == null) { log("LAST_STATE 拿不到"); return; }
ClassLoader payload = null;
for (Class<?> c : INST.getAllLoadedClasses()) {
if (c.getClassLoader() instanceof URLClassLoader) { payload = c.getClassLoader(); break; }
}
HQL = Class.forName("hq", true, payload);
ARR = Class.forName("fI", true, payload);
AO = Class.forName("dl", true, payload);
Class<?> og = null;
for (Class<?> c : INST.getAllLoadedClasses()) if (c.getName().equals("OG")) og = c;
Method onRender = null;
for (Method m : og.getDeclaredMethods()) {
Class<?>[] t = m.getParameterTypes();
if (m.getName().equals("B") && t.length == 1 && t[0] == Runnable.class) { onRender = m; break; }
}
onRender.setAccessible(true);
onRender.invoke(null, new Runnable() {
@Override public void run() {
try { work(STATE); } catch (Throwable e) { log("work 失败: " + e); e.printStackTrace(System.err); }
}
});
}
static void work(Object state) throws Exception {
Object root = state("root");
log("=========== 面板控件树(缩进=层级;坐标为 stage 绝对坐标)===========");
walk(root, 0, 0f, 0f, 6);
log("=========== 面板根再往上(谁给它多大地方)===========");
Object p = parent(root);
float ax = 0f, ay = 0f;
for (int i = 0; i < 6 && p != null; i++) {
log(" 上级[" + i + "] " + p.getClass().getName() + " " + geom(p));
Object np = parent(p);
if (np == p) break;
p = np;
}
log("=========== 关键对象定位 ===========");
Object scroll = state("listScroll"), table = state("listTable");
log(" listScroll " + geom(scroll) + " 子控件=" + size(childrenOf(scroll)));
log(" listTable " + geom(table) + " 行数=" + size(childrenOf(table)));
log(" listTable 的 stage 坐标 = " + stageX(table) + "," + stageY(table));
log("=======================================================");
}
static Object state(String name) throws Exception {
Field f = STATE.getClass().getDeclaredField(name);
f.setAccessible(true);
return f.get(STATE);
}
static void walk(Object actor, int depth, float px, float py, int maxDepth) {
if (actor == null || depth > maxDepth) return;
StringBuilder ind = new StringBuilder();
for (int i = 0; i < depth; i++) ind.append(" ");
float x = fl(actor, "c_"), y = fl(actor, "d_");
Object kids = childrenOf(actor);
int n = size(kids);
log(ind + actor.getClass().getName() + " " + geom(actor)
+ " stage=(" + r(px + x) + "," + r(py + y) + ") 子控件=" + n);
for (int i = 0; i < n; i++) {
Object child = get(kids, i);
walk(child, depth + 1, px + x, py + y, maxDepth);
}
}
static float stageX(Object actor) {
float x = 0f;
Object a = actor;
for (int i = 0; i < 8 && a != null; i++) { x += fl(a, "c_"); a = parent(a); }
return r(x);
}
static float stageY(Object actor) {
float y = 0f;
Object a = actor;
for (int i = 0; i < 8 && a != null; i++) { y += fl(a, "d_"); a = parent(a); }
return r(y);
}
static String geom(Object actor) {
if (actor == null) return "null";
return "[local x=" + r(fl(actor, "c_")) + " y=" + r(fl(actor, "d_"))
+ " w=" + r(fl(actor, "L")) + " h=" + r(fl(actor, "I"))
+ " top=" + r(fl(actor, "S")) + " right=" + r(fl(actor, "C")) + "]";
}
static Object parent(Object actor) {
if (actor == null) return null;
for (Method m : actor.getClass().getMethods()) {
if (m.getParameterTypes().length != 0) continue;
if (!m.getName().equals("D")) continue;
if (!m.getReturnType().getName().equals("do")) continue;
try { m.setAccessible(true); return m.invoke(actor); } catch (Throwable ignored) { }
}
return null;
}
static Object childrenOf(Object actor) {
if (actor == null) return null;
for (Method m : actor.getClass().getMethods()) {
if (m.getParameterTypes().length != 0) continue;
if (m.getReturnType() != HQL) continue;
try { m.setAccessible(true); return m.invoke(actor); } catch (Throwable ignored) { }
}
return null;
}
static int size(Object array) {
if (array == null) return 0;
for (Class<?> t = ARR; t != null && t != Object.class; t = t.getSuperclass()) {
for (Field f : t.getDeclaredFields()) {
if (f.getType() != int.class) continue;
f.setAccessible(true);
try { return f.getInt(array); } catch (Throwable ignored) { }
}
}
return 0;
}
static Object get(Object array, int index) {
if (array == null) return null;
for (Method m : array.getClass().getMethods()) {
if (!m.getName().equals("D")) continue;
Class<?>[] t = m.getParameterTypes();
if (t.length != 1 || t[0] != int.class || m.getReturnType() != Object.class) continue;
try { m.setAccessible(true); return m.invoke(array, Integer.valueOf(index)); } catch (Throwable ignored) { }
}
return null;
}
static float fl(Object actor, String name) {
if (actor == null) return -1f;
for (Method m : actor.getClass().getMethods()) {
if (!m.getName().equals(name)) continue;
if (m.getParameterTypes().length != 0 || m.getReturnType() != float.class) continue;
try { m.setAccessible(true); return ((Float) m.invoke(actor)).floatValue(); } catch (Throwable ignored) { }
}
return -1f;
}
static float r(float v) { return Math.round(v * 100f) / 100f; }
}