AS1/2 debugger

debugger listening only when really debugging started
fail to listen message
break reason displayed
This commit is contained in:
Jindra Petřík
2015-11-21 19:12:42 +01:00
parent 6d7ebb2619
commit 1237762e60
32 changed files with 1355 additions and 626 deletions

View File

@@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash;
import SevenZip.Compression.LZMA.Decoder;
import SevenZip.Compression.LZMA.Encoder;
import com.jpexs.debugger.flash.SWD;
import com.jpexs.decompiler.flash.abc.ABC;
import com.jpexs.decompiler.flash.abc.CachedDecompilation;
import com.jpexs.decompiler.flash.abc.ClassPath;
@@ -77,6 +78,7 @@ import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin;
import com.jpexs.decompiler.flash.helpers.collections.MyEntry;
import com.jpexs.decompiler.flash.helpers.hilight.Highlighting;
import com.jpexs.decompiler.flash.tags.ABCContainerTag;
import com.jpexs.decompiler.flash.tags.DebugIDTag;
import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag;
import com.jpexs.decompiler.flash.tags.DefineSpriteTag;
import com.jpexs.decompiler.flash.tags.DoInitActionTag;
@@ -157,6 +159,7 @@ import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -171,10 +174,15 @@ import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.Stack;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream;
@@ -1094,12 +1102,7 @@ public final class SWF implements SWFContainerItem, Timelined {
}
}
/*preload shape tags
for (Tag tag : tags) {
if (tag instanceof ShapeTag) {
((ShapeTag) tag).getShapes();
}
}*/
getASMs(true); // Add scriptNames to ASMs
}
@Override
@@ -1473,11 +1476,14 @@ public final class SWF implements SWFContainerItem, Timelined {
TreeItem realItem = treeItem instanceof TagScript ? ((TagScript) treeItem).getTag() : treeItem;
if (realItem instanceof ASMSource && (exportAll || exportNode)) {
String npath = path;
String exPath = path;
int ppos = 1;
while (asmsToExport.containsKey(npath)) {
ppos++;
npath = path + (exportFileNames ? "[" + ppos + "]" : "_" + ppos);
exPath = path + "[" + ppos + "]";
}
((ASMSource) realItem).setScriptName(exPath);
asmsToExport.put(npath, (ASMSource) realItem);
}
@@ -3143,6 +3149,8 @@ public final class SWF implements SWFContainerItem, Timelined {
int pos = 0;
boolean hasEnabled = false;
for (int i = 0; i < tags.size(); i++) {
Tag t = tags.get(i);
if (t instanceof MetadataTag) {
@@ -3152,23 +3160,183 @@ public final class SWF implements SWFContainerItem, Timelined {
pos = i + 1;
}
if (version >= 6 && (t instanceof EnableDebugger2Tag)) {
return;
hasEnabled = true;
break;
}
if (version == 5 && (t instanceof EnableDebuggerTag)) {
return;
hasEnabled = true;
break;
}
if (version < 5 && (t instanceof ProtectTag)) {
return;
hasEnabled = true;
break;
}
}
if (version >= 6) {
tags.add(pos, new EnableDebugger2Tag(this));
} else if (version == 5) {
tags.add(pos, new EnableDebuggerTag(this));
} else {
tags.add(pos, new ProtectTag(this));
if (!hasEnabled) {
if (version >= 6) {
tags.add(pos, new EnableDebugger2Tag(this));
} else if (version == 5) {
tags.add(pos, new EnableDebuggerTag(this));
} else {
tags.add(pos, new ProtectTag(this));
}
}
addDebugId();
}
public DebugIDTag getDebugId() {
for (Tag t : tags) {
if (t instanceof DebugIDTag) {
return (DebugIDTag) t;
}
}
return null;
}
public DebugIDTag addDebugId() {
DebugIDTag r = getDebugId();
if (r == null) {
for (int i = 0; i < tags.size(); i++) {
Tag t = tags.get(i);
if ((t instanceof EnableDebuggerTag) || (t instanceof EnableDebugger2Tag)) {
r = new DebugIDTag(this);
tags.add(i + 1, r);
new Random().nextBytes(r.debugId);
break;
}
}
}
return r;
}
public boolean generateSwdFile(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));
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) {
moduleId++;
CachedScript cs;
try {
cs = SWF.getCached(asms.get(name), asms.get(name).getActions());
} catch (InterruptedException ex) {
return false;
}
String txt = cs.text.replace("\r", "");
int line = 1;
Map<Integer, Integer> lineToOffset = new HashMap<>();
Map<Integer, String> regNames = new HashMap<>();
for (int pos = 0; pos < txt.length(); pos++) {
Highlighting h = Highlighting.searchPos(cs.hilights, pos);
if (h != null) {
int firstLineOffset = (int) h.getProperties().firstLineOffset;
if (firstLineOffset != -1) {
if (h.getProperties().declaration && h.getProperties().regIndex > -1) {
regNames.put(h.getProperties().regIndex, h.getProperties().localName);
/*List<Integer> curRegIndexes = new ArrayList<>(regNames.keySet());
List<String> curRegNames = new ArrayList<>();
for (int i = 0; i < curRegIndexes.size(); i++) {
curRegNames.add(regNames.get(i));
}
items.add(new SWD.DebugRegisters((int) h.getProperties().firstLineOffset, curRegIndexes, curRegNames));*/
}
}
if (firstLineOffset != -1 && !lineToOffset.containsKey(line)) {
lineToOffset.put(line, firstLineOffset);
}
}
if (txt.charAt(pos) == '\n') {
line++;
}
}
Map<Integer, Integer> offSetToLine = new TreeMap<>();
for (Map.Entry<Integer, Integer> en : lineToOffset.entrySet()) {
offSetToLine.put(en.getValue(), en.getKey());
}
//final String NONAME = "[No instance name assigned]";
String sname = name;
int bitmap = SWD.bitmapAction;
/* Matcher m;
int bitmap = SWD.bitmapAction;
m = Pattern.compile("^\\\\frame_([0-9]+)\\\\DoAction$").matcher(sname);
if (m.matches()) {
//TODO: scenes?, layers?
sname = "Actions for Scene 1: Frame " + m.group(1) + " of Layer Name Layer 1";
} else if ((m = Pattern.compile("^\\\\__Packages\\\\(.*)$").matcher(sname)).matches()) {
sname = m.group(1).replace("\\", ".") + ": .\\" + m.group(1) + ".as";
} else {
continue; //FIXME!
}
m = Pattern.compile("^\\\\DefineSprite_([0-9])+\\\\frame_([0-9]+)\\\\DoAction$").matcher(sname);
if (m.matches()) {
//TODO: layers?
//sname = "Actions for Symbol " + m.group(1) + ": Frame " + m.group(2) + " of Layer Name Layer 1";
continue; //FIXME!
}
//TODO: handle onxxx together ?
m = Pattern.compile("^\\\\DefineButton2?_([0-9]+)\\\\on\\(.*$").matcher(sname);
if (m.matches()) {
//bitmap = SWD.bitmapOnAction;
//sname = "Actions for " + NONAME + " (Symbol " + m.group(1) + ")";
continue; //FIXME!
}
//TODO: handle onClipEvent together ?
m = Pattern.compile("^\\\\frame_([0-9]+)\\\\PlaceObject[2-3]?_([0-9]+)_[^\\\\]*\\\\onClipEvent\\(.*$").matcher(sname);
if (m.matches()) {
//bitmap = SWD.bitmapOnClipAction;
//sname = "Actions for " + NONAME + " (Symbol " + m.group(2) + ")";
continue; //FIXME!
}//*/
items.add(new SWD.DebugScript(moduleId, bitmap, sname, txt));
for (int ofs : offSetToLine.keySet()) {
items.add(new SWD.DebugOffset(moduleId, offSetToLine.get(ofs), ofs));
}
if (breakpoints.containsKey(name)) {
Set<Integer> bplines = breakpoints.get(name);
for (int bpline : bplines) {
if (lineToOffset.containsKey(bpline)) {
items.add(new SWD.DebugBreakpoint(moduleId, bpline));
}
}
}
//moduleId++;
}
//items.addAll(swdOffsets);
//items.addAll(swfBps);
} catch (Throwable t) {
Logger.getLogger(SWF.class.getName()).log(Level.SEVERE, "message", t);
return false;
}
SWD swd = new SWD(7, items);
try (FileOutputStream fis = new FileOutputStream(file)) {
swd.saveTo(fis);
}
return true;
}
public boolean enableTelemetry(String password) {

View File

@@ -897,7 +897,7 @@ public class SWFInputStream implements AutoCloseable {
InflaterInputStream dis = new InflaterInputStream(new ByteArrayInputStream(data, offset, length));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[4096];
int c = 0;
int c;
while ((c = dis.read(buf)) > 0) {
baos.write(buf, 0, c);
}
@@ -1202,11 +1202,7 @@ public class SWFInputStream implements AutoCloseable {
break;
case DoActionTag.ID:
case DoInitActionTag.ID:
if (isAS3) {
doParse = false;
} else {
doParse = true;
}
doParse = !isAS3;
break;
case ShowFrameTag.ID:
case PlaceObjectTag.ID:
@@ -1233,7 +1229,7 @@ public class SWFInputStream implements AutoCloseable {
}
}
if (parseTags && doParse && parallel1 && tag instanceof TagStub) {
if (parseTags && doParse && parallel1 && tag instanceof TagStub && executor != null) {
Future<Tag> future = executor.submit(new TagResolutionTask((TagStub) tag, di, level, parallel1, skipUnusualTags, lazy));
futureResults.add(future);
} else {
@@ -1262,7 +1258,9 @@ public class SWFInputStream implements AutoCloseable {
}
}
executor.shutdown();
if (executor != null) {
executor.shutdown();
}
}
return tags;
}
@@ -1670,7 +1668,7 @@ public class SWFInputStream implements AutoCloseable {
* @throws IOException
*/
public Action readAction() throws IOException {
int actionCode = -1;
int actionCode;
try {
actionCode = readUI8("actionCode");

View File

@@ -61,6 +61,11 @@ public class AVM2Instruction implements Cloneable, GraphSourceItem {
private String file;
@Override
public long getLineOffset() {
return getOffset();
}
public void setFileLine(String file, int line) {
this.file = file;
this.line = line;

View File

@@ -21,6 +21,7 @@ import com.jpexs.decompiler.flash.BaseLocalData;
import com.jpexs.decompiler.flash.DisassemblyListener;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.abc.avm2.parser.script.Reference;
import com.jpexs.decompiler.flash.action.deobfuscation.ActionDeobfuscator;
import com.jpexs.decompiler.flash.action.model.ActionItem;
import com.jpexs.decompiler.flash.action.model.ConstantPool;
@@ -109,6 +110,8 @@ public abstract class Action implements GraphSourceItem {
private boolean ignored = false;
public long fileOffset = -1;
/**
* Action type identifier
*/
@@ -121,6 +124,11 @@ public abstract class Action implements GraphSourceItem {
private long address;
@Override
public long getLineOffset() {
return fileOffset;
}
/**
* Names of ActionScript properties
*/
@@ -937,11 +945,12 @@ public abstract class Action implements GraphSourceItem {
this.ignored = ignored;
}
public static List<GraphTargetItem> actionsPartToTree(HashMap<Integer, String> registerNames, HashMap<String, GraphTargetItem> variables, HashMap<String, GraphTargetItem> functions, TranslateStack stack, List<Action> actions, int start, int end, int version, int staticOperation, String path) throws InterruptedException {
public static List<GraphTargetItem> actionsPartToTree(Reference<GraphSourceItem> fi, HashMap<Integer, String> registerNames, HashMap<String, GraphTargetItem> variables, HashMap<String, GraphTargetItem> functions, TranslateStack stack, List<Action> actions, int start, int end, int version, int staticOperation, String path) throws InterruptedException {
if (start < actions.size() && (end > 0) && (start > 0)) {
logger.log(Level.FINE, "Entering {0}-{1}{2}", new Object[]{start, end, actions.size() > 0 ? (" (" + actions.get(start).toString() + " - " + actions.get(end == actions.size() ? end - 1 : end) + ")") : ""});
}
ActionLocalData localData = new ActionLocalData(registerNames, variables, functions);
localData.lineStartAction = fi.getVal();
List<GraphTargetItem> output = new ArrayList<>();
int ip = start;
boolean isWhile = false;
@@ -964,6 +973,10 @@ public abstract class Action implements GraphSourceItem {
ip++;
continue;
}
if (stack.isEmpty()) {
localData.lineStartAction = action;
fi.setVal(action);
}
if (action instanceof GraphSourceItemContainer) {
GraphSourceItemContainer cnt = (GraphSourceItemContainer) action;
//List<GraphTargetItem> out=actionsPartToTree(new HashMap<Integer, String>(), new HashMap<String, GraphTargetItem>(),new HashMap<String, GraphTargetItem>(), new TranslateStack(), src, ip+1,endip-1 , version);

View File

@@ -79,7 +79,11 @@ public class ActionGraphSource extends GraphSource {
@Override
public List<GraphTargetItem> translatePart(GraphPart part, BaseLocalData localData, TranslateStack stack, int start, int end, int staticOperation, String path) throws InterruptedException {
return Action.actionsPartToTree(registerNames, variables, functions, stack, actions, start, end, version, staticOperation, path);
Reference<GraphSourceItem> fi = new Reference<>(localData.lineStartInstruction);
List<GraphTargetItem> r = Action.actionsPartToTree(fi, registerNames, variables, functions, stack, actions, start, end, version, staticOperation, path);
localData.lineStartInstruction = fi.getVal();
return r;
}
private List<Long> posCache = null;

View File

@@ -796,6 +796,7 @@ public class ActionListReader {
if ((a = sis.readAction()) == null) {
break;
}
a.fileOffset = ip;
int actionLengthWithHeader = a.getTotalActionLength();

View File

@@ -524,6 +524,10 @@ public class Configuration {
@ConfigurationName("gui.avm2.splitPane.vars.dividerLocationPercent")
public static final ConfigurationItem<Double> guiAvm2VarsSplitPaneDividerLocationPercent = null;
@ConfigurationDefaultDouble(0.7)
@ConfigurationName("gui.action.splitPane.vars.dividerLocationPercent")
public static final ConfigurationItem<Double> guiActionVarsSplitPaneDividerLocationPercent = null;
@ConfigurationDefaultBoolean(true)
@ConfigurationCategory("script")
public static final ConfigurationItem<Boolean> debugHalt = null;

View File

@@ -184,7 +184,7 @@ public class HighlightedTextWriter extends GraphTextWriter {
ndata.merge(data);
ndata.offset = src.getOffset() + pos;
if (itemPos.startLineItem != null) {
ndata.firstLineOffset = itemPos.startLineItem.getOffset();
ndata.firstLineOffset = itemPos.startLineItem.getLineOffset();
}
h = new Highlighting(sb.length() - newLineCount, ndata, HighlightType.OFFSET, str);
instructionHilights.add(h);

View File

@@ -84,6 +84,13 @@ public class DefineButtonTag extends ButtonTag implements ASMSource {
private boolean isSingleFrame;
private String scriptName = "-";
@Override
public String getScriptName() {
return scriptName;
}
/**
* Constructor
*
@@ -108,6 +115,11 @@ public class DefineButtonTag extends ButtonTag implements ASMSource {
readData(sis, data, 0, false, false, false);
}
@Override
public void setScriptName(String scriptName) {
this.scriptName = scriptName;
}
@Override
public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException {
buttonId = sis.readUI16("buttonId");

View File

@@ -27,6 +27,7 @@ import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.flash.tags.base.ASMSource;
import com.jpexs.decompiler.flash.types.annotations.HideInRawEdit;
import com.jpexs.decompiler.flash.types.annotations.Internal;
import com.jpexs.decompiler.flash.types.annotations.SWFVersion;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.Helper;
@@ -54,6 +55,14 @@ public class DoActionTag extends Tag implements ASMSource {
@HideInRawEdit
public ByteArrayRange actionBytes;
@Internal
private String scriptName = "-";
@Override
public String getScriptName() {
return scriptName;
}
/**
* Constructor
*
@@ -87,6 +96,11 @@ public class DoActionTag extends Tag implements ASMSource {
readData(sis, data, 0, false, false, false);
}
@Override
public void setScriptName(String scriptName) {
this.scriptName = scriptName;
}
@Override
public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException {
actionBytes = sis.readByteRangeEx(sis.available(), "actionBytes");

View File

@@ -29,6 +29,7 @@ import com.jpexs.decompiler.flash.tags.base.ASMSource;
import com.jpexs.decompiler.flash.tags.base.CharacterIdTag;
import com.jpexs.decompiler.flash.types.BasicType;
import com.jpexs.decompiler.flash.types.annotations.HideInRawEdit;
import com.jpexs.decompiler.flash.types.annotations.Internal;
import com.jpexs.decompiler.flash.types.annotations.SWFType;
import com.jpexs.decompiler.flash.types.annotations.SWFVersion;
import com.jpexs.helpers.ByteArrayRange;
@@ -61,6 +62,14 @@ public class DoInitActionTag extends Tag implements CharacterIdTag, ASMSource {
@HideInRawEdit
public ByteArrayRange actionBytes;
@Internal
private String scriptName = "-";
@Override
public String getScriptName() {
return scriptName;
}
/**
* Constructor
*
@@ -71,6 +80,11 @@ public class DoInitActionTag extends Tag implements CharacterIdTag, ASMSource {
actionBytes = ByteArrayRange.EMPTY;
}
@Override
public void setScriptName(String scriptName) {
this.scriptName = scriptName;
}
/**
* Constructor
*

View File

@@ -91,4 +91,8 @@ public interface ASMSource extends Exportable {
public Tag getSourceTag();
public void setSourceTag(Tag t);
public String getScriptName();
public void setScriptName(String scriptName);
}

View File

@@ -47,6 +47,13 @@ public class BUTTONCONDACTION implements ASMSource, Serializable {
private Tag tag;
private String scriptName = "-";
@Override
public String getScriptName() {
return scriptName;
}
// Constructor for Generic tag editor.
public BUTTONCONDACTION() {
swf = null;
@@ -54,6 +61,11 @@ public class BUTTONCONDACTION implements ASMSource, Serializable {
actionBytes = new ByteArrayRange(SWFInputStream.BYTE_ARRAY_EMPTY);
}
@Override
public void setScriptName(String scriptName) {
this.scriptName = scriptName;
}
public BUTTONCONDACTION(SWF swf, SWFInputStream sis, Tag tag) throws IOException {
this.swf = swf;
this.tag = tag;

View File

@@ -42,6 +42,13 @@ import java.util.List;
*/
public class CLIPACTIONRECORD implements ASMSource, Serializable {
private String scriptName = "-";
@Override
public String getScriptName() {
return scriptName;
}
public static String keyToString(int key) {
if ((key < CLIPACTIONRECORD.KEYNAMES.length) && (key > 0) && (CLIPACTIONRECORD.KEYNAMES[key] != null)) {
return CLIPACTIONRECORD.KEYNAMES[key];
@@ -100,6 +107,11 @@ public class CLIPACTIONRECORD implements ASMSource, Serializable {
actionBytes = ByteArrayRange.EMPTY;
}
@Override
public void setScriptName(String scriptName) {
this.scriptName = scriptName;
}
public CLIPACTIONRECORD(SWF swf, SWFInputStream sis, Tag tag) throws IOException {
this.swf = swf;
this.tag = tag;

View File

@@ -37,6 +37,8 @@ public interface GraphSourceItem extends Serializable, Cloneable {
public long getOffset();
public long getLineOffset();
public boolean ignoredLoops();
public List<Integer> getBranches(GraphSource code);