Added ABC Explorer - list of usages of all items

Added ABC Explorer - items with zero usages are semi-transparent
Added ABC Explorer - copy path to clipboard
Added ABC Explorer - Go to path via Ctrl + G
This commit is contained in:
Jindra Petřík
2024-07-28 18:54:32 +02:00
parent 8876f4332e
commit 7b609e08e8
13 changed files with 1480 additions and 295 deletions

View File

@@ -46,19 +46,10 @@ import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter;
import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst;
import com.jpexs.decompiler.flash.abc.types.traits.TraitType;
import com.jpexs.decompiler.flash.abc.types.traits.Traits;
import com.jpexs.decompiler.flash.abc.usages.multinames.ClassNameMultinameUsage;
import com.jpexs.decompiler.flash.abc.usages.multinames.ConstVarNameMultinameUsage;
import com.jpexs.decompiler.flash.abc.usages.multinames.ConstVarTypeMultinameUsage;
import com.jpexs.decompiler.flash.abc.usages.MultinameUsageDetector;
import com.jpexs.decompiler.flash.abc.usages.Usage;
import com.jpexs.decompiler.flash.abc.usages.multinames.DefinitionUsage;
import com.jpexs.decompiler.flash.abc.usages.multinames.MethodBodyMultinameUsage;
import com.jpexs.decompiler.flash.abc.usages.multinames.MethodNameMultinameUsage;
import com.jpexs.decompiler.flash.abc.usages.multinames.MethodParamsMultinameUsage;
import com.jpexs.decompiler.flash.abc.usages.multinames.MethodReturnTypeMultinameUsage;
import com.jpexs.decompiler.flash.abc.usages.multinames.MultinameUsage;
import com.jpexs.decompiler.flash.abc.usages.multinames.SuperClassMultinameUsage;
import com.jpexs.decompiler.flash.abc.usages.multinames.SuperInterfaceMultinameUsage;
import com.jpexs.decompiler.flash.abc.usages.multinames.TraitMultinameUsage;
import com.jpexs.decompiler.flash.abc.usages.multinames.TypeNameMultinameUsage;
import com.jpexs.decompiler.flash.dumpview.DumpInfo;
import com.jpexs.decompiler.flash.dumpview.DumpInfoSpecial;
import com.jpexs.decompiler.flash.dumpview.DumpInfoSpecialType;
@@ -1170,184 +1161,8 @@ public class ABC implements Openable {
for (int i = 0; i < bodies.size(); i++) {
output.println("MethodBody[" + i + "]:"); //+ bodies[i].toString(this, constants, method_info));
}
}
private void checkMultinameUsedInMethod(int multinameIndex, boolean exactMatch, int methodInfo, List<MultinameUsage> ret, int scriptIndex, int classIndex, int traitIndex, int traitsType, boolean isInitializer, Traits traits, int parentTraitIndex) {
for (int p = 0; p < method_info.get(methodInfo).param_types.length; p++) {
if (isSameName(multinameIndex, method_info.get(methodInfo).param_types[p], exactMatch)) {
ret.add(new MethodParamsMultinameUsage(this, multinameIndex, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex));
break;
}
}
if (isSameName(multinameIndex, method_info.get(methodInfo).ret_type, exactMatch)) {
ret.add(new MethodReturnTypeMultinameUsage(this, multinameIndex, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex));
}
MethodBody body = findBody(methodInfo);
if (body != null) {
findMultinameUsageInTraits(body.traits, multinameIndex, exactMatch, traitsType, scriptIndex, classIndex, ret, traitIndex);
for (ABCException e : body.exceptions) {
if ((isSameName(multinameIndex, e.name_index, exactMatch)) || (isSameName(multinameIndex, e.type_index, exactMatch))) {
ret.add(new MethodBodyMultinameUsage(this, multinameIndex, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex));
return;
}
}
for (AVM2Instruction ins : body.getCode().code) {
for (int o = 0; o < ins.definition.operands.length; o++) {
if (ins.definition.operands[o] == AVM2Code.DAT_MULTINAME_INDEX && ins.operands[o] < constants.getMultinameCount()) {
if (isSameName(multinameIndex, ins.operands[o], exactMatch)) {
ret.add(new MethodBodyMultinameUsage(this, multinameIndex, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex));
return;
}
}
}
}
}
}
private void checkAllMultinameUsedInMethod(int methodInfo, List<List<MultinameUsage>> ret, int scriptIndex, int classIndex, int traitIndex, int traitsType, boolean isInitializer, Traits traits, int parentTraitIndex) {
boolean[] foundMultinames = new boolean[constants.getMultinameCount()];
for (int p = 0; p < method_info.get(methodInfo).param_types.length; p++) {
int methodParamsMultinameIndex = method_info.get(methodInfo).param_types[p];
if (!foundMultinames[methodParamsMultinameIndex]) {
ret.get(methodParamsMultinameIndex).add(new MethodParamsMultinameUsage(this, methodParamsMultinameIndex, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex));
foundMultinames[methodParamsMultinameIndex] = true;
}
}
int methodReturnTypeMultinameIndex = method_info.get(methodInfo).ret_type;
ret.get(methodReturnTypeMultinameIndex).add(new MethodReturnTypeMultinameUsage(this, methodReturnTypeMultinameIndex, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex));
MethodBody body = findBody(methodInfo);
if (body != null) {
findAllMultinameUsageInTraits(body.traits, traitsType, scriptIndex, classIndex, ret, traitIndex);
foundMultinames = new boolean[constants.getMultinameCount()];
for (ABCException e : body.exceptions) {
if (!foundMultinames[e.name_index]) {
ret.get(e.name_index).add(new MethodBodyMultinameUsage(this, e.name_index, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex));
foundMultinames[e.name_index] = true;
}
if (!foundMultinames[e.type_index]) {
ret.get(e.type_index).add(new MethodBodyMultinameUsage(this, e.type_index, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex));
foundMultinames[e.type_index] = true;
}
}
for (AVM2Instruction ins : body.getCode().code) {
for (int o = 0; o < ins.definition.operands.length; o++) {
if (ins.definition.operands[o] == AVM2Code.DAT_MULTINAME_INDEX) {
int mi = ins.operands[o];
if (mi < foundMultinames.length && !foundMultinames[mi]) {
ret.get(mi).add(new MethodBodyMultinameUsage(this, mi, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex));
foundMultinames[mi] = true;
}
}
}
}
}
}
private boolean isSameName(int expectedQNameIndex, int checkedNameIndex, boolean exactMatch) {
if (expectedQNameIndex == checkedNameIndex) {
return true;
}
if (exactMatch) {
return false;
}
Multiname expectedQName = constants.getMultiname(expectedQNameIndex);
Multiname checkedName = constants.getMultiname(checkedNameIndex);
if (checkedName == null) {
return false;
}
if (expectedQName.name_index != checkedName.name_index) {
return false;
}
if (checkedName.kind == Multiname.QNAME) {
return expectedQName.namespace_index == checkedName.namespace_index;
}
if (checkedName.kind != Multiname.MULTINAME) {
return false;
}
for (int ns : constants.getNamespaceSet(checkedName.namespace_set_index).namespaces) {
if (ns == expectedQName.namespace_index) {
return true;
}
}
return false;
}
private void findMultinameUsageInTraits(Traits traits, int multinameIndex, boolean exactMatch, int traitsType, int scriptIndex, int classIndex, List<MultinameUsage> ret, int parentTraitIndex) {
for (int t = 0; t < traits.traits.size(); t++) {
if (traits.traits.get(t) instanceof TraitClass) {
TraitClass tc = (TraitClass) traits.traits.get(t);
if (isSameName(multinameIndex, tc.name_index, exactMatch)) {
ret.add(new ClassNameMultinameUsage(this, multinameIndex, tc.class_info, scriptIndex));
}
int c = tc.class_info;
if (isSameName(multinameIndex, instance_info.get(c).super_index, exactMatch)) {
ret.add(new SuperClassMultinameUsage(this, multinameIndex, c, scriptIndex));
}
for (int i = 0; i < instance_info.get(c).interfaces.length; i++) {
if (isSameName(multinameIndex, instance_info.get(c).interfaces[i], exactMatch)) {
ret.add(new SuperInterfaceMultinameUsage(this, multinameIndex, c, scriptIndex));
}
}
checkMultinameUsedInMethod(multinameIndex, exactMatch, instance_info.get(c).iinit_index, ret, -1/*FIXME*/, c, 0, TraitMultinameUsage.TRAITS_TYPE_INSTANCE, true, null, -1);
checkMultinameUsedInMethod(multinameIndex, exactMatch, class_info.get(c).cinit_index, ret, -1/*FIXME*/, c, 0, TraitMultinameUsage.TRAITS_TYPE_CLASS, true, null, -1);
findMultinameUsageInTraits(instance_info.get(c).instance_traits, multinameIndex, exactMatch, TraitMultinameUsage.TRAITS_TYPE_INSTANCE, -1/*FIXME*/, c, ret, -1);
findMultinameUsageInTraits(class_info.get(c).static_traits, multinameIndex, exactMatch, TraitMultinameUsage.TRAITS_TYPE_CLASS, -1/*FIXME*/, c, ret, -1);
}
if (traits.traits.get(t) instanceof TraitSlotConst) {
TraitSlotConst tsc = (TraitSlotConst) traits.traits.get(t);
if (isSameName(multinameIndex, tsc.name_index, exactMatch)) {
ret.add(new ConstVarNameMultinameUsage(this, multinameIndex, scriptIndex, classIndex, t, traitsType, traits, parentTraitIndex));
}
if (isSameName(multinameIndex, tsc.type_index, exactMatch)) {
ret.add(new ConstVarTypeMultinameUsage(this, multinameIndex, scriptIndex, classIndex, t, traitsType, traits, parentTraitIndex));
}
}
if (traits.traits.get(t) instanceof TraitMethodGetterSetter) {
TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) traits.traits.get(t);
if (isSameName(multinameIndex, tmgs.name_index, exactMatch)) {
ret.add(new MethodNameMultinameUsage(this, multinameIndex, scriptIndex, classIndex, t, traitsType, false, traits, parentTraitIndex));
}
checkMultinameUsedInMethod(multinameIndex, exactMatch, tmgs.method_info, ret, scriptIndex, classIndex, t, traitsType, false, traits, parentTraitIndex);
}
}
}
private void findAllMultinameUsageInTraits(Traits traits, int traitsType, int scriptIndex, int classIndex, List<List<MultinameUsage>> ret, int parentTraitIndex) {
for (int t = 0; t < traits.traits.size(); t++) {
if (traits.traits.get(t) instanceof TraitClass) {
TraitClass tc = (TraitClass) traits.traits.get(t);
ret.get(tc.name_index).add(new ClassNameMultinameUsage(this, tc.name_index, tc.class_info, scriptIndex));
int c = tc.class_info;
int classNameMultinameIndex = instance_info.get(c).name_index;
ret.get(classNameMultinameIndex).add(new ClassNameMultinameUsage(this, classNameMultinameIndex, c, scriptIndex));
int extendsMultinameIndex = instance_info.get(c).super_index;
ret.get(extendsMultinameIndex).add(new SuperClassMultinameUsage(this, extendsMultinameIndex, c, scriptIndex));
for (int i = 0; i < instance_info.get(c).interfaces.length; i++) {
int implementsMultinameIndex = instance_info.get(c).interfaces[i];
ret.get(implementsMultinameIndex).add(new SuperInterfaceMultinameUsage(this, implementsMultinameIndex, c, scriptIndex));
}
checkAllMultinameUsedInMethod(instance_info.get(c).iinit_index, ret, -1/*FIXME*/, c, 0, TraitMultinameUsage.TRAITS_TYPE_INSTANCE, true, null, -1);
checkAllMultinameUsedInMethod(class_info.get(c).cinit_index, ret, -1/*FIXME*/, c, 0, TraitMultinameUsage.TRAITS_TYPE_CLASS, true, null, -1);
findAllMultinameUsageInTraits(instance_info.get(c).instance_traits, TraitMultinameUsage.TRAITS_TYPE_INSTANCE, -1/*FIXME*/, c, ret, -1);
findAllMultinameUsageInTraits(class_info.get(c).static_traits, TraitMultinameUsage.TRAITS_TYPE_CLASS, -1/*FIXME*/, c, ret, -1);
}
if (traits.traits.get(t) instanceof TraitSlotConst) {
TraitSlotConst tsc = (TraitSlotConst) traits.traits.get(t);
ret.get(tsc.name_index).add(new ConstVarNameMultinameUsage(this, tsc.name_index, scriptIndex, classIndex, t, traitsType, traits, parentTraitIndex));
ret.get(tsc.type_index).add(new ConstVarTypeMultinameUsage(this, tsc.type_index, scriptIndex, classIndex, t, traitsType, traits, parentTraitIndex));
}
if (traits.traits.get(t) instanceof TraitMethodGetterSetter) {
TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) traits.traits.get(t);
ret.get(tmgs.name_index).add(new MethodNameMultinameUsage(this, tmgs.name_index, scriptIndex, classIndex, t, traitsType, false, traits, parentTraitIndex));
checkAllMultinameUsedInMethod(tmgs.method_info, ret, scriptIndex, classIndex, t, traitsType, false, traits, parentTraitIndex);
}
}
}
}
public List<MultinameUsage> findMultinameDefinition(int multinameIndex) {
List<MultinameUsage> usages = findMultinameUsage(multinameIndex, false);
@@ -1369,6 +1184,16 @@ public class ABC implements Openable {
}
return ret;
}
public List<MultinameUsage> findMultinameUsage(int multinameIndex, boolean exactMatch) {
MultinameUsageDetector det = new MultinameUsageDetector();
List<Usage> retUsage = det.findMultinameUsage(this, multinameIndex, exactMatch);
List<MultinameUsage> ret = new ArrayList<>();
for (Usage u : retUsage) {
ret.add((MultinameUsage) u);
}
return ret;
}
/**
* Gets colliding usages of multinames. For example same name
@@ -1397,20 +1222,23 @@ public class ABC implements Openable {
}
Set<MultinameUsage> collidingUsages = new HashSet<>();
MultinameUsageDetector det = new MultinameUsageDetector();
// find context of names with count 2 or more
List<List<MultinameUsage>> usagesList = findAllMultinameUsage();
List<List<Usage>> usagesList = det.findAllUsage(this);
for (String name : nameToQNameIndices.keySet()) {
List<Integer> multinameIndices = nameToQNameIndices.get(name);
if (multinameIndices.size() > 1) {
List<List<MultinameUsage>> allUsages = new ArrayList<>();
List<List<Usage>> allUsages = new ArrayList<>();
for (int multinameIndex : multinameIndices) {
List<MultinameUsage> usages = usagesList.get(multinameIndex);
for (MultinameUsage usage : usages) {
for (List<MultinameUsage> prevUsages : allUsages) {
for (MultinameUsage prevUsage : prevUsages) {
if (prevUsage.collides(usage)) {
collidingUsages.add(usage);
collidingUsages.add(prevUsage);
List<Usage> usages = usagesList.get(multinameIndex);
for (Usage usage : usages) {
for (List<Usage> prevUsages : allUsages) {
for (Usage prevUsage : prevUsages) {
MultinameUsage usageM = (MultinameUsage) usage;
MultinameUsage prevUsageM = (MultinameUsage) prevUsage;
if (prevUsageM.collides(usageM)) {
collidingUsages.add(usageM);
collidingUsages.add(prevUsageM);
}
}
}
@@ -1442,63 +1270,7 @@ public class ABC implements Openable {
}
}
public List<MultinameUsage> findMultinameUsage(int multinameIndex, boolean exactMatch) {
List<MultinameUsage> ret = new ArrayList<>();
if (multinameIndex == 0) {
return ret;
}
for (int s = 0; s < script_info.size(); s++) {
checkMultinameUsedInMethod(multinameIndex, exactMatch, script_info.get(s).init_index, ret, s, -1, 0, TraitMultinameUsage.TRAITS_TYPE_SCRIPT, true, null, -1);
findMultinameUsageInTraits(script_info.get(s).traits, multinameIndex, exactMatch, TraitMultinameUsage.TRAITS_TYPE_SCRIPT, s, -1, ret, -1);
}
loopm:
for (int t = 1; t < constants.getMultinameCount(); t++) {
Multiname multiname = constants.getMultiname(t);
if (multiname.kind == Multiname.TYPENAME) {
if (multiname.qname_index == multinameIndex) {
ret.add(new TypeNameMultinameUsage(this, multinameIndex, t, -1));
continue;
}
for (int mp : multiname.params) {
if (mp == multinameIndex) {
ret.add(new TypeNameMultinameUsage(this, multinameIndex, t, -1));
continue loopm;
}
}
}
}
return ret;
}
public List<List<MultinameUsage>> findAllMultinameUsage() {
List<List<MultinameUsage>> ret = new ArrayList<>();
for (int i = 0; i < constants.getMultinameCount(); i++) {
ret.add(new ArrayList<>());
}
for (int s = 0; s < script_info.size(); s++) {
checkAllMultinameUsedInMethod(script_info.get(s).init_index, ret, s, -1, 0, TraitMultinameUsage.TRAITS_TYPE_SCRIPT, true, null, -1);
findAllMultinameUsageInTraits(script_info.get(s).traits, TraitMultinameUsage.TRAITS_TYPE_SCRIPT, s, -1, ret, -1);
}
boolean[] foundMultinames = new boolean[constants.getMultinameCount()];
for (int t = 1; t < constants.getMultinameCount(); t++) {
Multiname multiname = constants.getMultiname(t);
if (multiname.kind == Multiname.TYPENAME) {
if (!foundMultinames[multiname.qname_index]) {
ret.get(multiname.qname_index).add(new TypeNameMultinameUsage(this, multiname.qname_index, t, -1));
foundMultinames[multiname.qname_index] = true;
}
for (int mp : multiname.params) {
if (!foundMultinames[mp]) {
ret.get(mp).add(new TypeNameMultinameUsage(this, mp, t, -1));
foundMultinames[mp] = true;
}
}
}
}
return ret;
}
public int findMethodInfoByName(int classId, String methodNameWithSuffix) {
if (classId > -1) {
@@ -2503,5 +2275,5 @@ public class ABC implements Openable {
public long getDataSize() {
return dataSize;
}
}
}

View File

@@ -36,6 +36,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
/**
@@ -870,5 +871,5 @@ public class AVM2ConstantPool implements Cloneable {
for (int i = 0; i < constant_multiname.size(); i++) {
Multiname.checkTypeNameCyclic(this, i);
}
}
}
}

View File

@@ -0,0 +1,301 @@
/*
* Copyright (C) 2010-2024 JPEXS, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
package com.jpexs.decompiler.flash.abc.usages;
import com.jpexs.decompiler.flash.abc.ABC;
import com.jpexs.decompiler.flash.abc.avm2.AVM2Code;
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction;
import com.jpexs.decompiler.flash.abc.types.ABCException;
import com.jpexs.decompiler.flash.abc.types.MethodBody;
import com.jpexs.decompiler.flash.abc.types.Multiname;
import com.jpexs.decompiler.flash.abc.types.traits.TraitClass;
import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter;
import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst;
import com.jpexs.decompiler.flash.abc.types.traits.Traits;
import com.jpexs.decompiler.flash.abc.usages.multinames.ClassNameMultinameUsage;
import com.jpexs.decompiler.flash.abc.usages.multinames.ConstVarNameMultinameUsage;
import com.jpexs.decompiler.flash.abc.usages.multinames.ConstVarTypeMultinameUsage;
import com.jpexs.decompiler.flash.abc.usages.multinames.MethodBodyMultinameUsage;
import com.jpexs.decompiler.flash.abc.usages.multinames.MethodNameMultinameUsage;
import com.jpexs.decompiler.flash.abc.usages.multinames.MethodParamsMultinameUsage;
import com.jpexs.decompiler.flash.abc.usages.multinames.MethodReturnTypeMultinameUsage;
import com.jpexs.decompiler.flash.abc.usages.multinames.SuperClassMultinameUsage;
import com.jpexs.decompiler.flash.abc.usages.multinames.SuperInterfaceMultinameUsage;
import com.jpexs.decompiler.flash.abc.usages.multinames.TraitMultinameUsage;
import com.jpexs.decompiler.flash.abc.usages.multinames.TypeNameMultinameUsage;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author JPEXS
*/
public class MultinameUsageDetector implements UsageDetector {
@Override
public List<Usage> findUsages(ABC abc, int index) {
return findMultinameUsage(abc, index, true);
}
public List<Usage> findMultinameUsage(ABC abc, int multinameIndex, boolean exactMatch) {
List<Usage> ret = new ArrayList<>();
if (multinameIndex == 0) {
return ret;
}
for (int s = 0; s < abc.script_info.size(); s++) {
checkMultinameUsedInMethod(abc, multinameIndex, exactMatch, abc.script_info.get(s).init_index, ret, s, -1, 0, TraitMultinameUsage.TRAITS_TYPE_SCRIPT, true, null, -1);
findMultinameUsageInTraits(abc, abc.script_info.get(s).traits, multinameIndex, exactMatch, TraitMultinameUsage.TRAITS_TYPE_SCRIPT, s, -1, ret, -1);
}
loopm:
for (int t = 1; t < abc.constants.getMultinameCount(); t++) {
Multiname multiname = abc.constants.getMultiname(t);
if (multiname.kind == Multiname.TYPENAME) {
if (multiname.qname_index == multinameIndex) {
ret.add(new TypeNameMultinameUsage(abc, multinameIndex, t, -1));
continue;
}
for (int mp : multiname.params) {
if (mp == multinameIndex) {
ret.add(new TypeNameMultinameUsage(abc, multinameIndex, t, -1));
continue loopm;
}
}
}
}
return ret;
}
@Override
public List<List<Usage>> findAllUsage(ABC abc) {
List<List<Usage>> ret = new ArrayList<>();
for (int i = 0; i < abc.constants.getMultinameCount(); i++) {
ret.add(new ArrayList<>());
}
for (int s = 0; s < abc.script_info.size(); s++) {
checkAllMultinameUsedInMethod(abc, abc.script_info.get(s).init_index, ret, s, -1, 0, TraitMultinameUsage.TRAITS_TYPE_SCRIPT, true, null, -1);
findAllMultinameUsageInTraits(abc, abc.script_info.get(s).traits, TraitMultinameUsage.TRAITS_TYPE_SCRIPT, s, -1, ret, -1);
}
boolean[] foundMultinames = new boolean[abc.constants.getMultinameCount()];
for (int t = 1; t < abc.constants.getMultinameCount(); t++) {
Multiname multiname = abc.constants.getMultiname(t);
if (multiname.kind == Multiname.TYPENAME) {
if (!foundMultinames[multiname.qname_index]) {
ret.get(multiname.qname_index).add(new TypeNameMultinameUsage(abc, multiname.qname_index, t, -1));
foundMultinames[multiname.qname_index] = true;
}
for (int mp : multiname.params) {
if (!foundMultinames[mp]) {
ret.get(mp).add(new TypeNameMultinameUsage(abc, mp, t, -1));
foundMultinames[mp] = true;
}
}
}
}
return ret;
}
@Override
public String getKind() {
return "string";
}
private void findAllMultinameUsageInTraits(ABC abc, Traits traits, int traitsType, int scriptIndex, int classIndex, List<List<Usage>> ret, int parentTraitIndex) {
for (int t = 0; t < traits.traits.size(); t++) {
if (traits.traits.get(t) instanceof TraitClass) {
TraitClass tc = (TraitClass) traits.traits.get(t);
ret.get(tc.name_index).add(new ClassNameMultinameUsage(abc, tc.name_index, tc.class_info, scriptIndex));
int c = tc.class_info;
int classNameMultinameIndex = abc.instance_info.get(c).name_index;
ret.get(classNameMultinameIndex).add(new ClassNameMultinameUsage(abc, classNameMultinameIndex, c, scriptIndex));
int extendsMultinameIndex = abc.instance_info.get(c).super_index;
ret.get(extendsMultinameIndex).add(new SuperClassMultinameUsage(abc, extendsMultinameIndex, c, scriptIndex));
for (int i = 0; i < abc.instance_info.get(c).interfaces.length; i++) {
int implementsMultinameIndex = abc.instance_info.get(c).interfaces[i];
ret.get(implementsMultinameIndex).add(new SuperInterfaceMultinameUsage(abc, implementsMultinameIndex, c, scriptIndex));
}
checkAllMultinameUsedInMethod(abc, abc.instance_info.get(c).iinit_index, ret, -1/*FIXME*/, c, 0, TraitMultinameUsage.TRAITS_TYPE_INSTANCE, true, null, -1);
checkAllMultinameUsedInMethod(abc, abc.class_info.get(c).cinit_index, ret, -1/*FIXME*/, c, 0, TraitMultinameUsage.TRAITS_TYPE_CLASS, true, null, -1);
findAllMultinameUsageInTraits(abc, abc.instance_info.get(c).instance_traits, TraitMultinameUsage.TRAITS_TYPE_INSTANCE, -1/*FIXME*/, c, ret, -1);
findAllMultinameUsageInTraits(abc, abc.class_info.get(c).static_traits, TraitMultinameUsage.TRAITS_TYPE_CLASS, -1/*FIXME*/, c, ret, -1);
}
if (traits.traits.get(t) instanceof TraitSlotConst) {
TraitSlotConst tsc = (TraitSlotConst) traits.traits.get(t);
ret.get(tsc.name_index).add(new ConstVarNameMultinameUsage(abc, tsc.name_index, scriptIndex, classIndex, t, traitsType, traits, parentTraitIndex));
ret.get(tsc.type_index).add(new ConstVarTypeMultinameUsage(abc, tsc.type_index, scriptIndex, classIndex, t, traitsType, traits, parentTraitIndex));
}
if (traits.traits.get(t) instanceof TraitMethodGetterSetter) {
TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) traits.traits.get(t);
ret.get(tmgs.name_index).add(new MethodNameMultinameUsage(abc, tmgs.name_index, scriptIndex, classIndex, t, traitsType, false, traits, parentTraitIndex));
checkAllMultinameUsedInMethod(abc, tmgs.method_info, ret, scriptIndex, classIndex, t, traitsType, false, traits, parentTraitIndex);
}
}
}
private void checkAllMultinameUsedInMethod(ABC abc, int methodInfo, List<List<Usage>> ret, int scriptIndex, int classIndex, int traitIndex, int traitsType, boolean isInitializer, Traits traits, int parentTraitIndex) {
boolean[] foundMultinames = new boolean[abc.constants.getMultinameCount()];
for (int p = 0; p < abc.method_info.get(methodInfo).param_types.length; p++) {
int methodParamsMultinameIndex = abc.method_info.get(methodInfo).param_types[p];
if (!foundMultinames[methodParamsMultinameIndex]) {
ret.get(methodParamsMultinameIndex).add(new MethodParamsMultinameUsage(abc, methodParamsMultinameIndex, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex));
foundMultinames[methodParamsMultinameIndex] = true;
}
}
int methodReturnTypeMultinameIndex = abc.method_info.get(methodInfo).ret_type;
ret.get(methodReturnTypeMultinameIndex).add(new MethodReturnTypeMultinameUsage(abc, methodReturnTypeMultinameIndex, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex));
MethodBody body = abc.findBody(methodInfo);
if (body != null) {
findAllMultinameUsageInTraits(abc, body.traits, traitsType, scriptIndex, classIndex, ret, traitIndex);
foundMultinames = new boolean[abc.constants.getMultinameCount()];
for (ABCException e : body.exceptions) {
if (!foundMultinames[e.name_index]) {
ret.get(e.name_index).add(new MethodBodyMultinameUsage(abc, e.name_index, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex));
foundMultinames[e.name_index] = true;
}
if (!foundMultinames[e.type_index]) {
ret.get(e.type_index).add(new MethodBodyMultinameUsage(abc, e.type_index, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex));
foundMultinames[e.type_index] = true;
}
}
for (AVM2Instruction ins : body.getCode().code) {
for (int o = 0; o < ins.definition.operands.length; o++) {
if (ins.definition.operands[o] == AVM2Code.DAT_MULTINAME_INDEX) {
int mi = ins.operands[o];
if (mi < foundMultinames.length && !foundMultinames[mi]) {
ret.get(mi).add(new MethodBodyMultinameUsage(abc, mi, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex));
foundMultinames[mi] = true;
}
}
}
}
}
}
private boolean isSameName(ABC abc, int expectedQNameIndex, int checkedNameIndex, boolean exactMatch) {
if (expectedQNameIndex == checkedNameIndex) {
return true;
}
if (exactMatch) {
return false;
}
Multiname expectedQName = abc.constants.getMultiname(expectedQNameIndex);
Multiname checkedName = abc.constants.getMultiname(checkedNameIndex);
if (checkedName == null) {
return false;
}
if (expectedQName.name_index != checkedName.name_index) {
return false;
}
if (checkedName.kind == Multiname.QNAME) {
return expectedQName.namespace_index == checkedName.namespace_index;
}
if (checkedName.kind != Multiname.MULTINAME) {
return false;
}
for (int ns : abc.constants.getNamespaceSet(checkedName.namespace_set_index).namespaces) {
if (ns == expectedQName.namespace_index) {
return true;
}
}
return false;
}
private void findMultinameUsageInTraits(ABC abc, Traits traits, int multinameIndex, boolean exactMatch, int traitsType, int scriptIndex, int classIndex, List<Usage> ret, int parentTraitIndex) {
for (int t = 0; t < traits.traits.size(); t++) {
if (traits.traits.get(t) instanceof TraitClass) {
TraitClass tc = (TraitClass) traits.traits.get(t);
if (isSameName(abc, multinameIndex, tc.name_index, exactMatch)) {
ret.add(new ClassNameMultinameUsage(abc, multinameIndex, tc.class_info, scriptIndex));
}
int c = tc.class_info;
if (isSameName(abc, multinameIndex, abc.instance_info.get(c).super_index, exactMatch)) {
ret.add(new SuperClassMultinameUsage(abc, multinameIndex, c, scriptIndex));
}
for (int i = 0; i < abc.instance_info.get(c).interfaces.length; i++) {
if (isSameName(abc, multinameIndex, abc.instance_info.get(c).interfaces[i], exactMatch)) {
ret.add(new SuperInterfaceMultinameUsage(abc, multinameIndex, c, scriptIndex));
}
}
checkMultinameUsedInMethod(abc, multinameIndex, exactMatch, abc.instance_info.get(c).iinit_index, ret, -1/*FIXME*/, c, 0, TraitMultinameUsage.TRAITS_TYPE_INSTANCE, true, null, -1);
checkMultinameUsedInMethod(abc, multinameIndex, exactMatch, abc.class_info.get(c).cinit_index, ret, -1/*FIXME*/, c, 0, TraitMultinameUsage.TRAITS_TYPE_CLASS, true, null, -1);
findMultinameUsageInTraits(abc, abc.instance_info.get(c).instance_traits, multinameIndex, exactMatch, TraitMultinameUsage.TRAITS_TYPE_INSTANCE, -1/*FIXME*/, c, ret, -1);
findMultinameUsageInTraits(abc, abc.class_info.get(c).static_traits, multinameIndex, exactMatch, TraitMultinameUsage.TRAITS_TYPE_CLASS, -1/*FIXME*/, c, ret, -1);
}
if (traits.traits.get(t) instanceof TraitSlotConst) {
TraitSlotConst tsc = (TraitSlotConst) traits.traits.get(t);
if (isSameName(abc, multinameIndex, tsc.name_index, exactMatch)) {
ret.add(new ConstVarNameMultinameUsage(abc, multinameIndex, scriptIndex, classIndex, t, traitsType, traits, parentTraitIndex));
}
if (isSameName(abc, multinameIndex, tsc.type_index, exactMatch)) {
ret.add(new ConstVarTypeMultinameUsage(abc, multinameIndex, scriptIndex, classIndex, t, traitsType, traits, parentTraitIndex));
}
}
if (traits.traits.get(t) instanceof TraitMethodGetterSetter) {
TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) traits.traits.get(t);
if (isSameName(abc, multinameIndex, tmgs.name_index, exactMatch)) {
ret.add(new MethodNameMultinameUsage(abc, multinameIndex, scriptIndex, classIndex, t, traitsType, false, traits, parentTraitIndex));
}
checkMultinameUsedInMethod(abc, multinameIndex, exactMatch, tmgs.method_info, ret, scriptIndex, classIndex, t, traitsType, false, traits, parentTraitIndex);
}
}
}
private void checkMultinameUsedInMethod(ABC abc, int multinameIndex, boolean exactMatch, int methodInfo, List<Usage> ret, int scriptIndex, int classIndex, int traitIndex, int traitsType, boolean isInitializer, Traits traits, int parentTraitIndex) {
for (int p = 0; p < abc.method_info.get(methodInfo).param_types.length; p++) {
if (isSameName(abc, multinameIndex, abc.method_info.get(methodInfo).param_types[p], exactMatch)) {
ret.add(new MethodParamsMultinameUsage(abc, multinameIndex, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex));
break;
}
}
if (isSameName(abc, multinameIndex, abc.method_info.get(methodInfo).ret_type, exactMatch)) {
ret.add(new MethodReturnTypeMultinameUsage(abc, multinameIndex, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex));
}
MethodBody body = abc.findBody(methodInfo);
if (body != null) {
findMultinameUsageInTraits(abc, body.traits, multinameIndex, exactMatch, traitsType, scriptIndex, classIndex, ret, traitIndex);
for (ABCException e : body.exceptions) {
if ((isSameName(abc, multinameIndex, e.name_index, exactMatch)) || (isSameName(abc, multinameIndex, e.type_index, exactMatch))) {
ret.add(new MethodBodyMultinameUsage(abc, multinameIndex, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex));
return;
}
}
for (AVM2Instruction ins : body.getCode().code) {
for (int o = 0; o < ins.definition.operands.length; o++) {
if (ins.definition.operands[o] == AVM2Code.DAT_MULTINAME_INDEX && ins.operands[o] < abc.constants.getMultinameCount()) {
if (isSameName(abc, multinameIndex, ins.operands[o], exactMatch)) {
ret.add(new MethodBodyMultinameUsage(abc, multinameIndex, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex));
return;
}
}
}
}
}
}
@Override
public void filterUsages(ABC abc, UsagesFilter filter) {
throw new UnsupportedOperationException("Not supported yet.");
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (C) 2010-2024 JPEXS, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
package com.jpexs.decompiler.flash.abc.usages;
import com.jpexs.decompiler.flash.abc.ABC;
/**
*
* @author JPEXS
*/
public interface Usage {
public ABC getAbc();
public int getIndex();
public String getKind();
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2010-2024 JPEXS, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
package com.jpexs.decompiler.flash.abc.usages;
import com.jpexs.decompiler.flash.abc.ABC;
import java.util.List;
/**
*
* @author JPEXS
*/
public interface UsageDetector {
public void filterUsages(ABC abc, UsagesFilter filter);
public List<Usage> findUsages(ABC abc, int index);
public List<List<Usage>> findAllUsage(ABC abc);
public String getKind();
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright (C) 2010-2024 JPEXS, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
package com.jpexs.decompiler.flash.abc.usages;
/**
*
* @author JPEXS
*/
public interface UsagesFilter {
public int filterUsage(int originalIndex);
}

View File

@@ -19,6 +19,7 @@ package com.jpexs.decompiler.flash.abc.usages.multinames;
import com.jpexs.decompiler.flash.abc.ABC;
import com.jpexs.decompiler.flash.abc.types.Multiname;
import com.jpexs.decompiler.flash.abc.types.Namespace;
import com.jpexs.decompiler.flash.abc.usages.Usage;
import java.util.ArrayList;
import java.util.Objects;
@@ -26,7 +27,7 @@ import java.util.Objects;
*
* @author JPEXS
*/
public abstract class MultinameUsage {
public abstract class MultinameUsage implements Usage {
protected final ABC abc;
@@ -45,6 +46,7 @@ public abstract class MultinameUsage {
return multinameIndex;
}
@Override
public ABC getAbc() {
return abc;
}
@@ -124,4 +126,14 @@ public abstract class MultinameUsage {
}
public abstract boolean collides(MultinameUsage other);
@Override
public int getIndex() {
return multinameIndex;
}
@Override
public String getKind() {
return "multiname";
}
}

View File

@@ -0,0 +1,448 @@
/*
* Copyright (C) 2010-2023 JPEXS, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
package com.jpexs.decompiler.flash.abc.usages.simple;
import com.jpexs.decompiler.flash.abc.ABC;
import com.jpexs.decompiler.flash.abc.avm2.AVM2Code;
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction;
import com.jpexs.decompiler.flash.abc.types.InstanceInfo;
import com.jpexs.decompiler.flash.abc.types.MetadataInfo;
import com.jpexs.decompiler.flash.abc.types.MethodBody;
import com.jpexs.decompiler.flash.abc.types.MethodInfo;
import com.jpexs.decompiler.flash.abc.types.Multiname;
import com.jpexs.decompiler.flash.abc.types.Namespace;
import com.jpexs.decompiler.flash.abc.types.NamespaceSet;
import com.jpexs.decompiler.flash.abc.types.ValueKind;
import com.jpexs.decompiler.flash.abc.types.traits.Trait;
import com.jpexs.decompiler.flash.abc.types.traits.TraitClass;
import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction;
import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter;
import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
/**
*
* @author JPEXS
*/
public class ABCSimpleUsageDetector {
public static enum ItemKind {
INT,
UINT,
DOUBLE,
STRING,
NAMESPACE,
NAMESPACESET,
MULTINAME,
METADATAINFO,
METHODINFO,
METHODBODY,
CLASS
}
private final Map<ItemKind, List<List<String>>> usages = new HashMap<>();
private final ABC abc;
public ABCSimpleUsageDetector(ABC abc) {
this.abc = abc;
}
private void initUsages(ItemKind kind, int itemCount, boolean atleastOne) {
List<List<String>> list = new ArrayList<>();
if (atleastOne && itemCount == 0) {
itemCount = 1;
}
for (int i = 0; i < itemCount; i++) {
list.add(new ArrayList<>());
}
usages.put(kind, list);
}
public void detect() {
usages.clear();
initUsages(ItemKind.INT, abc.constants.getIntCount(), true);
initUsages(ItemKind.UINT, abc.constants.getUIntCount(), true);
initUsages(ItemKind.DOUBLE, abc.constants.getDoubleCount(), true);
initUsages(ItemKind.STRING, abc.constants.getStringCount(), true);
initUsages(ItemKind.NAMESPACE, abc.constants.getNamespaceCount(), true);
initUsages(ItemKind.NAMESPACESET, abc.constants.getNamespaceSetCount(), true);
initUsages(ItemKind.MULTINAME, abc.constants.getMultinameCount(), true);
initUsages(ItemKind.METADATAINFO, abc.metadata_info.size(), false);
initUsages(ItemKind.METHODINFO, abc.method_info.size(), false);
initUsages(ItemKind.METHODBODY, abc.bodies.size(), false);
initUsages(ItemKind.CLASS, abc.class_info.size(), false);
ABCWalker walker = new ABCWalker() {
protected void handleUsageNamespace(int index, String usageDescription) {
if (!handleUsage(ItemKind.NAMESPACE, index, usageDescription)) {
return;
}
Namespace ns = abc.constants.getNamespace(index);
if (ns == null) {
return;
}
handleUsage(ItemKind.STRING, ns.name_index, "ns" + index + "/name");
}
protected void handleUsageNamespaceSet(int index, String usageDescription) {
if (!handleUsage(ItemKind.NAMESPACESET, index, usageDescription)) {
return;
}
NamespaceSet nss = abc.constants.getNamespaceSet(index);
if (nss == null) {
return;
}
for (int i = 0; i < nss.namespaces.length; i++) {
handleUsageNamespace(nss.namespaces[i], "nss" + index + "/ns" + nss.namespaces[i]);
}
}
protected void handleUsageMultiname(int index, String usageDescription) {
if (!handleUsage(ItemKind.MULTINAME, index, usageDescription)) {
return;
}
Multiname m = abc.constants.getMultiname(index);
if (m == null) {
return;
}
if (m.hasOwnName()) {
handleUsage(ItemKind.STRING, m.name_index, "mn" + index + "/name");
}
if (m.hasOwnNamespace()) {
handleUsageNamespace(m.namespace_index, "mn" + index + "/namespace");
}
if (m.hasOwnNamespaceSet()) {
handleUsageNamespaceSet(m.namespace_set_index, "mn" + index + "/namespace_set");
}
if (m.kind == Multiname.TYPENAME) {
handleUsageMultiname(m.qname_index, "mn" + index + "/qname");
for (int i = 0; i < m.params.length; i++) {
handleUsageMultiname(m.params[i], "mn" + index + "/param" + i);
}
}
}
protected void handleUsageMethodInfo(int index, String usageDescription) {
if (!handleUsage(ItemKind.METHODINFO, index, usageDescription)) {
return;
}
MethodInfo m = abc.method_info.get(index);
handleUsage(ItemKind.STRING, m.name_index, usageDescription + "/name");
if (m.flagHas_paramnames()) {
for (int i = 0; i < m.paramNames.length; i++) {
handleUsage(ItemKind.STRING, m.paramNames[i], usageDescription + "/param_names/pn" + i);
}
}
if (m.flagHas_optional()) {
for (int i = 0; i < m.optional.length; i++) {
handleUsageValueKind(m.optional[i].value_kind, m.optional[i].value_index, usageDescription + "/optional/op" + i);
}
}
for (int i = 0; i < m.param_types.length; i++) {
handleUsageMultiname(m.param_types[i], usageDescription + "/param_types/pt" + i);
}
handleUsageMultiname(m.ret_type, usageDescription + "/return_type");
int bodyIndex = abc.findBodyIndex(index);
if (bodyIndex > -1) {
handleUsageMethodBody(bodyIndex, usageDescription + "/method_body");
}
}
protected void handleUsageMethodBody(int index, String usageDescription) {
if (!handleUsage(ItemKind.METHODBODY, index, usageDescription)) {
return;
}
MethodBody body = abc.bodies.get(index);
for (int i = 0; i < body.exceptions.length; i++) {
handleUsageMultiname(body.exceptions[i].name_index, usageDescription + "/exceptions/ex" + i + "/name");
handleUsageMultiname(body.exceptions[i].type_index, usageDescription + "/exceptions/ex" + i + "/type");
}
List<AVM2Instruction> code = body.getCode().code;
for (int i = 0; i < code.size(); i++) {
AVM2Instruction ins = code.get(i);
for (int operandIndex = 0; operandIndex < ins.definition.operands.length; operandIndex++) {
int operand = ins.operands[operandIndex];
String operandDescription = usageDescription + "/code/ins" + i + "/op" + operandIndex;
switch (ins.definition.operands[operandIndex]) {
case AVM2Code.DAT_CLASS_INDEX:
break;
case AVM2Code.DAT_DOUBLE_INDEX:
handleUsage(ItemKind.DOUBLE, operand, operandDescription);
break;
case AVM2Code.DAT_INT_INDEX:
handleUsage(ItemKind.INT, operand, operandDescription);
break;
case AVM2Code.DAT_METHOD_INDEX:
//handleUsageMethodInfo(operand, operandDescription);
break;
case AVM2Code.DAT_MULTINAME_INDEX:
handleUsageMultiname(operand, operandDescription);
break;
case AVM2Code.DAT_NAMESPACE_INDEX:
handleUsageNamespace(operand, operandDescription);
break;
case AVM2Code.DAT_STRING_INDEX:
handleUsage(ItemKind.STRING, operand, operandDescription);
break;
case AVM2Code.DAT_UINT_INDEX:
handleUsage(ItemKind.UINT, operand, operandDescription);
break;
}
}
}
}
@Override
protected void handleScript(ABC abc, int index) {
}
@Override
protected void handleMetadataInfo(ABC abc, int index, Trait trait, int scriptIndex, int scriptTraitIndex, int classIndex, int traitIndex, int traitMetadataIndex, ABCWalker.WalkType walkType) {
String description = "si" + scriptIndex + "/traits/t" + scriptTraitIndex;
if (walkType == WalkType.Class && traitIndex > -1) {
description += "/class_info/traits/t" + traitIndex;
} else if (walkType == WalkType.Instance && traitIndex > -1) {
description += "/instance_info/traits/t" + traitIndex;
}
description += "/metadata/md" + index;
//?? classIndex
if (!handleUsage(ItemKind.METADATAINFO, index, description)) {
return;
}
MetadataInfo md = abc.metadata_info.get(index);
handleUsage(ItemKind.STRING, md.name_index, description + "/name");
for (int i = 0; i < md.keys.length; i++) {
handleUsage(ItemKind.STRING, md.keys[i], description + "/pairs/p" + i + "/key");
}
for (int i = 0; i < md.values.length; i++) {
handleUsage(ItemKind.STRING, md.values[i], description + "/pairs/p" + i + "/value");
}
}
@Override
protected void handleTraitClass(ABC abc, TraitClass trait, int scriptIndex, int scriptTraitIndex) {
String description = "si" + scriptIndex + "/traits/t" + scriptTraitIndex;
if (!handleUsage(ItemKind.CLASS, trait.class_info, description)) {
return;
}
InstanceInfo ii = abc.instance_info.get(trait.class_info);
if ((ii.flags & InstanceInfo.CLASS_PROTECTEDNS) != 0) {
handleUsageNamespace(ii.protectedNS, description + "/instance_info/protected_ns");
}
handleUsageMultiname(ii.name_index, description + "/instance_info/name");
handleUsageMultiname(ii.super_index, description + "/instance_info/super");
for (int i = 0; i < ii.interfaces.length; i++) {
handleUsageMultiname(ii.interfaces[i], description + "/instance_info/interfaces/in" + i);
}
/*
handleUsageMethodInfo(abc.class_info.get(trait.class_info).cinit_index, description + "/class_info/cinit");
handleUsageMethodInfo(abc.instance_info.get(trait.class_info).iinit_index, description + "/instance_info/iinit");
*/
}
@Override
protected void handleTraitMethodGetterSetter(ABC abc, TraitMethodGetterSetter trait, int scriptIndex, int scriptTraitIndex, int classIndex, int traitIndex, ABCWalker.WalkType walkType) {
handleTraitMethodBase(abc, trait, scriptIndex, scriptTraitIndex, classIndex, traitIndex, walkType);
}
protected void handleTraitMethodBase(ABC abc, Trait trait, int scriptIndex, int scriptTraitIndex, int classIndex, int traitIndex, ABCWalker.WalkType walkType) {
String description = "";
if (scriptIndex > -1) {
description += "si" + scriptIndex + "/traits/t" + scriptTraitIndex;
if (walkType == WalkType.Class && traitIndex > -1) {
description += "/class_info/traits/t" + traitIndex;
} else if (walkType == WalkType.Instance && traitIndex > -1) {
description += "/instance_info/traits/t" + traitIndex;
}
} else {
if (walkType == WalkType.Class && traitIndex > -1) {
description += "ci" + classIndex + "/class_info/traits/t" + traitIndex;
} else if (walkType == WalkType.Instance && traitIndex > -1) {
description += "ii" + classIndex + "/instance_info/traits/t" + traitIndex;
}
}
//description += " " + trait.getKindToStr();
handleUsageMultiname(trait.name_index, description + "/name");
//handleUsageMethodInfo(trait.method_info, description + "/method_info");
}
@Override
protected void handleTraitFunction(ABC abc, TraitFunction trait, int scriptIndex, int scriptTraitIndex, int classIndex, int traitIndex, ABCWalker.WalkType walkType) {
handleTraitMethodBase(abc, trait, scriptIndex, scriptTraitIndex, classIndex, traitIndex, walkType);
}
protected void handleUsageValueKind(int value_kind, int value_index, String description) {
switch (value_kind) {
case ValueKind.CONSTANT_Int:
handleUsage(ItemKind.INT, value_index, description);
break;
case ValueKind.CONSTANT_UInt:
handleUsage(ItemKind.UINT, value_index, description);
break;
case ValueKind.CONSTANT_Double:
handleUsage(ItemKind.DOUBLE, value_index, description);
break;
case ValueKind.CONSTANT_Utf8:
handleUsage(ItemKind.STRING, value_index, description);
break;
case ValueKind.CONSTANT_Namespace:
case ValueKind.CONSTANT_PackageNamespace:
case ValueKind.CONSTANT_PackageInternalNs:
case ValueKind.CONSTANT_ProtectedNamespace:
case ValueKind.CONSTANT_ExplicitNamespace:
case ValueKind.CONSTANT_StaticProtectedNs:
case ValueKind.CONSTANT_PrivateNs:
handleUsage(ItemKind.NAMESPACE, value_index, description);
break;
}
}
@Override
protected void handleTraitSlotConst(ABC abc, TraitSlotConst trait, int scriptIndex, int scriptTraitIndex, int classIndex, int traitIndex, int bodyIndex, int bodyTraitIndex, ABCWalker.WalkType walkType, Stack<Integer> callStack) {
String description = "";
if (callStack.size() > 1) {
if (bodyTraitIndex != -1) {
int methodInfo = callStack.peek();
description += "mi" + methodInfo + "/method_body/traits/t" + bodyTraitIndex;
}
} else if (scriptIndex > -1) {
description += "si" + scriptIndex + "/traits/t" + scriptTraitIndex;
if (walkType == WalkType.Class && traitIndex > -1) {
description += "/class_info/traits/t" + traitIndex;
} else if (walkType == WalkType.Instance && traitIndex > -1) {
description += "/instance_info/traits/t" + traitIndex;
}
if (bodyTraitIndex != -1) {
description += "/method_info/method_body/traits/t" + bodyTraitIndex;
}
} else {
if (walkType == WalkType.Class && traitIndex > -1) {
description += "ci" + classIndex + "/class_info/traits/t" + traitIndex;
} else if (walkType == WalkType.Instance && traitIndex > -1) {
description += "ii" + classIndex + "/instance_info/traits/t" + traitIndex;
} else if (bodyTraitIndex > -1) {
description += "mb" + bodyIndex + "/traits/t" + bodyTraitIndex;
}
}
//description += " " + trait.getKindToStr();
handleUsageMultiname(trait.name_index, description + "/name");
handleUsageMultiname(trait.type_index, description + "/type");
handleUsageValueKind(trait.value_kind, trait.value_index, description + "/value_index");
}
@Override
protected void handleMethodInfo(ABC abc, int index, int scriptIndex, int scriptTraitIndex, int classIndex, int traitIndex, ABCWalker.WalkType walkType, boolean initializer, Stack<Integer> callStack) {
if (callStack.size() > 1) { //it is an anonymous submethod
int prevMethod = callStack.get(callStack.size() - 2);
handleUsageMethodInfo(index, "mi" + prevMethod + "/method_body/code");
return;
}
if (initializer) {
switch (walkType) {
case Class:
if (scriptIndex != -1) {
handleUsageMethodInfo(index, "si" + scriptIndex + "/traits/t" + scriptTraitIndex + "/class_info/cinit");
} else {
handleUsageMethodInfo(index, "ci" + classIndex + "/class_info/cinit");
}
break;
case Instance:
if (scriptIndex != -1) {
handleUsageMethodInfo(index, "si" + scriptIndex + "/traits/t" + scriptTraitIndex + "/instance_info/iinit");
} else {
handleUsageMethodInfo(index, "ii" + classIndex + "/instance_info/iinit");
}
break;
case Script:
handleUsageMethodInfo(index, "si" + scriptIndex + "/init");
break;
}
return;
}
String description = "";
if (scriptIndex > -1) {
description += "si" + scriptIndex + "/traits/t" + scriptTraitIndex;
if (walkType == WalkType.Class && traitIndex > -1) {
description += "/class_info/traits/t" + traitIndex;
} else if (walkType == WalkType.Instance && traitIndex > -1) {
description += "/instance_info/traits/t" + traitIndex;
}
} else {
if (walkType == WalkType.Class && traitIndex > -1) {
description += "ci" + classIndex + "/class_info/traits/t" + traitIndex;
} else if (walkType == WalkType.Instance && traitIndex > -1) {
description += "ii" + classIndex + "/instance_info/traits/t" + traitIndex;
}
}
description += "/method_info";
handleUsageMethodInfo(index, description);
}
};
walker.walkABC(abc, false);
}
/**
*
* @param kind
* @param index
* @param usageDescription
* @return True if it is new
*/
private boolean handleUsage(ItemKind kind, int index, String usageDescription) {
List<String> kindList = usages.get(kind).get(index);
kindList.add(usageDescription);
return kindList.size() == 1;
}
public Map<ItemKind, List<List<String>>> getUsages() {
return Collections.unmodifiableMap(usages);
}
public List<String> getUsages(ItemKind kind, int index) {
return Collections.unmodifiableList(usages.get(kind).get(index));
}
public List<List<String>> getUsages(ItemKind kind) {
return Collections.unmodifiableList(usages.get(kind));
}
}

View File

@@ -0,0 +1,203 @@
/*
* Copyright (C) 2010-2023 JPEXS, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
package com.jpexs.decompiler.flash.abc.usages.simple;
import com.jpexs.decompiler.flash.abc.ABC;
import com.jpexs.decompiler.flash.abc.avm2.AVM2Code;
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction;
import com.jpexs.decompiler.flash.abc.types.ScriptInfo;
import com.jpexs.decompiler.flash.abc.types.traits.Trait;
import com.jpexs.decompiler.flash.abc.types.traits.TraitClass;
import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction;
import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter;
import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst;
import com.jpexs.decompiler.flash.abc.types.traits.Traits;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Stack;
/**
*
* @author JPEXS
*/
public abstract class ABCWalker {
public static enum WalkType {
Orphan,
Script,
Class,
Instance
}
public final void walkABC(ABC abc, boolean walkOrphanItems) {
Set<Integer> handledClasses = new HashSet<>();
Set<Integer> handledMethodInfos = new HashSet<>();
Set<Integer> handledMethodBodies = new HashSet<>();
for (int i = 0; i < abc.script_info.size(); i++) {
handleScript(abc, i);
ScriptInfo si = abc.script_info.get(i);
optionalHandleMethodInfo(handledMethodBodies, handledMethodInfos, abc, si.init_index, i, -1, -1, -1, WalkType.Script, true, new Stack<>());
walkTraits(abc, si.traits, i, -1, -1, -1, -1, handledMethodBodies, handledMethodInfos, handledClasses, WalkType.Script, new Stack<>());
}
if (walkOrphanItems) {
for (int i = 0; i < abc.method_info.size(); i++) {
optionalHandleMethodInfo(handledMethodBodies, handledMethodInfos, abc, i, -1, -1, -1, -1, WalkType.Orphan, false, new Stack<>());
}
for (int i = 0; i < abc.bodies.size(); i++) {
optionalHandleMethodBody(handledMethodBodies, handledMethodInfos, abc, i, -1, -1, -1, -1, WalkType.Orphan, false, new Stack<>());
}
for (int i = 0; i < abc.class_info.size(); i++) {
optionalHandleClass(handledClasses, handledMethodBodies, handledMethodInfos, abc, i, -1, -1, -1, WalkType.Orphan);
}
}
}
private boolean optionalHandleClass(Set<Integer> handledClasses, Set<Integer> handledMethodBodies, Set<Integer> handledMethodInfos, ABC abc, int index, int scriptIndex, int scriptTraitIndex, int traitIndex, WalkType walkType) {
if (handledClasses.contains(index)) {
return false;
}
handleClass(abc, index, scriptIndex, traitIndex, walkType);
optionalHandleMethodInfo(handledMethodBodies, handledMethodInfos, abc, abc.instance_info.get(index).iinit_index, scriptIndex, scriptTraitIndex, index, -1, WalkType.Instance, true, new Stack<>());
walkTraits(abc, abc.instance_info.get(index).instance_traits, scriptIndex, scriptTraitIndex, index, -1, -1, handledMethodBodies, handledMethodInfos, handledClasses, WalkType.Instance, new Stack<>());
optionalHandleMethodInfo(handledMethodBodies, handledMethodInfos, abc, abc.class_info.get(index).cinit_index, scriptIndex, scriptTraitIndex, index, -1, WalkType.Class, true, new Stack<>());
walkTraits(abc, abc.class_info.get(index).static_traits, scriptIndex, scriptTraitIndex, index, -1, -1, handledMethodBodies, handledMethodInfos, handledClasses, WalkType.Class, new Stack<>());
return true;
}
private void optionalHandleMethodInfo(Set<Integer> handledMethodBodies, Set<Integer> handledMethodInfos, ABC abc, int index, int scriptIndex, int scriptTraitIndex, int classIndex, int traitIndex, WalkType walkType, boolean initializer, Stack<Integer> callStack) {
if (handledMethodInfos.contains(index)) {
return;
}
handledMethodInfos.add(index);
if (callStack != null) {
if (callStack.contains(index)) {
return;
}
callStack.push(index);
}
handleMethodInfo(abc, index, scriptIndex, scriptTraitIndex, classIndex, traitIndex, walkType, initializer, callStack);
int bodyIndex = abc.findBodyIndex(index);
if (bodyIndex > -1) {
optionalHandleMethodBody(handledMethodBodies, handledMethodInfos, abc, bodyIndex, scriptIndex, scriptTraitIndex, classIndex, traitIndex, walkType, initializer, callStack);
}
if (callStack != null) {
callStack.pop();
}
}
private void optionalHandleMethodBody(Set<Integer> handledMethodBodies, Set<Integer> handledMethodInfos, ABC abc, int index, int scriptIndex, int scriptTraitIndex, int classIndex, int traitIndex, WalkType walkType, boolean initializer, Stack<Integer> callStack) {
if (handledMethodBodies.contains(index)) {
return;
}
handledMethodBodies.add(index);
handleMethodBody(abc, index, scriptIndex, classIndex, traitIndex, walkType, initializer);
List<AVM2Instruction> code = abc.bodies.get(index).getCode().code;
for (AVM2Instruction ins : code) {
for (int o = 0; o < ins.definition.operands.length; o++) {
if (ins.definition.operands[o] == AVM2Code.DAT_METHOD_INDEX) {
optionalHandleMethodInfo(handledMethodBodies, handledMethodInfos, abc, ins.operands[o], scriptIndex, scriptTraitIndex, classIndex, traitIndex, walkType, initializer, callStack);
}
}
}
walkTraits(abc, abc.bodies.get(index).traits, scriptIndex, scriptTraitIndex, classIndex, traitIndex, index, handledMethodBodies, handledMethodInfos, handledMethodInfos, walkType, callStack);
}
private void walkTraits(ABC abc, Traits traits, int scriptIndex, int scriptTraitIndex, int classIndex, int traitIndex, int bodyIndex, Set<Integer> handledMethodBodies, Set<Integer> handledMethodInfos, Set<Integer> handledClasses, WalkType walkType, Stack<Integer> callStack) {
int bodyTraitIndex = -1;
for (int i = 0; i < traits.traits.size(); i++) {
if (classIndex == -1) {
scriptTraitIndex = i;
}
if (bodyIndex == -1 && classIndex != -1) {
traitIndex = i;
}
if (bodyIndex != -1) {
bodyTraitIndex = i;
}
Trait t = traits.traits.get(i);
if ((t.kindFlags & Trait.ATTR_Metadata) > 0) {
for (int m = 0; m < t.metadata.length; m++) {
handleMetadataInfo(abc, t.metadata[m], t, scriptIndex, scriptTraitIndex, classIndex, i, m, walkType);
}
}
if (t instanceof TraitClass) {
TraitClass tc = (TraitClass) t;
handleTraitClass(abc, tc, scriptIndex, i);
int subClassIndex = tc.class_info;
optionalHandleClass(handledClasses, handledMethodBodies, handledMethodInfos, abc, subClassIndex, scriptIndex, scriptTraitIndex, i, walkType);
}
if (t instanceof TraitMethodGetterSetter) {
TraitMethodGetterSetter tm = (TraitMethodGetterSetter) t;
optionalHandleMethodInfo(handledMethodBodies, handledMethodInfos, abc, tm.method_info, scriptIndex, scriptTraitIndex, classIndex, i, walkType, false, new Stack<>());
handleTraitMethodGetterSetter(abc, tm, scriptIndex, scriptTraitIndex, classIndex, i, walkType);
}
if (t instanceof TraitFunction) {
TraitFunction tf = (TraitFunction) t;
optionalHandleMethodInfo(handledMethodBodies, handledMethodInfos, abc, tf.method_info, scriptIndex, scriptTraitIndex, classIndex, i, walkType, false, new Stack<>());
handleTraitFunction(abc, tf, scriptIndex, scriptTraitIndex, classIndex, i, walkType);
}
if (t instanceof TraitSlotConst) {
TraitSlotConst tsc = (TraitSlotConst) t;
handleTraitSlotConst(abc, tsc, scriptIndex, scriptTraitIndex, classIndex, traitIndex, bodyIndex, bodyTraitIndex, walkType, callStack);
}
}
}
protected void handleMethodInfo(ABC abc, int index, int scriptIndex, int scriptTraitIndex, int classIndex, int traitIndex, WalkType walkType, boolean initializer, Stack<Integer> callStack) {
}
protected void handleMethodBody(ABC abc, int index, int scriptIndex, int classIndex, int traitIndex, WalkType walkType, boolean initializer) {
}
protected void handleClass(ABC abc, int index, int scriptIndex, int traitIndex, WalkType walkType) {
}
protected void handleScript(ABC abc, int index) {
}
protected void handleTraitSlotConst(ABC abc, TraitSlotConst trait, int scriptIndex, int scriptTraitIndex, int classIndex, int traitIndex, int bodyIndex, int bodyTraitIndex, WalkType walkType, Stack<Integer> callStack) {
}
protected void handleTraitMethodGetterSetter(ABC abc, TraitMethodGetterSetter trait, int scriptIndex, int scriptTraitIndex, int classIndex, int traitIndex, WalkType walkType) {
}
protected void handleTraitFunction(ABC abc, TraitFunction trait, int scriptIndex, int scriptTraitIndex, int classIndex, int traitIndex, WalkType walkType) {
}
protected void handleTraitClass(ABC abc, TraitClass trait, int scriptIndex, int scriptTraitIndex) {
}
protected void handleMetadataInfo(ABC abc, int index, Trait trait, int scriptIndex, int scriptTraitIndex, int classIndex, int traitIndex, int traitMetadataIndex, WalkType walkType) {
}
}