mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-07-03 17:44:24 +00:00
Debug (breakpoints, step) P-code for both AS1/2 and AS3
This commit is contained in:
@@ -3149,13 +3149,54 @@ public final class SWF implements SWFContainerItem, Timelined {
|
||||
* @param telemetry Enable telemetry info?
|
||||
*/
|
||||
public void enableDebugging(boolean injectAS3Code, File decompileDir, boolean telemetry) {
|
||||
enableDebugging(injectAS3Code, decompileDir, telemetry, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects debugline and debugfile instructions to AS3 P-code (lines of
|
||||
* P-code)
|
||||
*/
|
||||
public void injectAS3PcodeDebugInfo() {
|
||||
List<ScriptPack> packs = getAS3Packs();
|
||||
for (ScriptPack s : packs) {
|
||||
int abcIndex = s.allABCs.indexOf(s.abc);
|
||||
if (s.isSimple) {
|
||||
s.injectPCodeDebugInfo(abcIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects debugline and debugfile instructions to AS3 code
|
||||
*
|
||||
* @param decompileDir Directory to set file information paths
|
||||
*/
|
||||
public void injectAS3DebugInfo(File decompileDir) {
|
||||
List<ScriptPack> packs = getAS3Packs();
|
||||
for (ScriptPack s : packs) {
|
||||
if (s.isSimple) {
|
||||
s.injectDebugInfo(decompileDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables debugging. Adds tags to enable debugging and injects debugline
|
||||
* and debugfile instructions to AS3 code. Optionally enables Telemetry
|
||||
*
|
||||
* @param injectAS3Code Modify AS3 code with debugfile / debugline ?
|
||||
* @param decompileDir Directory to virtual decompile (will affect
|
||||
* debugfile)
|
||||
* @param telemetry Enable telemetry info?
|
||||
* @param pcodeLevel inject Pcode lines instead of decompiled lines
|
||||
*/
|
||||
public void enableDebugging(boolean injectAS3Code, File decompileDir, boolean telemetry, boolean pcodeLevel) {
|
||||
|
||||
if (injectAS3Code) {
|
||||
List<ScriptPack> packs = getAS3Packs();
|
||||
for (ScriptPack s : packs) {
|
||||
if (s.isSimple) {
|
||||
s.injectDebugInfo(decompileDir);
|
||||
}
|
||||
if (pcodeLevel) {
|
||||
injectAS3PcodeDebugInfo();
|
||||
} else {
|
||||
injectAS3DebugInfo(decompileDir);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3233,6 +3274,82 @@ public final class SWF implements SWFContainerItem, Timelined {
|
||||
return r;
|
||||
}
|
||||
|
||||
public boolean generatePCodeSwdFile(File file, Map<String, Set<Integer>> breakpoints) throws IOException {
|
||||
DebugIDTag dit = getDebugId();
|
||||
if (dit == null) {
|
||||
return false;
|
||||
}
|
||||
List<SWD.DebugItem> items = new ArrayList<>();
|
||||
Map<String, ASMSource> asms = getASMs(true);
|
||||
|
||||
try {
|
||||
items.add(new SWD.DebugId(dit.debugId));
|
||||
|
||||
} catch (Throwable t) {
|
||||
Logger.getLogger(SWF.class.getName()).log(Level.SEVERE, "message", t);
|
||||
return false;
|
||||
}
|
||||
|
||||
int moduleId = 0;
|
||||
List<String> names = new ArrayList<>(asms.keySet());
|
||||
Collections.sort(names);
|
||||
for (String name : names) {
|
||||
moduleId++;
|
||||
String sname = "#PCODE " + name;
|
||||
int bitmap = SWD.bitmapAction;
|
||||
items.add(new SWD.DebugScript(moduleId, bitmap, sname, ""));
|
||||
|
||||
HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true);
|
||||
try {
|
||||
asms.get(name).getASMSource(ScriptExportMode.PCODE, writer, asms.get(name).getActions());
|
||||
} catch (InterruptedException ex) {
|
||||
logger.log(Level.SEVERE, null, ex);
|
||||
}
|
||||
List<Highlighting> hls = writer.instructionHilights;
|
||||
|
||||
Map<Integer, Integer> offsetToLine = new TreeMap<>();
|
||||
String txt = writer.toString();
|
||||
txt = txt.replace("\r", "");
|
||||
int line = 1;
|
||||
for (int i = 0; i < txt.length(); i++) {
|
||||
Highlighting h = Highlighting.searchPos(hls, i);
|
||||
if (h != null) {
|
||||
int of = (int) h.getProperties().fileOffset;
|
||||
if (of > -1 && !offsetToLine.containsKey(of) && !offsetToLine.containsValue(line)) {
|
||||
offsetToLine.put(of, line);
|
||||
}
|
||||
}
|
||||
if (txt.charAt(i) == '\n') {
|
||||
line++;
|
||||
}
|
||||
}
|
||||
|
||||
for (int ofs : offsetToLine.keySet()) {
|
||||
items.add(new SWD.DebugOffset(moduleId, offsetToLine.get(ofs), ofs));
|
||||
}
|
||||
|
||||
if (breakpoints.containsKey(sname)) {
|
||||
Set<Integer> bplines = breakpoints.get(sname);
|
||||
for (int bpline : bplines) {
|
||||
if (offsetToLine.containsValue(bpline)) {
|
||||
try {
|
||||
SWD.DebugBreakpoint dbp = new SWD.DebugBreakpoint(moduleId, bpline);
|
||||
items.add(dbp);
|
||||
} catch (IllegalArgumentException iex) {
|
||||
Logger.getLogger(SWF.class.getName()).log(Level.WARNING, "Cannot generate breakpoint to SWD: {0}", iex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SWD swd = new SWD(7, items);
|
||||
try (FileOutputStream fis = new FileOutputStream(file)) {
|
||||
swd.saveTo(fis);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean generateSwdFile(File file, Map<String, Set<Integer>> breakpoints) throws IOException {
|
||||
DebugIDTag dit = getDebugId();
|
||||
if (dit == null) {
|
||||
@@ -3243,15 +3360,10 @@ public final class SWF implements SWFContainerItem, Timelined {
|
||||
|
||||
try {
|
||||
items.add(new SWD.DebugId(dit.debugId));
|
||||
Random rnd = new Random();
|
||||
|
||||
//Map<String, Integer> moduleIds = new HashMap<>();
|
||||
List<SWD.DebugOffset> swdOffsets = new ArrayList<>();
|
||||
List<SWD.DebugBreakpoint> swfBps = new ArrayList<>();
|
||||
int moduleId = 0;
|
||||
List<String> names = new ArrayList<>(asms.keySet());
|
||||
Collections.sort(names);
|
||||
//Collections.reverse(names);
|
||||
for (String name : names) {
|
||||
List<SWD.DebugRegisters> regitems = new ArrayList<>();
|
||||
moduleId++;
|
||||
|
||||
@@ -37,6 +37,8 @@ import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.settings.ScriptExportSettings;
|
||||
import com.jpexs.decompiler.flash.helpers.FileTextWriter;
|
||||
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
|
||||
import com.jpexs.decompiler.flash.helpers.HighlightedText;
|
||||
import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter;
|
||||
import com.jpexs.decompiler.flash.helpers.NulWriter;
|
||||
import com.jpexs.decompiler.flash.helpers.hilight.Highlighting;
|
||||
import com.jpexs.decompiler.flash.tags.Tag;
|
||||
@@ -510,4 +512,94 @@ public class ScriptPack extends AS3ClassTreeItem {
|
||||
|
||||
((Tag) abc.parentTag).setModified(true);
|
||||
}
|
||||
|
||||
public void injectPCodeDebugInfo(int abcIndex) {
|
||||
|
||||
Map<Integer, String> bodyToIdentifier = new HashMap<>();
|
||||
|
||||
try {
|
||||
CachedDecompilation decompiled = SWF.getCached(this);
|
||||
String txt = decompiled.text;
|
||||
txt = txt.replace("\r", "");
|
||||
|
||||
for (int i = 0; i < txt.length(); i++) {
|
||||
blk:
|
||||
{
|
||||
Highlighting sh = Highlighting.searchPos(decompiled.specialHilights, i);
|
||||
|
||||
Highlighting cls = Highlighting.searchPos(decompiled.classHilights, i);
|
||||
Highlighting trt = Highlighting.searchPos(decompiled.traitHilights, i);
|
||||
Highlighting method = Highlighting.searchPos(decompiled.methodHilights, i);
|
||||
if (method == null) {
|
||||
break blk;
|
||||
}
|
||||
|
||||
int classIndex = cls == null ? -1 : (int) cls.getProperties().index;
|
||||
int methodIndex = (int) method.getProperties().index;
|
||||
int bodyIndex = abc.findBodyIndex(methodIndex);
|
||||
if (bodyIndex == -1) {
|
||||
break blk;
|
||||
}
|
||||
|
||||
Trait trait;
|
||||
int traitIndex = -10;
|
||||
if (trt != null && cls != null) {
|
||||
traitIndex = (int) trt.getProperties().index;
|
||||
|
||||
trait = abc.findTraitByTraitId(classIndex, traitIndex);
|
||||
if (((trait instanceof TraitMethodGetterSetter) && (((TraitMethodGetterSetter) trait).method_info != methodIndex))
|
||||
|| ((trait instanceof TraitFunction) && (((TraitFunction) trait).method_info != methodIndex))) {
|
||||
continue; //inner anonymous function - ignore. TODO: make work
|
||||
}
|
||||
}
|
||||
bodyToIdentifier.put(bodyIndex, "abc:" + abcIndex + ",script:" + scriptIndex + ",class:" + classIndex + ",trait:" + traitIndex + ",method:" + methodIndex + ",body:" + bodyIndex);
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
Logger.getLogger(ScriptPack.class.getName()).log(Level.SEVERE, "Cannot decompile", ex);
|
||||
}
|
||||
|
||||
int scriptInitBody = abc.findBodyIndex(abc.script_info.get(scriptIndex).init_index);
|
||||
|
||||
if (!bodyToIdentifier.containsKey(scriptInitBody)) {
|
||||
bodyToIdentifier.put(scriptInitBody, "abc:" + abcIndex + ",script:" + scriptIndex + ",class:-1,trait:-3,method:" + abc.script_info.get(scriptIndex).init_index);
|
||||
}
|
||||
|
||||
String pkg = path.packageStr.toString();
|
||||
String cls = path.className;
|
||||
|
||||
for (int bodyIndex : bodyToIdentifier.keySet()) {
|
||||
String bodyName = bodyToIdentifier.get(bodyIndex);
|
||||
|
||||
MethodBody b = abc.bodies.get(bodyIndex);
|
||||
List<AVM2Instruction> list = b.getCode().code;
|
||||
|
||||
int siz = list.size();
|
||||
|
||||
for (int i = 0; i < siz; i++) {
|
||||
b.insertInstruction(i * 2, new AVM2Instruction(0, AVM2Instructions.DebugLine, new int[]{i + 1}));
|
||||
}
|
||||
for (int i = 1 /*odd, even are new debuglines*/; i < list.size(); i += 2) {
|
||||
if (list.get(i).definition instanceof DebugLineIns) {
|
||||
b.removeInstruction(i);
|
||||
b.removeInstruction(i - 1); //remove its new debugline too
|
||||
i -= 2; //for loop to work correctly
|
||||
} else if (list.get(i).definition instanceof DebugFileIns) {
|
||||
b.removeInstruction(i);
|
||||
b.removeInstruction(i - 1);
|
||||
i -= 2;
|
||||
} else if (list.get(i).definition instanceof DebugIns) {
|
||||
b.removeInstruction(i);
|
||||
b.removeInstruction(i - 1);
|
||||
i -= 2;
|
||||
}
|
||||
}
|
||||
String filename = "#PCODE " + bodyName + ";" + pkg.replace(".", File.separator) + ";" + cls + ".as";
|
||||
|
||||
b.insertInstruction(0, new AVM2Instruction(0, AVM2Instructions.DebugFile, new int[]{abc.constants.getStringId(filename, true)}));
|
||||
b.setModified();
|
||||
}
|
||||
|
||||
((Tag) abc.parentTag).setModified(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1263,7 +1263,7 @@ public class AVM2Code implements Cloneable {
|
||||
|
||||
if (!ins.isIgnored()) {
|
||||
if (markOffsets) {
|
||||
writer.append("", ofs);
|
||||
writer.append("", ofs, ins.getFileOffset());
|
||||
}
|
||||
|
||||
writer.appendNoHilight(ins.toStringNoAddress(constants, new ArrayList<>()));
|
||||
|
||||
@@ -57,6 +57,11 @@ public class AVM2Instruction implements Cloneable, GraphSourceItem {
|
||||
|
||||
private String file;
|
||||
|
||||
@Override
|
||||
public long getFileOffset() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLineOffset() {
|
||||
return getOffset();
|
||||
|
||||
@@ -375,6 +375,11 @@ public abstract class Action implements GraphSourceItem {
|
||||
return baos2.toByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFileOffset() {
|
||||
return fileOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts list of Actions to bytes
|
||||
*
|
||||
@@ -602,7 +607,7 @@ public abstract class Action implements GraphSourceItem {
|
||||
//lastPush = false;
|
||||
}
|
||||
|
||||
writer.append("", offset);
|
||||
writer.append("", offset, a.getFileOffset());
|
||||
|
||||
int fixBranch = -1;
|
||||
if (a instanceof ActionIf) {
|
||||
|
||||
@@ -337,7 +337,7 @@ public class ActionPush extends Action {
|
||||
if (pos > 0) {
|
||||
writer.appendNoHilight(" ");
|
||||
}
|
||||
writer.append(toString(i), getAddress() + pos + 1);
|
||||
writer.append(toString(i), getAddress() + pos + 1, getFileOffset());
|
||||
pos++;
|
||||
}
|
||||
return writer;
|
||||
|
||||
@@ -65,7 +65,7 @@ public class FileTextWriter extends GraphTextWriter implements AutoCloseable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileTextWriter append(String str, long offset) {
|
||||
public FileTextWriter append(String str, long offset, long fileOffset) {
|
||||
writeToFile(str);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ public abstract class GraphTextWriter {
|
||||
|
||||
public abstract GraphTextWriter append(String str);
|
||||
|
||||
public abstract GraphTextWriter append(String str, long offset);
|
||||
public abstract GraphTextWriter append(String str, long offset, long fileOffset);
|
||||
|
||||
public abstract GraphTextWriter appendNoHilight(int i);
|
||||
|
||||
|
||||
@@ -202,6 +202,7 @@ public class HighlightedTextWriter extends GraphTextWriter {
|
||||
ndata.merge(itemPos.data);
|
||||
ndata.merge(data);
|
||||
ndata.offset = src.getOffset() + pos;
|
||||
ndata.fileOffset = src.getFileOffset();
|
||||
if (itemPos.startLineItem != null) {
|
||||
ndata.firstLineOffset = itemPos.startLineItem.getLineOffset();
|
||||
}
|
||||
@@ -217,11 +218,12 @@ public class HighlightedTextWriter extends GraphTextWriter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public HighlightedTextWriter append(String str, long offset) {
|
||||
public HighlightedTextWriter append(String str, long offset, long fileOffset) {
|
||||
Highlighting h = null;
|
||||
if (hilight) {
|
||||
HighlightData data = new HighlightData();
|
||||
data.offset = offset;
|
||||
data.fileOffset = fileOffset;
|
||||
h = new Highlighting(sb.length() - newLineCount, data, HighlightType.OFFSET, str);
|
||||
instructionHilights.add(h);
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ public class NulWriter extends GraphTextWriter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public NulWriter append(String str, long offset) {
|
||||
public NulWriter append(String str, long offset, long fileOffset) {
|
||||
stringAdded = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,8 @@ public class HighlightData implements Cloneable, Serializable {
|
||||
|
||||
public long offset;
|
||||
|
||||
public long fileOffset = -1;
|
||||
|
||||
public long firstLineOffset = -1;
|
||||
|
||||
public int regIndex = -1;
|
||||
@@ -46,7 +48,8 @@ public class HighlightData implements Cloneable, Serializable {
|
||||
public boolean isEmpty() {
|
||||
return !declaration && declaredType == null && localName == null
|
||||
&& subtype == null && specialValue == null
|
||||
&& index == 0 && offset == 0 && regIndex == -1 && firstLineOffset == -1;
|
||||
&& index == 0 && offset == 0 && regIndex == -1 && firstLineOffset == -1
|
||||
&& fileOffset == -1;
|
||||
}
|
||||
|
||||
public void merge(HighlightData data) {
|
||||
@@ -80,6 +83,9 @@ public class HighlightData implements Cloneable, Serializable {
|
||||
if (data.firstLineOffset != -1) {
|
||||
firstLineOffset = data.firstLineOffset;
|
||||
}
|
||||
if (data.fileOffset != -1) {
|
||||
fileOffset = data.fileOffset;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -100,7 +100,7 @@ public class Highlighting implements Serializable {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (pos == -1 || (pos >= h.startPos && (pos < h.startPos + h.len))) {
|
||||
if (pos == -1 || (pos >= h.startPos && ((h.len == 0 && pos == h.startPos) || pos < h.startPos + h.len))) {
|
||||
if (ret == null || h.startPos > ret.startPos) { //get the closest one
|
||||
ret = h;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ public interface GraphSourceItem extends Serializable, Cloneable {
|
||||
|
||||
public int getStackPushCount(BaseLocalData localData, TranslateStack stack);
|
||||
|
||||
public long getFileOffset();
|
||||
|
||||
public boolean isJump();
|
||||
|
||||
public boolean isBranch();
|
||||
|
||||
Reference in New Issue
Block a user