From f7869abe0bcb0af0dca16ed25f0ccdd27677b200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sun, 18 Dec 2022 21:46:14 +0100 Subject: [PATCH] Added AS3 P-code - Editing interface methods --- CHANGELOG.md | 1 + .../decompiler/flash/abc/avm2/AVM2Code.java | 76 +------------------ .../abc/avm2/parser/pcode/ASM3Parser.java | 15 +++- .../flash/abc/types/MethodInfo.java | 75 ++++++++++++++++++ .../jpexs/decompiler/flash/gui/MainPanel.java | 3 +- .../flash/gui/abc/ASMSourceEditorPane.java | 49 ++++++++---- .../flash/gui/abc/DecompiledEditorPane.java | 10 ++- .../flash/gui/abc/MethodCodePanel.java | 12 ++- 8 files changed, 141 insertions(+), 100 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d50749ab..3d17c8a59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ All notable changes to this project will be documented in this file. - [#1901] Double click tree node to start edit (can be enabled in settings) - Info about editation in status bar - AS3 P-code keyword "Unknown(N)", where N is index. For constants out of bounds. (mostly in dead code) +- AS3 P-code - Editing interface methods ### Fixed - [#1897] Close menu button without selecting specific item diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java index 31815998c..9b296104a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java @@ -1192,85 +1192,15 @@ public class AVM2Code implements Cloneable { } public GraphTextWriter toASMSource(ABC abc, AVM2ConstantPool constants, MethodInfo info, MethodBody body, List outputMap, ScriptExportMode exportMode, GraphTextWriter writer) { - + if (info != null) { writer.appendNoHilight("method"); if (Configuration.indentAs3PCode.get()) { writer.indent(); } writer.newLine(); - writer.appendNoHilight("name "); - writer.hilightSpecial(info.name_index == 0 ? "null" : "\"" + Helper.escapeActionScriptString(info.getName(constants)) + "\"", HighlightSpecialType.METHOD_NAME); - writer.newLine(); - if (info.flagExplicit()) { - writer.appendNoHilight("flag "); - writer.hilightSpecial("EXPLICIT", HighlightSpecialType.FLAG_EXPLICIT); - writer.newLine(); - } - if (info.flagHas_optional()) { - writer.appendNoHilight("flag "); - writer.hilightSpecial("HAS_OPTIONAL", HighlightSpecialType.FLAG_HAS_OPTIONAL); - writer.newLine(); - } - if (info.flagHas_paramnames()) { - writer.appendNoHilight("flag "); - writer.hilightSpecial("HAS_PARAM_NAMES", HighlightSpecialType.FLAG_HAS_PARAM_NAMES); - writer.newLine(); - } - if (info.flagIgnore_rest()) { - writer.appendNoHilight("flag "); - writer.hilightSpecial("EXPLICIT", HighlightSpecialType.FLAG_IGNORE_REST); - writer.newLine(); - } - if (info.flagNeed_activation()) { - writer.appendNoHilight("flag "); - writer.hilightSpecial("NEED_ACTIVATION", HighlightSpecialType.FLAG_NEED_ACTIVATION); - writer.newLine(); - } - if (info.flagNeed_arguments()) { - writer.appendNoHilight("flag "); - writer.hilightSpecial("NEED_ARGUMENTS", HighlightSpecialType.FLAG_NEED_ARGUMENTS); - writer.newLine(); - } - if (info.flagNeed_rest()) { - writer.appendNoHilight("flag "); - writer.hilightSpecial("NEED_REST", HighlightSpecialType.FLAG_NEED_REST); - writer.newLine(); - } - if (info.flagSetsdxns()) { - writer.appendNoHilight("flag "); - writer.hilightSpecial("SET_DXNS", HighlightSpecialType.FLAG_SET_DXNS); - writer.newLine(); - } - for (int p = 0; p < info.param_types.length; p++) { - writer.appendNoHilight("param "); - writer.hilightSpecial(constants.multinameToString(info.param_types[p]), HighlightSpecialType.PARAM, p); - writer.newLine(); - } - if (info.flagHas_paramnames()) { - for (int n : info.paramNames) { - writer.appendNoHilight("paramname "); - if (n == 0) { - writer.appendNoHilight("null"); - } else { - writer.appendNoHilight("\""); - writer.appendNoHilight(constants.getString(n)); - writer.appendNoHilight("\""); - } - writer.newLine(); - } - } - if (info.flagHas_optional()) { - for (int i = 0; i < info.optional.length; i++) { - ValueKind vk = info.optional[i]; - writer.appendNoHilight("optional "); - writer.hilightSpecial(vk.toASMString(constants), HighlightSpecialType.OPTIONAL, i); - writer.newLine(); - } - } - writer.appendNoHilight("returns "); - writer.hilightSpecial(constants.multinameToString(info.ret_type), HighlightSpecialType.RETURNS); - writer.newLine(); + + info.toASMSource(constants, writer); } writer.newLine(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/pcode/ASM3Parser.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/pcode/ASM3Parser.java index 5479eef9a..5ff2de100 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/pcode/ASM3Parser.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/pcode/ASM3Parser.java @@ -666,10 +666,15 @@ public class ASM3Parser { List paramNames = new ArrayList<>(); List optional = new ArrayList<>(); Stack blockStack = new Stack<>(); - body.traits = new Traits(); + if (body != null) { + body.traits = new Traits(); + } do { symb = lexer.lex(); if (Arrays.asList(ParsedSymbol.TYPE_KEYWORD_BODY, ParsedSymbol.TYPE_KEYWORD_CODE, ParsedSymbol.TYPE_KEYWORD_METHOD).contains(symb.type)) { + if (body == null && symb.type == ParsedSymbol.TYPE_KEYWORD_BODY) { + throw new AVM2ParseException("This method cannot have a body.", lexer.yyline()); + } blockStack.push(symb.type); continue; } @@ -1248,9 +1253,11 @@ public class ASM3Parser { } ins.operands[oi.insOperandIndex] = relOffset; } - body.exceptions = new ABCException[exceptions.size()]; - for (int e = 0; e < exceptions.size(); e++) { - body.exceptions[e] = exceptions.get(e); + if (body != null) { + body.exceptions = new ABCException[exceptions.size()]; + for (int e = 0; e < exceptions.size(); e++) { + body.exceptions[e] = exceptions.get(e); + } } info.param_types = new int[paramTypes.size()]; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodInfo.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodInfo.java index 18a0198e5..444f60767 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodInfo.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodInfo.java @@ -399,4 +399,79 @@ public class MethodInfo { } return rname; } + + public void toASMSource(AVM2ConstantPool constants, GraphTextWriter writer) { + writer.appendNoHilight("name "); + writer.hilightSpecial(name_index == 0 ? "null" : "\"" + Helper.escapeActionScriptString(getName(constants)) + "\"", HighlightSpecialType.METHOD_NAME); + writer.newLine(); + if (flagExplicit()) { + writer.appendNoHilight("flag "); + writer.hilightSpecial("EXPLICIT", HighlightSpecialType.FLAG_EXPLICIT); + writer.newLine(); + } + if (flagHas_optional()) { + writer.appendNoHilight("flag "); + writer.hilightSpecial("HAS_OPTIONAL", HighlightSpecialType.FLAG_HAS_OPTIONAL); + writer.newLine(); + } + if (flagHas_paramnames()) { + writer.appendNoHilight("flag "); + writer.hilightSpecial("HAS_PARAM_NAMES", HighlightSpecialType.FLAG_HAS_PARAM_NAMES); + writer.newLine(); + } + if (flagIgnore_rest()) { + writer.appendNoHilight("flag "); + writer.hilightSpecial("EXPLICIT", HighlightSpecialType.FLAG_IGNORE_REST); + writer.newLine(); + } + if (flagNeed_activation()) { + writer.appendNoHilight("flag "); + writer.hilightSpecial("NEED_ACTIVATION", HighlightSpecialType.FLAG_NEED_ACTIVATION); + writer.newLine(); + } + if (flagNeed_arguments()) { + writer.appendNoHilight("flag "); + writer.hilightSpecial("NEED_ARGUMENTS", HighlightSpecialType.FLAG_NEED_ARGUMENTS); + writer.newLine(); + } + if (flagNeed_rest()) { + writer.appendNoHilight("flag "); + writer.hilightSpecial("NEED_REST", HighlightSpecialType.FLAG_NEED_REST); + writer.newLine(); + } + if (flagSetsdxns()) { + writer.appendNoHilight("flag "); + writer.hilightSpecial("SET_DXNS", HighlightSpecialType.FLAG_SET_DXNS); + writer.newLine(); + } + for (int p = 0; p < param_types.length; p++) { + writer.appendNoHilight("param "); + writer.hilightSpecial(constants.multinameToString(param_types[p]), HighlightSpecialType.PARAM, p); + writer.newLine(); + } + if (flagHas_paramnames()) { + for (int n : paramNames) { + writer.appendNoHilight("paramname "); + if (n == 0) { + writer.appendNoHilight("null"); + } else { + writer.appendNoHilight("\""); + writer.appendNoHilight(constants.getString(n)); + writer.appendNoHilight("\""); + } + writer.newLine(); + } + } + if (flagHas_optional()) { + for (int i = 0; i < optional.length; i++) { + ValueKind vk = optional[i]; + writer.appendNoHilight("optional "); + writer.hilightSpecial(vk.toASMString(constants), HighlightSpecialType.OPTIONAL, i); + writer.newLine(); + } + } + writer.appendNoHilight("returns "); + writer.hilightSpecial(constants.multinameToString(ret_type), HighlightSpecialType.RETURNS); + writer.newLine(); + } } diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index 8c90f377e..8eee2c4c7 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -3668,6 +3668,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se SWF swf = abcPanel.getSwf(); swf.deobfuscate(level); } else { + int mi = abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getMethodIndex(); int bi = abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getBodyIndex(); DecompiledEditorPane decompiledTextArea = abcPanel.decompiledTextArea; Trait t = abcPanel.decompiledTextArea.getCurrentTrait(); @@ -3678,7 +3679,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se boolean isStatic = decompiledTextArea.getIsStatic(); abc.bodies.get(bi).deobfuscate(level, t, scriptIndex, classIndex, isStatic, ""/*FIXME*/); } - abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setBodyIndex(decompiledTextArea.getScriptLeaf().getPathScriptName(), bi, abc, t, abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getScriptIndex()); + abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setMethod(decompiledTextArea.getScriptLeaf().getPathScriptName(), mi, bi, abc, t, abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getScriptIndex()); } } catch (Exception ex) { logger.log(Level.SEVERE, "Deobfuscation error", ex); diff --git a/src/com/jpexs/decompiler/flash/gui/abc/ASMSourceEditorPane.java b/src/com/jpexs/decompiler/flash/gui/abc/ASMSourceEditorPane.java index 358f3982f..04a0b999a 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/ASMSourceEditorPane.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/ASMSourceEditorPane.java @@ -73,6 +73,9 @@ public class ASMSourceEditorPane extends DebuggableEditorPane implements CaretLi public ABC abc; public int bodyIndex = -1; + + + public int methodIndex = -1; private int scriptIndex = -1; @@ -125,8 +128,23 @@ public class ASMSourceEditorPane extends DebuggableEditorPane implements CaretLi if (trait != null && exportMode != ScriptExportMode.AS && exportMode != ScriptExportMode.AS_METHOD_STUBS) { trait.convertTraitHeader(abc, writer); } - MethodBody body = abc.bodies.get(bodyIndex); - abc.bodies.get(bodyIndex).getCode().toASMSource(abc, abc.constants, abc.method_info.get(body.method_info), body, exportMode, writer); + if (bodyIndex > -1) { + MethodBody body = abc.bodies.get(bodyIndex); + abc.bodies.get(bodyIndex).getCode().toASMSource(abc, abc.constants, abc.method_info.get(body.method_info), body, exportMode, writer); + } else { + writer.appendNoHilight("method"); + if (Configuration.indentAs3PCode.get()) { + writer.indent(); + } + writer.newLine(); + + abc.method_info.get(methodIndex).toASMSource(abc.constants, writer); + + if (Configuration.indentAs3PCode.get()) { + writer.unindent(); + } + writer.appendNoHilight("end ; method").newLine(); + } if (trait != null && exportMode != ScriptExportMode.AS && exportMode != ScriptExportMode.AS_METHOD_STUBS) { if (Configuration.indentAs3PCode.get()) { writer.unindent(); @@ -158,7 +176,9 @@ public class ASMSourceEditorPane extends DebuggableEditorPane implements CaretLi changeContentType("text/plain"); if (textHexOnly == null) { HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true); - Helper.byteArrayToHexWithHeader(writer, abc.bodies.get(bodyIndex).getCodeBytes()); + if (bodyIndex > -1) { + Helper.byteArrayToHexWithHeader(writer, abc.bodies.get(bodyIndex).getCodeBytes()); + } textHexOnly = new HighlightedText(writer); } setText(textHexOnly); @@ -220,15 +240,13 @@ public class ASMSourceEditorPane extends DebuggableEditorPane implements CaretLi return super.getName(); } - public void setBodyIndex(String scriptPathName, int bodyIndex, ABC abc, String name, Trait trait, int scriptIndex) { + public void setMethod(String scriptPathName, int methodIndex, int bodyIndex, ABC abc, String name, Trait trait, int scriptIndex) { + this.methodIndex = methodIndex; this.bodyIndex = bodyIndex; this.abc = abc; this.name = name; this.trait = trait; - this.scriptIndex = scriptIndex; - if (bodyIndex == -1) { - return; - } + this.scriptIndex = scriptIndex; textWithHex = null; textNoHex = null; textHexOnly = null; @@ -270,9 +288,11 @@ public class ASMSourceEditorPane extends DebuggableEditorPane implements CaretLi try { String text = getText(); if (text.trim().startsWith(Helper.hexData)) { - byte[] data = Helper.getBytesFromHexaText(text); - MethodBody mb = abc.bodies.get(bodyIndex); - mb.setCodeBytes(data); + if (bodyIndex > -1) { + byte[] data = Helper.getBytesFromHexaText(text); + MethodBody mb = abc.bodies.get(bodyIndex); + mb.setCodeBytes(data); + } } else { AVM2Code acode = ASM3Parser.parse(abc, new StringReader(text), trait, new MissingSymbolHandler() { //no longer ask for adding new constants @@ -310,9 +330,10 @@ public class ASMSourceEditorPane extends DebuggableEditorPane implements CaretLi public boolean missingFloat4(Float4 value) { return true; } - }, abc.bodies.get(bodyIndex), abc.method_info.get(abc.bodies.get(bodyIndex).method_info)); - //acode.getBytes(abc.bodies.get(bodyIndex).getCodeBytes()); - abc.bodies.get(bodyIndex).setCode(acode); + }, bodyIndex == -1 ? null : abc.bodies.get(bodyIndex), abc.method_info.get(methodIndex)); + if (bodyIndex > -1) { + abc.bodies.get(bodyIndex).setCode(acode); + } } ((Tag) abc.parentTag).setModified(true); diff --git a/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java b/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java index fa1b85c48..3b0523a85 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java @@ -205,9 +205,11 @@ public class DecompiledEditorPane extends DebuggableEditorPane implements CaretL return false; } int bi = abc.findBodyIndex(methodIndex); - if (bi == -1) { + + //in interfaces, there is no body + /*if (bi == -1) { return false; - } + }*/ //fix for inner functions: if (trait instanceof TraitMethodGetterSetter) { @@ -224,8 +226,8 @@ public class DecompiledEditorPane extends DebuggableEditorPane implements CaretL } abcPanel.detailPanel.showCard(DetailPanel.METHOD_GETTER_SETTER_TRAIT_CARD, trait, traitIndex, abc); MethodCodePanel methodCodePanel = abcPanel.detailPanel.methodTraitPanel.methodCodePanel; - if (reset || (methodCodePanel.getBodyIndex() != bi)) { - methodCodePanel.setBodyIndex(scriptName, bi, abc, name, trait, script.scriptIndex); + if (reset || (methodCodePanel.getMethodIndex()!= methodIndex)) { + methodCodePanel.setMethod(scriptName, methodIndex, bi, abc, name, trait, script.scriptIndex); abcPanel.detailPanel.setEditMode(false); this.isStatic = isStatic; } diff --git a/src/com/jpexs/decompiler/flash/gui/abc/MethodCodePanel.java b/src/com/jpexs/decompiler/flash/gui/abc/MethodCodePanel.java index 63cf90a2b..e7738d13a 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/MethodCodePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/MethodCodePanel.java @@ -91,17 +91,21 @@ public class MethodCodePanel extends JPanel { sourceTextArea.hilighSpecial(type, specialValue); } - public void setBodyIndex(String scriptPathName, int bodyIndex, ABC abc, Trait trait, int scriptIndex) { - sourceTextArea.setBodyIndex(scriptPathName, bodyIndex, abc, sourceTextArea.getName(), trait, scriptIndex); + public void setMethod(String scriptPathName, int methodIndex, int bodyIndex, ABC abc, Trait trait, int scriptIndex) { + sourceTextArea.setMethod(scriptPathName, methodIndex, bodyIndex, abc, sourceTextArea.getName(), trait, scriptIndex); } - public void setBodyIndex(String scriptPathName, int bodyIndex, ABC abc, String name, Trait trait, int scriptIndex) { - sourceTextArea.setBodyIndex(scriptPathName, bodyIndex, abc, name, trait, scriptIndex); + public void setMethod(String scriptPathName, int methodIndex, int bodyIndex, ABC abc, String name, Trait trait, int scriptIndex) { + sourceTextArea.setMethod(scriptPathName, methodIndex, bodyIndex, abc, name, trait, scriptIndex); } public int getBodyIndex() { return sourceTextArea.bodyIndex; } + + public int getMethodIndex() { + return sourceTextArea.methodIndex; + } public void clear() { sourceTextArea.clear();