From 87f94768a0df81222a896ff50d88a38cc29e12f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sun, 22 Feb 2026 18:33:27 +0100 Subject: [PATCH] Added: Debugger - Remove watch Added: Debugger - List of watches Added: Debugger - Show flag of watch type (read/write) Fixed: Debugger - Threading issues with multiple SWFs Fixed: Debugger - Reading variables on 64bit flash players (like in browsers) Enhanced debug game test SWFs --- CHANGELOG.md | 4 + lib/flashdebugger.jar | Bin 261135 -> 261421 bytes libsrc/ffdec_lib/lib/flashdebugger.jar | Bin 261135 -> 261421 bytes .../testdata/debug_game/as2_scoring.swd | Bin 0 -> 1347 bytes .../testdata/debug_game/as2_scoring.swf | Bin 8795 -> 9330 bytes .../debug_game/as2_scoring/DOMDocument.xml | 53 +- .../as2_scoring/LIBRARY/ButtonDown.xml | 90 ++++ .../as2_scoring/LIBRARY/ButtonUp.xml | 90 ++++ .../as2_scoring/META-INF/metadata.xml | 12 +- .../as2_scoring/bin/SymDepend.cache | Bin 73 -> 107 bytes .../testdata/debug_game/as3_scoring.swf | Bin 9271 -> 9845 bytes .../debug_game/as3_scoring/DOMDocument.xml | 57 +- .../as3_scoring/LIBRARY/ButtonDown.xml | 90 ++++ .../as3_scoring/LIBRARY/ButtonUp.xml | 90 ++++ .../as3_scoring/META-INF/metadata.xml | 12 +- .../as3_scoring/bin/SymDepend.cache | Bin 73 -> 107 bytes .../decompiler/flash/gui/DebugPanel.java | 492 +++++++++++------- .../decompiler/flash/gui/DebugStackPanel.java | 61 ++- .../decompiler/flash/gui/DebuggerHandler.java | 27 + .../decompiler/flash/gui/DebuggerSession.java | 195 +++++-- src/com/jpexs/decompiler/flash/gui/Main.java | 11 +- .../jpexs/decompiler/flash/gui/MainPanel.java | 4 +- .../decompiler/flash/gui/abc/ABCPanel.java | 272 +++++++--- .../flash/gui/action/ActionPanel.java | 2 +- .../flash/gui/locales/MainFrame.properties | 9 +- .../flash/gui/locales/MainFrame_ca.properties | 2 +- .../flash/gui/locales/MainFrame_cs.properties | 9 +- .../flash/gui/locales/MainFrame_de.properties | 5 + .../flash/gui/locales/MainFrame_ja.properties | 2 +- .../flash/gui/locales/MainFrame_nl.properties | 2 +- .../gui/locales/MainFrame_pt_BR.properties | 2 +- .../flash/gui/locales/MainFrame_sk.properties | 7 +- .../flash/gui/locales/MainFrame_sl.properties | 2 +- .../flash/gui/locales/MainFrame_sv.properties | 2 +- .../flash/gui/locales/MainFrame_tr.properties | 2 +- .../flash/gui/locales/MainFrame_zh.properties | 2 +- 36 files changed, 1239 insertions(+), 369 deletions(-) create mode 100644 libsrc/ffdec_lib/testdata/debug_game/as2_scoring.swd create mode 100644 libsrc/ffdec_lib/testdata/debug_game/as2_scoring/LIBRARY/ButtonDown.xml create mode 100644 libsrc/ffdec_lib/testdata/debug_game/as2_scoring/LIBRARY/ButtonUp.xml create mode 100644 libsrc/ffdec_lib/testdata/debug_game/as3_scoring/LIBRARY/ButtonDown.xml create mode 100644 libsrc/ffdec_lib/testdata/debug_game/as3_scoring/LIBRARY/ButtonUp.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index 7605a292c..2dde4aee9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,14 @@ All notable changes to this project will be documented in this file. ## [Unreleased] ### Added - Debugger - Debugged SWF file name in the session title +- Debugger - Remove watch +- Debugger - List of watches +- Debugger - Show flag of watch type (read/write) ### Fixed - [#2639] Export to FLA - missing sound streams - Debugger - Threading issues with multiple SWFs +- Debugger - Reading variables on 64bit flash players (like in browsers) ## [25.1.1] - 2026-02-19 ### Fixed diff --git a/lib/flashdebugger.jar b/lib/flashdebugger.jar index 9e5d8b116dd047de22fb44c45d34058c44273e5c..b0477c2741ee55e138e74eefe5628c28027e79c5 100644 GIT binary patch delta 15884 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 QrW$|hJ}$jJL+#D}4~Yx3p#T5? diff --git a/libsrc/ffdec_lib/lib/flashdebugger.jar b/libsrc/ffdec_lib/lib/flashdebugger.jar index 9e5d8b116dd047de22fb44c45d34058c44273e5c..b0477c2741ee55e138e74eefe5628c28027e79c5 100644 GIT binary patch delta 15884 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 QrW$|hJ}$jJL+#D}4~Yx3p#T5? diff --git a/libsrc/ffdec_lib/testdata/debug_game/as2_scoring.swd b/libsrc/ffdec_lib/testdata/debug_game/as2_scoring.swd new file mode 100644 index 0000000000000000000000000000000000000000..87c1bc7e0bb5f2a2f59d4a2a7071880579f0a403 GIT binary patch literal 1347 zcmbu8J#W-N5QY~BL^cYDG_+G8`yhvXC`7_gAP z@=Y#F6Ev#2Vv9%b2flxFk~Xpk!lW{pOd}INpSI=^v1zONivO+p)QE1ud2gkt z-^5DMP?@NISUovQsd?5`vJ}1H%dj7=LfnqSWMZgITI5J_-Nssb6`9t9(Hw{ywf)b7 zy$sXGd$3d~buu5>SXaK`Fg@6}4%4iCLZh I>+d4}0y9Kd+W-In literal 0 HcmV?d00001 diff --git a/libsrc/ffdec_lib/testdata/debug_game/as2_scoring.swf b/libsrc/ffdec_lib/testdata/debug_game/as2_scoring.swf index da5f1d2ae6b986b3133dd9e38ff427e785c7ded1..5f3f0af3747e5548c9ad4e7541378b93c1fa1e72 100644 GIT binary patch literal 9330 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

pUAw0fu# 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 TBOYzL8IpYWhD7CxQTKlU(gUQ) literal 8795 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 + + @@ -43,6 +45,7 @@ stop();]]> var score = parseInt(_root.inpScore.text); score = score + 5; _root.inpScore.text = score; + trace("plus 5"); }]]> @@ -58,6 +61,7 @@ stop();]]> var score = parseInt(_root.inpScore.text); score = score * 2; _root.inpScore.text = score; + trace("multiply 2"); }]]> @@ -100,7 +104,7 @@ stop();]]> - + @@ -110,6 +114,48 @@ stop();]]> + + + + + + + + + Speed + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -122,6 +168,10 @@ stop();]]> + + + + @@ -138,6 +188,5 @@ stop();]]> - \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/debug_game/as2_scoring/LIBRARY/ButtonDown.xml b/libsrc/ffdec_lib/testdata/debug_game/as2_scoring/LIBRARY/ButtonDown.xml new file mode 100644 index 000000000..3d0048d06 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/debug_game/as2_scoring/LIBRARY/ButtonDown.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/debug_game/as2_scoring/LIBRARY/ButtonUp.xml b/libsrc/ffdec_lib/testdata/debug_game/as2_scoring/LIBRARY/ButtonUp.xml new file mode 100644 index 000000000..d2e081df9 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/debug_game/as2_scoring/LIBRARY/ButtonUp.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/debug_game/as2_scoring/META-INF/metadata.xml b/libsrc/ffdec_lib/testdata/debug_game/as2_scoring/META-INF/metadata.xml index 0ac4224b3..e4344c771 100644 --- a/libsrc/ffdec_lib/testdata/debug_game/as2_scoring/META-INF/metadata.xml +++ b/libsrc/ffdec_lib/testdata/debug_game/as2_scoring/META-INF/metadata.xml @@ -5,8 +5,8 @@ xmlns:xmp="http://ns.adobe.com/xap/1.0/"> Adobe Flash Professional CS6 - build 481 2026-02-16T10:31:42-08:00 - 2026-02-16T11:18:35-08:00 - 2026-02-16T11:18:35-08:00 + 2026-02-21T09:35:10-08:00 + 2026-02-21T09:35:10-08:00 @@ -17,7 +17,7 @@ xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#"> xmp.did:7FE581FA660BF11197008EF33C376745 - xmp.iid:9BDF39F36B0BF11197008EF33C376745 + xmp.iid:377DE8AB4B0FF111A0D3B09FD2F43FD4 xmp.did:7FE581FA660BF11197008EF33C376745 @@ -39,6 +39,12 @@ 2026-02-16T10:31:42-08:00 Adobe Flash Professional CS6 - build 481 + + created + xmp.iid:377DE8AB4B0FF111A0D3B09FD2F43FD4 + 2026-02-16T10:31:42-08:00 + Adobe Flash Professional CS6 - build 481 + diff --git a/libsrc/ffdec_lib/testdata/debug_game/as2_scoring/bin/SymDepend.cache b/libsrc/ffdec_lib/testdata/debug_game/as2_scoring/bin/SymDepend.cache index ce963c485552532348ee698e5e39c3f4e2b5a6d3..62634e4c36d804c3bcd2d1c8aebd2a8f9bf78980 100644 GIT binary patch literal 107 zcmYdiU|@K~&cwjRAj-hapw1uy?CxcUINlAX5F+(woX~<9kV;V73>{|;|g#-Y; CHVmZz diff --git a/libsrc/ffdec_lib/testdata/debug_game/as3_scoring.swf b/libsrc/ffdec_lib/testdata/debug_game/as3_scoring.swf index 4280165d6f59ecb1ef93b515db54403b7de62da2..d5bb3532bbdc3ac35feffbbd75d27e6ea97df29c 100644 GIT binary patch literal 9845 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≪o?h}z3=Y8bsEC799^5RnLH4z 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 + + @@ -42,7 +44,8 @@ setTimeout(decreaseScore, delay); function onPlus5(e:MouseEvent):void { var score:int = getScore(); score = score + 5; - setScore(score); + setScore(score); + trace("plus 5"); } butPlus5.addEventListener(MouseEvent.CLICK, onPlus5); @@ -51,15 +54,32 @@ function onMultiply2(e:MouseEvent):void { var score:int = getScore(); score = score * 2; setScore(score); + trace("multiply 2"); } butMultiply2.addEventListener(MouseEvent.CLICK, onMultiply2); function onReset(e:MouseEvent):void { setScore(0); + trace("reset"); } -butReset.addEventListener(MouseEvent.CLICK, onReset);]]> +butReset.addEventListener(MouseEvent.CLICK, onReset); + +function onSpeedUp(e:MouseEvent): void { + delay = Math.round(delay / 1.1); + trace("speed up"); +} + +btnSpeedUp.addEventListener(MouseEvent.CLICK, onSpeedUp); + +function onSpeedDown(e:MouseEvent): void { + delay = Math.round(delay * 1.1); + trace("speed down"); +} + +btnSpeedDown.addEventListener(MouseEvent.CLICK, onSpeedDown); +]]> @@ -125,6 +145,35 @@ butReset.addEventListener(MouseEvent.CLICK, onReset);]]> + + + + + + + Speed + + + + + + + + + + + + + + + + + + + + + + @@ -134,6 +183,8 @@ butReset.addEventListener(MouseEvent.CLICK, onReset);]]> + + @@ -152,7 +203,5 @@ butReset.addEventListener(MouseEvent.CLICK, onReset);]]> - - \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/debug_game/as3_scoring/LIBRARY/ButtonDown.xml b/libsrc/ffdec_lib/testdata/debug_game/as3_scoring/LIBRARY/ButtonDown.xml new file mode 100644 index 000000000..3d0048d06 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/debug_game/as3_scoring/LIBRARY/ButtonDown.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/debug_game/as3_scoring/LIBRARY/ButtonUp.xml b/libsrc/ffdec_lib/testdata/debug_game/as3_scoring/LIBRARY/ButtonUp.xml new file mode 100644 index 000000000..d2e081df9 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/debug_game/as3_scoring/LIBRARY/ButtonUp.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/debug_game/as3_scoring/META-INF/metadata.xml b/libsrc/ffdec_lib/testdata/debug_game/as3_scoring/META-INF/metadata.xml index 445a6caa0..fe30b6721 100644 --- a/libsrc/ffdec_lib/testdata/debug_game/as3_scoring/META-INF/metadata.xml +++ b/libsrc/ffdec_lib/testdata/debug_game/as3_scoring/META-INF/metadata.xml @@ -5,8 +5,8 @@ xmlns:xmp="http://ns.adobe.com/xap/1.0/"> Adobe Flash Professional CS6 - build 481 2026-02-16T10:31:42-08:00 - 2026-02-16T11:18:44-08:00 - 2026-02-16T11:18:44-08:00 + 2026-02-21T09:45:46-08:00 + 2026-02-21T09:45:46-08:00 @@ -22,7 +22,7 @@ xmp.did:7FE581FA660BF11197008EF33C376745 xmp.did:EDF1CDD4690BF11197008EF33C376745 - xmp.iid:9DDF39F36B0BF11197008EF33C376745 + xmp.iid:3B7DE8AB4B0FF111A0D3B09FD2F43FD4 xmp.did:7FE581FA660BF11197008EF33C376745 @@ -45,6 +45,12 @@ 2026-02-16T10:31:42-08:00 Adobe Flash Professional CS6 - build 481 + + created + xmp.iid:3B7DE8AB4B0FF111A0D3B09FD2F43FD4 + 2026-02-16T10:31:42-08:00 + Adobe Flash Professional CS6 - build 481 + diff --git a/libsrc/ffdec_lib/testdata/debug_game/as3_scoring/bin/SymDepend.cache b/libsrc/ffdec_lib/testdata/debug_game/as3_scoring/bin/SymDepend.cache index dbecb1715196b728d95316064ad8feed85320d9d..2debd5f6d7ea9338d214b47df3754a1229a9d0b2 100644 GIT binary patch literal 107 zcmYdiU|@K~&cwjRAj-hapvE8#?CxcUINlAX5F@kBtPyrJ)WT@D;7N`md006UY B3{U_7 diff --git a/src/com/jpexs/decompiler/flash/gui/DebugPanel.java b/src/com/jpexs/decompiler/flash/gui/DebugPanel.java index 7c5b7703c..7a5e9b9c9 100644 --- a/src/com/jpexs/decompiler/flash/gui/DebugPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/DebugPanel.java @@ -16,6 +16,8 @@ */ package com.jpexs.decompiler.flash.gui; +import com.jpexs.debugger.flash.Debugger; +import com.jpexs.debugger.flash.DebuggerCommands; import com.jpexs.debugger.flash.Variable; import com.jpexs.debugger.flash.messages.in.InConstantPool; import com.jpexs.debugger.flash.messages.in.InFrame; @@ -70,7 +72,9 @@ public class DebugPanel extends JPanel { private MyTreeTable debugRegistersTable; - private MyTreeTable debugLocalsTable; //JTable debugLocalsTable; + private MyTreeTable debugLocalsTable; + + private MyTreeTable debugWatchesTable; private MyTreeTable debugScopeTable; @@ -91,14 +95,14 @@ public class DebugPanel extends JPanel { private boolean loading = false; public ABCPanel.VariablesTableModel localsTable; - + private WeakReference currentSessionRef = null; private boolean as3; - + public static enum SelectedTab { - LOG, STACK, SCOPECHAIN, LOCALS, REGISTERS, CALLSTACK, CONSTANTPOOL + LOG, STACK, SCOPECHAIN, LOCALS, WATCHES, REGISTERS, CALLSTACK, CONSTANTPOOL } public synchronized boolean isLoading() { @@ -107,6 +111,7 @@ public class DebugPanel extends JPanel { public synchronized void setLoading(boolean loading) { this.loading = loading; + //varTabs.setVisible(!loading); } private SelectedTab selectedTab = null; @@ -157,15 +162,16 @@ public class DebugPanel extends JPanel { //ignore } if (wasEmpty) { - refresh(session); + refresh(); } } public DebugPanel(boolean as3) { super(new BorderLayout()); this.as3 = as3; - debugRegistersTable = new MyTreeTable(new ABCPanel.VariablesTableModel(as3, debugRegistersTable, new ArrayList<>()), false); - debugLocalsTable = new MyTreeTable(new ABCPanel.VariablesTableModel(as3, debugLocalsTable, new ArrayList<>()), false); + debugRegistersTable = new MyTreeTable(new ABCPanel.VariablesTableModel(null, as3, debugRegistersTable, new ArrayList<>(), new ArrayList<>()), false); + debugLocalsTable = new MyTreeTable(new ABCPanel.VariablesTableModel(null, as3, debugLocalsTable, new ArrayList<>(), new ArrayList<>()), false); + debugWatchesTable = new MyTreeTable(new ABCPanel.VariablesTableModel(null, as3, debugWatchesTable, new ArrayList<>(), new ArrayList<>()), false); MouseAdapter watchHandler = new MouseAdapter() { @@ -184,7 +190,7 @@ public class DebugPanel extends JPanel { } private void dopop(MouseEvent e) { - + if (currentSessionRef == null) { return; } @@ -192,11 +198,13 @@ public class DebugPanel extends JPanel { if (session == null) { return; } - - if (debugLocalsTable.getSelectedRow() == -1) { + + MyTreeTable src = (MyTreeTable) e.getSource(); + + if (src.getSelectedRow() == -1) { return; } - Object node = debugLocalsTable.getTree().getPathForRow(debugLocalsTable.getSelectedRow()).getLastPathComponent(); + Object node = src.getTree().getPathForRow(src.getSelectedRow()).getLastPathComponent(); Variable v; ABCPanel.VariableNode vn; if (node instanceof ABCPanel.VariableNode) { @@ -261,7 +269,7 @@ public class DebugPanel extends JPanel { if (igv == null) { return; } - Variable debugConnectionClass = igv.parent; + Variable debugConnectionClass = igv.parent; String dataStr = (String) session.callMethod(debugConnectionClass, "readCommaSeparatedFromByteArray", Arrays.asList(v)).variables.get(0).value; String[] parts = dataStr.split(","); byte[] data = new byte[parts.length]; @@ -356,35 +364,63 @@ public class DebugPanel extends JPanel { watchReadMenuItem.addActionListener((ActionEvent e1) -> { if (!Main.addWatch(session, v, watchParentId, true, false)) { ViewMessages.showMessageDialog(DebugPanel.this, AppStrings.translate("error.debug.watch.add"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + return; } + refresh(); }); JMenuItem watchWriteMenuItem = new JMenuItem(AppStrings.translate("debug.watch.add.write")); watchWriteMenuItem.addActionListener((ActionEvent e1) -> { if (!Main.addWatch(session, v, watchParentId, false, true)) { ViewMessages.showMessageDialog(DebugPanel.this, AppStrings.translate("error.debug.watch.add"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + return; } + refresh(); }); JMenuItem watchReadWriteMenuItem = new JMenuItem(AppStrings.translate("debug.watch.add.readwrite")); watchReadWriteMenuItem.addActionListener((ActionEvent e1) -> { if (!Main.addWatch(session, v, watchParentId, true, true)) { ViewMessages.showMessageDialog(DebugPanel.this, AppStrings.translate("error.debug.watch.add"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + return; } + refresh(); }); addWatchMenu.add(watchReadMenuItem); addWatchMenu.add(watchWriteMenuItem); addWatchMenu.add(watchReadWriteMenuItem); pm.add(addWatchMenu); + + JMenuItem removeWatcheMenuItem = new JMenuItem(AppStrings.translate("debug.watch.remove")); + removeWatcheMenuItem.addActionListener((ActionEvent e1) -> { + if (!Main.removeWatch(session, v, watchParentId)) { + ViewMessages.showMessageDialog(DebugPanel.this, AppStrings.translate("error.debug.watch.remove"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + return; + } + refresh(); + }); + if (session.getWatch(v.name, watchParentId) != null) { + pm.add(removeWatcheMenuItem); + } pm.show(e.getComponent(), e.getX(), e.getY()); } }; debugLocalsTable.addMouseListener(watchHandler); + debugWatchesTable.addMouseListener(watchHandler); //debugScopeTable.addMouseListener(watchHandler); - debugScopeTable = new MyTreeTable(new ABCPanel.VariablesTableModel(as3, debugScopeTable, new ArrayList<>()), false); + debugScopeTable = new MyTreeTable(new ABCPanel.VariablesTableModel(null, as3, debugScopeTable, new ArrayList<>(), new ArrayList<>()), false); - constantPoolTable = new JTable(); + constantPoolTable = new JTable(new DefaultTableModel(new Object[2][0], new Object[]{ + AppStrings.translate("constantpool.header.id"), + AppStrings.translate("constantpool.header.value") + }) { + @Override + public boolean isCellEditable(int row, int column) { + return false; + } + + }); traceLogTextarea = new JTextArea(); traceLogTextarea.setEditable(false); traceLogTextarea.setOpaque(false); @@ -408,7 +444,7 @@ public class DebugPanel extends JPanel { public void errorException(DebuggerSession session, String message, Variable thrownVar) { logAdd(session, "unhandled exception: " + message); selectedTab = tabTypes.get(tabTypes.size() - 1); - refresh(session); + refresh(); } }); @@ -420,19 +456,26 @@ public class DebugPanel extends JPanel { @Override public void disconnected(DebuggerSession session) { - refresh(session); + refresh(); + } + }); + + Main.getDebugHandler().addSelectionListener(new DebuggerHandler.SessionSelectionListener() { + @Override + public void sessionSelected(DebuggerSession newSession, int oldSessionId) { + refresh(); } }); Main.getDebugHandler().addFrameChangeListener(frameChangeListener = new DebuggerHandler.FrameChangeListener() { @Override public void frameChanged(DebuggerSession session) { - View.execInEventDispatchLater(new Runnable() { - @Override - public void run() { - refresh(session); - } - }); + /*DebuggerSession currentSession = Main.getCurrentDebugSession(); + if (session != currentSession) { + return; + } */ + + refresh(); } }); @@ -440,22 +483,18 @@ public class DebugPanel extends JPanel { @Override public void doContinue(DebuggerSession session) { - View.execInEventDispatch(new Runnable() { - @Override - public void run() { - refresh(Main.getCurrentDebugSession()); - } - }); + refresh(); } @Override public void breakAt(DebuggerSession session, String scriptName, int line, int classIndex, int traitIndex, int methodIndex) { - View.execInEventDispatch(new Runnable() { + /*View.execInEventDispatch(new Runnable() { @Override public void run() { refresh(Main.getCurrentDebugSession()); } - }); + });*/ + //reacts to frameChanged instead } }); @@ -479,7 +518,7 @@ public class DebugPanel extends JPanel { }); add(new HeaderLabel(AppStrings.translate("debugpanel.header")), BorderLayout.NORTH); - add(varTabs, BorderLayout.CENTER); + add(varTabs, BorderLayout.CENTER); } /* private void getVariableList() { @@ -509,190 +548,269 @@ public class DebugPanel extends JPanel { } return v.getMembers(this); }*/ - public void refresh(DebuggerSession session) { - - if (session != null && !session.isPaused()) { - return; + private final Object refreshLock = new Object(); + + public void refresh() { + DebuggerSession session = Main.getCurrentDebugSession(); + + if (session != null) { + SWF swf = session.getCurrentSwf(); + if (swf != null) { + if (swf.isAS3() != as3) { + return; + } + } } - + currentSessionRef = session == null ? null : new WeakReference<>(session); - View.execInEventDispatch(new Runnable() { + Runnable r = new Runnable() { @Override public void run() { - if (session != null && !session.isPaused()) { - return; - } - setLoading(true); - synchronized (DebugPanel.this) { + synchronized (refreshLock) { + try { + setLoading(true); - SelectedTab oldSel = selectedTab; - localsTable = null; - InFrame f = session == null ? null : session.getFrame(); - if (f != null) { - - - List locals = new ArrayList<>(); - - - Map placedObjects = session.getPlacedObjects(); - for (String poName : placedObjects.keySet()) { - String realName = poName; - if ("/".equals(realName)) { - realName = "_root"; - } else if (realName.startsWith("/")) { - continue; - } - InGetVariable igv = session.getVariable(0, realName, false, false); - if (igv != null) { - Variable placedVar = igv.parent; - if (placedVar != null) { - locals.add(placedVar); + int sessionId = -1; + + if (session != null) { + sessionId = session.getId(); + ABCPanel.VariableNode.stopLoading(sessionId); //stop any previous loading + } + + Logger.getLogger(DebugPanel.class.getName()).log(Level.FINE, "session{0}: Refreshing debug panel", new Object[]{sessionId}); + + localsTable = null; + InFrame f = session == null ? null : session.getFrame(); + + ABCPanel.VariablesTableModel registersTableModel = new ABCPanel.VariablesTableModel(session, as3, debugRegistersTable, new ArrayList<>(), new ArrayList<>()); + ABCPanel.VariablesTableModel localsTableModel = new ABCPanel.VariablesTableModel(session, as3, debugLocalsTable, new ArrayList<>(), new ArrayList<>()); + ABCPanel.VariablesTableModel scopeTableModel = new ABCPanel.VariablesTableModel(session, as3, debugScopeTable, new ArrayList<>(), new ArrayList<>()); + ABCPanel.VariablesTableModel watchesTableModel = new ABCPanel.VariablesTableModel(session, as3, debugWatchesTable, new ArrayList<>(), new ArrayList<>()); + ABCPanel.VariablesTableModel fRegistersTableModel; + ABCPanel.VariablesTableModel fLocalsTableModel; + ABCPanel.VariablesTableModel fScopeTableModel; + ABCPanel.VariablesTableModel fWatchesTableModel; + Object[][] fCpoolData; + + if (f != null) { + + Logger.getLogger(DebugPanel.class.getName()).log(Level.FINE, "session{0}: Debug panel - has frame", new Object[]{sessionId}); + + List locals = new ArrayList<>(); + + Map placedObjects = session.getPlacedObjects(); + for (String poName : placedObjects.keySet()) { + String realName = poName; + if ("/".equals(realName)) { + realName = "_root"; + } else if (realName.startsWith("/")) { + continue; + } + Logger.getLogger(DebugPanel.class.getName()).log(Level.FINE, "session{0}: Getting placedObject {1}", new Object[]{sessionId, realName}); + InGetVariable igv = session.getVariable(0, realName, false, false); + if (igv != null) { + Variable placedVar = igv.parent; + if (placedVar != null) { + locals.add(placedVar); + } + } else { + Logger.getLogger(DebugPanel.class.getName()).log(Level.FINE, "session{0}: Cannot get placedObject {1}", new Object[]{sessionId, realName}); } } + + locals.addAll(f.arguments); + locals.addAll(f.variables); + + localsTable = new ABCPanel.VariablesTableModel(session, as3, debugLocalsTable, locals, null); + + List watchedVars = new ArrayList<>(); + List watchedParentIds = new ArrayList<>(); + for (DebuggerCommands.Watch w : session.getWatches().values()) { + InGetVariable igv = session.getVariable(w.varId, w.varName, false, false); + if (igv != null) { + Variable wVar = igv.parent; + if (wVar != null) { + watchedVars.add(wVar); + watchedParentIds.add(w.varId); + } + } + } + + TreeModelListener refreshListener = new TreeModelListener() { + @Override + public void treeNodesChanged(TreeModelEvent e) { + synchronized (DebugPanel.this) { + if (loading) { + return; + } + } + session.refreshFrame(); + } + + @Override + public void treeNodesInserted(TreeModelEvent e) { + } + + @Override + public void treeNodesRemoved(TreeModelEvent e) { + + } + + @Override + public void treeStructureChanged(TreeModelEvent e) { + + } + }; + + registersTableModel = new ABCPanel.VariablesTableModel(session, as3, debugRegistersTable, f.registers, null); + localsTableModel = localsTable; + scopeTableModel = new ABCPanel.VariablesTableModel(session, as3, debugScopeTable, f.scopeChain, null); + watchesTableModel = new ABCPanel.VariablesTableModel(session, as3, debugWatchesTable, watchedVars, watchedParentIds); + + localsTableModel.addTreeModelListener(refreshListener); + scopeTableModel.addTreeModelListener(refreshListener); + watchesTableModel.addTreeModelListener(refreshListener); } - - safeSetTreeModel(debugRegistersTable, new ABCPanel.VariablesTableModel(as3, debugRegistersTable, f.registers)); - - locals.addAll(f.arguments); - locals.addAll(f.variables); - - localsTable = new ABCPanel.VariablesTableModel(as3, debugLocalsTable, locals); - safeSetTreeModel(debugLocalsTable, localsTable); - safeSetTreeModel(debugScopeTable, new ABCPanel.VariablesTableModel(as3, debugScopeTable, f.scopeChain)); - - /*TableModelListener refreshListener = new TableModelListener() { - @Override - public void tableChanged(TableModelEvent e) { - Main.getDebugHandler().refreshFrame(); - refresh(); - } - };*/ - TreeModelListener refreshListener = new TreeModelListener() { - @Override - public void treeNodesChanged(TreeModelEvent e) { - session.refreshFrame(); - refresh(session); + InConstantPool cpool = session == null ? null : session.getConstantPool(); + Object[][] cpoolData = new Object[0][2]; + if (cpool != null) { + cpoolData = new Object[cpool.vars.size()][2]; + for (int i = 0; i < cpool.vars.size(); i++) { + cpoolData[i][0] = cpool.ids.get(i); + cpoolData[i][1] = cpool.vars.get(i).value; } - - @Override - public void treeNodesInserted(TreeModelEvent e) { - session.refreshFrame(); - refresh(session); - } - - @Override - public void treeNodesRemoved(TreeModelEvent e) { - session.refreshFrame(); - refresh(session); - } - - @Override - public void treeStructureChanged(TreeModelEvent e) { - session.refreshFrame(); - refresh(session); - } - }; - debugLocalsTable.getTreeTableModel().addTreeModelListener(refreshListener); - debugScopeTable.getTreeTableModel().addTreeModelListener(refreshListener); - } else { - debugRegistersTable.setTreeModel(new ABCPanel.VariablesTableModel(as3, debugRegistersTable, new ArrayList<>())); - debugLocalsTable.setTreeModel(new ABCPanel.VariablesTableModel(as3, debugLocalsTable, new ArrayList<>())); - debugScopeTable.setTreeModel(new ABCPanel.VariablesTableModel(as3, debugScopeTable, new ArrayList<>())); - } - InConstantPool cpool = session == null ? null : session.getConstantPool(); - if (cpool != null) { - Object[][] data2 = new Object[cpool.vars.size()][2]; - for (int i = 0; i < cpool.vars.size(); i++) { - data2[i][0] = cpool.ids.get(i); - data2[i][1] = cpool.vars.get(i).value; } - constantPoolTable.setModel(new DefaultTableModel(data2, new Object[]{ - AppStrings.translate("constantpool.header.id"), - AppStrings.translate("constantpool.header.value") - }) { + + fRegistersTableModel = registersTableModel; + fLocalsTableModel = localsTableModel; + fScopeTableModel = scopeTableModel; + fWatchesTableModel = watchesTableModel; + fCpoolData = cpoolData; + + View.execInEventDispatch(new Runnable() { @Override - public boolean isCellEditable(int row, int column) { - return false; - } + public void run() { + try { + if (f != null) { + safeSetTreeModel(debugRegistersTable, fRegistersTableModel); + safeSetTreeModel(debugLocalsTable, fLocalsTableModel); + safeSetTreeModel(debugScopeTable, fScopeTableModel); + safeSetTreeModel(debugWatchesTable, fWatchesTableModel); + } else { + debugRegistersTable.setTreeModel(fRegistersTableModel); + debugLocalsTable.setTreeModel(fLocalsTableModel); + debugScopeTable.setTreeModel(fScopeTableModel); + debugWatchesTable.setTreeModel(fWatchesTableModel); + } - }); - } else { - constantPoolTable.setModel(new DefaultTableModel()); - } + constantPoolTable.setModel(new DefaultTableModel(fCpoolData, new Object[]{ + AppStrings.translate("constantpool.header.id"), + AppStrings.translate("constantpool.header.value") + }) { + @Override + public boolean isCellEditable(int row, int column) { + return false; + } - varTabs.removeAll(); - tabTypes.clear(); - JPanel pa; - if (debugRegistersTable.getRowCount() > 0) { - tabTypes.add(SelectedTab.REGISTERS); - pa = new JPanel(new BorderLayout()); - pa.add(new FasterScrollPane(debugRegistersTable), BorderLayout.CENTER); - varTabs.addTab(AppStrings.translate("variables.header.registers"), pa); - } - if (debugLocalsTable.getRowCount() > 0) { - tabTypes.add(SelectedTab.LOCALS); + }); - pa = new JPanel(new BorderLayout()); - pa.add(new FasterScrollPane(debugLocalsTable), BorderLayout.CENTER); - varTabs.addTab(AppStrings.translate("variables.header.locals"), pa); - } + varTabs.removeAll(); + tabTypes.clear(); + JPanel pa; + if (debugRegistersTable.getRowCount() > 0) { + tabTypes.add(SelectedTab.REGISTERS); + pa = new JPanel(new BorderLayout()); + pa.add(new FasterScrollPane(debugRegistersTable), BorderLayout.CENTER); + varTabs.addTab(AppStrings.translate("variables.header.registers"), pa); + } + if (debugLocalsTable.getRowCount() > 0) { + tabTypes.add(SelectedTab.LOCALS); - if (debugScopeTable.getRowCount() > 0) { - tabTypes.add(SelectedTab.SCOPECHAIN); + pa = new JPanel(new BorderLayout()); + pa.add(new FasterScrollPane(debugLocalsTable), BorderLayout.CENTER); + varTabs.addTab(AppStrings.translate("variables.header.locals"), pa); + } - pa = new JPanel(new BorderLayout()); - pa.add(new FasterScrollPane(debugScopeTable), BorderLayout.CENTER); - varTabs.addTab(AppStrings.translate("variables.header.scopeChain"), pa); - } + if (debugWatchesTable.getRowCount() > 0) { + tabTypes.add(SelectedTab.WATCHES); - if (constantPoolTable.getRowCount() > 0) { - tabTypes.add(SelectedTab.CONSTANTPOOL); + pa = new JPanel(new BorderLayout()); + pa.add(new FasterScrollPane(debugWatchesTable), BorderLayout.CENTER); + varTabs.addTab(AppStrings.translate("variables.header.watches"), pa); + } - pa = new JPanel(new BorderLayout()); - pa.add(new FasterScrollPane(constantPoolTable), BorderLayout.CENTER); - varTabs.addTab(AppStrings.translate("constantpool.header"), pa); - } + if (debugScopeTable.getRowCount() > 0) { + tabTypes.add(SelectedTab.SCOPECHAIN); - if (logLength > 0) { - tabTypes.add(SelectedTab.LOG); + pa = new JPanel(new BorderLayout()); + pa.add(new FasterScrollPane(debugScopeTable), BorderLayout.CENTER); + varTabs.addTab(AppStrings.translate("variables.header.scopeChain"), pa); + } - pa = new JPanel(new BorderLayout()); - pa.add(new FasterScrollPane(traceLogTextarea), BorderLayout.CENTER); - JButton clearButton = new JButton(AppStrings.translate("debuglog.button.clear")); - clearButton.addActionListener(new ActionListener() { + if (constantPoolTable.getRowCount() > 0) { + tabTypes.add(SelectedTab.CONSTANTPOOL); + + pa = new JPanel(new BorderLayout()); + pa.add(new FasterScrollPane(constantPoolTable), BorderLayout.CENTER); + varTabs.addTab(AppStrings.translate("constantpool.header"), pa); + } + + if (logLength > 0) { + tabTypes.add(SelectedTab.LOG); + + pa = new JPanel(new BorderLayout()); + pa.add(new FasterScrollPane(traceLogTextarea), BorderLayout.CENTER); + JButton clearButton = new JButton(AppStrings.translate("debuglog.button.clear")); + clearButton.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + traceLogTextarea.setText(""); + logLength = 0; + refresh(); + } + }); + JPanel butPanel = new JPanel(new FlowLayout()); + butPanel.add(clearButton); + pa.add(butPanel, BorderLayout.SOUTH); + varTabs.addTab(AppStrings.translate("debuglog.header"), pa); + } + boolean newVisible = !tabTypes.isEmpty(); + if (newVisible != isVisible()) { + setVisible(newVisible); + } + + SelectedTab oldSel = selectedTab; + + if (!tabTypes.isEmpty()) { + if (oldSel != null && !tabTypes.contains(oldSel)) { + oldSel = null; + } + } + if (oldSel != null) { + selectedTab = oldSel; + varTabs.setSelectedIndex(tabTypes.indexOf(selectedTab)); + } + } catch (Throwable t) { + int sessionId = session == null ? -1 : session.getId(); + Logger.getLogger(DebugPanel.class.getName()).log(Level.FINE, "session" + sessionId + ": Error refeshing debug panel in UI thread", t); + } + + setLoading(false); + Logger.getLogger(DebugPanel.class.getName()).log(Level.FINE, "session{0}: Refreshed debug panel", new Object[]{session == null ? -1 : session.getId()}); - @Override - public void actionPerformed(ActionEvent e) { - traceLogTextarea.setText(""); - logLength = 0; - refresh(session); } }); - JPanel butPanel = new JPanel(new FlowLayout()); - butPanel.add(clearButton); - pa.add(butPanel, BorderLayout.SOUTH); - varTabs.addTab(AppStrings.translate("debuglog.header"), pa); + } catch (Throwable t) { + int sessionId = session == null ? -1 : session.getId(); + Logger.getLogger(DebugPanel.class.getName()).log(Level.FINE, "session" + sessionId + ": Error refeshing debug panel", t); } - boolean newVisible = !tabTypes.isEmpty(); - if (newVisible != isVisible()) { - setVisible(newVisible); - } - if (!tabTypes.isEmpty()) { - if (oldSel != null && !tabTypes.contains(oldSel)) { - oldSel = null; - } - } - if (oldSel != null) { - selectedTab = oldSel; - varTabs.setSelectedIndex(tabTypes.indexOf(selectedTab)); - } - setLoading(false); } - } - }); + }; + DebuggerHandler.getPool().submit(r); } public void dispose() { diff --git a/src/com/jpexs/decompiler/flash/gui/DebugStackPanel.java b/src/com/jpexs/decompiler/flash/gui/DebugStackPanel.java index 294e5662a..8b3508a6a 100644 --- a/src/com/jpexs/decompiler/flash/gui/DebugStackPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/DebugStackPanel.java @@ -45,6 +45,7 @@ import javax.swing.SwingUtilities; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableModel; /** * @author JPEXS @@ -67,8 +68,23 @@ public class DebugStackPanel extends JPanel { private int[] traitIndices = new int[0]; private WeakReference currentSessionRef = null; + private DefaultTableModel getStackTableModel(Object[][] data) { + return new DefaultTableModel(data, new Object[]{ + AppStrings.translate("callStack.header.swf"), + AppStrings.translate("callStack.header.file"), + AppStrings.translate("callStack.header.line"), + AppStrings.translate("stack.header.item") + }) { + @Override + public boolean isCellEditable(int row, int column) { + return false; + } + + }; + } + public DebugStackPanel() { - stackTable = new JTable(); + stackTable = new JTable(getStackTableModel(new Object[0][4])); Main.getDebugHandler().addFrameChangeListener(new DebuggerHandler.FrameChangeListener() { @Override public void frameChanged(DebuggerSession session) { @@ -160,8 +176,10 @@ public class DebugStackPanel extends JPanel { sessionComboBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - if (sessionComboBoxCreating) { - return; + synchronized (DebugStackPanel.this) { + if (sessionComboBoxCreating) { + return; + } } SessionItem selection = (SessionItem) sessionComboBox.getSelectedItem(); if (selection != null) { @@ -174,12 +192,13 @@ public class DebugStackPanel extends JPanel { lastSessionComboBoxIndex = sessionComboBox.getSelectedIndex(); Main.getDebugHandler().setSelectedSessionId(session.getId()); + //session.refreshFrame(); View.execInEventDispatch(new Runnable() { @Override public void run() { refresh(session); } - }); + }); View.execInEventDispatchLater(new Runnable() { @Override public void run() { @@ -210,7 +229,7 @@ public class DebugStackPanel extends JPanel { SessionItem item = (SessionItem) value; if (item != null) { DebuggerSession session = Main.getDebugHandler().getSessionById(item.id); - if (!session.isPaused()) { + if (session == null || !session.isPaused()) { c.setForeground(Color.GRAY); c.setBackground(list.getBackground()); } else { @@ -279,8 +298,8 @@ public class DebugStackPanel extends JPanel { } } - public void clear() { - stackTable.setModel(new DefaultTableModel()); + public void clear() { + stackTable.setModel(getStackTableModel(new Object[0][4])); if (Main.getDebugHandler().getActiveSessions().isEmpty()) { active = false; } @@ -304,7 +323,10 @@ public class DebugStackPanel extends JPanel { model.addElement(new SessionItem(id)); j++; } - sessionComboBoxCreating = true; + synchronized (this) { + sessionComboBoxCreating = true; + } + if (itemIndex > -1) { final int fItemIndex = itemIndex; View.execInEventDispatchLater(new Runnable() { @@ -315,7 +337,9 @@ public class DebugStackPanel extends JPanel { sessionComboBox.setSelectedIndex(fItemIndex); } lastSessionComboBoxIndex = fItemIndex; - sessionComboBoxCreating = false; + synchronized (DebugStackPanel.this) { + sessionComboBoxCreating = false; + } } }); } @@ -378,19 +402,7 @@ public class DebugStackPanel extends JPanel { newTraitIndices[i] = newTraitIndex == null ? -1 : newTraitIndex; } - DefaultTableModel tm = new DefaultTableModel(data, new Object[]{ - AppStrings.translate("callStack.header.swf"), - AppStrings.translate("callStack.header.file"), - AppStrings.translate("callStack.header.line"), - AppStrings.translate("stack.header.item") - }) { - @Override - public boolean isCellEditable(int row, int column) { - return false; - } - - }; - stackTable.setModel(tm); + stackTable.setModel(getStackTableModel(data)); this.swfHashes = newSwfHashes; this.classIndices = newClassIndices; this.methodIndices = newMethodIndices; @@ -410,11 +422,12 @@ public class DebugStackPanel extends JPanel { } }; - if (stackTable.getColumnModel().getColumnCount() >= 3) { + stackTable.setDefaultRenderer(String.class, renderer); + /*if (stackTable.getColumnModel().getColumnCount() >= 3) { stackTable.getColumnModel().getColumn(0).setCellRenderer(renderer); stackTable.getColumnModel().getColumn(1).setCellRenderer(renderer); stackTable.getColumnModel().getColumn(2).setCellRenderer(renderer); - } + }*/ repaint(); } }); diff --git a/src/com/jpexs/decompiler/flash/gui/DebuggerHandler.java b/src/com/jpexs/decompiler/flash/gui/DebuggerHandler.java index 019ebfc81..9c2560f5c 100644 --- a/src/com/jpexs/decompiler/flash/gui/DebuggerHandler.java +++ b/src/com/jpexs/decompiler/flash/gui/DebuggerHandler.java @@ -31,6 +31,10 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.WeakHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; /** * @author JPEXS @@ -46,6 +50,8 @@ public class DebuggerHandler implements DebugConnectionListener { private boolean terminating = false; private Map>> allBreakPoints = new WeakHashMap<>(); + + private static ExecutorService pool = null; public static class ActionScriptException extends Exception { @@ -263,6 +269,11 @@ public class DebuggerHandler implements DebugConnectionListener { synchronized (this) { terminating = false; } + + if (pool != null) { + pool.shutdownNow(); + pool = null; + } } public synchronized Map> getAllSessionsBreakPoints(SWF swf) { @@ -423,4 +434,20 @@ public class DebuggerHandler implements DebugConnectionListener { } return true; } + + + + public static synchronized ExecutorService getPool() { + if (pool == null) { + AtomicInteger counter = new AtomicInteger(1); + + ThreadFactory namedThreadFactory = runnable -> { + Thread t = new Thread(runnable); + t.setName("debugger-handler-thread-" + counter.getAndIncrement()); + return t; + }; + pool = Executors.newCachedThreadPool(namedThreadFactory); + } + return pool; + } } diff --git a/src/com/jpexs/decompiler/flash/gui/DebuggerSession.java b/src/com/jpexs/decompiler/flash/gui/DebuggerSession.java index 4df57d67c..5647bc689 100644 --- a/src/com/jpexs/decompiler/flash/gui/DebuggerSession.java +++ b/src/com/jpexs/decompiler/flash/gui/DebuggerSession.java @@ -17,6 +17,7 @@ package com.jpexs.decompiler.flash.gui; import com.jpexs.debugger.flash.DebugMessageListener; +import com.jpexs.debugger.flash.Debugger; import com.jpexs.debugger.flash.DebuggerCommands; import com.jpexs.debugger.flash.DebuggerConnection; import com.jpexs.debugger.flash.Variable; @@ -34,6 +35,7 @@ import com.jpexs.debugger.flash.messages.in.InFrame; import com.jpexs.debugger.flash.messages.in.InGetSwf; import com.jpexs.debugger.flash.messages.in.InGetVariable; import com.jpexs.debugger.flash.messages.in.InNumScript; +import com.jpexs.debugger.flash.messages.in.InOption; import com.jpexs.debugger.flash.messages.in.InPlaceObject; import com.jpexs.debugger.flash.messages.in.InProcessTag; import com.jpexs.debugger.flash.messages.in.InScript; @@ -47,6 +49,7 @@ import com.jpexs.debugger.flash.messages.out.OutGetSwf; import com.jpexs.debugger.flash.messages.out.OutPlay; import com.jpexs.debugger.flash.messages.out.OutProcessedTag; import com.jpexs.debugger.flash.messages.out.OutRewind; +import com.jpexs.debugger.flash.messages.out.OutSetOption; import com.jpexs.debugger.flash.messages.out.OutSwfInfo; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.configuration.Configuration; @@ -56,6 +59,7 @@ import java.io.IOException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -136,14 +140,17 @@ public class DebuggerSession { private InSetBreakpoint inSetBreakpoint; private int id; - + private String title = ""; - + private static final int DEFAULT_TIMEOUT = 1000; + private List getSwfThreadList = Collections.synchronizedList(new ArrayList<>()); + private Map watches = new LinkedHashMap<>(); + public DebuggerSession(DebuggerHandler handler, DebuggerConnection con, Map>> breakpoints) { - id = con.getId(); + id = con.getId(); toAddBPointMap = breakpoints; this.handler = handler; @@ -157,10 +164,8 @@ public class DebuggerSession { @Override public void run() { Main.getMainFrame().getPanel().updateMenu(); - } + } }); - - //enlog(DebuggerConnection.class); //enlog(DebuggerCommands.class); @@ -291,6 +296,10 @@ public class DebuggerSession { commands.setGetterTimeout(1500); commands.setSetterTimeout(5000); + if (commands.playerConcurrency()) { + commands.debuggerSetConcurrency(); + } + commands.squelch(true); con.addMessageListener(new DebugMessageListener() { @@ -340,16 +349,16 @@ public class DebuggerSession { if (inGetSwf == null) { Logger.getLogger(DebuggerSession.class.getName()).log(Level.FINE, "session{0}: Cannot read SWF", id); continue; - } + } String sha256 = Helper.byteArrayToHex(MessageDigest.getInstance("SHA-256").digest(inGetSwf.swfData)); Logger.getLogger(DebuggerSession.class.getName()).log(Level.FINE, "session{0}: Received SWF hash = {1}", new Object[]{id, sha256}); - + String originalHash = Main.getHashFromMetadataFromSwfBytes(inGetSwf.swfData); if (originalHash != null) { Logger.getLogger(DebuggerSession.class.getName()).log(Level.FINE, "session{0}: Received SWF original hash = {1}", new Object[]{id, originalHash}); sha256 = originalHash; } - + SWF debuggedSwf = Main.findOpenedSwfByHash(sha256); if (debuggedSwf == null) { con.disconnect(); @@ -379,9 +388,9 @@ public class DebuggerSession { } try { - + Set hashes = new LinkedHashSet<>(); - + for (int file : modulePaths.keySet()) { String path = modulePaths.get(file); if (!path.contains(":")) { @@ -391,22 +400,22 @@ public class DebuggerSession { String hash = path.substring(0, path.indexOf(":")); hashes.add(hash); } - for (String hash : hashes) { + for (String hash : hashes) { if (Main.findOpenedSwfByHash(hash) == null) { //This is probably SWF file instrumented by another software throw new IOException("SWF with hash " + hash + " not found"); } - } - + } + if (!debuggedSwfs.isEmpty() && modulesEmptyBefore) { if (title.isEmpty()) { title = debuggedSwfs.values().iterator().next().toString(); } if (con.isAS3) { - //Widelines - only AS3, it hangs in AS1/2 and SWD does not support UI32 lines - con.wideLines = commands.getOption("wide_line_player", "false").equals("true"); + //Widelines - only AS3, it hangs in AS1/2 and SWD does not support UI32 lines + con.wideLines = commands.playerIsWideLine(); if (con.wideLines) { - commands.setOption("wide_line_debugger", "on"); + commands.debuggerSetWideLine();; } } else { Logger.getLogger(DebuggerSession.class.getName()).log(Level.FINER, "session{0}: End of connect - sending continue", id); @@ -461,7 +470,19 @@ public class DebuggerSession { con.addMessageListener(new DebugMessageListener() { @Override public void message(InPlaceObject t) { - placedObjects.put(t.path, t.objId); + //Sometimes the player sends _global variable with 4 byte PTR instead of 8 byte + byte[] globalVar = new byte[]{(byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x5f, (byte) 0x67, (byte) 0x6c, (byte) 0x6f, (byte) 0x62, (byte) 0x61, (byte) 0x6c, (byte) 0x00}; + String placeName = t.path; + long placeId = t.objId; + if (Arrays.equals(t.data, globalVar)) { + Logger.getLogger(DebuggerSession.class.getName()).log(Level.FINER, "session{0}: Received mangled placeobject _global", new Object[]{id}); + placeName = "_global"; + placeId = -2; + } else { + Logger.getLogger(DebuggerSession.class.getName()).log(Level.FINER, "session{0}: Received placeobject {1}", new Object[]{id, t.path}); + } + Logger.getLogger(DebuggerSession.class.getName()).log(Level.FINEST, "session{0}: Placeobject hex data: {1}", new Object[]{id, Helper.byteArrayToHex(t.data)}); + placedObjects.put(placeName, placeId); con.dropMessage(t); } }); @@ -474,10 +495,10 @@ public class DebuggerSession { synchronized (DebuggerSession.this) { paused = false; Logger.getLogger(DebuggerSession.class.getName()).log(Level.FINE, "session{0}: continued", id); - } + } for (DebuggerHandler.BreakListener bl : handler.getBreakListeners()) { bl.doContinue(DebuggerSession.this); - } + } } }); @@ -491,14 +512,13 @@ public class DebuggerSession { @Override public void run() { Logger.getLogger(DebuggerSession.class.getName()).log(Level.FINE, "session{0}: In break at start", id); - + synchronized (DebuggerSession.this) { if (!connected) { Logger.getLogger(DebuggerSession.class.getName()).log(Level.FINE, "session{0}: Received break while not connected", id); return; } - - + paused = true; Logger.getLogger(DebuggerSession.class.getName()).log(Level.FINE, "session{0}: paused", id); } @@ -543,7 +563,7 @@ public class DebuggerSession { sendBreakPoints(); } catch (IOException ex) { //ignore - Logger.getLogger(DebuggerSession.class.getName()).log(Level.FINE, "session{0}: send breakpoints exception: {1}", new Object[] {id, ex.getMessage()}); + Logger.getLogger(DebuggerSession.class.getName()).log(Level.FINE, "session{0}: send breakpoints exception: {1}", new Object[]{id, ex.getMessage()}); } synchronized (DebuggerSession.this) { @@ -574,14 +594,15 @@ public class DebuggerSession { } else { Main.startWork(AppStrings.translate("debug.session").replace("%id%", "" + id) + " - " + AppStrings.translate("work.breakat") + userBreakScriptName + ":" + message.line + " " + AppStrings.translate("debug.break.reason." + reason), null, true); } - depth = 0; - refreshFrame(); - + //If there is single one left paused, switch to it if (Main.getDebugHandler().getNumberOfPausedSessions() == 1) { Main.getDebugHandler().setSelectedSessionId(getId()); } - + + depth = 0; + refreshFrame(); + for (DebuggerHandler.BreakListener l : handler.getBreakListeners()) { l.breakAt(DebuggerSession.this, newBreakScriptName, message.line, moduleToClassIndex.containsKey(message.file) ? moduleToClassIndex.get(message.file) : -1, @@ -631,7 +652,7 @@ public class DebuggerSession { } catch (IOException ex) { Logger.getLogger(DebuggerSession.class.getName()).log(Level.SEVERE, null, ex); } - }*/ + }*/ con.addMessageListener(new DebugMessageListener() { @Override @@ -657,8 +678,6 @@ public class DebuggerSession { public int getId() { return id; } - - public boolean containsSwf(SWF swf) { return debuggedSwfs.containsValue(swf); @@ -705,9 +724,9 @@ public class DebuggerSession { return stackLines; } - public InGetVariable getVariable(long parentId, String varName, boolean children, boolean useGetter) { + public synchronized InGetVariable getVariable(long parentId, String varName, boolean children, boolean useGetter) { try { - return commands.getVariableWithTimeout(parentId, varName, useGetter, children, 100); + return commands.getVariableWithTimeout(parentId, varName, useGetter, children, DEFAULT_TIMEOUT); } catch (IOException ex) { return null; } @@ -740,7 +759,7 @@ public class DebuggerSession { } public void removeBreakPoint(SWF swf, String scriptName, int line) { - synchronized (this) { + synchronized (this) { if (isBreakpointInvalid(swf, scriptName, line)) { invalidBreakPointMap.get(swf).get(scriptName).remove(line); if (invalidBreakPointMap.get(swf).get(scriptName).isEmpty()) { @@ -772,10 +791,19 @@ public class DebuggerSession { private int watchTag = 1; - public synchronized com.jpexs.debugger.flash.DebuggerCommands.Watch addWatch(Variable v, long v_id, boolean watchRead, boolean watchWrite) { + public synchronized DebuggerCommands.Watch addWatch(String varName, long varId, boolean watchRead, boolean watchWrite) { + if (getWatch(varName, varId) != null) { + if (!removeWatch(varName, varId)) { + return null; + } + } int tag = watchTag++; try { - return commands.addWatch(v_id, v.name, (watchRead ? OutAddWatch2.FLAG_READ : 0) | (watchWrite ? OutAddWatch2.FLAG_WRITE : 0), tag); + com.jpexs.debugger.flash.DebuggerCommands.Watch w = commands.addWatch(varId, varName, (watchRead ? OutAddWatch2.FLAG_READ : 0) | (watchWrite ? OutAddWatch2.FLAG_WRITE : 0), tag); + if (w != null) { + watches.put("" + w.varId + ":" + w.varName, w); + } + return w; } catch (IOException ex) { return null; } @@ -953,8 +981,11 @@ public class DebuggerSession { } public void setDepth(int depth) { + boolean depthChanged = depth != this.depth; this.depth = depth; - refreshFrame(); + if (depthChanged) { + refreshFrame(); + } } public String moduleToString(int file) { @@ -991,20 +1022,42 @@ public class DebuggerSession { } public void refreshFrame() { - synchronized (this) { + synchronized (DebuggerSession.this) { if (!paused) { return; } - try { - frame = commands.getFrameWithTimeout(depth, 100); - pool = commands.getConstantPoolWithTimeout(0, 100); - } catch (IOException ex) { - //ignore + } + + DebuggerHandler.getPool().submit(new Runnable() { + @Override + public void run() { + synchronized (DebuggerSession.this) { + if (!paused) { + return; + } + } + try { + frame = commands.getFrameWithTimeout(depth, DEFAULT_TIMEOUT); + pool = commands.getConstantPoolWithTimeout(0, DEFAULT_TIMEOUT); + } catch (IOException ex) { + //ignore + } + + Logger.getLogger(DebugPanel.class.getName()).log(Level.FINE, "session{0}: Frame loaded", new Object[]{getId()}); + + if (frame == null) { + Logger.getLogger(DebugPanel.class.getName()).log(Level.FINE, "session{0}: Frame is null!", new Object[]{getId()}); + } + + if (pool == null) { + Logger.getLogger(DebugPanel.class.getName()).log(Level.FINE, "session{0}: CPool is null!", new Object[]{getId()}); + } + + for (DebuggerHandler.FrameChangeListener l : handler.getFrameChangeListeners()) { + l.frameChanged(DebuggerSession.this); + } } - } - for (DebuggerHandler.FrameChangeListener l : handler.getFrameChangeListeners()) { - l.frameChanged(DebuggerSession.this); - } + }); } public synchronized InFrame getFrame() { @@ -1066,17 +1119,17 @@ public class DebuggerSession { } } } - + //If there is single one left paused, switch to it if (Main.getDebugHandler().getNumberOfPausedSessions() == 1) { - for (DebuggerSession session : Main.getDebugHandler().getActiveSessions().values()) { + for (DebuggerSession session : Main.getDebugHandler().getActiveSessions().values()) { if (session.isPaused()) { Main.getDebugHandler().setSelectedSessionId(session.getId()); break; } } } - + for (DebuggerHandler.ConnectionListener l : handler.getConnectionListeners()) { l.disconnected(DebuggerSession.this); } @@ -1282,5 +1335,45 @@ public class DebuggerSession { public String getTitle() { return title; - } + } + + public Map getWatches() { + return new LinkedHashMap<>(watches); + } + + public DebuggerCommands.Watch getWatch(String varName, long varId) { + return watches.get("" + varId + ":" + varName); + } + + public boolean removeWatch(String varName, long variId) { + String key = "" + variId + ":" + varName; + if (!watches.containsKey(key)) { + return false; + } + try { + if (commands.removeWatch(variId, varName)) { + watches.remove(key); + return true; + } + } catch (IOException ex) { + //false + } + return false; + } + + public SWF getCurrentSwf() { + InBreakAtExt info = getBreakInfo(); + if (info == null) { + return null; + } + //Object[][] data = new Object[info.files.size()][4]; + String moduleName = moduleToString(info.file); + if (moduleName.contains(":")) { + String swfHash = moduleName.substring(0, moduleName.indexOf(":")); + return Main.findOpenedSwfByHash(swfHash); + } else { + List debuggedSwfs = new ArrayList<>(getDebuggedSwfs().values()); + return debuggedSwfs.get(debuggedSwfs.size() - 1); + } + } } diff --git a/src/com/jpexs/decompiler/flash/gui/Main.java b/src/com/jpexs/decompiler/flash/gui/Main.java index 964054641..09fb9fd9e 100644 --- a/src/com/jpexs/decompiler/flash/gui/Main.java +++ b/src/com/jpexs/decompiler/flash/gui/Main.java @@ -441,9 +441,13 @@ public class Main { } public static synchronized boolean addWatch(DebuggerSession session, Variable v, long v_id, boolean watchRead, boolean watchWrite) { - DebuggerCommands.Watch w = session.addWatch(v, v_id, watchRead, watchWrite); + DebuggerCommands.Watch w = session.addWatch(v.name, v_id, watchRead, watchWrite); return w != null; } + + public static synchronized boolean removeWatch(DebuggerSession session, Variable v, long v_id) { + return session.removeWatch(v.name, v_id); + } public static void runPlayer(String title, final String exePath, String file, String flashVars) { if (!new File(file).exists()) { @@ -1137,7 +1141,7 @@ public class Main { } public static void updateSession() { - DebuggerSession session = getCurrentDebugSession(); + /*DebuggerSession session = getCurrentDebugSession(); for (DebuggerHandler.FrameChangeListener l : getDebugHandler().getFrameChangeListeners()) { l.frameChanged(session); } @@ -1145,6 +1149,7 @@ public class Main { if (getDebugHandler().getNumberOfPausedSessions() > 0) { mainFrame.getPanel().showDebugStackFrame(); } + */ } public static void ensureMainFrame() { @@ -3197,7 +3202,7 @@ public class Main { @Override public void disconnected(DebuggerSession session) { - Main.updateSession(); + //Main.updateSession(); if (Main.mainFrame != null) { Main.mainFrame.getMenu().updateComponents(); } diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index 3ede7ad82..6c0973547 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -1636,7 +1636,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se private void updateUi(final Openable openable) { View.checkAccess(); - Main.updateSession(); + //Main.updateSession(); SWF swf = null; if (openable instanceof SWF) { swf = (SWF) openable; @@ -5785,7 +5785,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se return; } - Main.updateSession(); + //Main.updateSession(); if (!(treeItem instanceof OpenableList)) { Openable openable = treeItem.getOpenable(); diff --git a/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java b/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java index 4201d1175..6e6bcc54b 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java @@ -16,10 +16,13 @@ */ package com.jpexs.decompiler.flash.gui.abc; +import com.jpexs.debugger.flash.DebuggerCommands; +import com.jpexs.debugger.flash.DebuggerMessage; import com.jpexs.debugger.flash.Variable; import com.jpexs.debugger.flash.VariableFlags; import com.jpexs.debugger.flash.VariableType; import com.jpexs.debugger.flash.messages.in.InGetVariable; +import com.jpexs.debugger.flash.messages.out.OutAddWatch2; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.ClassPath; @@ -266,7 +269,7 @@ public class ABCPanel extends JPanel implements ItemListener, SearchListener loaderList = Collections.synchronizedList(new ArrayList<>()); - private static final Object loaderLock = new Object(); - + private static Map sessionIdToVariableLoaderThread = new HashMap<>(); + private static Map> sessionIdToLoaderList = new HashMap<>(); + private static Map> sessionIdToLoadedVariableNode = new HashMap<>(); + private static Map sessionIdToLoaderLock = new HashMap<>(); + public List path = new ArrayList<>(); public Variable var; @@ -322,8 +326,25 @@ public class ABCPanel extends JPanel implements ItemListener, SearchListener childs; public List traits = new ArrayList<>(); + private DebuggerSession session; + private MyTreeTable treeTable; private boolean as3; - + + public static void stopLoading(int sessionId) { + if (!sessionIdToLoadedVariableNode.containsKey(sessionId)) { + return; + } + sessionIdToLoaderList.get(sessionId).clear(); + for (VariableNode node : sessionIdToLoadedVariableNode.get(sessionId)) { + synchronized (node) { + node.childs = new ArrayList<>(); + node.loading = false; + node.loaded = true; + } + } + sessionIdToLoadedVariableNode.get(sessionId).clear(); + } + @Override public int hashCode() { int hash = 3; @@ -361,6 +382,8 @@ public class ABCPanel extends JPanel implements ItemListener, SearchListener 0) { varInsideGetter = igv.parent; @@ -416,10 +444,10 @@ public class ABCPanel extends JPanel implements ItemListener, SearchListener 0) { continue; } - + if (!isTraits(igv.childs.get(i))) { Long parentObjectId = varToObjectId(varInsideGetter); - childs.add(new VariableNode(as3, path, level + 1, igv.childs.get(i), parentObjectId, curTrait)); + childs.add(new VariableNode(currentSession, treeTable, as3, path, level + 1, igv.childs.get(i), parentObjectId, curTrait)); } else { curTrait = igv.childs.get(i); traits.add(curTrait); @@ -427,62 +455,130 @@ public class ABCPanel extends JPanel implements ItemListener, SearchListener()); + sessionIdToLoaderLock.put(sessionId, new Object()); + Thread t = new Thread(r, "Variable loader for session " + sessionId); + sessionIdToVariableLoaderThread.put(sessionId, t); + t.setDaemon(true); + t.start(); + try { + Thread.sleep(100); + } catch (InterruptedException ex) { + //ignore + } } + + sessionIdToLoaderList.get(sessionId).add(new Runnable() { + @Override + public void run() { + synchronized (VariableNode.this) { + if (!loading) { + Logger.getLogger(VariableNode.class.getName()).log(Level.FINE, "session{0}: Already loaded {1}, skipping", new Object[]{session.getId(), var.name}); + return; + } + } + Logger.getLogger(VariableNode.class.getName()).log(Level.FINE, "session{0}: Loading children of {1}", new Object[]{session.getId(), var.name}); + + reloadChildren(); + + synchronized (VariableNode.this) { + if (!loading) { + Logger.getLogger(VariableNode.class.getName()).log(Level.FINE, "session{0}: Already loaded {1}, not firing", new Object[]{session.getId(), var.name}); + return; + } + loaded = true; + loading = false; + Logger.getLogger(VariableNode.class.getName()).log(Level.FINE, "session{0}: Children of {1} loaded", new Object[]{session.getId(), var.name}); + } + + DebuggerSession currentSession = Main.getCurrentDebugSession(); + if (currentSession == null || currentSession.getId() != sessionId) { + return; + } + + VariablesTableModel variableModel = (VariablesTableModel) treeTable.getTreeTableModel(); + Object[] changedPath = new Object[path.size()]; + for (int i = 0; i < path.size(); i++) { + changedPath[i] = path.get(i); + } + View.execInEventDispatch(new Runnable() { + @Override + public void run() { + Logger.getLogger(VariableNode.class.getName()).log(Level.FINE, "session{0}: Firing change of {1} - {2} childs", new Object[]{session.getId(), var.name, childs.size()}); + int[] childIds = new int[childs.size()]; + Object[] childObjs = new Object[childs.size()]; + + for (int i = 0; i < childIds.length; i++) { + childIds[i] = i; + childObjs[i] = childs.get(i); + } + + //variableModel.fireTreeStructureChanged(treeTable.getTree(), changedPath, new int[0], null); + variableModel.fireTreeNodesInserted(treeTable.getTree(), changedPath, childIds, childObjs); + treeTable.repaint(); + } + }); + } + }); + if (!sessionIdToLoadedVariableNode.containsKey(sessionId)) { + sessionIdToLoadedVariableNode.put(sessionId, new ArrayList<>()); + } + sessionIdToLoadedVariableNode.get(sessionId).add(this); + Object lock = sessionIdToLoaderLock.get(sessionId); + Logger.getLogger(VariableNode.class.getName()).log(Level.FINE, "session{0}: Scheduling loading children of {1}", new Object[]{session.getId(), var.name}); + synchronized (lock) { + lock.notify(); + } + /*reloadChildren(); + loaded = true;*/ } public VariableNode getChildAt(int index) { ensureLoaded(); + if (!loaded) { + return null; + } return childs.get(index); } public int getChildCount() { ensureLoaded(); + if (!loaded) { + return 0; + } return childs.size(); } - public VariableNode(boolean as3, List parentPath, int level, Variable var, Long parentObjectId, Variable trait) { + public VariableNode(DebuggerSession session, MyTreeTable treeTable, boolean as3, List parentPath, int level, Variable var, Long parentObjectId, Variable trait) { this.var = var; this.varInsideGetter = var; this.parentObjectId = parentObjectId; @@ -491,10 +587,12 @@ public class ABCPanel extends JPanel implements ItemListener, SearchListener parentPath, int level, Variable var, Long parentObjectId, Variable trait, List subvars) { + public VariableNode(DebuggerSession session, MyTreeTable treeTable, boolean as3, List parentPath, int level, Variable var, Long parentObjectId, Variable trait, List subvars) { this.var = var; this.varInsideGetter = var; this.parentObjectId = parentObjectId; @@ -510,6 +608,8 @@ public class ABCPanel extends JPanel implements ItemListener, SearchListener vars) { - this.ttable = ttable; + public VariablesTableModel(DebuggerSession session, boolean as3, MyTreeTable treeTable, List vars, List parentIds) { + this.treeTable = treeTable; List childs = new ArrayList<>(); for (int i = 0; i < vars.size(); i++) { - childs.add(new VariableNode(as3, new ArrayList<>(), 1, vars.get(i), 0L, null)); + childs.add(new VariableNode(session, treeTable, as3, new ArrayList<>(), 1, vars.get(i), parentIds == null ? 0L : parentIds.get(i), null)); } - root = new VariableNode(as3, new ArrayList<>(), 0, null, 0L, null, childs); + root = new VariableNode(session, treeTable, as3, new ArrayList<>(), 0, null, 0L, null, childs); + root.loaded = true; } @Override @@ -767,7 +868,26 @@ public class ABCPanel extends JPanel implements ItemListener, SearchListener 0) { + if (!flags.isEmpty()) { + flags += ", "; + } + + flags += "watch:read"; + } + if ((w.flags & OutAddWatch2.FLAG_WRITE) > 0) { + if (!flags.isEmpty()) { + flags += ", "; + } + + flags += "watch:write"; + } + } + return flags; case COLUMN_TYPE: String typeStr = val.getTypeAsStr(); if ("Object".equals(typeStr)) { @@ -782,7 +902,7 @@ public class ABCPanel extends JPanel implements ItemListener, SearchListener(), ci.abc.getSwf(), true); - - if (swfRef.getVal() == abc.getSwf()) { + + if (swfRef.getVal() == abc.getSwf()) { hilightScript(getOpenable(), scriptNamePrintable); return; } @@ -1131,12 +1251,12 @@ public class ABCPanel extends JPanel implements ItemListener, SearchListener foundStatic = new Reference<>(null); @@ -1212,7 +1332,7 @@ public class ABCPanel extends JPanel implements ItemListener, SearchListener