diff --git a/CHANGELOG.md b/CHANGELOG.md index 27c3e01f9..63c7e8719 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,7 @@ All notable changes to this project will be documented in this file. - Optimized (faster) deleting items for large SWF trees - AS debugger - More varible flags - AS3 direct editation - edit files with native keyword +- [#1383] AS Debugger - debugging nested SWFs (enable "Open loaded SWFs while playing") ### Fixed - Debugger - getting children of top level variables @@ -3480,6 +3481,7 @@ Major version of SWF to XML export changed to 2. [#1809]: https://www.free-decompiler.com/flash/issues/1809 [#873]: https://www.free-decompiler.com/flash/issues/873 [#1644]: https://www.free-decompiler.com/flash/issues/1644 +[#1383]: https://www.free-decompiler.com/flash/issues/1383 [#2149]: https://www.free-decompiler.com/flash/issues/2149 [#2172]: https://www.free-decompiler.com/flash/issues/2172 [#2174]: https://www.free-decompiler.com/flash/issues/2174 diff --git a/lib/flashdebugger.jar b/lib/flashdebugger.jar index 579e06cc4..5608d0a2b 100644 Binary files a/lib/flashdebugger.jar and b/lib/flashdebugger.jar differ diff --git a/libsrc/debugswf/com/jpexs/decompiler/flash/debugger/DebugByteArrayLoadedEvent.as b/libsrc/debugswf/com/jpexs/decompiler/flash/debugger/DebugByteArrayLoadedEvent.as new file mode 100644 index 000000000..5250d45d6 --- /dev/null +++ b/libsrc/debugswf/com/jpexs/decompiler/flash/debugger/DebugByteArrayLoadedEvent.as @@ -0,0 +1,10 @@ +package com.jpexs.decompiler.flash.debugger { + import flash.events.Event; + + public class DebugByteArrayLoadedEvent extends Event { + + public DebugByteArrayLoadedEvent() { + super("DebugByteArrayLoadedEvent"); + } + } +} diff --git a/libsrc/debugswf/com/jpexs/decompiler/flash/debugger/DebugConnection.as b/libsrc/debugswf/com/jpexs/decompiler/flash/debugger/DebugConnection.as index 8cad454b0..46b25b6dc 100644 --- a/libsrc/debugswf/com/jpexs/decompiler/flash/debugger/DebugConnection.as +++ b/libsrc/debugswf/com/jpexs/decompiler/flash/debugger/DebugConnection.as @@ -16,6 +16,7 @@ private static var name:String; private static var failed:Boolean = false; private static var fillByteArrays = []; + private static var fillByteArraysEvents = []; private static var lenBytes:Array = [ -1, -1, -1, -1 ]; @@ -32,7 +33,8 @@ public static const MSG_LOADER_BYTES = 2; public static const MSG_DUMP_BYTEARRAY = 3; public static const MSG_REQUEST_BYTEARRAY = 4; - + public static const MSG_LOADER_URL_INFO = 5; + public static const MSG_LOADER_MODIFY_BYTES = 6; private static function sendQueue(){ var qo = q; @@ -125,7 +127,7 @@ private static function onSocketData(event:ProgressEvent):void { while (s.bytesAvailable > 0) { - if (lenBytePos < 4) { + if (lenBytePos < 4) { lenBytes[lenBytePos] = s.readUnsignedByte(); lenBytePos++; if (lenBytePos == 4) { @@ -136,9 +138,8 @@ var readLen:int = s.bytesAvailable <= len ? s.bytesAvailable : len; s.readBytes(readBa, readBa.length, readLen); len -= readLen; - - if (len == 0) { - lenBytePos = 0; + if (len == 0) { + lenBytePos = 0; var ba:ByteArray = fillByteArrays.pop(); var pos = ba.position; ba.position = 0; @@ -149,6 +150,10 @@ } else { ba.position = pos; } + var onComplete = fillByteArraysEvents.pop(); + if (onComplete != null) { + onComplete.call(onComplete); + } } } } @@ -163,6 +168,10 @@ public static function writeLoaderBytes(data:ByteArray){ writeMsg(data,MSG_LOADER_BYTES); } + + public static function modifyLoaderBytesWithUrl(data:ByteArray, outputData:ByteArray, url:String, onComplete:Function){ + writeMsg({"inputData": data, "outputData": outputData, "url" : url, "onComplete" : onComplete},MSG_LOADER_MODIFY_BYTES); + } public static function writeCommaSeparatedToByteArray(s:String, ba:ByteArray) { var bytes:Array = s.split(","); @@ -232,8 +241,14 @@ writeBytes(msg); break; case MSG_REQUEST_BYTEARRAY: - fillByteArrays.push(msg); + fillByteArrays.push(msg); break; + case MSG_LOADER_MODIFY_BYTES: + writeString(msg["url"]); + writeBytes(msg["inputData"]); + fillByteArraysEvents.push(msg["onComplete"]); + fillByteArrays.push(msg["outputData"]); + break; } s.flush(); }else{ diff --git a/libsrc/debugswf/com/jpexs/decompiler/flash/debugger/DebugLoader.as b/libsrc/debugswf/com/jpexs/decompiler/flash/debugger/DebugLoader.as index 503b11ae6..2e2b235bc 100644 --- a/libsrc/debugswf/com/jpexs/decompiler/flash/debugger/DebugLoader.as +++ b/libsrc/debugswf/com/jpexs/decompiler/flash/debugger/DebugLoader.as @@ -4,18 +4,45 @@ import flash.net.URLRequest; import flash.system.LoaderContext; import flash.utils.ByteArray; + import flash.net.URLLoader; + import flash.net.URLLoaderDataFormat; + import flash.net.URLRequest; + import flash.events.Event; - public class DebugLoader extends Loader { - + public class DebugLoader extends Loader { + + private var lastLoadedContext:LoaderContext = null; + private var urlLoader:URLLoader = null; + private var lastLoadedRequest:URLRequest = null; + private var lastModifiedByteArray:ByteArray = null; public override function load(request:URLRequest, context:LoaderContext = null):void { - DebugConnection.writeLoaderURL(request.url); - super.load(request,context); + lastLoadedRequest = request; + lastLoadedContext = context; + + urlLoader = new URLLoader(); + urlLoader.dataFormat = URLLoaderDataFormat.BINARY; + urlLoader.addEventListener(Event.COMPLETE, onURLLoaderComplete); + urlLoader.load(request); } + + private function onURLLoaderComplete(event:Event):void { + var dataBytes:ByteArray = urlLoader.data as ByteArray; + loadBytesInternal(dataBytes, lastLoadedContext, lastLoadedRequest.url); + } + private function loadBytesInternal(bytes:ByteArray, context:LoaderContext = null, url:String = "") { + lastModifiedByteArray = new ByteArray(); + lastLoadedContext = context; + DebugConnection.modifyLoaderBytesWithUrl(bytes, lastModifiedByteArray, url, onModifiedDataLoaded); + } + + private function onModifiedDataLoaded() { + super.loadBytes(lastModifiedByteArray, lastLoadedContext); + } + public override function loadBytes(bytes:ByteArray, context:LoaderContext = null):void { - DebugConnection.writeLoaderBytes(bytes); - super.loadBytes(bytes,context); + loadBytesInternal(bytes, context); } public override function toString():String { diff --git a/libsrc/debugswf/debug/DOMDocument.xml b/libsrc/debugswf/debug/DOMDocument.xml index dcadb265f..605551470 100644 --- a/libsrc/debugswf/debug/DOMDocument.xml +++ b/libsrc/debugswf/debug/DOMDocument.xml @@ -14,6 +14,16 @@ + + + + + + + + + + @@ -24,15 +34,5 @@ - - - - - - - - - - \ No newline at end of file diff --git a/libsrc/debugswf/debug/META-INF/metadata.xml b/libsrc/debugswf/debug/META-INF/metadata.xml index d682531f2..dc3d51ac8 100644 --- a/libsrc/debugswf/debug/META-INF/metadata.xml +++ b/libsrc/debugswf/debug/META-INF/metadata.xml @@ -5,8 +5,8 @@ xmlns:xmp="http://ns.adobe.com/xap/1.0/"> Adobe Flash Professional CS6 - build 537 2014-10-26T15:59:21+01:00 - 2023-11-19T08:32:38-08:00 - 2023-11-19T08:32:38-08:00 + 2024-08-04T02:20:55-07:00 + 2024-08-04T02:20:55-07:00 @@ -15,7 +15,7 @@ - xmp.iid:D4132A45B886EE11B43B9B2E6D6D7C97 + xmp.iid:C1FFFAAC2852EF1194768CEE75293134 xmp.did:1476A545885CE411B13FACEEFCD7D43C xmp.did:1476A545885CE411B13FACEEFCD7D43C @@ -56,6 +56,12 @@ 2014-10-26T15:59:21+01:00 Adobe Flash Professional CS6 - build 481 + + created + xmp.iid:C1FFFAAC2852EF1194768CEE75293134 + 2014-10-26T15:59:21+01:00 + Adobe Flash Professional CS6 - build 481 + diff --git a/libsrc/debugswf/debug/PublishSettings.xml b/libsrc/debugswf/debug/PublishSettings.xml index 618fc2897..e317964e7 100644 --- a/libsrc/debugswf/debug/PublishSettings.xml +++ b/libsrc/debugswf/debug/PublishSettings.xml @@ -79,7 +79,7 @@ . CONFIG::FLASH_AUTHORING="true"; - 1 + 0 1 0 diff --git a/libsrc/debugswf/debug/bin/SymDepend.cache b/libsrc/debugswf/debug/bin/SymDepend.cache index 8ece03aba..3a10c8f2e 100644 Binary files a/libsrc/debugswf/debug/bin/SymDepend.cache and b/libsrc/debugswf/debug/bin/SymDepend.cache differ diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java index 1674db1f8..37690f21f 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java @@ -3990,6 +3990,9 @@ public final class SWF implements SWFContainerItem, Timelined, Openable { * P-code) */ public void injectAS3PcodeDebugInfo() throws InterruptedException { + injectAS3PcodeDebugInfo("main"); + } + public void injectAS3PcodeDebugInfo(String swfHash) throws InterruptedException { List packs = getAS3Packs(); int i = 0; for (ScriptPack s : packs) { @@ -4000,7 +4003,7 @@ public final class SWF implements SWFContainerItem, Timelined, Openable { informListeners("inject_debuginfo", "" + i + "/" + packs.size() + ": " + s.getPath()); int abcIndex = s.allABCs.indexOf(s.abc); if (s.isSimple) { - s.injectPCodeDebugInfo(abcIndex); + s.injectPCodeDebugInfo(abcIndex, swfHash); } } } @@ -4011,6 +4014,10 @@ public final class SWF implements SWFContainerItem, Timelined, Openable { * @param decompileDir Directory to set file information paths */ public void injectAS3DebugInfo(File decompileDir) throws InterruptedException { + injectAS3DebugInfo(decompileDir, "main"); + } + + public void injectAS3DebugInfo(File decompileDir, String swfHash) throws InterruptedException { List packs = getAS3Packs(); int i = 0; for (ScriptPack s : packs) { @@ -4021,7 +4028,7 @@ public final class SWF implements SWFContainerItem, Timelined, Openable { informListeners("inject_debuginfo", "" + i + "/" + packs.size() + ": " + s.getPath()); if (s.isSimple) { try { - s.injectDebugInfo(decompileDir); + s.injectDebugInfo(decompileDir, swfHash); } catch (Throwable t) { Logger.getLogger(SWF.class.getName()).log(Level.SEVERE, "Errorr injecting debug info", t); } @@ -4072,12 +4079,17 @@ public final class SWF implements SWFContainerItem, Timelined, Openable { * @param pcodeLevel inject Pcode lines instead of decompiled lines */ public void enableDebugging(boolean injectAS3Code, File decompileDir, boolean telemetry, boolean pcodeLevel) throws InterruptedException { + enableDebugging(injectAS3Code, decompileDir, telemetry, pcodeLevel, "main"); + } + + public void enableDebugging(boolean injectAS3Code, File decompileDir, boolean telemetry, boolean pcodeLevel, String swfHash) throws InterruptedException { + if (injectAS3Code) { if (pcodeLevel) { - injectAS3PcodeDebugInfo(); + injectAS3PcodeDebugInfo(swfHash); } else { - injectAS3DebugInfo(decompileDir); + injectAS3DebugInfo(decompileDir, swfHash); } } @@ -4156,6 +4168,10 @@ public final class SWF implements SWFContainerItem, Timelined, Openable { } public boolean generatePCodeSwdFile(File file, Map> breakpoints) throws IOException, InterruptedException { + return generatePCodeSwdFile(file, breakpoints, "main"); + } + + public boolean generatePCodeSwdFile(File file, Map> breakpoints, String swfHash) throws IOException, InterruptedException { DebugIDTag dit = getDebugId(); if (dit == null) { return false; @@ -4180,7 +4196,7 @@ public final class SWF implements SWFContainerItem, Timelined, Openable { } informListeners("generate_swd", name); moduleId++; - String sname = "#PCODE " + name; + String sname = swfHash + ":" + "#PCODE " + name; int bitmap = SWD.bitmapAction; items.add(new SWD.DebugScript(moduleId, bitmap, sname, "")); @@ -4237,6 +4253,10 @@ public final class SWF implements SWFContainerItem, Timelined, Openable { } public boolean generateSwdFile(File file, Map> breakpoints) throws IOException { + return generateSwdFile(file, breakpoints, "main"); + } + + public boolean generateSwdFile(File file, Map> breakpoints, String swfHash) throws IOException { DebugIDTag dit = getDebugId(); if (dit == null) { return false; @@ -4299,7 +4319,7 @@ public final class SWF implements SWFContainerItem, Timelined, Openable { } //final String NONAME = "[No instance name assigned]"; - String sname = name; + String sname = swfHash + ":" + name; int bitmap = SWD.bitmapAction; /* Matcher m; int bitmap = SWD.bitmapAction; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ScriptPack.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ScriptPack.java index 455d9f3d9..37c0ca4fb 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ScriptPack.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ScriptPack.java @@ -396,6 +396,10 @@ public class ScriptPack extends AS3ClassTreeItem { * http://securityevaluators.com/knowledge/flash/ */ public void injectDebugInfo(File directoryPath) { + injectDebugInfo(directoryPath, "main"); + } + + public void injectDebugInfo(File directoryPath, String swfHash) { Map> bodyToPosToLine = new HashMap<>(); Map> bodyLineToPos = new HashMap<>(); Map> bodyToRegToName = new HashMap<>(); @@ -517,6 +521,7 @@ public class ScriptPack extends AS3ClassTreeItem { String cls = path.className; String filename = new File(directoryPath, path.packageStr.toFilePath()).getPath().replace(";", "{{semicolon}}") + ";" + + swfHash + ":" + pkg.replace(".", File.separator).replace(";", "{{semicolon}}") + ";" + cls.replace(";", "{{semicolon}}") @@ -689,7 +694,7 @@ public class ScriptPack extends AS3ClassTreeItem { ((Tag) abc.parentTag).setModified(true); } - public void injectPCodeDebugInfo(int abcIndex) { + public void injectPCodeDebugInfo(int abcIndex, String swfHash) { Map bodyToIdentifier = new HashMap<>(); @@ -765,7 +770,7 @@ public class ScriptPack extends AS3ClassTreeItem { i -= 2; } } - String filename = "#PCODE " + bodyName + ";" + pkg.replace(".", File.separator) + ";" + cls + ".as"; + String filename = swfHash + ":" + "#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(); diff --git a/libsrc/ffdec_lib/testdata/debug/debug_as2.swf b/libsrc/ffdec_lib/testdata/debug/debug_as2.swf new file mode 100644 index 000000000..4379aa0b7 Binary files /dev/null and b/libsrc/ffdec_lib/testdata/debug/debug_as2.swf differ diff --git a/libsrc/ffdec_lib/testdata/debug/debug_as3.swf b/libsrc/ffdec_lib/testdata/debug/debug_as3.swf new file mode 100644 index 000000000..162f5a11e Binary files /dev/null and b/libsrc/ffdec_lib/testdata/debug/debug_as3.swf differ diff --git a/libsrc/ffdec_lib/testdata/debug_inner/inner.swf b/libsrc/ffdec_lib/testdata/debug_inner/inner.swf new file mode 100644 index 000000000..0a7bf764f Binary files /dev/null and b/libsrc/ffdec_lib/testdata/debug_inner/inner.swf differ diff --git a/libsrc/ffdec_lib/testdata/debug_inner/inner/InnerSWF.as3proj b/libsrc/ffdec_lib/testdata/debug_inner/inner/InnerSWF.as3proj new file mode 100644 index 000000000..7c7d91598 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/debug_inner/inner/InnerSWF.as3proj @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/debug_inner/inner/obj/InnerSWFConfig.old b/libsrc/ffdec_lib/testdata/debug_inner/inner/obj/InnerSWFConfig.old new file mode 100644 index 000000000..3fb908ad7 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/debug_inner/inner/obj/InnerSWFConfig.old @@ -0,0 +1,48 @@ + + + + + 25.0 + false + true + + + CONFIG::debug + true + + + CONFIG::release + false + + + CONFIG::timeStamp + '04.08.2024' + + + CONFIG::air + false + + + CONFIG::mobile + false + + + CONFIG::desktop + false + + true + + C:\FlashRelated\test_debugger\inner_debug\inner\src + C:\Program Files (x86)\FlashDevelop\Library\AS3\classes + + + + C:\FlashRelated\test_debugger\inner_debug\inner\src\InnerMain.as + + #FFFFFF + 30 + + 800 + 600 + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/debug_inner/inner/obj/InnerSWFConfig.xml b/libsrc/ffdec_lib/testdata/debug_inner/inner/obj/InnerSWFConfig.xml new file mode 100644 index 000000000..3fb908ad7 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/debug_inner/inner/obj/InnerSWFConfig.xml @@ -0,0 +1,48 @@ + + + + + 25.0 + false + true + + + CONFIG::debug + true + + + CONFIG::release + false + + + CONFIG::timeStamp + '04.08.2024' + + + CONFIG::air + false + + + CONFIG::mobile + false + + + CONFIG::desktop + false + + true + + C:\FlashRelated\test_debugger\inner_debug\inner\src + C:\Program Files (x86)\FlashDevelop\Library\AS3\classes + + + + C:\FlashRelated\test_debugger\inner_debug\inner\src\InnerMain.as + + #FFFFFF + 30 + + 800 + 600 + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/debug_inner/inner/src/InnerMain.as b/libsrc/ffdec_lib/testdata/debug_inner/inner/src/InnerMain.as new file mode 100644 index 000000000..710cb6b23 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/debug_inner/inner/src/InnerMain.as @@ -0,0 +1,24 @@ +package +{ + import flash.display.Sprite; + import flash.events.Event; + + public class InnerMain extends Sprite + { + MyInnerClass; + + public function InnerMain() + { + if (stage) init(); + else addEventListener(Event.ADDED_TO_STAGE, init); + } + + private function init(e:Event = null):void + { + removeEventListener(Event.ADDED_TO_STAGE, init); + // entry point + } + + } + +} \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/debug_inner/inner/src/MyInnerClass.as b/libsrc/ffdec_lib/testdata/debug_inner/inner/src/MyInnerClass.as new file mode 100644 index 000000000..b90f7d2ad --- /dev/null +++ b/libsrc/ffdec_lib/testdata/debug_inner/inner/src/MyInnerClass.as @@ -0,0 +1,59 @@ +package +{ + import flash.display.Loader; + import flash.display.LoaderInfo; + import flash.display.Sprite; + import flash.events.Event; + import flash.text.TextField; + import flash.text.TextFormat; + import flash.utils.ByteArray; + + public class MyInnerClass + { + [Embed(source="../../inner2.swf", mimeType="application/octet-stream")] + public var binaryData2Class:Class; + + private var root:Sprite; + + public function MyInnerClass() + { + } + + public function run(s:Sprite): void { + var myvar:int = 1; + trace("hello from inner class 1"); + + var textField:TextField = new TextField(); + + textField.text = "Hello from inner1 !"; + + var textFormat:TextFormat = new TextFormat(); + textFormat.size = 24; + textFormat.color = 0x000000; + textField.setTextFormat(textFormat); + + textField.width = 200; + + s.addChild(textField); + + textField.x = 50; + textField.y = 50; + + root = s; + var byteArray:ByteArray = new binaryData2Class() as ByteArray; + var loader:Loader = new Loader(); + loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaderComplete); + loader.loadBytes(byteArray); + } + + private function onLoaderComplete(event:Event):void { + var loaderInfo:LoaderInfo = event.target as LoaderInfo; + var className:String = "MyInnerClass2"; + var LoadedClass:Class = loaderInfo.applicationDomain.getDefinition(className) as Class; + var instance:* = new LoadedClass(); + instance.run(root); + } + + } + +} \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/debug_inner/inner2.swf b/libsrc/ffdec_lib/testdata/debug_inner/inner2.swf new file mode 100644 index 000000000..da6ab5aef Binary files /dev/null and b/libsrc/ffdec_lib/testdata/debug_inner/inner2.swf differ diff --git a/libsrc/ffdec_lib/testdata/debug_inner/inner2/InnerSWF2.as3proj b/libsrc/ffdec_lib/testdata/debug_inner/inner2/InnerSWF2.as3proj new file mode 100644 index 000000000..2bf448b4c --- /dev/null +++ b/libsrc/ffdec_lib/testdata/debug_inner/inner2/InnerSWF2.as3proj @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/debug_inner/inner2/obj/InnerSWF2Config.old b/libsrc/ffdec_lib/testdata/debug_inner/inner2/obj/InnerSWF2Config.old new file mode 100644 index 000000000..a1eafadb6 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/debug_inner/inner2/obj/InnerSWF2Config.old @@ -0,0 +1,48 @@ + + + + + 25.0 + false + true + + + CONFIG::debug + true + + + CONFIG::release + false + + + CONFIG::timeStamp + '04.08.2024' + + + CONFIG::air + false + + + CONFIG::mobile + false + + + CONFIG::desktop + false + + true + + C:\FlashRelated\test_debugger\inner_debug\inner2\src + C:\Program Files (x86)\FlashDevelop\Library\AS3\classes + + + + C:\FlashRelated\test_debugger\inner_debug\inner2\src\Main.as + + #FFFFFF + 30 + + 800 + 600 + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/debug_inner/inner2/obj/InnerSWF2Config.xml b/libsrc/ffdec_lib/testdata/debug_inner/inner2/obj/InnerSWF2Config.xml new file mode 100644 index 000000000..a1eafadb6 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/debug_inner/inner2/obj/InnerSWF2Config.xml @@ -0,0 +1,48 @@ + + + + + 25.0 + false + true + + + CONFIG::debug + true + + + CONFIG::release + false + + + CONFIG::timeStamp + '04.08.2024' + + + CONFIG::air + false + + + CONFIG::mobile + false + + + CONFIG::desktop + false + + true + + C:\FlashRelated\test_debugger\inner_debug\inner2\src + C:\Program Files (x86)\FlashDevelop\Library\AS3\classes + + + + C:\FlashRelated\test_debugger\inner_debug\inner2\src\Main.as + + #FFFFFF + 30 + + 800 + 600 + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/debug_inner/inner2/src/Main.as b/libsrc/ffdec_lib/testdata/debug_inner/inner2/src/Main.as new file mode 100644 index 000000000..1e9bfa3ff --- /dev/null +++ b/libsrc/ffdec_lib/testdata/debug_inner/inner2/src/Main.as @@ -0,0 +1,24 @@ +package +{ + import flash.display.Sprite; + import flash.events.Event; + + public class Main extends Sprite + { + MyInnerClass2; + + public function Main() + { + if (stage) init(); + else addEventListener(Event.ADDED_TO_STAGE, init); + } + + private function init(e:Event = null):void + { + removeEventListener(Event.ADDED_TO_STAGE, init); + // entry point + } + + } + +} \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/debug_inner/inner2/src/MyInnerClass2.as b/libsrc/ffdec_lib/testdata/debug_inner/inner2/src/MyInnerClass2.as new file mode 100644 index 000000000..aff712b6c --- /dev/null +++ b/libsrc/ffdec_lib/testdata/debug_inner/inner2/src/MyInnerClass2.as @@ -0,0 +1,37 @@ +package +{ + import flash.display.Sprite; + import flash.text.TextField; + import flash.text.TextFormat; + + public class MyInnerClass2 + { + + public function MyInnerClass2() + { + + } + + public function run(s:Sprite): void { + var myvar:int = 2; + trace("hello from inner class 2"); + + var textField:TextField = new TextField(); + + textField.text = "Hello from inner2 !"; + + var textFormat:TextFormat = new TextFormat(); + textFormat.size = 24; + textFormat.color = 0x000000; + textField.setTextFormat(textFormat); + textField.width = 200; + + s.addChild(textField); + + textField.x = 50; + textField.y = 75; + + } + } + +} \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/debug_inner/outer.swf b/libsrc/ffdec_lib/testdata/debug_inner/outer.swf new file mode 100644 index 000000000..4902490c2 Binary files /dev/null and b/libsrc/ffdec_lib/testdata/debug_inner/outer.swf differ diff --git a/libsrc/ffdec_lib/testdata/debug_inner/outer/OuterSwf.as3proj b/libsrc/ffdec_lib/testdata/debug_inner/outer/OuterSwf.as3proj new file mode 100644 index 000000000..f5a39481c --- /dev/null +++ b/libsrc/ffdec_lib/testdata/debug_inner/outer/OuterSwf.as3proj @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/debug_inner/outer/obj/OuterSwfConfig.old b/libsrc/ffdec_lib/testdata/debug_inner/outer/obj/OuterSwfConfig.old new file mode 100644 index 000000000..3c1960d44 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/debug_inner/outer/obj/OuterSwfConfig.old @@ -0,0 +1,48 @@ + + + + + 25.0 + false + true + + + CONFIG::debug + true + + + CONFIG::release + false + + + CONFIG::timeStamp + '04.08.2024' + + + CONFIG::air + false + + + CONFIG::mobile + false + + + CONFIG::desktop + false + + true + + C:\FlashRelated\test_debugger\inner_debug\outer\src + C:\Program Files (x86)\FlashDevelop\Library\AS3\classes + + + + C:\FlashRelated\test_debugger\inner_debug\outer\src\Main.as + + #FFFFFF + 30 + + 800 + 600 + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/debug_inner/outer/obj/OuterSwfConfig.xml b/libsrc/ffdec_lib/testdata/debug_inner/outer/obj/OuterSwfConfig.xml new file mode 100644 index 000000000..3c1960d44 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/debug_inner/outer/obj/OuterSwfConfig.xml @@ -0,0 +1,48 @@ + + + + + 25.0 + false + true + + + CONFIG::debug + true + + + CONFIG::release + false + + + CONFIG::timeStamp + '04.08.2024' + + + CONFIG::air + false + + + CONFIG::mobile + false + + + CONFIG::desktop + false + + true + + C:\FlashRelated\test_debugger\inner_debug\outer\src + C:\Program Files (x86)\FlashDevelop\Library\AS3\classes + + + + C:\FlashRelated\test_debugger\inner_debug\outer\src\Main.as + + #FFFFFF + 30 + + 800 + 600 + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/debug_inner/outer/src/Main.as b/libsrc/ffdec_lib/testdata/debug_inner/outer/src/Main.as new file mode 100644 index 000000000..d4bb99cfe --- /dev/null +++ b/libsrc/ffdec_lib/testdata/debug_inner/outer/src/Main.as @@ -0,0 +1,44 @@ +package +{ + import flash.display.Sprite; + import flash.events.Event; + import flash.display.Loader; + import flash.display.LoaderInfo; + import flash.utils.ByteArray; + + /** + * ... + * @author Jindra + */ + public class Main extends Sprite + { + [Embed(source="../../inner.swf", mimeType="application/octet-stream")] + public var binaryDataClass:Class; + + public function Main() + { + if (stage) init(); + else addEventListener(Event.ADDED_TO_STAGE, init); + } + + private function init(e:Event = null):void + { + removeEventListener(Event.ADDED_TO_STAGE, init); + + var byteArray:ByteArray = new binaryDataClass() as ByteArray; + var loader:Loader = new Loader(); + loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaderComplete); + loader.loadBytes(byteArray); + } + + private function onLoaderComplete(event:Event):void { + var loaderInfo:LoaderInfo = event.target as LoaderInfo; + var className:String = "MyInnerClass"; + var LoadedClass:Class = loaderInfo.applicationDomain.getDefinition(className) as Class; + var instance:* = new LoadedClass(); + instance.run(this); + } + + } + +} \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/gui/BreakpointListDialog.java b/src/com/jpexs/decompiler/flash/gui/BreakpointListDialog.java index eca81d5e1..8628f7a65 100644 --- a/src/com/jpexs/decompiler/flash/gui/BreakpointListDialog.java +++ b/src/com/jpexs/decompiler/flash/gui/BreakpointListDialog.java @@ -176,7 +176,7 @@ public class BreakpointListDialog extends AppDialog { } }*/ Pattern abcPcodePattern = Pattern.compile("^#PCODE abc:(?[0-9]+),body:(?[0-9]+);.*"); - Matcher m = abcPcodePattern.matcher(breakpoint.scriptName); + Matcher m = abcPcodePattern.matcher(breakpoint.scriptName); if (m.matches()) { int abcIndex = Integer.parseInt(m.group("abc")); int bodyIndex = Integer.parseInt(m.group("body")); diff --git a/src/com/jpexs/decompiler/flash/gui/DebugStackPanel.java b/src/com/jpexs/decompiler/flash/gui/DebugStackPanel.java index 7d0b8cd9e..a9220f35b 100644 --- a/src/com/jpexs/decompiler/flash/gui/DebugStackPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/DebugStackPanel.java @@ -17,6 +17,7 @@ package com.jpexs.decompiler.flash.gui; import com.jpexs.debugger.flash.messages.in.InBreakAtExt; +import com.jpexs.decompiler.flash.SWF; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; @@ -43,6 +44,7 @@ public class DebugStackPanel extends JPanel { private int depth = 0; + private String[] swfHashes = new String[0]; private int[] classIndices = new int[0]; private int[] methodIndices = new int[0]; private int[] traitIndices = new int[0]; @@ -90,9 +92,11 @@ public class DebugStackPanel extends JPanel { if (e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton(e)) { int row = stackTable.rowAtPoint(e.getPoint()); if (row >= 0) { - String scriptName = (String) stackTable.getModel().getValueAt(row, 0); - int line = (int) (Integer) stackTable.getModel().getValueAt(row, 1); - Main.getMainFrame().getPanel().gotoScriptLine(Main.getMainFrame().getPanel().getCurrentSwf(), + String swfHash = swfHashes[row]; + String scriptName = (String) stackTable.getModel().getValueAt(row, 1); + int line = (int) (Integer) stackTable.getModel().getValueAt(row, 2); + SWF swf = swfHash == null ? Main.getRunningSWF() : Main.getSwfByHash(swfHash); + Main.getMainFrame().getPanel().gotoScriptLine(swf, scriptName, line, classIndices[row], traitIndices[row], methodIndices[row], Main.isDebugPCode()); Main.getDebugHandler().setDepth(row); } @@ -117,15 +121,24 @@ public class DebugStackPanel extends JPanel { return; } active = true; - Object[][] data = new Object[info.files.size()][3]; + Object[][] data = new Object[info.files.size()][4]; + String[] newSwfHashes = new String[info.files.size()]; int[] newClassIndices = new int[info.files.size()]; int[] newMethodIndices = new int[info.files.size()]; int[] newTraitIndices = new int[info.files.size()]; for (int i = 0; i < info.files.size(); i++) { int f = info.files.get(i); - data[i][0] = Main.getDebugHandler().moduleToString(f); - data[i][1] = info.lines.get(i); - data[i][2] = info.stacks.get(i); + String moduleName = Main.getDebugHandler().moduleToString(f); + String swfHash = null; + if (moduleName.contains(":")) { + swfHash = moduleName.substring(0, moduleName.indexOf(":")); + moduleName = moduleName.substring(moduleName.indexOf(":") + 1); + } + newSwfHashes[i] = swfHash; + data[i][0] = swfHash == null ? "unknown" : Main.getSwfByHash(swfHash).toString(); + data[i][1] = moduleName; + data[i][2] = info.lines.get(i); + data[i][3] = info.stacks.get(i); Integer newClassIndex = Main.getDebugHandler().moduleToClassIndex(f); newClassIndices[i] = newClassIndex == null ? -1 : newClassIndex; Integer newMethodIndex = Main.getDebugHandler().moduleToMethodIndex(f); @@ -135,6 +148,7 @@ public class DebugStackPanel extends JPanel { } DefaultTableModel tm = new DefaultTableModel(data, new Object[]{ + AppStrings.translate("callStack.header.swf"), AppStrings.translate("callStack.header.file"), AppStrings.translate("callStack.header.line"), AppStrings.translate("stack.header.item") @@ -146,6 +160,7 @@ public class DebugStackPanel extends JPanel { }; stackTable.setModel(tm); + this.swfHashes = newSwfHashes; this.classIndices = newClassIndices; this.methodIndices = newMethodIndices; this.traitIndices = newTraitIndices; diff --git a/src/com/jpexs/decompiler/flash/gui/DebuggerHandler.java b/src/com/jpexs/decompiler/flash/gui/DebuggerHandler.java index 606bfd0bf..7aa0df343 100644 --- a/src/com/jpexs/decompiler/flash/gui/DebuggerHandler.java +++ b/src/com/jpexs/decompiler/flash/gui/DebuggerHandler.java @@ -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 modulePaths = new HashMap<>(); private Map moduleToSwfIndex = new HashMap<>(); - + + //Marks swfIndices that are fully loaded - at least one break was on it (including onloaded break) + private Set swfIndicesCommited = new HashSet<>(); + + private Map swfIndicesNewToSwfHash = new HashMap<>(); + + private Map scriptToModule = new HashMap<>(); private Map moduleToTraitIndex = new HashMap<>(); @@ -110,7 +117,7 @@ public class DebuggerHandler implements DebugConnectionListener { private List stackLines = new ArrayList<>(); - private SWF debuggedSwf = null; + private List 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 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 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("^(?[0-9a-z_]+):#PCODE abc:(?[0-9]+),script:(?