diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/IdentifiersDeobfuscation.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/IdentifiersDeobfuscation.java index 84a3e1be0..0cc7a8691 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/IdentifiersDeobfuscation.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/IdentifiersDeobfuscation.java @@ -620,8 +620,53 @@ public class IdentifiersDeobfuscation { } return writer; - } + } + + /** + * Escapes obfuscated identifier. + * + * @param s String + * @return Escaped string + */ + public static String escapeOIdentifier(String s) { + StringBuilder ret = new StringBuilder(s.length()); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '\n') { + ret.append("\\n"); + } else if (c == '\r') { + ret.append("\\r"); + } else if (c == '\t') { + ret.append("\\t"); + } else if (c == '\b') { + ret.append("\\b"); + } else if (c == '\f') { + ret.append("\\f"); + } else if (c == '\\') { + ret.append("\\\\"); + } else if (c == '\u00A7') { + ret.append("\\\u00A7"); + } else if (c < 32) { + ret.append("\\x").append(Helper.byteToHex((byte) c)); + } else { + int num = 1; + for (int j = i + 1; j < s.length(); j++) { + if (s.charAt(j) == c) { + num++; + } else { + break; + } + } + if (num > Configuration.limitSameChars.get()) { + ret.append("\\{").append(num).append("}"); + i += num - 1; + } + ret.append(c); + } + } + return ret.toString(); + } /** * Unescapes deobfuscated identifier @@ -678,52 +723,6 @@ public class IdentifiersDeobfuscation { return ret.toString(); } - - /** - * Escapes obfuscated identifier. - * - * @param s String - * @return Escaped string - */ - public static String escapeOIdentifier(String s) { - StringBuilder ret = new StringBuilder(s.length()); - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - if (c == '\n') { - ret.append("\\n"); - } else if (c == '\r') { - ret.append("\\r"); - } else if (c == '\t') { - ret.append("\\t"); - } else if (c == '\b') { - ret.append("\\b"); - } else if (c == '\f') { - ret.append("\\f"); - } else if (c == '\\') { - ret.append("\\\\"); - } else if (c == '\u00A7') { - ret.append("\\\u00A7"); - } else if (c < 32) { - ret.append("\\x").append(Helper.byteToHex((byte) c)); - } else { - int num = 1; - for (int j = i + 1; j < s.length(); j++) { - if (s.charAt(j) == c) { - num++; - } else { - break; - } - } - if (num > Configuration.limitSameChars.get()) { - ret.append("\\{").append(num).append("}"); - i += num - 1; - } - ret.append(c); - } - } - - return ret.toString(); - } /** * Clears cache. 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 ecc5398c0..bf8570b39 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java @@ -585,6 +585,9 @@ public final class SWF implements SWFContainerItem, Timelined, Openable { @Internal private UninitializedClassFieldsDetector uninitializedClassFieldsDetector = new UninitializedClassFieldsDetector(); + + @Internal + private final Object uninitializedClassFieldsLock = new Object(); /** * ExporterInfo tag. @@ -3349,15 +3352,28 @@ public final class SWF implements SWFContainerItem, Timelined, Openable { if (treeItem instanceof AS2Package) { AS2Package pkg = (AS2Package) treeItem; if (pkg.isFlat()) { - String[] parts = pkg.getName().split("\\."); - for (int i = 0; i < parts.length; i++) { - parts[i] = Helper.makeFileName(parts[i]); + if (exportFileName) { + String[] parts = pkg.getName().split("\\."); + for (int i = 0; i < parts.length; i++) { + parts[i] = Helper.makeFileName(parts[i]); + } + return String.join(File.separator, parts); } - return String.join(File.separator, parts); + return DottedChain.parseNoSuffix(pkg.getName()).toPrintableString(false); } } if (!exportFileName) { + + if (treeItem instanceof DoInitActionTag) { + DoInitActionTag tag = (DoInitActionTag) treeItem; + String expName = tag.getSwf().getExportName(tag.getCharacterId()); + if (expName != null && !expName.isEmpty()) { + String[] pathParts = expName.contains(".") ? expName.split("\\.") : new String[]{expName}; + return IdentifiersDeobfuscation.printIdentifier(false, pathParts[pathParts.length - 1]); + } + } + return treeItem.toString(); } @@ -6221,15 +6237,20 @@ public final class SWF implements SWFContainerItem, Timelined, Openable { * @throws java.lang.InterruptedException On interruption */ public void calculateAs2UninitializedClassTraits() throws InterruptedException { + waitForUninitializedClassDetector(); setDetectingUninitialized(true); uninitializedAs2ClassTraits = new HashMap<>(); try { uninitializedAs2ClassTraits = getUninitializedClassFieldsDetector().calculateAs2UninitializedClassTraits(this); } catch (Throwable t) { + //System.err.println("Calculating AS2 uninitialized fields cancelled"); uninitializedAs2ClassTraits = null; throw t; } finally { setDetectingUninitialized(false); + synchronized (uninitializedClassFieldsLock) { + uninitializedClassFieldsLock.notifyAll(); + } } } @@ -6237,7 +6258,7 @@ public final class SWF implements SWFContainerItem, Timelined, Openable { this.detectingUninitializedClassFields = val; } - private synchronized boolean isDetectingUninitialized() { + public synchronized boolean isDetectingUninitialized() { return detectingUninitializedClassFields; } @@ -6291,4 +6312,17 @@ public final class SWF implements SWFContainerItem, Timelined, Openable { public boolean isDestroyed() { return destroyed; } + + public void waitForUninitializedClassDetector() { + if (!isDetectingUninitialized()) { + return; + } + try { + synchronized (uninitializedClassFieldsLock) { + uninitializedClassFieldsLock.wait(); + } + } catch (InterruptedException ex) { + //ignore + } + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraphSource.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraphSource.java index a679b8c11..e23d93678 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraphSource.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraphSource.java @@ -261,7 +261,7 @@ public class ActionGraphSource extends GraphSource { return size(); } if (ret == -1) { - Logger.getLogger(ActionGraphSource.class.getName()).log(Level.SEVERE, "{0} - address loc{1} not found", new Object[]{path, Helper.formatAddress(adr)}); + Logger.getLogger(ActionGraphSource.class.getName()).log(Level.SEVERE, "address loc{1} not found in {0}", new Object[]{path, Helper.formatAddress(adr)}); } } return ret; @@ -307,4 +307,7 @@ public class ActionGraphSource extends GraphSource { return variables; } + public String getPath() { + return path; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/as2/ActionScript2ClassDetector.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/as2/ActionScript2ClassDetector.java index 7b1391dd1..86829bd07 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/as2/ActionScript2ClassDetector.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/as2/ActionScript2ClassDetector.java @@ -635,7 +635,7 @@ public class ActionScript2ClassDetector { String getterNameStr = getAsString(((GetMemberActionItem) propertyGetter).memberName, "getter memberName"); if (!(getterNameStr.equals("__get__" + propertyNameStr))) { //throw new AssertException("getter does not match property name"); - Logger.getLogger(ActionScript2ClassDetector.class.getName()).warning(scriptPath + ": getter " + IdentifiersDeobfuscation.printIdentifier(false, getterNameStr) + " does not match property name " + IdentifiersDeobfuscation.printIdentifier(false, propertyNameStr)); + //Logger.getLogger(ActionScript2ClassDetector.class.getName()).warning(scriptPath + ": getter " + IdentifiersDeobfuscation.printIdentifier(false, getterNameStr) + " does not match property name " + IdentifiersDeobfuscation.printIdentifier(false, propertyNameStr)); continue; } @@ -669,7 +669,7 @@ public class ActionScript2ClassDetector { } String setterNameStr = getAsString(((GetMemberActionItem) propertySetter).memberName, "setter memberNAme"); if (!(setterNameStr.equals("__set__" + propertyNameStr))) { - Logger.getLogger(ActionScript2ClassDetector.class.getName()).warning(scriptPath + ": setter " + IdentifiersDeobfuscation.printIdentifier(false, setterNameStr) + " does not match property name " + IdentifiersDeobfuscation.printIdentifier(false, propertyNameStr)); + //Logger.getLogger(ActionScript2ClassDetector.class.getName()).warning(scriptPath + ": setter " + IdentifiersDeobfuscation.printIdentifier(false, setterNameStr) + " does not match property name " + IdentifiersDeobfuscation.printIdentifier(false, propertyNameStr)); continue; //throw new AssertException("setter does not match property name"); } @@ -845,7 +845,7 @@ public class ActionScript2ClassDetector { // goto next line and check next classes return true; } catch (AssertException ex) { - logger.log(Level.WARNING, "{0}: Cannot detect class - {1}", new Object[]{scriptPath, ex.getCondition()}); + logger.log(Level.WARNING, "Cannot detect class - {0} in {1}", new Object[]{ex.getCondition(), scriptPath}); } return false; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/as2/UninitializedClassFieldsDetector.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/as2/UninitializedClassFieldsDetector.java index c7ff75791..a6c9bd6df 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/as2/UninitializedClassFieldsDetector.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/as2/UninitializedClassFieldsDetector.java @@ -40,10 +40,13 @@ import com.jpexs.helpers.Cache; import com.jpexs.helpers.CancellableWorker; import com.jpexs.helpers.ProgressListener; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; /** * Uninitialized class fields detector for ActionScript 2. @@ -53,15 +56,15 @@ import java.util.Map; public class UninitializedClassFieldsDetector { private List progressListeners = new ArrayList<>(); - + public void addProgressListener(ProgressListener listener) { progressListeners.add(listener); } - + public void removeProgressListener(ProgressListener listener) { progressListeners.remove(listener); } - + private void fireProgress(ASMSource asm, String asmPath) { if (asm instanceof DoInitActionTag) { DoInitActionTag doi = (DoInitActionTag) asm; @@ -74,7 +77,7 @@ public class UninitializedClassFieldsDetector { listener.status(asmPath); } } - + /** * Gets path of variable and its getMembers: a.b.c.d => [a,b,c,d]. * @@ -218,254 +221,263 @@ public class UninitializedClassFieldsDetector { public Map> calculateAs2UninitializedClassTraits(SWF swf) throws InterruptedException { if (swf.isAS3()) { return new HashMap<>(); - } - final Map> result = new LinkedHashMap<>(); + } + final Map> result = Collections.synchronizedMap(new LinkedHashMap<>()); Map asms = swf.getASMs(false); - + CancellableWorker worker = CancellableWorker.getCurrent(); - - /*Map> trees = new HashMap<>(); - asms.entrySet().parallelStream() - .forEach(item -> { - CancellableWorker.assignThreadToWorker(Thread.currentThread(), worker); - if (worker.isCancelled()) { - return; + + final Map> classTraits = Collections.synchronizedMap(new LinkedHashMap<>(ActionScript2Classes.getClassToTraits())); + final Map> classInheritance = Collections.synchronizedMap(new HashMap<>(ActionScript2Classes.getClassInheritance())); + + Map> cachedTrees = Collections.synchronizedMap(new HashMap<>()); + + final Set subThreads = Collections.synchronizedSet(new HashSet<>()); + + Runnable cancelListener = new Runnable() { + @Override + public void run() { + for (Thread t : subThreads) { + t.interrupt(); } - fireProgress(item.getValue(), item.getKey()); - try { - trees.put(item.getKey(), item.getValue().getActionsToTree()); - } catch (Throwable t) { - trees.put(item.getKey(), new ArrayList<>()); - //ignore - } - }); */ + } + }; - - final Map> classTraits = new LinkedHashMap<>(ActionScript2Classes.getClassToTraits()); - final Map> classInheritance = new HashMap<>(ActionScript2Classes.getClassInheritance()); + worker.addCancelListener(cancelListener); - - Map> cachedTrees = new HashMap<>(); - - asms.entrySet().parallelStream() - .filter(item -> item.getValue() instanceof DoInitActionTag) - .forEach(entry -> { - CancellableWorker.assignThreadToWorker(Thread.currentThread(), worker); - if (worker.isCancelled()) { - return; - } - fireProgress(entry.getValue(), entry.getKey()); - - String key = entry.getKey(); - ASMSource asm = asms.get(key); - if (asm instanceof DoInitActionTag) { - DoInitActionTag doi = (DoInitActionTag) asm; - String exportName = doi.getSwf().getCharacter(doi.getCharacterId()).getExportName(); - if (exportName != null && exportName.startsWith("__Packages.")) { - //fireProgress(doi, key); - List tree = new ArrayList<>(); - try { - tree = asm.getActionsToTree(); - } catch (Throwable t) { - //ignore - } - cachedTrees.put(key, tree); - //get all assigned traits and inheritance tree - for (GraphTargetItem item : tree) { - if (item instanceof InterfaceActionItem) { - InterfaceActionItem iai = (InterfaceActionItem) item; - String className = String.join(".", getMembersPath(iai.name)); - classInheritance.put(className, new ArrayList<>()); - if (iai.superInterfaces != null) { - for (GraphTargetItem imp : iai.superInterfaces) { - String imtName = String.join(".", getMembersPath(imp)); - classInheritance.get(className).add(imtName); - } - } - } - if (item instanceof ClassActionItem) { - ClassActionItem cai = (ClassActionItem) item; - final String className = String.join(".", getMembersPath(cai.className)); - classInheritance.put(className, new ArrayList<>()); - if (!classTraits.containsKey(className)) { - classTraits.put(className, new LinkedHashMap<>()); - } - for (int i = 0; i < cai.traits.size(); i++) { - MyEntry en = cai.traits.get(i); - if (!(en.getKey() instanceof DirectValueActionItem)) { - continue; - } - DirectValueActionItem dv = (DirectValueActionItem) en.getKey(); - String name = dv.getAsString(); - GraphTargetItem value = en.getValue(); - boolean isStatic = cai.traitsStatic.get(i); - if (value instanceof FunctionActionItem) { - if (name.startsWith("__get__") || name.startsWith("__set__")) { - String vname = name.substring(7); - Variable v = new Variable(isStatic, vname, vname, className); - classTraits.get(className).put(vname, v); - } - Method m = new Method(isStatic, name, "Unknown" /*FIXME?*/, className); - classTraits.get(className).put(name, m); - } else { - Variable v = new Variable(isStatic, name, name, className); - classTraits.get(className).put(name, v); - } - } - if (cai.extendsOp != null) { - String parentClassName = String.join(".", getMembersPath(cai.extendsOp)); - classInheritance.get(className).add(parentClassName); - } else { - classInheritance.get(className).add("Object"); - } - if (cai.implementsOp != null) { - for (GraphTargetItem imp : cai.implementsOp) { - String imtName = String.join(".", getMembersPath(imp)); - classInheritance.get(className).add(imtName); - } - } - } - } - - //Detect this.x assigns - for (GraphTargetItem item : tree) { - if (item instanceof ClassActionItem) { - ClassActionItem cai = (ClassActionItem) item; - final String className = String.join(".", getMembersPath(cai.className)); - for (int i = 0; i < cai.traits.size(); i++) { - MyEntry en = cai.traits.get(i); - if (!(en.getKey() instanceof DirectValueActionItem)) { - continue; - } - GraphTargetItem value = en.getValue(); - if (value instanceof GraphTargetItem) { - AbstractGraphTargetVisitor visitor = new AbstractGraphTargetVisitor() { - @Override - public boolean visit(GraphTargetItem item) { - List path = getFullPath(item); - if (path != null) { - List parent = new ArrayList<>(path); - parent.remove(parent.size() - 1); - if (parent.size() == 1) { - if (parent.get(0).equals("this")) { - String name = path.get(path.size() - 1); - if (!containsTrait(classTraits, classInheritance, className, name) && (!result.containsKey(className) || !result.get(className).containsKey(name))) { - Variable v = new Variable(false, name, null, className); - if (!result.containsKey(className)) { - result.put(className, new LinkedHashMap<>()); - } - result.get(className).put(name, v); - } - } - } - } - return true; - } - }; - visitor.visit(value); - value.visitRecursively(visitor); - } - } - } - } - } - } - - - }); - - //Complete inheritance tree - for (String className : classInheritance.keySet()) { - for (int i = 0; i < classInheritance.get(className).size(); i++) { - String parentClass = classInheritance.get(className).get(i); - if (classInheritance.containsKey(parentClass)) { - for (String p : classInheritance.get(parentClass)) { - if (!classInheritance.get(className).contains(p)) { - classInheritance.get(className).add(p); + try { + asms.entrySet().parallelStream() + .filter(item -> item.getValue() instanceof DoInitActionTag) + .forEach(entry -> { + if (worker.isCancelled()) { + return; } - } - } - } - } + subThreads.add(Thread.currentThread()); + fireProgress(entry.getValue(), entry.getKey()); - //getting static classname.x assigns - asms.entrySet().parallelStream().forEach(entry -> { - CancellableWorker.assignThreadToWorker(Thread.currentThread(), worker); - if (worker.isCancelled()) { - return; - } - fireProgress(entry.getValue(), entry.getKey()); - String key = entry.getKey(); - ASMSource asm = asms.get(key); - //fireProgress(asm, key); - List tree = new ArrayList<>(); - if (cachedTrees.containsKey(key)) { - tree = cachedTrees.get(key); - } else { - try { - tree = asm.getActionsToTree(); - } catch(Throwable t) { - //ignore - } - } - for (GraphTargetItem item : tree) { - AbstractGraphTargetVisitor visitor = new AbstractGraphTargetVisitor() { - @Override - public boolean visit(GraphTargetItem item) { - if ((item instanceof SetMemberActionItem) - || (item instanceof CallMethodActionItem) - || (item instanceof NewMethodActionItem) - || (item instanceof DeleteActionItem) - || (item instanceof GetMemberActionItem)) { - List path = getFullPath(item); - if (path != null) { - List parent = new ArrayList<>(path); - parent.remove(parent.size() - 1); - String name = path.get(path.size() - 1); - - String className = String.join(".", parent); - if (classInheritance.containsKey(className)) { - //it's a class - if (!containsTrait(classTraits, classInheritance, className, name) && (!result.containsKey(className) || !result.get(className).containsKey(name))) { - if (!result.containsKey(className)) { - result.put(className, new LinkedHashMap<>()); + String key = entry.getKey(); + ASMSource asm = asms.get(key); + if (asm instanceof DoInitActionTag) { + DoInitActionTag doi = (DoInitActionTag) asm; + String exportName = doi.getSwf().getCharacter(doi.getCharacterId()).getExportName(); + if (exportName != null && exportName.startsWith("__Packages.")) { + //fireProgress(doi, key); + List tree = new ArrayList<>(); + try { + tree = asm.getActionsToTree(); + } catch (Throwable t) { + //ignore + } + cachedTrees.put(key, tree); + //get all assigned traits and inheritance tree + for (GraphTargetItem item : tree) { + if (item instanceof InterfaceActionItem) { + InterfaceActionItem iai = (InterfaceActionItem) item; + String className = String.join(".", getMembersPath(iai.name)); + classInheritance.put(className, new ArrayList<>()); + if (iai.superInterfaces != null) { + for (GraphTargetItem imp : iai.superInterfaces) { + String imtName = String.join(".", getMembersPath(imp)); + classInheritance.get(className).add(imtName); + } + } + } + if (item instanceof ClassActionItem) { + ClassActionItem cai = (ClassActionItem) item; + final String className = String.join(".", getMembersPath(cai.className)); + classInheritance.put(className, new ArrayList<>()); + if (!classTraits.containsKey(className)) { + classTraits.put(className, new LinkedHashMap<>()); + } + for (int i = 0; i < cai.traits.size(); i++) { + MyEntry en = cai.traits.get(i); + if (!(en.getKey() instanceof DirectValueActionItem)) { + continue; + } + DirectValueActionItem dv = (DirectValueActionItem) en.getKey(); + String name = dv.getAsString(); + GraphTargetItem value = en.getValue(); + boolean isStatic = cai.traitsStatic.get(i); + if (value instanceof FunctionActionItem) { + if (name.startsWith("__get__") || name.startsWith("__set__")) { + String vname = name.substring(7); + Variable v = new Variable(isStatic, vname, vname, className); + classTraits.get(className).put(vname, v); + } + Method m = new Method(isStatic, name, "Unknown" /*FIXME?*/, className); + classTraits.get(className).put(name, m); + } else { + Variable v = new Variable(isStatic, name, name, className); + classTraits.get(className).put(name, v); + } + } + if (cai.extendsOp != null) { + String parentClassName = String.join(".", getMembersPath(cai.extendsOp)); + classInheritance.get(className).add(parentClassName); + } else { + classInheritance.get(className).add("Object"); + } + if (cai.implementsOp != null) { + for (GraphTargetItem imp : cai.implementsOp) { + String imtName = String.join(".", getMembersPath(imp)); + classInheritance.get(className).add(imtName); + } + } + } + } + + //Detect this.x assigns + for (GraphTargetItem item : tree) { + if (item instanceof ClassActionItem) { + ClassActionItem cai = (ClassActionItem) item; + final String className = String.join(".", getMembersPath(cai.className)); + for (int i = 0; i < cai.traits.size(); i++) { + MyEntry en = cai.traits.get(i); + if (!(en.getKey() instanceof DirectValueActionItem)) { + continue; + } + GraphTargetItem value = en.getValue(); + if (value instanceof GraphTargetItem) { + AbstractGraphTargetVisitor visitor = new AbstractGraphTargetVisitor() { + @Override + public boolean visit(GraphTargetItem item) { + List path = getFullPath(item); + if (path != null) { + List parent = new ArrayList<>(path); + parent.remove(parent.size() - 1); + if (parent.size() == 1) { + if (parent.get(0).equals("this")) { + String name = path.get(path.size() - 1); + if (!containsTrait(classTraits, classInheritance, className, name) && (!result.containsKey(className) || !result.get(className).containsKey(name))) { + Variable v = new Variable(false, name, null, className); + if (!result.containsKey(className)) { + result.put(className, new LinkedHashMap<>()); + } + result.get(className).put(name, v); + } + } + } + } + return true; + } + }; + visitor.visit(value); + value.visitRecursively(visitor); + } } - Variable v = new Variable(true, name, null, className); - result.get(className).put(name, v); } } } } - return true; - } - }; - visitor.visit(item); - item.visitRecursively(visitor); - } - }); - - - //Removed cached version of classes - allow reparsing using detected uninitialized fields - for (String key : asms.keySet()) { - if (CancellableWorker.isInterrupted()) { + + }); + + if (worker.isCancelled()) { throw new InterruptedException(); - } - ASMSource asm = asms.get(key); - if (asm instanceof DoInitActionTag) { - DoInitActionTag doi = (DoInitActionTag) asm; - String exportName = doi.getSwf().getCharacter(doi.getCharacterId()).getExportName(); - if (exportName != null && exportName.startsWith("__Packages.")) { - SWF.uncache(doi); + } + + //Complete inheritance tree + for (String className : classInheritance.keySet()) { + for (int i = 0; i < classInheritance.get(className).size(); i++) { + String parentClass = classInheritance.get(className).get(i); + if (classInheritance.containsKey(parentClass)) { + for (String p : classInheritance.get(parentClass)) { + if (!classInheritance.get(className).contains(p)) { + classInheritance.get(className).add(p); + } + } + } } } - } - /*for (String cls:result.keySet()) { - System.err.println("class "+cls); - for(String name:result.get(cls).keySet()) { - System.err.println("- " +result.get(cls).get(name)); + if (worker.isCancelled()) { + throw new InterruptedException(); } - }*/ - return result; + + //getting static classname.x assigns + asms.entrySet().parallelStream().forEach(entry -> { + if (worker.isCancelled()) { + return; + } + subThreads.add(Thread.currentThread()); + fireProgress(entry.getValue(), entry.getKey()); + String key = entry.getKey(); + ASMSource asm = asms.get(key); + //fireProgress(asm, key); + List tree = new ArrayList<>(); + if (cachedTrees.containsKey(key)) { + tree = cachedTrees.get(key); + } else { + try { + tree = asm.getActionsToTree(); + } catch (Throwable t) { + //ignore + } + } + for (GraphTargetItem item : tree) { + AbstractGraphTargetVisitor visitor = new AbstractGraphTargetVisitor() { + @Override + public boolean visit(GraphTargetItem item) { + if ((item instanceof SetMemberActionItem) + || (item instanceof CallMethodActionItem) + || (item instanceof NewMethodActionItem) + || (item instanceof DeleteActionItem) + || (item instanceof GetMemberActionItem)) { + List path = getFullPath(item); + if (path != null) { + List parent = new ArrayList<>(path); + parent.remove(parent.size() - 1); + String name = path.get(path.size() - 1); + + String className = String.join(".", parent); + if (classInheritance.containsKey(className)) { + //it's a class + if (!containsTrait(classTraits, classInheritance, className, name) && (!result.containsKey(className) || !result.get(className).containsKey(name))) { + if (!result.containsKey(className)) { + result.put(className, new LinkedHashMap<>()); + } + Variable v = new Variable(true, name, null, className); + result.get(className).put(name, v); + } + } + } + } + return true; + } + }; + visitor.visit(item); + item.visitRecursively(visitor); + } + }); + + if (worker.isCancelled()) { + throw new InterruptedException(); + } + + + /*for (String cls:result.keySet()) { + System.err.println("class "+cls); + for(String name:result.get(cls).keySet()) { + System.err.println("- " +result.get(cls).get(name)); + } + }*/ + if (worker.isCancelled()) { + throw new InterruptedException(); + } + return result; + } finally { + //Removed cached version of classes - allow reparsing using detected uninitialized fields + for (String key : asms.keySet()) { + ASMSource asm = asms.get(key); + if (asm instanceof DoInitActionTag) { + DoInitActionTag doi = (DoInitActionTag) asm; + String exportName = doi.getSwf().getCharacter(doi.getCharacterId()).getExportName(); + if (exportName != null && exportName.startsWith("__Packages.")) { + SWF.uncache(doi); + } + } + } + worker.removeCancelListener(cancelListener); + } } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf4/ActionIf.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf4/ActionIf.java index 538e383b4..8651f142c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf4/ActionIf.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf4/ActionIf.java @@ -19,6 +19,7 @@ package com.jpexs.decompiler.flash.action.swf4; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.ActionGraphSource; import com.jpexs.decompiler.flash.action.ActionList; import com.jpexs.decompiler.flash.action.LocalDataArea; import com.jpexs.decompiler.flash.action.parser.ActionParseException; @@ -178,8 +179,8 @@ public class ActionIf extends Action { long targetAddress = getTargetAddress(); int jmp = code.adr2pos(targetAddress); int after = code.adr2pos(getAddress() + length); - if (jmp == -1) { - Logger.getLogger(ActionIf.class.getName()).log(Level.SEVERE, "Invalid IF jump to ofs{0}", Helper.formatAddress(targetAddress)); + if (jmp == -1) { + Logger.getLogger(ActionIf.class.getName()).log(Level.SEVERE, "Invalid IF jump to ofs{0} from ofs{1} in {2}", new Object[]{Helper.formatAddress(targetAddress), Helper.formatAddress(getAddress()), ((ActionGraphSource) code).getPath()}); ret.add(after); } else { ret.add(jmp); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf4/ActionJump.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf4/ActionJump.java index ff7688fe3..3207c5a5c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf4/ActionJump.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf4/ActionJump.java @@ -19,6 +19,7 @@ package com.jpexs.decompiler.flash.action.swf4; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.ActionGraphSource; import com.jpexs.decompiler.flash.action.ActionList; import com.jpexs.decompiler.flash.action.LocalDataArea; import com.jpexs.decompiler.flash.action.parser.ActionParseException; @@ -168,8 +169,8 @@ public class ActionJump extends Action { int ofs = code.adr2pos(targetAddress); if (ofs == -1) { int length = getBytesLength(); - ofs = code.adr2pos(getAddress() + length); - Logger.getLogger(ActionJump.class.getName()).log(Level.SEVERE, "Invalid jump to ofs{0} from ofs{1}", new Object[]{Helper.formatAddress(targetAddress), Helper.formatAddress(getAddress())}); + ofs = code.adr2pos(getAddress() + length); + Logger.getLogger(ActionJump.class.getName()).log(Level.SEVERE, "Invalid jump to ofs{0} from ofs{1} in {2}", new Object[]{Helper.formatAddress(targetAddress), Helper.formatAddress(getAddress()), ((ActionGraphSource) code).getPath()}); } ret.add(ofs); return ret; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DoABC2Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DoABC2Tag.java index 536ae01a5..a1393b986 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DoABC2Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DoABC2Tag.java @@ -62,7 +62,7 @@ public class DoABC2Tag extends Tag implements ABCContainerTag { /** * The name assigned to the bytecode. - */ + */ public String name; /** diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DoActionTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DoActionTag.java index c43cb6a52..5b91e6ffa 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DoActionTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DoActionTag.java @@ -255,7 +255,7 @@ public class DoActionTag extends Tag implements ASMSource { @Override public List getActionsToTree() { try { - return Action.actionsToTree(new HashMap<>(), false, false, getActions(), swf.version, 0, "", swf.getCharset()); + return Action.actionsToTree(new HashMap<>(), false, false, getActions(), swf.version, 0, getScriptName(), swf.getCharset()); } catch (InterruptedException ex) { return new ArrayList<>(); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DoInitActionTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DoInitActionTag.java index 3efaa9274..0db3f1358 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DoInitActionTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DoInitActionTag.java @@ -297,7 +297,7 @@ public class DoInitActionTag extends Tag implements CharacterIdTag, ASMSource { @Override public List getActionsToTree() { try { - return Action.actionsToTree(swf.getUninitializedAs2ClassTraits(), true, false, getActions(), swf.version, 0, "", swf.getCharset()); + return Action.actionsToTree(swf.getUninitializedAs2ClassTraits(), true, false, getActions(), swf.version, 0, getScriptName(), swf.getCharset()); } catch (InterruptedException ex) { return new ArrayList<>(); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject2Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject2Tag.java index 37eea0e2f..7dd12bed5 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject2Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject2Tag.java @@ -30,9 +30,11 @@ import com.jpexs.decompiler.flash.types.ColorTransform; import com.jpexs.decompiler.flash.types.MATRIX; import com.jpexs.decompiler.flash.types.RGBA; import com.jpexs.decompiler.flash.types.annotations.Conditional; +import com.jpexs.decompiler.flash.types.annotations.DottedIdentifier; import com.jpexs.decompiler.flash.types.annotations.SWFType; import com.jpexs.decompiler.flash.types.annotations.SWFVersion; import com.jpexs.decompiler.flash.types.filters.FILTER; +import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.helpers.ByteArrayRange; import java.io.IOException; import java.util.ArrayList; @@ -129,6 +131,7 @@ public class PlaceObject2Tag extends PlaceObjectTypeTag implements ASMSourceCont * If PlaceFlagHasName, Name of character */ @Conditional("placeFlagHasName") + @DottedIdentifier public String name; /** @@ -455,7 +458,7 @@ public class PlaceObject2Tag extends PlaceObjectTypeTag implements ASMSourceCont public Map getNameProperties() { Map ret = super.getNameProperties(); if (placeFlagHasName) { - ret.put("nm", "" + name); + ret.put("nm", DottedChain.parseNoSuffix(name).toPrintableString(false)); } return ret; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject3Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject3Tag.java index 6cd02ded8..162b3c6df 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject3Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject3Tag.java @@ -32,6 +32,7 @@ import com.jpexs.decompiler.flash.types.ColorTransform; import com.jpexs.decompiler.flash.types.MATRIX; import com.jpexs.decompiler.flash.types.RGBA; import com.jpexs.decompiler.flash.types.annotations.Conditional; +import com.jpexs.decompiler.flash.types.annotations.DottedIdentifier; import com.jpexs.decompiler.flash.types.annotations.EnumValue; import com.jpexs.decompiler.flash.types.annotations.Internal; import com.jpexs.decompiler.flash.types.annotations.Reserved; @@ -39,6 +40,7 @@ import com.jpexs.decompiler.flash.types.annotations.SWFArray; import com.jpexs.decompiler.flash.types.annotations.SWFType; import com.jpexs.decompiler.flash.types.annotations.SWFVersion; import com.jpexs.decompiler.flash.types.filters.FILTER; +import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.helpers.ByteArrayRange; import java.io.IOException; import java.util.ArrayList; @@ -179,6 +181,7 @@ public class PlaceObject3Tag extends PlaceObjectTypeTag implements ASMSourceCont * If PlaceFlagHasName, Name of character */ @Conditional("placeFlagHasName") + @DottedIdentifier public String name; /** @@ -695,7 +698,7 @@ public class PlaceObject3Tag extends PlaceObjectTypeTag implements ASMSourceCont public Map getNameProperties() { Map ret = super.getNameProperties(); if (placeFlagHasName) { - ret.put("nm", "" + name); + ret.put("nm", DottedChain.parseNoSuffix(name).toPrintableString(false)); } return ret; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java index d51690907..031eae7a5 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java @@ -33,6 +33,7 @@ import com.jpexs.decompiler.flash.types.ColorTransform; import com.jpexs.decompiler.flash.types.MATRIX; import com.jpexs.decompiler.flash.types.RGBA; import com.jpexs.decompiler.flash.types.annotations.Conditional; +import com.jpexs.decompiler.flash.types.annotations.DottedIdentifier; import com.jpexs.decompiler.flash.types.annotations.EnumValue; import com.jpexs.decompiler.flash.types.annotations.Internal; import com.jpexs.decompiler.flash.types.annotations.Reserved; @@ -40,6 +41,7 @@ import com.jpexs.decompiler.flash.types.annotations.SWFArray; import com.jpexs.decompiler.flash.types.annotations.SWFType; import com.jpexs.decompiler.flash.types.annotations.SWFVersion; import com.jpexs.decompiler.flash.types.filters.FILTER; +import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.helpers.ByteArrayRange; import java.io.IOException; import java.util.ArrayList; @@ -182,6 +184,7 @@ public class PlaceObject4Tag extends PlaceObjectTypeTag implements ASMSourceCont * If PlaceFlagHasName, Name of character */ @Conditional("placeFlagHasName") + @DottedIdentifier public String name; /** @@ -708,7 +711,7 @@ public class PlaceObject4Tag extends PlaceObjectTypeTag implements ASMSourceCont public Map getNameProperties() { Map ret = super.getNameProperties(); if (placeFlagHasName) { - ret.put("nm", "" + name); + ret.put("nm", DottedChain.parseNoSuffix(name).toPrintableString(false)); } return ret; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ButtonAction.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ButtonAction.java index a108c70c4..efc027968 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ButtonAction.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ButtonAction.java @@ -219,7 +219,7 @@ public class ButtonAction implements ASMSource { @Override public List getActionsToTree() { try { - return Action.actionsToTree(new HashMap<>(), false, false, getActions(), buttonTag.getSwf().version, 0, "", buttonTag.getSwf().getCharset()); + return Action.actionsToTree(new HashMap<>(), false, false, getActions(), buttonTag.getSwf().version, 0, getScriptName(), buttonTag.getSwf().getCharset()); } catch (InterruptedException ex) { return new ArrayList<>(); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/BUTTONCONDACTION.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/BUTTONCONDACTION.java index 54f763c94..ab1e9f8c1 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/BUTTONCONDACTION.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/BUTTONCONDACTION.java @@ -393,7 +393,7 @@ public class BUTTONCONDACTION implements ASMSource, Serializable, HasSwfAndTag { @Override public List getActionsToTree() { try { - return Action.actionsToTree(new HashMap<>(), false, false, getActions(), swf.version, 0, "", swf.getCharset()); + return Action.actionsToTree(new HashMap<>(), false, false, getActions(), swf.version, 0, getScriptName(), swf.getCharset()); } catch (InterruptedException ex) { return new ArrayList<>(); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/CLIPACTIONRECORD.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/CLIPACTIONRECORD.java index 1ebcbbd33..456a8bfe2 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/CLIPACTIONRECORD.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/CLIPACTIONRECORD.java @@ -405,7 +405,7 @@ public class CLIPACTIONRECORD implements ASMSource, Serializable, HasSwfAndTag { @Override public List getActionsToTree() { try { - return Action.actionsToTree(new HashMap<>(), false, false, getActions(), swf.version, 0, "", swf.getCharset()); + return Action.actionsToTree(new HashMap<>(), false, false, getActions(), swf.version, 0, getScriptName(), swf.getCharset()); } catch (InterruptedException ex) { return new ArrayList<>(); } diff --git a/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java b/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java index 9ed4364a0..9d4e6c89f 100644 --- a/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java @@ -527,7 +527,11 @@ public class ActionPanel extends JPanel implements SearchListener