Added: #1383 AS Debugger - debugging nested SWFs

This commit is contained in:
Jindra Petřík
2024-08-04 22:26:00 +02:00
parent c3389dbfd1
commit 84d6ad8591
45 changed files with 1409 additions and 192 deletions

View File

@@ -57,6 +57,7 @@ import com.jpexs.decompiler.graph.DottedChain;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -85,7 +86,13 @@ public class DebuggerHandler implements DebugConnectionListener {
private Map<Integer, String> modulePaths = new HashMap<>();
private Map<Integer, Integer> moduleToSwfIndex = new HashMap<>();
//Marks swfIndices that are fully loaded - at least one break was on it (including onloaded break)
private Set<Integer> swfIndicesCommited = new HashSet<>();
private Map<Integer,String> swfIndicesNewToSwfHash = new HashMap<>();
private Map<String, Integer> scriptToModule = new HashMap<>();
private Map<Integer, Integer> moduleToTraitIndex = new HashMap<>();
@@ -110,7 +117,7 @@ public class DebuggerHandler implements DebugConnectionListener {
private List<Integer> stackLines = new ArrayList<>();
private SWF debuggedSwf = null;
private List<SWF> debuggedSwfs = new ArrayList<>();
public static class ActionScriptException extends Exception {
@@ -130,12 +137,13 @@ public class DebuggerHandler implements DebugConnectionListener {
}
}
public void setDebuggedSwf(SWF debuggedSwf) {
this.debuggedSwf = debuggedSwf;
public void setMainDebuggedSwf(SWF debuggedSwf) {
debuggedSwfs.clear();
//debuggedSwfs.add(debuggedSwf);
}
public SWF getDebuggedSwf() {
return debuggedSwf;
public List<SWF> getDebuggedSwfs() {
return debuggedSwfs;
}
public int getBreakIp() {
@@ -538,11 +546,11 @@ public class DebuggerHandler implements DebugConnectionListener {
return frame;
}
public synchronized int moduleIdOf(String pack) {
if (scriptToModule.containsKey(pack)) {
return scriptToModule.get(pack);
public synchronized int moduleIdOf(String packWithHash) {
if (!scriptToModule.containsKey(packWithHash)) {
return -1;
}
return -1;
return scriptToModule.get(packWithHash);
}
public boolean isPaused() {
@@ -569,35 +577,38 @@ public class DebuggerHandler implements DebugConnectionListener {
}
commands = null;
synchronized (this) {
if (confirmedPointMap.containsKey(debuggedSwf)) {
for (String scriptName : confirmedPointMap.get(debuggedSwf).keySet()) {
if (!toAddBPointMap.containsKey(debuggedSwf)) {
toAddBPointMap.put(debuggedSwf, new HashMap<>());
for (SWF debuggedSwf : debuggedSwfs) {
if (confirmedPointMap.containsKey(debuggedSwf)) {
for (String scriptName : confirmedPointMap.get(debuggedSwf).keySet()) {
if (!toAddBPointMap.containsKey(debuggedSwf)) {
toAddBPointMap.put(debuggedSwf, new HashMap<>());
}
if (!toAddBPointMap.get(debuggedSwf).containsKey(scriptName)) {
toAddBPointMap.get(debuggedSwf).put(scriptName, new TreeSet<>());
}
toAddBPointMap.get(debuggedSwf).get(scriptName).addAll(confirmedPointMap.get(debuggedSwf).get(scriptName));
}
if (!toAddBPointMap.get(debuggedSwf).containsKey(scriptName)) {
toAddBPointMap.get(debuggedSwf).put(scriptName, new TreeSet<>());
}
toAddBPointMap.get(debuggedSwf).get(scriptName).addAll(confirmedPointMap.get(debuggedSwf).get(scriptName));
confirmedPointMap.get(debuggedSwf).clear();
}
confirmedPointMap.get(debuggedSwf).clear();
}
if (invalidBreakPointMap.containsKey(debuggedSwf)) {
for (String scriptName : invalidBreakPointMap.get(debuggedSwf).keySet()) {
if (!toAddBPointMap.containsKey(debuggedSwf)) {
toAddBPointMap.put(debuggedSwf, new HashMap<>());
if (invalidBreakPointMap.containsKey(debuggedSwf)) {
for (String scriptName : invalidBreakPointMap.get(debuggedSwf).keySet()) {
if (!toAddBPointMap.containsKey(debuggedSwf)) {
toAddBPointMap.put(debuggedSwf, new HashMap<>());
}
if (!toAddBPointMap.get(debuggedSwf).containsKey(scriptName)) {
toAddBPointMap.get(debuggedSwf).put(scriptName, new TreeSet<>());
}
toAddBPointMap.get(debuggedSwf).get(scriptName).addAll(invalidBreakPointMap.get(debuggedSwf).get(scriptName));
}
if (!toAddBPointMap.get(debuggedSwf).containsKey(scriptName)) {
toAddBPointMap.get(debuggedSwf).put(scriptName, new TreeSet<>());
}
toAddBPointMap.get(debuggedSwf).get(scriptName).addAll(invalidBreakPointMap.get(debuggedSwf).get(scriptName));
invalidBreakPointMap.get(debuggedSwf).clear();
}
invalidBreakPointMap.get(debuggedSwf).clear();
}
}
for (ConnectionListener l : clisteners) {
l.disconnected();
}
debuggedSwfs.clear();
}
public synchronized boolean isConnected() {
@@ -628,7 +639,9 @@ public class DebuggerHandler implements DebugConnectionListener {
@Override
public void connected(DebuggerConnection con) {
makeBreakPointsUnconfirmed(debuggedSwf);
/*for (SWF debuggedSwf : debuggedSwfs) {
makeBreakPointsUnconfirmed(debuggedSwf);
}*/
Main.startWork(AppStrings.translate("work.debugging"), null);
@@ -661,13 +674,15 @@ public class DebuggerHandler implements DebugConnectionListener {
});
swfs.clear();
swfIndicesCommited.clear();
swfIndicesNewToSwfHash.clear();
Map<Integer, String> moduleNames = new HashMap<>();
final Pattern patAS3 = Pattern.compile("^(.*);(.*);(.*)\\.as$");
final Pattern patAS3PCode = Pattern.compile("^#PCODE abc:([0-9]+),script:([0-9]+),class:(-?[0-9]+),trait:(-?[0-9]+),method:([0-9]+),body:([0-9]+);(.*)$");
final Pattern patAS3PCode = Pattern.compile("^(?<hash>[0-9a-z_]+):#PCODE abc:(?<abc>[0-9]+),script:(?<script>[0-9]+),class:(?<class>-?[0-9]+),trait:(?<trait>-?[0-9]+),method:(?<method>[0-9]+),body:(?<body>[0-9]+);(.*)$");
boolean isAS3 = (Main.getMainFrame().getPanel().getCurrentSwf().isAS3());
boolean isAS3 = Main.getRunningSWF().isAS3();
try {
con.addMessageListener(new DebugMessageListener<InErrorException>() {
@@ -687,39 +702,69 @@ public class DebuggerHandler implements DebugConnectionListener {
}
});
modulePaths = new HashMap<>();
scriptToModule = new HashMap<>();
modulePaths.clear();
scriptToModule.clear();
moduleNames.clear();
moduleToClassIndex.clear();
moduleToTraitIndex.clear();
moduleToMethodIndex.clear();
moduleToSwfIndex.clear();
con.addMessageListener(new DebugMessageListener<InScript>() {
@Override
public void message(InScript sc) {
moduleNames.put(sc.module, sc.name);
moduleToSwfIndex.put(sc.module, sc.swfIndex);
int file = sc.module;
String name = sc.name;
synchronized (DebuggerHandler.this) {
moduleNames.put(sc.module, sc.name);
int file = sc.module;
String name = sc.name;
int swfIndex = sc.swfIndex;
name = name.replaceAll("\\[(invalid_utf8=[0-9]+)\\]", "{$1}");
name = name.replaceAll("\\[(invalid_utf8=[0-9]+)\\]", "{$1}");
Logger.getLogger(DebuggerHandler.class.getName()).log(Level.FINE, "Received script added - index {0} name: {1}", new Object[]{file, name});
Logger.getLogger(DebuggerHandler.class.getName()).log(Level.FINE, "Received script added - index {0} name: {1}", new Object[]{file, name});
Matcher m;
if ((m = patAS3.matcher(name)).matches()) {
String clsNameWithSuffix = m.group(3).replace("{{semicolon}}", ";");
String pkg = m.group(2).replace("{{semicolon}}", ";").replace("\\", ".");
m = patAS3PCode.matcher(name);
if (m.matches()) {
moduleToClassIndex.put(file, Integer.parseInt(m.group(3)));
moduleToTraitIndex.put(file, Integer.parseInt(m.group(4)));
moduleToMethodIndex.put(file, Integer.parseInt(m.group(5)));
name = DottedChain.parseWithSuffix(pkg).addWithSuffix(clsNameWithSuffix).toString();
name = "#PCODE abc:" + m.group(1) + ",body:" + m.group(6) + ";" + name;
String swfHash = "main";
Matcher m;
if ((m = patAS3.matcher(name)).matches()) {
String clsNameWithSuffix = m.group(3).replace("{{semicolon}}", ";");
String pkg = m.group(2).replace("{{semicolon}}", ";").replace("\\", ".");
m = patAS3PCode.matcher(name);
if (m.matches()) {
moduleToClassIndex.put(file, Integer.parseInt(m.group("class")));
moduleToTraitIndex.put(file, Integer.parseInt(m.group("trait")));
moduleToMethodIndex.put(file, Integer.parseInt(m.group("method")));
name = DottedChain.parseWithSuffix(pkg).addWithSuffix(clsNameWithSuffix).toString();
swfHash = m.group("hash");
name = swfHash + ":" + "#PCODE abc:" + m.group("abc") + ",body:" + m.group("body") + ";" + name;
} else {
if (pkg.contains(":")) {
swfHash = pkg.substring(0, pkg.indexOf(":"));
pkg = pkg.substring(pkg.indexOf(":") + 1);
}
name = swfHash + ":" + DottedChain.parseWithSuffix(pkg).addWithSuffix(clsNameWithSuffix).toPrintableString(con.isAS3);
}
} else {
name = DottedChain.parseWithSuffix(pkg).addWithSuffix(clsNameWithSuffix).toPrintableString(con.isAS3);
if (name.contains(":")) {
swfHash = name.substring(0, name.indexOf(":"));
}
}
Logger.getLogger(DebuggerHandler.class.getName()).log(Level.FINE, "Script added - index {0} name: {1}", new Object[]{file, name});
modulePaths.put(file, name);
scriptToModule.put(name, file);
moduleToSwfIndex.put(file, swfIndex);
if (swfIndex > debuggedSwfs.size() - 1) {
SWF swf = Main.getSwfByHash(swfHash);
if (swf == null) {
Logger.getLogger(DebuggerHandler.class.getName()).log(Level.WARNING, "SWF with hash {0} not found", swfHash);
} else {
Logger.getLogger(DebuggerHandler.class.getName()).log(Level.FINE, "adding {0} to debugSwfs", swfIndex);
debuggedSwfs.add(swf);
}
} else {
Logger.getLogger(DebuggerHandler.class.getName()).log(Level.FINE, "Swf index already commited");
}
}
Logger.getLogger(DebuggerHandler.class.getName()).log(Level.FINE, "Script added - index {0} name: {1}", new Object[]{file, name});
modulePaths.put(file, name);
scriptToModule.put(name, file);
con.dropMessage(sc);
}
});
@@ -781,6 +826,7 @@ public class DebuggerHandler implements DebugConnectionListener {
synchronized (this) {
for (int i = 0; i < isb.files.size(); i++) {
String sname = moduleNames.get(isb.files.get(i));
SWF debuggedSwf = debuggedSwfs.get(moduleToSwfIndex.get(isb.files.get(i)));
if (!confirmedPointMap.containsKey(debuggedSwf)) {
confirmedPointMap.put(debuggedSwf, new HashMap<>());
}
@@ -826,22 +872,37 @@ public class DebuggerHandler implements DebugConnectionListener {
Logger.getLogger(DebuggerHandler.class.getName()).log(Level.FINE, "paused");
}
try {
//ignore single InSetBreakpoint, otherwise setting breakpoints later won't work
con.getMessage(InSetBreakpoint.class, DebuggerConnection.PREF_RESPONSE_TIMEOUT);
breakInfo = con.getMessage(InBreakAtExt.class, DebuggerConnection.PREF_RESPONSE_TIMEOUT);
breakReason = con.sendMessageWithTimeout(new OutGetBreakReason(con), InBreakReason.class);
int reasonInt = breakReason == null ? 0 : breakReason.reason;
String newBreakScriptName = "unknown";
String userBreakScriptName = "unknown";
if (modulePaths.containsKey(message.file)) {
newBreakScriptName = modulePaths.get(message.file);
userBreakScriptName = newBreakScriptName;
if (newBreakScriptName.contains(":")) {
String swfHash = newBreakScriptName.substring(0, newBreakScriptName.indexOf(":"));
SWF swf = Main.getSwfByHash(swfHash);
userBreakScriptName = swf.toString() + ": " + newBreakScriptName.substring(newBreakScriptName.indexOf(":") + 1);
}
} else if (reasonInt != InBreakReason.REASON_SCRIPT_LOADED) {
Logger.getLogger(DebuggerCommands.class.getName()).log(Level.SEVERE, "Invalid file: {0}", message.file);
//return;
}
for (int i = 0; i < debuggedSwfs.size(); i++) {
swfIndicesCommited.add(i);
}
final String[] reasonNames = new String[]{"unknown", "breakpoint", "watch", "fault", "stopRequest", "step", "halt", "scriptLoaded"};
String reason = reasonInt < reasonNames.length ? reasonNames[reasonInt] : reasonNames[0];
@@ -869,8 +930,8 @@ public class DebuggerHandler implements DebugConnectionListener {
return;
}
Main.startWork(AppStrings.translate("work.halted"), null);
} else {
Main.startWork(AppStrings.translate("work.breakat") + newBreakScriptName + ":" + message.line + " " + AppStrings.translate("debug.break.reason." + reason), null);
} else {
Main.startWork(AppStrings.translate("work.breakat") + userBreakScriptName + ":" + message.line + " " + AppStrings.translate("debug.break.reason." + reason), null);
}
depth = 0;
refreshFrame();
@@ -933,53 +994,79 @@ public class DebuggerHandler implements DebugConnectionListener {
return;
}
synchronized (this) {
if (toRemoveBPointMap.containsKey(debuggedSwf)) {
for (String scriptName : toRemoveBPointMap.get(debuggedSwf).keySet()) {
int file = moduleIdOf(scriptName);
if (file > -1) {
for (int line : toRemoveBPointMap.get(debuggedSwf).get(scriptName)) {
if (isBreakpointConfirmed(debuggedSwf, scriptName, line)) {
commands.removeBreakPoint(file, line);
confirmedPointMap.get(debuggedSwf).get(scriptName).remove(line);
if (confirmedPointMap.get(debuggedSwf).get(scriptName).isEmpty()) {
confirmedPointMap.get(debuggedSwf).remove(scriptName);
Logger.getLogger(DebuggerHandler.class.getName()).log(Level.FINEST, "sending breakpoints of {0} swfs", debuggedSwfs.size());
for (int swfIndex = 0; swfIndex < debuggedSwfs.size(); swfIndex++) {
if (!swfIndicesCommited.contains(swfIndex)) {
continue;
}
SWF debuggedSwf = debuggedSwfs.get(swfIndex);
String hash = Main.getSwfHash(debuggedSwf);
Logger.getLogger(DebuggerHandler.class.getName()).log(Level.FINEST, "sending breakpoints of ", hash);
if (toRemoveBPointMap.containsKey(debuggedSwf)) {
for (String scriptName : toRemoveBPointMap.get(debuggedSwf).keySet()) {
if (scriptName.startsWith("#PCODE") != Main.isDebugPCode()) {
continue;
}
int file = moduleIdOf(hash + ":" + scriptName);
if (file > -1) {
for (int line : toRemoveBPointMap.get(debuggedSwf).get(scriptName)) {
if (isBreakpointConfirmed(debuggedSwf, scriptName, line)) {
commands.removeBreakPoint(file, line);
confirmedPointMap.get(debuggedSwf).get(scriptName).remove(line);
if (confirmedPointMap.get(debuggedSwf).get(scriptName).isEmpty()) {
confirmedPointMap.get(debuggedSwf).remove(scriptName);
}
}
Logger.getLogger(DebuggerHandler.class.getName()).log(Level.INFO, "Breakpoint {0}:{1} removed", new Object[]{scriptName, line});
}
Logger.getLogger(DebuggerHandler.class.getName()).log(Level.INFO, "Breakpoint {0}:{1} removed", new Object[]{scriptName, line});
}
}
toRemoveBPointMap.get(debuggedSwf).clear();
if (toRemoveBPointMap.get(debuggedSwf).isEmpty()) {
toRemoveBPointMap.remove(debuggedSwf);
}
}
toRemoveBPointMap.clear();
}
if (toAddBPointMap.containsKey(debuggedSwf)) {
for (String scriptName : toAddBPointMap.get(debuggedSwf).keySet()) {
int file = moduleIdOf(scriptName);
if (file > -1) {
for (int line : toAddBPointMap.get(debuggedSwf).get(scriptName)) {
if (commands.addBreakPoint(file, line)) {
Logger.getLogger(DebuggerHandler.class.getName()).log(Level.INFO, "Breakpoint {0}:{1} submitted successfully", new Object[]{scriptName, line});
if (!confirmedPointMap.containsKey(debuggedSwf)) {
confirmedPointMap.put(debuggedSwf, new HashMap<>());
if (toAddBPointMap.containsKey(debuggedSwf)) {
for (String scriptName : toAddBPointMap.get(debuggedSwf).keySet()) {
if (scriptName.startsWith("#PCODE") != Main.isDebugPCode()) {
continue;
}
Logger.getLogger(DebuggerHandler.class.getName()).log(Level.FINEST, "searching for module of {0}:{1}", new Object[]{hash, scriptName});
int file = moduleIdOf(hash + ":" + scriptName);
Logger.getLogger(DebuggerHandler.class.getName()).log(Level.FINEST, "module = {0}", file);
if (file > -1) {
for (int line : toAddBPointMap.get(debuggedSwf).get(scriptName)) {
if (commands.addBreakPoint(file, line)) {
Logger.getLogger(DebuggerHandler.class.getName()).log(Level.INFO, "Breakpoint {0}:{1} submitted successfully", new Object[]{scriptName, line});
if (!confirmedPointMap.containsKey(debuggedSwf)) {
confirmedPointMap.put(debuggedSwf, new HashMap<>());
}
if (!confirmedPointMap.get(debuggedSwf).containsKey(scriptName)) {
confirmedPointMap.get(debuggedSwf).put(scriptName, new TreeSet<>());
}
confirmedPointMap.get(debuggedSwf).get(scriptName).add(line);
} else {
Logger.getLogger(DebuggerHandler.class.getName()).log(Level.INFO, "Breakpoint {0}:{1} unable to submit", new Object[]{scriptName, line});
markBreakPointInvalid(debuggedSwf, scriptName, line);
}
if (!confirmedPointMap.get(debuggedSwf).containsKey(scriptName)) {
confirmedPointMap.get(debuggedSwf).put(scriptName, new TreeSet<>());
}
confirmedPointMap.get(debuggedSwf).get(scriptName).add(line);
} else {
Logger.getLogger(DebuggerHandler.class.getName()).log(Level.INFO, "Breakpoint {0}:{1} unable to submit", new Object[]{scriptName, line});
}
} else {
for (int line : toAddBPointMap.get(debuggedSwf).get(scriptName)) {
markBreakPointInvalid(debuggedSwf, scriptName, line);
}
}
} else {
for (int line : toAddBPointMap.get(debuggedSwf).get(scriptName)) {
markBreakPointInvalid(debuggedSwf, scriptName, line);
}
}
Logger.getLogger(DebuggerHandler.class.getName()).log(Level.FINEST, "clearing toAddBps of {0}", hash);
toAddBPointMap.get(debuggedSwf).clear();
if (toAddBPointMap.get(debuggedSwf).isEmpty()) {
toAddBPointMap.remove(debuggedSwf);
}
}
toAddBPointMap.clear();
}
}
Logger.getLogger(DebuggerHandler.class.getName()).log(Level.FINEST, "sending bps finished");