diff --git a/src/com/jpexs/decompiler/flash/gui/DebugPanel.java b/src/com/jpexs/decompiler/flash/gui/DebugPanel.java index d36a02279..b60bf4a9b 100644 --- a/src/com/jpexs/decompiler/flash/gui/DebugPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/DebugPanel.java @@ -26,20 +26,30 @@ import de.hameister.treetable.MyTreeTable; import de.hameister.treetable.MyTreeTableModel; import java.awt.BorderLayout; import java.awt.Color; +import java.awt.EventQueue; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.List; import javax.swing.JButton; import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; import javax.swing.JPanel; +import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JTable; import javax.swing.JTextArea; +import javax.swing.JTree; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; import javax.swing.event.TreeModelEvent; import javax.swing.event.TreeModelListener; import javax.swing.table.DefaultTableModel; @@ -105,6 +115,9 @@ public class DebugPanel extends JPanel { View.expandTreeNodes(tt.getTree(), expanded); for (int i = 0; i < selRows.length; i++) { selRows[i] = tt.getTree().getRowForPath(selPaths[i]); + if (selRows[i] == -1) { + continue; + } if (i == 0) { tt.setRowSelectionInterval(selRows[i], selRows[i]); } else { @@ -112,6 +125,14 @@ public class DebugPanel extends JPanel { } } + int ROW_HEIGHT = new JLabel("A").getPreferredSize().height; + + JTree tree = tt.getTree(); + tree.setRowHeight(ROW_HEIGHT); + for (int i = 0; i < tt.getRowCount(); i++) { + tt.setRowHeight(i, ROW_HEIGHT); + } + } public DebugPanel() { @@ -119,74 +140,81 @@ public class DebugPanel extends JPanel { debugRegistersTable = new MyTreeTable(new ABCPanel.VariablesTableModel(debugRegistersTable, new ArrayList<>(), new ArrayList<>()), false); debugLocalsTable = new MyTreeTable(new ABCPanel.VariablesTableModel(debugLocalsTable, new ArrayList<>(), new ArrayList<>()), false); - //Add watch feature, commented out. I tried it, but without success. I can't add watch in Flash Pro or FDB either. :-( - /* - //locales - debug.watch.add = Add watch to %name% - debug.watch.add.read = Read - debug.watch.add.write = Write - debug.watch.add.readwrite = Read+Write + MouseAdapter watchHandler = new MouseAdapter() { - error.debug.watch.add = Cannot add watch to this variable. + @Override + public void mousePressed(MouseEvent e) { + if (e.isPopupTrigger()) { + dopop(e); + } + } + @Override + public void mouseReleased(MouseEvent e) { + if (e.isPopupTrigger()) { + dopop(e); + } + } - MouseAdapter watchHandler = new MouseAdapter() { + private void dopop(MouseEvent e) { + if (debugLocalsTable.getSelectedRow() == -1) { + return; + } + Object node = debugLocalsTable.getTree().getPathForRow(debugLocalsTable.getSelectedRow()).getLastPathComponent(); + Variable v; + ABCPanel.VariableNode vn; + if (node instanceof ABCPanel.VariableNode) { + vn = ((ABCPanel.VariableNode) node); + v = vn.var; + } else { + return; + } - @Override - public void mousePressed(MouseEvent e) { - if (e.isPopupTrigger()) { - dopop(e); - } - } + JPopupMenu pm = new JPopupMenu(); + //TODO!! + /*if (v.typeName != null && v.typeName.startsWith("flash.utils::ByteArray")) { + JMenu exportMenu = new JMenu("Export %name%".replace("%name%", v.name)); + JMenuItem exportByteArray = new JMenuItem("Export bytearray"); + exportByteArray.addActionListener((ActionEvent e1) -> { + Main.dumpBytes(v); + }); - @Override - public void mouseReleased(MouseEvent e) { - if (e.isPopupTrigger()) { - dopop(e); - } - } + exportMenu.add(exportByteArray); + pm.add(exportMenu); + }*/ + long watchParentId = vn.parentObjectId; - private void dopop(MouseEvent e) { - if (debugLocalsTable.getSelectedRow() == -1) { - return; - } - Variable v = ((ABCPanel.VariablesTableModel) debugLocalsTable.getModel()).getVars().get(debugLocalsTable.getSelectedRow()); - final long v_id = ((ABCPanel.VariablesTableModel) debugLocalsTable.getModel()).getVarIds().get(debugLocalsTable.getSelectedRow()); + JMenu addWatchMenu = new JMenu(AppStrings.translate("debug.watch.add").replace("%name%", v.name)); + JMenuItem watchReadMenuItem = new JMenuItem(AppStrings.translate("debug.watch.add.read")); + watchReadMenuItem.addActionListener((ActionEvent e1) -> { + if (!Main.addWatch(v, watchParentId, true, false)) { + View.showMessageDialog(DebugPanel.this, AppStrings.translate("error.debug.watch.add"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + } + }); + JMenuItem watchWriteMenuItem = new JMenuItem(AppStrings.translate("debug.watch.add.write")); + watchWriteMenuItem.addActionListener((ActionEvent e1) -> { + if (!Main.addWatch(v, watchParentId, false, true)) { + View.showMessageDialog(DebugPanel.this, AppStrings.translate("error.debug.watch.add"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + } + }); + JMenuItem watchReadWriteMenuItem = new JMenuItem(AppStrings.translate("debug.watch.add.readwrite")); + watchReadWriteMenuItem.addActionListener((ActionEvent e1) -> { + if (!Main.addWatch(v, watchParentId, true, true)) { + View.showMessageDialog(DebugPanel.this, AppStrings.translate("error.debug.watch.add"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + } + }); - JPopupMenu pm = new JPopupMenu(); - JMenu addWatchMenu = new JMenu(AppStrings.translate("debug.watch.add").replace("%name%", v.name)); - JMenuItem watchReadMenuItem = new JMenuItem(AppStrings.translate("debug.watch.add.read")); - watchReadMenuItem.addActionListener((ActionEvent e1) -> { - if (!Main.addWatch(v, v_id, true, false)) { - View.showMessageDialog(DebugPanel.this, AppStrings.translate("debug.watch.add"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - } - }); - JMenuItem watchWriteMenuItem = new JMenuItem(AppStrings.translate("debug.watch.add.write")); - watchWriteMenuItem.addActionListener((ActionEvent e1) -> { - if (!Main.addWatch(v, v_id, false, true)) { - View.showMessageDialog(DebugPanel.this, AppStrings.translate("debug.watch.add"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - } - }); - JMenuItem watchReadWriteMenuItem = new JMenuItem(AppStrings.translate("debug.watch.add.readwrite")); - watchReadWriteMenuItem.addActionListener((ActionEvent e1) -> { - if (!Main.addWatch(v, v_id, true, true)) { - View.showMessageDialog(DebugPanel.this, AppStrings.translate("debug.watch.add"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - } - }); + addWatchMenu.add(watchReadMenuItem); + addWatchMenu.add(watchWriteMenuItem); + addWatchMenu.add(watchReadWriteMenuItem); + pm.add(addWatchMenu); + pm.show(e.getComponent(), e.getX(), e.getY()); + } + }; - addWatchMenu.add(watchReadMenuItem); - addWatchMenu.add(watchWriteMenuItem); - addWatchMenu.add(watchReadWriteMenuItem); - pm.add(addWatchMenu); + debugLocalsTable.addMouseListener(watchHandler); - pm.show(e.getComponent(), e.getX(), e.getY()); - } - }; - - debugLocalsTable.addMouseListener(watchHandler); - debugScopeTable.addMouseListener(watchHandler); - - */ + //debugScopeTable.addMouseListener(watchHandler); debugScopeTable = new MyTreeTable(new ABCPanel.VariablesTableModel(debugScopeTable, new ArrayList<>(), new ArrayList<>()), false); callStackTable = new JTable(); @@ -281,6 +309,33 @@ public class DebugPanel extends JPanel { add(varTabs, BorderLayout.CENTER); } + /* private void getVariableList() { + // make sure the player has stopped and send our message awaiting a response + + Main.getDebugHandler().getVariable(VariableConstants.GLOBAL_ID, TOOL_TIP_TEXT_KEY, loading) + + requestFrame(0, isolateId); // our 0th frame gets our local context + + // now let's request all of the special variables too + getValueWorker(VariableConstants.GLOBAL_ID, isolateId); + getValueWorker(VariableConstants.THIS_ID, isolateId); + getValueWorker(VariableConstants.ROOT_ID, isolateId); + + // request as many levels as we can get + int i = 0; + Value v = null; + do { + v = getValueWorker(Value.LEVEL_ID - i, isolateId); + } while (i++ < 128 && v != null); + + // now that we've primed the DManager we can request the base variable whose + // children are the variables that are available + v = m_manager.getValue(Value.BASE_ID, isolateId); + if (v == null) { + throw new VersionException(); + } + return v.getMembers(this); + }*/ public void refresh() { View.execInEventDispatch(new Runnable() { diff --git a/src/com/jpexs/decompiler/flash/gui/Main.java b/src/com/jpexs/decompiler/flash/gui/Main.java index c554c43b1..8532a7e77 100644 --- a/src/com/jpexs/decompiler/flash/gui/Main.java +++ b/src/com/jpexs/decompiler/flash/gui/Main.java @@ -19,6 +19,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.VariableType; +import com.jpexs.debugger.flash.messages.in.InCallFunction; import com.jpexs.decompiler.flash.ApplicationInfo; import com.jpexs.decompiler.flash.EventListener; import com.jpexs.decompiler.flash.SWF; @@ -206,6 +208,29 @@ public class Main { return runProcess != null && !runProcessDebug; } + /** + * FIXME! + * + * @param v + */ + public static synchronized void dumpBytes(Variable v) { + InCallFunction icf; + try { + long objectId = 0l; + if ((v.vType == VariableType.OBJECT || v.vType == VariableType.MOVIECLIP)) { + objectId = (Long) v.value; + } + Object oldPos = getDebugHandler().getVariable(objectId, "position", true).parent.value; + getDebugHandler().setVariable(objectId, "position", VariableType.NUMBER, 0); + icf = getDebugHandler().callFunction(false, "readUTF", v, new ArrayList<>()); + System.out.println("Result=" + icf.variables.get(0).value); + getDebugHandler().setVariable(objectId, "position", VariableType.NUMBER, oldPos); + } catch (DebuggerHandler.ActionScriptException ex) { + Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); + } + + } + public static synchronized boolean addWatch(Variable v, long v_id, boolean watchRead, boolean watchWrite) { DebuggerCommands.Watch w = getDebugHandler().addWatch(v, v_id, watchRead, watchWrite); return w != null; diff --git a/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java b/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java index 359a0c926..90577159b 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java @@ -309,25 +309,23 @@ public class ABCPanel extends JPanel implements ItemListener, SearchListener path = new ArrayList<>(); - public Long parentId; + public Variable var; + public Variable varInsideGetter; + + public Long parentObjectId; public int level; - public Variable thisVar; + public Variable trait; + public long traitId; - public Variable thisTrait; - - public long thisTraitId; - - private List childs; - - private List childTraits; + private List childs; @Override public int hashCode() { int hash = 3; - hash = 53 * hash + Objects.hashCode(this.parentId); - hash = 53 * hash + (this.thisVar == null ? 0 : Objects.hashCode(this.thisVar.name)); + hash = 53 * hash + Objects.hashCode(this.parentObjectId); + hash = 53 * hash + (this.var == null ? 0 : Objects.hashCode(this.var.name)); return hash; } @@ -343,19 +341,19 @@ public class ABCPanel extends JPanel implements ItemListener, SearchListener 1) { path.get(path.size() - 2).reloadChildren(); - } else { - //Main.getDebugHandler().refreshFrame(); - //InFrame fr = Main.getDebugHandler().getFrame(); } } private void reloadChildren() { childs = new ArrayList<>(); - childTraits = new ArrayList<>(); - InGetVariable igv = Main.getDebugHandler().getVariable(parentId, thisVar.name, true); + if ("".equals(var.name)) { + return; + } + InGetVariable igv; - if (thisVar.vType != VariableType.FUNCTION || ((thisVar.flags & VariableFlags.HAS_GETTER) > 0)) { - if (parentId != 0) { - thisVar = igv.parent; - } + Long objectId = varToObjectId(varInsideGetter); + + if (parentObjectId == 0 && objectId != 0) { + igv = Main.getDebugHandler().getVariable(objectId, "", true); + } else { + igv = Main.getDebugHandler().getVariable(parentObjectId, var.name, true); + } + + //current var is getter function - set it to value really got + //if ((var.flags & VariableFlags.HAS_GETTER) > 0) + { + varInsideGetter = igv.parent; } Variable curTrait = null; - for (int i = 0; i < igv.childs.size(); i++) { if (!isTraits(igv.childs.get(i))) { - childs.add(igv.childs.get(i)); - childTraits.add(curTrait); + Long parentObjectId = varToObjectId(var); + childs.add(new VariableNode(path, level + 1, igv.childs.get(i), parentObjectId, curTrait));//igv.parentId } else { curTrait = igv.childs.get(i); } @@ -413,14 +417,7 @@ public class ABCPanel extends JPanel implements ItemListener, SearchListener parentPath, int level, Variable var, Long parentObjectId, Variable trait) { + this.var = var; + this.parentObjectId = parentObjectId; this.level = level; - this.thisTrait = thisTrait; + this.trait = trait; + this.path.addAll(parentPath); + this.path.add(this); + loaded = false; } - public VariableNode(int level, Variable thisVar, Long parentId, Variable thisTrait, Long thisTraitId, List vars, List varTraits) { - this.parentId = parentId; - - this.thisVar = thisVar; - + public VariableNode(List parentPath, int level, Variable var, Long parentObjectId, Variable trait, List subvars) { + this.var = var; + this.parentObjectId = parentObjectId; this.level = level; - this.childs = vars; + this.trait = trait; - this.thisTrait = thisTrait; - - this.childTraits = varTraits; + this.childs = subvars; + this.path.addAll(parentPath); this.path.add(this); - + for (VariableNode vn : subvars) { + vn.path.clear(); + vn.path.addAll(this.path); + vn.path.add(vn); + } loaded = true; } } + public static Long varToObjectId(Variable var) { + if (var != null && (var.vType == VariableType.OBJECT)) //|| var.vType == VariableType.MOVIECLIP)) { + { + return (Long) var.value; + } else { + return 0L; + } + } + public static class VariablesTableModel implements MyTreeTableModel { List tableListeners = new ArrayList<>(); @@ -474,27 +484,49 @@ public class ABCPanel extends JPanel implements ItemListener, SearchListener vars, List parentIds) { this.ttable = ttable; - List varTraits = new ArrayList<>(); + + List childs = new ArrayList<>(); + for (int i = 0; i < vars.size(); i++) { - varTraits.add(null); + childs.add(new VariableNode(new ArrayList<>(), 1, vars.get(i), 0L/*parentIds.get(i)*/, null)); } - root = new VariableNode(0, null, 0L, null, 0L, vars, varTraits); + root = new VariableNode(new ArrayList<>(), 0, null, 0L, null, childs); } @Override public int getColumnCount() { - return 3; + return 6; } + public Variable getVarAt(Object node) { + if (node == root) { + return null; + } + return ((VariableNode) node).var; + } + + private static final int COLUMN_NAME = 0; + private static final int COLUMN_TRAIT = 1; + private static final int COLUMN_SCOPE = 2; + private static final int COLUMN_FLAGS = 3; + private static final int COLUMN_TYPE = 4; + private static final int COLUMN_VALUE = 5; + @Override public String getColumnName(int columnIndex) { switch (columnIndex) { - case 0: + case COLUMN_NAME: return AppStrings.translate("variables.column.name"); - case 1: + case COLUMN_SCOPE: + return AppStrings.translate("variables.column.scope"); + case COLUMN_FLAGS: + return AppStrings.translate("variables.column.flags"); + case COLUMN_TYPE: return AppStrings.translate("variables.column.type"); - case 2: + case COLUMN_VALUE: return AppStrings.translate("variables.column.value"); + case COLUMN_TRAIT: + return AppStrings.translate("variables.column.trait"); default: return null; } @@ -502,12 +534,118 @@ public class ABCPanel extends JPanel implements ItemListener, SearchListener getColumnClass(int columnIndex) { - if (columnIndex == 0) { + if (columnIndex == COLUMN_NAME) { return MyTreeTableModel.class; } return String.class; } + private String flagsToScopeString(int flags) { + int scope = flags & VariableFlags.SCOPE_MASK; + switch (scope) { + case VariableFlags.PRIVATE_SCOPE: + return "private"; + case VariableFlags.PROTECTED_SCOPE: + return "protected"; + case VariableFlags.PUBLIC_SCOPE: + return "public"; + case VariableFlags.NAMESPACE_SCOPE: + return "namespace"; + case VariableFlags.INTERNAL_SCOPE: + return "internal"; + default: + return "?"; + } + } + + /* + 1 DONT_ENUMERATE + 2 ? + 4 READ_ONLY + 8 ? + 16 ? + 32 IS_LOCAL + 64 + 128 + 256 + 512 + 1024 + 2048 + 4096 + 8192 + 16384 + 32768 + 65536 IS_ARGUMENT + 131072 IS_DYNAMIC + 262144 IS_EXCEPTION + 524288 HAS_GETTER + 1048576 HAS_SETTER + 2097152 IS_STATIC + 4194304 IS_CONST + 8388608,16777216,33554432 SCOPE + 67108864 IS_CLASS + */ + private String flagsToString(int flags) { + + Integer unknownFlags[] = new Integer[]{ + 2, + 8, + 16, + 64, + 128, + 256, + 512, + 1024, + 2048, + 4096, + 8192, + 16384, + 32768 + }; + List flagsStr = new ArrayList<>(); + + if ((flags & VariableFlags.DONT_ENUMERATE) > 0) { + flagsStr.add("dontEnumerate"); + } + for (Integer f : unknownFlags) { + if ((flags & f) > 0) { + flagsStr.add("unk" + f); + } + } + if ((flags & VariableFlags.HAS_GETTER) > 0) { + flagsStr.add("get"); + } + if ((flags & VariableFlags.HAS_SETTER) > 0) { + flagsStr.add("set"); + } + if ((flags & VariableFlags.READ_ONLY) > 0) { + flagsStr.add("readonly"); + } + if ((flags & VariableFlags.IS_CONST) > 0) { + flagsStr.add("const"); + } + if ((flags & VariableFlags.IS_DYNAMIC) > 0) { + flagsStr.add("dynamic"); + } + if ((flags & VariableFlags.IS_CLASS) > 0) { + flagsStr.add("class"); + } + if ((flags & VariableFlags.IS_ARGUMENT) > 0) { + flagsStr.add("argument"); + } + if ((flags & VariableFlags.IS_EXCEPTION) > 0) { + flagsStr.add("exception"); + } + if ((flags & VariableFlags.IS_LOCAL) > 0) { + flagsStr.add("local"); + } + if ((flags & VariableFlags.IS_STATIC) > 0) { + flagsStr.add("static"); + } + + return String.join(", ", flagsStr); + } + @Override public Object getValueAt(Object node, int columnIndex) { if (node == root) { @@ -516,39 +654,59 @@ public class ABCPanel extends JPanel implements ItemListener, SearchListener 0; + boolean hasGetter = (var.flags & VariableFlags.HAS_GETTER) > 0; + boolean hasSetter = (var.flags & VariableFlags.HAS_SETTER) > 0; + boolean onlySetter = hasSetter && !hasGetter; + + //String flagStr = flagsToString(var.flags); + Variable val = var; + if (var_getter != null) { + val = var_getter; + } switch (columnIndex) { - case 0: - return v.name; - case 1: - String typeStr = v.getTypeAsStr(); + case COLUMN_NAME: + return var.name; + case COLUMN_SCOPE: + return flagsToScopeString(var.flags); + case COLUMN_FLAGS: + return flagsToString(var.flags); + case COLUMN_TYPE: + String typeStr = val.getTypeAsStr(); if ("Object".equals(typeStr)) { - typeStr = v.className; + typeStr = val.className; } if ("Object".equals(typeStr)) { - typeStr = v.typeName; + typeStr = val.typeName; } return typeStr; - case 2: - switch (v.vType) { + case COLUMN_VALUE: + switch (val.vType) { case VariableType.OBJECT: case VariableType.MOVIECLIP: case VariableType.FUNCTION: - return v.getTypeAsStr() + "(" + v.value + ")"; + return var.getTypeAsStr() + "(" + val.value + ")"; case VariableType.STRING: - return "\"" + Helper.escapeActionScriptString("" + v.value) + "\""; + return "\"" + Helper.escapeActionScriptString("" + val.value) + "\""; default: - return EcmaScript.toString(v.value); + return EcmaScript.toString(val.value); } - + case COLUMN_TRAIT: + if (trait != null) { + return trait.name; + } + return ""; } return null; } @Override public boolean isCellEditable(Object node, int column) { - return column == 0 || (column == 2 && node != root && ((VariableNode) node).thisVar.isPrimitive); + return column == COLUMN_NAME || (column == COLUMN_VALUE && node != root && ((VariableNode) node).var.isPrimitive); } @Override @@ -584,7 +742,7 @@ public class ABCPanel extends JPanel implements ItemListener, SearchListener