Fixed Incorrect debugger line numbers when "Open loaded while playing" is enabled

Fixed AS3 debugger - Slow injecting debug info - now faster
Fixed AS3 debugger - obfuscated classes debugging
Fixed Delayed open loaded SWFs while playing
Fixed AS3 Direct editation - script initializer for main document class

Changed Wrong unicode escape `{invalid_utf8:xxx}` changed to `{invalid_utf8=xxx}` for compatibility with file names
This commit is contained in:
Jindra Petřík
2023-11-19 02:00:27 +01:00
parent e54973b761
commit bd6c953218
53 changed files with 325 additions and 80 deletions

View File

@@ -73,6 +73,8 @@ public class DecompilerPool {
src.getActionScriptSource(writer, actions);
writer.endFunction();
writer.finishHilights();
HighlightedText result = new HighlightedText(writer);
SWF swf = src.getSwf();
if (swf != null) {
@@ -107,6 +109,7 @@ public class DecompilerPool {
HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true);
pack.toSource(abcIndex, writer, script == null ? null : script.traits.traits, new ConvertData(), ScriptExportMode.AS, parallel, false);
writer.finishHilights();
HighlightedText result = new HighlightedText(writer);
Openable openable = pack.getOpenable();
SWF swf = (openable instanceof SWF) ? (SWF) openable : ((ABC) openable).getSwf();

View File

@@ -94,6 +94,7 @@ import com.jpexs.decompiler.flash.helpers.NulWriter;
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.helpers.hilight.HighlightingList;
import com.jpexs.decompiler.flash.tags.ABCContainerTag;
import com.jpexs.decompiler.flash.tags.DebugIDTag;
import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag;
@@ -3945,7 +3946,7 @@ public final class SWF implements SWFContainerItem, Timelined, Openable {
*
* @param decompileDir Directory to set file information paths
*/
public void injectAS3DebugInfo(File decompileDir) throws InterruptedException {
public void injectAS3DebugInfo(File decompileDir) throws InterruptedException {
List<ScriptPack> packs = getAS3Packs();
int i = 0;
for (ScriptPack s : packs) {
@@ -3954,10 +3955,14 @@ public final class SWF implements SWFContainerItem, Timelined, Openable {
}
i++;
informListeners("inject_debuginfo", "" + i + "/" + packs.size() + ": " + s.getPath());
if (s.isSimple) {
s.injectDebugInfo(decompileDir);
if (s.isSimple) {
try {
s.injectDebugInfo(decompileDir);
} catch (Throwable t) {
Logger.getLogger(SWF.class.getName()).log(Level.SEVERE, "Errorr injecting debug info", t);
}
}
}
}
}
/**
@@ -4121,7 +4126,8 @@ public final class SWF implements SWFContainerItem, Timelined, Openable {
} catch (InterruptedException ex) {
logger.log(Level.SEVERE, null, ex);
}
List<Highlighting> hls = writer.instructionHilights;
writer.finishHilights();
HighlightingList hls = writer.instructionHilights;
Map<Integer, Integer> offsetToLine = new TreeMap<>();
String txt = writer.toString();

View File

@@ -84,6 +84,8 @@ public class SourceGeneratorLocalData implements Serializable {
public List<List<Long>> catchesOpenedLoops = new ArrayList<>();
public List<Integer> catchesTempRegs = new ArrayList<>();
public String documentClass;
public boolean secondRun = false;

View File

@@ -1186,6 +1186,7 @@ public class AVM2Code implements Cloneable {
public String toASMSource(ABC abc, AVM2ConstantPool constants) {
HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), false);
toASMSource(abc, constants, null, null, new ArrayList<>(), ScriptExportMode.PCODE, writer);
writer.finishHilights();
return writer.toString();
}

View File

@@ -45,6 +45,7 @@ public class NewClassIns extends InstructionDefinition {
int clsIndex = ins.operands[0];
HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), false);
stack.pop().toString(writer, LocalData.create(localData.callStack /*??*/, localData.abcIndex, localData.abc, localData.localRegNames, localData.fullyQualifiedNames, new HashSet<>()));
writer.finishHilights();
String baseType = writer.toString();
ABC abc = localData.abc;
stack.push(new UnparsedAVM2Item(ins, localData.lineStartInstruction, "§§newclass(" + abc.constants.getMultiname(abc.instance_info.get(clsIndex).name_index).getName(localData.getConstants(), localData.fullyQualifiedNames, false, true) + "," + baseType + ")"));

View File

@@ -841,6 +841,7 @@ public class AVM2SourceGenerator implements SourceGenerator {
newlocalData.traitUsages = localData.traitUsages;
newlocalData.currentScript = localData.currentScript;
newlocalData.scriptIndex = localData.scriptIndex;
newlocalData.documentClass = localData.documentClass;
newlocalData.privateNs = localData.privateNs;
newlocalData.protectedNs = localData.protectedNs;
newlocalData.isStatic = isStatic;
@@ -1815,6 +1816,12 @@ public class AVM2SourceGenerator implements SourceGenerator {
mbCode.add(ins(AVM2Instructions.PushScope));
int traitScope = 1;
String documentClassStr = localData.documentClass;
DottedChain documentClass = null;
if (documentClassStr != null && !documentClassStr.isEmpty()) {
documentClass = DottedChain.parseNoSuffix(documentClassStr);
}
Map<Trait, Integer> initScopes = new HashMap<>();
@@ -1823,9 +1830,15 @@ public class AVM2SourceGenerator implements SourceGenerator {
TraitClass tc = (TraitClass) t;
DottedChain className = tc.getName(abc).getNameWithNamespace(abc.constants, true);
List<Integer> parents = new ArrayList<>();
int[] nsset = new int[]{constants.getMultiname(tc.name_index).namespace_index};
mbCode.add(ins(AVM2Instructions.FindPropertyStrict, constants.getMultinameId(Multiname.createMultiname(false, constants.getMultiname(tc.name_index).name_index, constants.getNamespaceSetId(nsset, true)), true)));
if (documentClass != null && documentClass.equals(className)) {
mbCode.add(ins(AVM2Instructions.GetScopeObject, 0));
} else {
int[] nsset = new int[]{constants.getMultiname(tc.name_index).namespace_index};
mbCode.add(ins(AVM2Instructions.FindPropertyStrict, constants.getMultinameId(Multiname.createMultiname(false, constants.getMultiname(tc.name_index).name_index, constants.getNamespaceSetId(nsset, true)), true)));
}
traitScope++;
if (abc.instance_info.get(tc.class_info).isInterface()) {
mbCode.add(ins(AVM2Instructions.PushNull));

View File

@@ -2612,10 +2612,11 @@ public class ActionScript3Parser {
return ret;
}
public void addScriptFromTree(List<List<NamespaceItem>> allOpenedNamespaces, List<GraphTargetItem> items, int classPos) throws AVM2ParseException, CompilationException {
public void addScriptFromTree(List<List<NamespaceItem>> allOpenedNamespaces, List<GraphTargetItem> items, int classPos, String documentClass) throws AVM2ParseException, CompilationException {
AVM2SourceGenerator gen = new AVM2SourceGenerator(abcIndex);
SourceGeneratorLocalData localData = new SourceGeneratorLocalData(
new HashMap<>(), 0, Boolean.FALSE, 0);
localData.documentClass = documentClass;
ScriptInfo si = new ScriptInfo();
int scriptIndex = abcIndex.getSelectedAbc().script_info.size();
abcIndex.getSelectedAbc().script_info.add(si);
@@ -2629,10 +2630,10 @@ public class ActionScript3Parser {
abcIndex.getSelectedAbc().fireChanged();
}
public void addScript(String s, String fileName, int classPos, int scriptIndex) throws AVM2ParseException, IOException, CompilationException, InterruptedException {
public void addScript(String s, String fileName, int classPos, int scriptIndex, String documentClass) throws AVM2ParseException, IOException, CompilationException, InterruptedException {
List<List<NamespaceItem>> allOpenedNamespaces = new ArrayList<>();
List<GraphTargetItem> traits = scriptTraitsFromString(allOpenedNamespaces, s, fileName, scriptIndex);
addScriptFromTree(allOpenedNamespaces, traits, classPos);
addScriptFromTree(allOpenedNamespaces, traits, classPos, documentClass);
}
public ActionScript3Parser(AbcIndexing abcIndex) throws IOException, InterruptedException {
@@ -2641,14 +2642,14 @@ public class ActionScript3Parser {
this.abcIndex = abcIndex;
}
public static void compile(String src, ABC abc, AbcIndexing abcIndex, String fileName, int classPos, int scriptIndex, boolean air) throws AVM2ParseException, IOException, InterruptedException, CompilationException {
public static void compile(String src, ABC abc, AbcIndexing abcIndex, String fileName, int classPos, int scriptIndex, boolean air, String documentClass) throws AVM2ParseException, IOException, InterruptedException, CompilationException {
//List<ABC> parABCs = new ArrayList<>();
SWF.initPlayer();
ActionScript3Parser parser = new ActionScript3Parser(abcIndex);
boolean success = false;
ABC originalAbc = ((ABCContainerTag) ((Tag) abc.parentTag).cloneTag()).getABC();
try {
parser.addScript(src, fileName, classPos, scriptIndex);
parser.addScript(src, fileName, classPos, scriptIndex, documentClass);
success = true;
} finally {
if (!success) {
@@ -2673,7 +2674,7 @@ public class ActionScript3Parser {
AbcIndexing abcIndex = swf.getAbcIndex();
abcIndex.selectAbc(abc);
ActionScript3Parser parser = new ActionScript3Parser(abcIndex);
parser.addScript(new String(Helper.readFile(src), Utf8Helper.charset), src, classPos, scriptIndex);
parser.addScript(new String(Helper.readFile(src), Utf8Helper.charset), src, classPos, scriptIndex, swf.getDocumentClass());
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(new File(dst)))) {
abc.saveToStream(fos);
}

View File

@@ -434,6 +434,7 @@ public final class MethodBody implements Cloneable {
writer.indent().indent().indent();
toString(callStack, abcIndex, "", ScriptExportMode.AS, abc, null, writer, new ArrayList<>(), seenMethods);
writer.unindent().unindent().unindent();
writer.finishHilights();
return writer.toString();
} catch (InterruptedException ex) {
logger.log(Level.SEVERE, null, ex);

View File

@@ -73,6 +73,7 @@ public abstract class ConstVarMultinameUsage extends TraitMultinameUsage {
} catch (InterruptedException ex) {
// ignore
}
writer.finishHilights();
return writer.toString().trim();
}

View File

@@ -90,6 +90,7 @@ public abstract class MethodMultinameUsage extends TraitMultinameUsage {
}
((TraitMethodGetterSetter) traits.traits.get(traitIndex)).toStringHeader(null, convertData, "", abc, traitsType == TRAITS_TYPE_CLASS, ScriptExportMode.AS, -1/*FIXME*/, classIndex, writer, new ArrayList<>(), false, insideInterface);
}
writer.finishHilights();
return writer.toString().trim();
}

View File

@@ -556,6 +556,7 @@ public class ActionList extends ArrayList<Action> {
public String toString() {
HighlightedTextWriter writer = new HighlightedTextWriter(new CodeFormatting(), false);
Action.actionsToString(new ArrayList<>(), 0, this, SWF.DEFAULT_VERSION, ScriptExportMode.PCODE, writer);
writer.finishHilights();
return writer.toString();
}

View File

@@ -377,8 +377,9 @@ public class ActionPush extends Action {
@Override
public String toString() {
HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), false);
HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), false);
toString(writer);
writer.finishHilights();
return writer.toString();
}

View File

@@ -17,6 +17,7 @@
package com.jpexs.decompiler.flash.helpers;
import com.jpexs.decompiler.flash.helpers.hilight.Highlighting;
import com.jpexs.decompiler.flash.helpers.hilight.HighlightingList;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@@ -32,33 +33,33 @@ public class HighlightedText implements Serializable {
public String text;
private final List<Highlighting> traitHighlights;
private final HighlightingList traitHighlights;
private final List<Highlighting> classHighlights;
private final HighlightingList classHighlights;
private final List<Highlighting> methodHighlights;
private final HighlightingList methodHighlights;
private final List<Highlighting> instructionHighlights;
private final HighlightingList instructionHighlights;
private final List<Highlighting> specialHighlights;
private final HighlightingList specialHighlights;
public List<Highlighting> getTraitHighlights() {
public HighlightingList getTraitHighlights() {
return traitHighlights;
}
public List<Highlighting> getMethodHighlights() {
public HighlightingList getMethodHighlights() {
return methodHighlights;
}
public List<Highlighting> getClassHighlights() {
public HighlightingList getClassHighlights() {
return classHighlights;
}
public List<Highlighting> getInstructionHighlights() {
public HighlightingList getInstructionHighlights() {
return instructionHighlights;
}
public List<Highlighting> getSpecialHighlights() {
public HighlightingList getSpecialHighlights() {
return specialHighlights;
}
@@ -77,10 +78,10 @@ public class HighlightedText implements Serializable {
public HighlightedText(String text) {
this.text = text;
this.traitHighlights = new ArrayList<>();
this.classHighlights = new ArrayList<>();
this.methodHighlights = new ArrayList<>();
this.instructionHighlights = new ArrayList<>();
this.specialHighlights = new ArrayList<>();
this.traitHighlights = new HighlightingList();
this.classHighlights = new HighlightingList();
this.methodHighlights = new HighlightingList();
this.instructionHighlights = new HighlightingList();
this.specialHighlights = new HighlightingList();
}
}

View File

@@ -21,10 +21,9 @@ import com.jpexs.decompiler.flash.helpers.hilight.HighlightData;
import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType;
import com.jpexs.decompiler.flash.helpers.hilight.HighlightType;
import com.jpexs.decompiler.flash.helpers.hilight.Highlighting;
import com.jpexs.decompiler.flash.helpers.hilight.HighlightingList;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.helpers.Helper;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/**
@@ -50,16 +49,24 @@ public class HighlightedTextWriter extends GraphTextWriter {
private final Stack<Highlighting> hilightStack = new Stack<>();
public List<Highlighting> traitHilights = new ArrayList<>();
public HighlightingList traitHilights = new HighlightingList();
public List<Highlighting> classHilights = new ArrayList<>();
public HighlightingList classHilights = new HighlightingList();
public List<Highlighting> methodHilights = new ArrayList<>();
public HighlightingList methodHilights = new HighlightingList();
public List<Highlighting> instructionHilights = new ArrayList<>();
public HighlightingList instructionHilights = new HighlightingList();
public List<Highlighting> specialHilights = new ArrayList<>();
public HighlightingList specialHilights = new HighlightingList();
public void finishHilights() {
traitHilights.finish();
classHilights.finish();
methodHilights.finish();
instructionHilights.finish();
specialHilights.finish();
}
public HighlightedTextWriter(CodeFormatting formatting, boolean hilight) {
super(formatting);
this.hilight = hilight;

View File

@@ -0,0 +1,128 @@
/*
* Copyright (C) 2010-2023 JPEXS, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
package com.jpexs.decompiler.flash.helpers.hilight;
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
/**
*
* @author JPEXS
*/
public class HighlightingList extends ArrayList<Highlighting> {
private boolean finished = false;
public void finish() {
this.finished = true;
}
public boolean isFinished() {
return finished;
}
private void checkWriteAccess() {
if (finished) {
throw new RuntimeException("Cannot add to readonly list");
}
}
@Override
public boolean add(Highlighting e) {
checkWriteAccess();
return super.add(e);
}
@Override
public void add(int index, Highlighting element) {
checkWriteAccess();
super.add(index, element);
}
@Override
public boolean addAll(Collection<? extends Highlighting> c) {
checkWriteAccess();
return super.addAll(c);
}
@Override
public boolean addAll(int index, Collection<? extends Highlighting> c) {
checkWriteAccess();
return super.addAll(index, c);
}
@Override
public boolean remove(Object o) {
checkWriteAccess();
return super.remove(o);
}
@Override
public Highlighting remove(int index) {
checkWriteAccess();
return super.remove(index);
}
@Override
public boolean removeAll(Collection<?> c) {
checkWriteAccess();
return super.removeAll(c);
}
@Override
public boolean removeIf(Predicate<? super Highlighting> filter) {
checkWriteAccess();
return super.removeIf(filter);
}
@Override
public void replaceAll(UnaryOperator<Highlighting> operator) {
checkWriteAccess();
super.replaceAll(operator);
}
@Override
protected void removeRange(int fromIndex, int toIndex) {
checkWriteAccess();
super.removeRange(fromIndex, toIndex);
}
@Override
public boolean retainAll(Collection<?> c) {
checkWriteAccess();
return super.retainAll(c);
}
@Override
public void clear() {
checkWriteAccess();
super.clear();
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
@Override
public boolean equals(Object o) {
return o == this;
}
}

View File

@@ -29,6 +29,7 @@ public class As3ScriptReplaceException extends Exception {
public As3ScriptReplaceException(String message) {
super(message);
this.exceptionItems = new ArrayList<>();
}
public As3ScriptReplaceException(List<As3ScriptReplaceExceptionItem> exceptionItems) {

View File

@@ -72,7 +72,7 @@ public class FFDecAs3ScriptReplacer implements As3ScriptReplacerInterface {
AbcIndexing abcIndex = swf.getAbcIndex();
abcIndex.selectAbc(abc);
ActionScript3Parser.compile(text, abc, abcIndex, scriptName, newClassIndex, oldIndex, air);
ActionScript3Parser.compile(text, abc, abcIndex, scriptName, newClassIndex, oldIndex, air, swf.getDocumentClass());
if (pack.isSimple) {
// Move newly added script to its position
abc.script_info.set(oldIndex, abc.script_info.get(newIndex));

View File

@@ -72,6 +72,7 @@ public class ActionScriptSearch {
HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true);
asm.getASMSource(ScriptExportMode.PCODE, writer, null);
writer.finishHilights();
String text = writer.toString();
if (pat.matcher(text).find()) {
found.add(new ActionSearchResult(asm, pcode, item.getKey()));
@@ -187,6 +188,7 @@ public class ActionScriptSearch {
MethodBody body = abc.bodies.get(bodyIndex);
HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true);
abc.bodies.get(bodyIndex).getCode().toASMSource(abc, abc.constants, abc.method_info.get(body.method_info), body, ScriptExportMode.PCODE, writer);
writer.finishHilights();
String text = writer.toString();
if (pat.matcher(text).find()) {
ABCSearchResult searchResult = new ABCSearchResult(pack, methodInfo.getClassIndex(), methodInfo.getTraitId());

View File

@@ -636,6 +636,7 @@ public class DefineEditTextTag extends TextTag {
String text = initialText.replace("\\", "\\\\").replace("[", "\\[").replace("]", "\\]");
writer.hilightSpecial(text, HighlightSpecialType.TEXT);
}
writer.finishHilights();
return new HighlightedText(writer);
}

View File

@@ -309,6 +309,7 @@ public abstract class StaticTextTag extends TextTag {
}
}
}
writer.finishHilights();
return new HighlightedText(writer);
}

View File

@@ -1654,6 +1654,7 @@ public class XFLConverter {
} catch (InterruptedException ex) {
logger.log(Level.SEVERE, null, ex);
}
writer.finishHilights();
return writer.toString();
}

View File

@@ -328,6 +328,7 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
public String toString(LocalData localData) throws InterruptedException {
HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), false);
toString(writer, localData);
writer.finishHilights();
return writer.toString();
}
@@ -422,6 +423,7 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
try {
HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), false);
toStringNoQuotes(writer, localData);
writer.finishHilights();
return writer.toString();
} catch (InterruptedException ex) {
//ignore

View File

@@ -104,19 +104,30 @@ public class Utf8Helper {
}
}
public static String stripEscapes(String string) {
return string.replaceAll("\\{invalid_utf8=[0-9]+\\}", "")
.replaceAll("\\{\\+(\\+*invalid_utf8=[0-9]+)\\}", "{$1}");
}
public static byte[] getBytes(String string) {
if (!string.contains("{invalid_utf8:")) {
if (!string.contains("invalid_utf8")) {
return string.getBytes(charset);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
Pattern invPattern = Pattern.compile("^(\\{invalid_utf8:([0-9]+)\\}).*", Pattern.DOTALL);
Pattern invPattern = Pattern.compile("^(\\{invalid_utf8[=:]([0-9]+)\\}).*", Pattern.DOTALL);
for (int i = 0; i < string.length(); i++) {
char c = string.charAt(i);
char c = string.charAt(i);
if (c == '{') {
String subStr = string.substring(i);
if (!subStr.isEmpty() && subStr.charAt(0) == '+') {
baos.write(("" + c).getBytes(charset));
i++;
continue;
}
Matcher m = invPattern.matcher(subStr);
if (m.matches()) {
int v = Integer.parseInt(m.group(2));
@@ -125,6 +136,7 @@ public class Utf8Helper {
i--;
continue;
}
}
baos.write(("" + c).getBytes(charset));
}
@@ -141,7 +153,8 @@ public class Utf8Helper {
}
private static String escapeInvalidUtf8Char(int v) {
return "{invalid_utf8:" + v + "}";
//Note: for writing the string "{invalid_utf8=xxx}" itself, you can escape it with "{+invalid_utf8=xxx}"
return "{invalid_utf8=" + v + "}";
}
public static String decode(byte[] data) {