diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2ConstantPool.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2ConstantPool.java index eb7e52f7c..48d3eef55 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2ConstantPool.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2ConstantPool.java @@ -88,7 +88,19 @@ public class AVM2ConstantPool implements Cloneable { @Internal public Map dottedChainCache = new HashMap<>(); + + @Internal + public Map multinameWithNamespaceCache = new HashMap<>(); + + public DottedChain getCachedMultinameWithNamespace(Multiname multiName) { + return multinameWithNamespaceCache.get(multiName); + } + + public void cacheMultinameWithNamespace(Multiname multiName, DottedChain multinameWithNamespace) { + multinameWithNamespaceCache.put(multiName, multinameWithNamespace); + } + private void ensureDefault(List list) { if (list.isEmpty()) { list.add(null); @@ -681,6 +693,7 @@ public class AVM2ConstantPool implements Cloneable { ret.constant_namespace_set = new HashArrayList<>(constant_namespace_set); ret.constant_multiname = new HashArrayList<>(constant_multiname); ret.dottedChainCache = new HashMap<>(); + ret.multinameWithNamespaceCache = new HashMap<>(); return ret; } catch (CloneNotSupportedException ex) { throw new RuntimeException(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AVM2SourceGenerator.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AVM2SourceGenerator.java index 442d0c7eb..d618e4ffc 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AVM2SourceGenerator.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AVM2SourceGenerator.java @@ -111,6 +111,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; @@ -2787,7 +2788,7 @@ public class AVM2SourceGenerator implements SourceGenerator { if (m != null) { Namespace ns = ci.abc.instance_info.get(ci.index).getName(ci.abc.constants).getNamespace(ci.abc.constants); String n = m.getName(ci.abc.constants, new ArrayList<>(), true, true /*FIXME!!*/); - String nsn = ns == null ? null : ns.getName(ci.abc.constants).toRawString(); + String nsn = ns == null ? null : ns.getRawName(ci.abc.constants); name_index = constants.getQnameId( n, ns == null ? Namespace.KIND_PACKAGE : ns.kind, @@ -2795,10 +2796,11 @@ public class AVM2SourceGenerator implements SourceGenerator { } } + String pkgRaw = pkg.toRawString(); for (int i = 1; i < constants.getMultinameCount(); i++) { Multiname mname = constants.getMultiname(i); if (mname != null && name.equals(mname.getName(constants, null, true, true /*FIXME!!*/))) { - if (mname.getNamespace(constants) != null && pkg.equals(mname.getNamespace(constants).getName(constants))) { + if (mname.getNamespace(constants) != null && Objects.equals(pkgRaw, mname.getNamespace(constants).getRawName(constants))){ name_index = i; break; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AbcIndexing.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AbcIndexing.java index 0906df45b..8bf72e2f9 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AbcIndexing.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AbcIndexing.java @@ -536,29 +536,29 @@ public final class AbcIndexing { */ //search all static first if (findStatic && classProperties.containsKey(prop)) { - if (!classProperties.containsKey(prop)) { - if (parent != null) { - TraitIndex ret = parent.findProperty(prop, findStatic, findInstance, findProtected); - if (ret != null) { - return ret; - } - } - } else { - return classProperties.get(prop); + TraitIndex ti = classProperties.get(prop); + if (ti != null) { + return ti; } + if (parent != null) { + TraitIndex ret = parent.findProperty(prop, findStatic, findInstance, findProtected); + if (ret != null) { + return ret; + } + } } //now search instance if (findInstance && instanceProperties.containsKey(prop)) { - if (!instanceProperties.containsKey(prop)) { - if (parent != null) { - TraitIndex ret = parent.findProperty(prop, findStatic, findInstance, findProtected); - if (ret != null) { - return ret; - } + TraitIndex ti = instanceProperties.get(prop); + if (ti != null) { + return ti; + } + if (parent != null) { + TraitIndex ret = parent.findProperty(prop, findStatic, findInstance, findProtected); + if (ret != null) { + return ret; } - } else { - return instanceProperties.get(prop); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/Multiname.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/Multiname.java index 61b24ccf7..b2084d3e6 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/Multiname.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/Multiname.java @@ -406,6 +406,10 @@ public class Multiname { } public DottedChain getNameWithNamespace(AVM2ConstantPool constants, boolean withSuffix) { + DottedChain cached = constants.getCachedMultinameWithNamespace(this); + if (cached != null) { + return cached; + } Namespace ns = getNamespace(constants); if (ns == null) { NamespaceSet nss = getNamespaceSet(constants); @@ -416,10 +420,14 @@ public class Multiname { } } String name = getName(constants, null, true, false); + DottedChain ret; if (ns != null) { - return ns.getName(constants).add(name, withSuffix ? getNamespaceSuffix() : ""); + ret = ns.getName(constants).add(name, withSuffix ? getNamespaceSuffix() : ""); + } else { + ret = new DottedChain(new String[]{name}, new String[]{withSuffix ? getNamespaceSuffix() : ""}); } - return new DottedChain(new String[]{name}, new String[]{withSuffix ? getNamespaceSuffix() : ""}); + constants.cacheMultinameWithNamespace(this, ret); + return ret; } public Namespace getNamespace(AVM2ConstantPool constants) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/DottedChain.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/DottedChain.java index 1861bea78..aa4131067 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/DottedChain.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/DottedChain.java @@ -23,6 +23,7 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; /** * @@ -31,7 +32,7 @@ import java.util.List; public class DottedChain implements Serializable, Comparable { public static final DottedChain EMPTY = new DottedChain(true); - + public static final DottedChain UNBOUNDED = new DottedChain(new String[]{"*"}); public static final DottedChain TOPLEVEL = new DottedChain(new String[]{}); @@ -64,30 +65,21 @@ public class DottedChain implements Serializable, Comparable { public static final DottedChain ALL = new DottedChain(new String[]{"*"}); - private final String[] parts; - - private final boolean[] attributes; - - private final int length; - - private final int hash; + private List parts; private boolean isNull = false; - private String[] namespaceSuffixes; - public String getNamespaceSuffix(int index) { - return namespaceSuffixes[index]; + return parts.get(index).namespaceSuffix; } public String getLastNamespaceSuffix() { - if (length == 0) { + if (parts.isEmpty()) { return ""; } - return namespaceSuffixes[length - 1]; + return parts.get(parts.size() - 1).namespaceSuffix; } - public static final DottedChain parseNoSuffix(String name) { if (name == null) { return DottedChain.EMPTY; @@ -95,18 +87,15 @@ public class DottedChain implements Serializable, Comparable { return DottedChain.TOPLEVEL; } else { String parts[] = name.split("\\."); - String nsSuffixes[] = new String[parts.length]; - int i = 0; + List newParts = new ArrayList<>(); for (String part : parts) { - String nameNoSuffix = part; - parts[i] = nameNoSuffix; - nsSuffixes[i] = ""; - i++; + newParts.add(new PathPart(part, false, "")); } - - return new DottedChain(parts, nsSuffixes); + + return new DottedChain(newParts, false); } } + public static final DottedChain parseWithSuffix(String name) { if (name == null) { return DottedChain.EMPTY; @@ -114,86 +103,63 @@ public class DottedChain implements Serializable, Comparable { return DottedChain.TOPLEVEL; } else { String parts[] = name.split("\\."); - String nsSuffixes[] = new String[parts.length]; - int i = 0; + List newParts = new ArrayList<>(); for (String part : parts) { String nameNoSuffix = part; String namespaceSuffix = ""; if (part.matches(".*#[0-9]+$")) { nameNoSuffix = part.substring(0, part.lastIndexOf("#")); namespaceSuffix = part.substring(part.lastIndexOf("#")); - } - parts[i] = nameNoSuffix; - nsSuffixes[i] = namespaceSuffix; - i++; + } + newParts.add(new PathPart(nameNoSuffix, false, namespaceSuffix)); } - - return new DottedChain(parts, nsSuffixes); + + return new DottedChain(newParts, false); } } private DottedChain(boolean isNull) { this.isNull = isNull; - this.parts = new String[0]; - this.length = 0; - this.hash = 0; - this.attributes = new boolean[0]; - this.namespaceSuffixes = new String[0]; + this.parts = new ArrayList<>(); } public DottedChain(DottedChain src) { - this(src.parts, src.namespaceSuffixes); + this.parts = new ArrayList<>(src.parts); this.isNull = src.isNull; } public DottedChain(String[] parts) { this(Arrays.asList(parts)); } - - public DottedChain(List parts) { - length = parts.size(); - this.parts = parts.toArray(new String[length]); - attributes = new boolean[length]; - hash = calcHash(); - this.namespaceSuffixes = new String[length]; - for(int i = 0; i < length; i++) { - namespaceSuffixes[i] = ""; - } + + private DottedChain(List parts, boolean isNull) { + this.parts = parts; + this.isNull = isNull; + } + + public DottedChain(List parts) { + List newParts = new ArrayList<>(); + for (String part : parts) { + newParts.add(new PathPart(part, false, "")); + } + this.parts = newParts; } - /*public DottedChain(String onePart, String namespaceSuffix) { - this(new String[]{onePart}, namespaceSuffix); - }*/ public DottedChain(String[] parts, String[] namespaceSuffixes) { this(new boolean[parts.length], parts, namespaceSuffixes); } - - public DottedChain(boolean attributes[], String[] parts, String[] namespaceSuffixes) { - if (parts.length == 1 && parts[0].isEmpty()) { - length = 0; - this.parts = new String[0]; - } else { - length = parts.length; - this.parts = parts; - } - this.attributes = attributes; - hash = calcHash(); - this.namespaceSuffixes = namespaceSuffixes; - } - private DottedChain(String[] parts, int length) { - this.length = length; - this.parts = parts; - attributes = new boolean[length]; - this.namespaceSuffixes = new String[length]; - for(int i = 0; i < length; i++) { - namespaceSuffixes[i] = ""; + public DottedChain(boolean attributes[], String[] parts, String[] namespaceSuffixes) { + List newParts = new ArrayList<>(); + for (int i = 0; i < attributes.length; i++) { + newParts.add(new PathPart(parts[i], attributes[i], namespaceSuffixes[i])); } - hash = calcHash(); + this.parts = newParts; + } public boolean isTopLevel() { - return !isNull && length == 0; + return !isNull && parts.isEmpty(); } public boolean isEmpty() { @@ -201,51 +167,51 @@ public class DottedChain implements Serializable, Comparable { } public int size() { - return length; + return parts.size(); } public String get(int index) { - if (index >= length) { + if (index >= parts.size()) { throw new ArrayIndexOutOfBoundsException(); } - return parts[index]; + return parts.get(index).name; } - + public boolean isAttribute(int index) { - if (index >= length) { + if (index >= parts.size()) { throw new ArrayIndexOutOfBoundsException(); } - return attributes[index]; + return parts.get(index).attribute; } public DottedChain subChain(int count) { - if (count > length) { + if (count > parts.size()) { throw new ArrayIndexOutOfBoundsException(); } - return new DottedChain(parts, count); + return new DottedChain(new ArrayList<>(parts.subList(0, count)), isNull); } public String getLast() { if (isNull) { return null; } - if (length == 0) { + if (parts.isEmpty()) { return ""; } else { - return parts[length - 1]; + return parts.get(parts.size() - 1).name; } } - + public boolean isLastAttribute() { if (isNull) { return false; } - if (length == 0) { + if (parts.isEmpty()) { return false; } else { - return attributes[length - 1]; + return parts.get(parts.size() - 1).attribute; } } @@ -253,11 +219,11 @@ public class DottedChain implements Serializable, Comparable { if (isNull) { return null; } - if (length < 2) { + if (parts.size() < 2) { return EMPTY; } - return new DottedChain(parts, length - 1); + return subChain(parts.size() - 1); } public DottedChain addWithSuffix(String name) { @@ -273,49 +239,40 @@ public class DottedChain implements Serializable, Comparable { public DottedChain add(String name, String namespaceSuffix) { return add(false, name, namespaceSuffix); } + public DottedChain add(boolean attribute, String name, String namespaceSuffix) { if (name == null) { return new DottedChain(this); } - String[] nparts = new String[length + 1]; - boolean[] nattributes = new boolean[length + 1]; - String[] nnamespaceSuffixes = new String[length + 1]; - if (length > 0) { - System.arraycopy(parts, 0, nparts, 0, length); - System.arraycopy(attributes, 0, nattributes, 0, length); - System.arraycopy(namespaceSuffixes, 0, nnamespaceSuffixes, 0, length); - } - - nattributes[nattributes.length - 1] = attribute; - nparts[nparts.length - 1] = name; - nnamespaceSuffixes[nparts.length - 1] = namespaceSuffix; - return new DottedChain(nattributes, nparts, nnamespaceSuffixes); + List newParts = new ArrayList<>(parts); + newParts.add(new PathPart(name, attribute, namespaceSuffix)); + return new DottedChain(newParts, false); } protected String toString(boolean as3, boolean raw, boolean withSuffix) { if (isNull) { return ""; } - if (length == 0) { + if (parts.isEmpty()) { return ""; } StringBuilder ret = new StringBuilder(); - for (int i = 0; i < length; i++) { + for (int i = 0; i < parts.size(); i++) { if (i > 0) { ret.append("."); } - if (attributes[i]) { + if (parts.get(i).attribute) { ret.append("@"); } - String part = parts[i]; - boolean lastStar = i == length - 1 && "*".equals(part); + String part = parts.get(i).name; + boolean lastStar = i == parts.size() - 1 && "*".equals(part); ret.append((raw || lastStar) ? part : IdentifiersDeobfuscation.printIdentifier(as3, part)); if (withSuffix) { - ret.append(namespaceSuffixes[i]); + ret.append(parts.get(i).namespaceSuffix); } } - + return ret.toString(); } @@ -323,23 +280,27 @@ public class DottedChain implements Serializable, Comparable { if (isNull) { return ""; } - if (length == 0) { + if (parts.isEmpty()) { return ""; } StringBuilder ret = new StringBuilder(); - for (int i = 0; i < length; i++) { + for (int i = 0; i < parts.size(); i++) { if (i > 0) { ret.append(File.separator); } - ret.append(Helper.makeFileName(IdentifiersDeobfuscation.printIdentifier(true, parts[i]))); + ret.append(Helper.makeFileName(IdentifiersDeobfuscation.printIdentifier(true, parts.get(i).name))); } return ret.toString(); } public List toList() { - return new ArrayList<>(Arrays.asList(parts)); + List ret = new ArrayList<>(); + for (PathPart p : parts) { + ret.add(p.name); + } + return ret; } public String toPrintableString(boolean as3) { @@ -357,23 +318,17 @@ public class DottedChain implements Serializable, Comparable { @Override public int hashCode() { + int hash = 7; + hash = 41 * hash + Objects.hashCode(this.parts); + hash = 41 * hash + (this.isNull ? 1 : 0); return hash; } - private int calcHash() { - if (isNull) { - return 0; - } - int result = 1; - for (int i = 0; i < length; i++) { - result = 31 * result + parts[i].hashCode(); - } - - return result; - } - @Override public boolean equals(Object obj) { + if (this == obj) { + return true; + } if (obj == null) { return false; } @@ -381,32 +336,57 @@ public class DottedChain implements Serializable, Comparable { return false; } final DottedChain other = (DottedChain) obj; - if (isNull && other.isNull) { - return true; - } - if (length != other.length) { + if (this.isNull != other.isNull) { return false; } - - for (int i = 0; i < length; i++) { - String s1 = parts[i]; - String s2 = other.parts[i]; - if (!s1.equals(s2)) { - return false; - } - if (attributes[i] != other.attributes[i]) { - return false; - } - if (!namespaceSuffixes[i].equals(other.namespaceSuffixes[i])) { - return false; - } - } - - return true; + return Objects.equals(this.parts, other.parts); } @Override public int compareTo(DottedChain o) { return toRawString().compareTo(o.toRawString()); } + + private static class PathPart { + + public String name; + public boolean attribute; + public String namespaceSuffix; + + public PathPart(String name, boolean attribute, String namespaceSuffix) { + this.name = name; + this.attribute = attribute; + this.namespaceSuffix = namespaceSuffix; + } + + @Override + public int hashCode() { + int hash = 5; + hash = 79 * hash + Objects.hashCode(this.name); + hash = 79 * hash + (this.attribute ? 1 : 0); + hash = 79 * hash + Objects.hashCode(this.namespaceSuffix); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final PathPart other = (PathPart) obj; + if (this.attribute != other.attribute) { + return false; + } + if (!Objects.equals(this.name, other.name)) { + return false; + } + return Objects.equals(this.namespaceSuffix, other.namespaceSuffix); + } + } }