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 e32cbc71b..1c4880084 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java @@ -2502,6 +2502,7 @@ public final class SWF implements SWFContainerItem, Timelined { } public void clearImageCache() { + jtt = null; frameCache.clear(); rectCache.clear(); for (Tag tag : getTags()) { @@ -2539,10 +2540,14 @@ public final class SWF implements SWFContainerItem, Timelined { System.gc(); } + public void clearAbcListCache() { + abcList = null; + } + public void clearAllCache() { characters = null; characterIdTags = null; - abcList = null; + clearAbcListCache(); timeline = null; clearReadOnlyListCache(); clearImageCache(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABC.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABC.java index d293a81d4..fef18dfc8 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABC.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABC.java @@ -1,1401 +1,1837 @@ -/* - * Copyright (C) 2010-2016 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; - -import com.jpexs.decompiler.flash.EndOfStreamException; -import com.jpexs.decompiler.flash.EventListener; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Deobfuscation; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallPropertyIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushStringIns; -import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException; -import com.jpexs.decompiler.flash.abc.avm2.parser.script.ActionScript3Parser; -import com.jpexs.decompiler.flash.abc.types.ABCException; -import com.jpexs.decompiler.flash.abc.types.ClassInfo; -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.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 com.jpexs.decompiler.flash.abc.usages.ClassNameMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.ConstVarNameMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.ConstVarTypeMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.DefinitionUsage; -import com.jpexs.decompiler.flash.abc.usages.ExtendsMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.ImplementsMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.MethodBodyMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.MethodNameMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.MethodParamsMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.MethodReturnTypeMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.MultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.TypeNameMultinameUsage; -import com.jpexs.decompiler.flash.dumpview.DumpInfo; -import com.jpexs.decompiler.flash.dumpview.DumpInfoSpecial; -import com.jpexs.decompiler.flash.dumpview.DumpInfoSpecialType; -import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.types.annotations.Internal; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.DottedChain; -import com.jpexs.helpers.utf8.Utf8PrintWriter; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author JPEXS - */ -public class ABC { - - public ABCVersion version = new ABCVersion(47, 16); - - public AVM2ConstantPool constants = new AVM2ConstantPool(); - - public List method_info = new ArrayList<>(); - - public List metadata_info = new ArrayList<>(); - - public List instance_info = new ArrayList<>(); - - public List class_info = new ArrayList<>(); - - public List script_info = new ArrayList<>(); - - public List bodies = new ArrayList<>(); - - private ABCMethodIndexing abcMethodIndexing; - - public static final int MINORwithDECIMAL = 17; - - protected Set listeners = new HashSet<>(); - - private static final Logger logger = Logger.getLogger(ABC.class.getName()); - - private AVM2Deobfuscation deobfuscation; - - @Internal - public ABCContainerTag parentTag; - - /* Map from multiname index of namespace value to namespace name**/ - private Map namespaceMap; - - public ABC(ABCContainerTag tag) { - this.parentTag = tag; - this.deobfuscation = null; - } - - public SWF getSwf() { - return parentTag.getSwf(); - } - - public List getAbcTags() { - return getSwf().getAbcList(); - } - - public int addMethodBody(MethodBody body) { - bodies.add(body); - abcMethodIndexing = null; - return bodies.size() - 1; - } - - public int addMethodInfo(MethodInfo mi) { - method_info.add(mi); - return method_info.size() - 1; - } - - public TraitMethodGetterSetter addMethod(int classId, String name, boolean isStatic) { - Multiname multiname = new Multiname(); - multiname.kind = Multiname.QNAME; - multiname.name_index = constants.getStringId(name, true); - multiname.namespace_index = constants.getNamespaceId(Namespace.KIND_PACKAGE, "", 0, true); - int multinameId = constants.getMultinameId(multiname, true); - - MethodInfo methodInfo = new MethodInfo(); - int methodInfoId = addMethodInfo(methodInfo); - MethodBody methodBody = new MethodBody(); - methodBody.method_info = methodInfoId; - addMethodBody(methodBody); - - TraitMethodGetterSetter trait = new TraitMethodGetterSetter(); - trait.name_index = multinameId; - trait.kindType = Trait.TRAIT_METHOD; - if (isStatic) { - trait.kindFlags = Trait.ATTR_Final; - } - - trait.method_info = methodInfoId; - if (isStatic) { - ClassInfo classInfo = class_info.get(classId); - classInfo.static_traits.addTrait(trait); - trait.disp_id = classInfo.getNextDispId(); - } else { - InstanceInfo instanceInfo = instance_info.get(classId); - instanceInfo.instance_traits.addTrait(trait); - } - - return trait; - } - - public void addEventListener(EventListener listener) { - listeners.add(listener); - } - - public void removeEventListener(EventListener listener) { - listeners.remove(listener); - } - - protected void informListeners(String event, Object data) { - for (EventListener listener : listeners) { - listener.handleEvent(event, data); - } - } - - public int removeTraps() throws InterruptedException { - int rem = 0; - for (int s = 0; s < script_info.size(); s++) { - rem += script_info.get(s).removeTraps(s, this, ""); - } - return rem; - } - - public int removeDeadCode() throws InterruptedException { - int rem = 0; - for (MethodBody body : bodies) { - rem += body.removeDeadCode(constants, null/*FIXME*/, method_info.get(body.method_info)); - } - return rem; - } - - public Set getNsStringUsages() { - Set ret = new HashSet<>(); - for (int n = 1; n < constants.getNamespaceCount(); n++) { - ret.add(constants.getNamespace(n).name_index); - } - return ret; - } - - public Set getStringUsages() { - Set ret = new HashSet<>(); - for (MethodBody body : bodies) { - for (AVM2Instruction ins : body.getCode().code) { - for (int i = 0; i < ins.definition.operands.length; i++) { - if (ins.definition.operands[i] == AVM2Code.DAT_STRING_INDEX) { - ret.add(ins.operands[i]); - } - } - } - } - return ret; - } - - private void setStringUsageType(Map ret, int strIndex, String usageType) { - if (ret.containsKey(strIndex)) { - if (!"name".equals(usageType)) { - if (!ret.get(strIndex).equals(usageType)) { - ret.put(strIndex, "name"); - } - } - } else { - ret.put(strIndex, usageType); - } - } - - private void getStringUsageTypes(Map ret, Traits traits, boolean classesOnly) { - for (Trait t : traits.traits) { - int strIndex = constants.getMultiname(t.name_index).name_index; - String usageType = ""; - if (t instanceof TraitClass) { - TraitClass tc = (TraitClass) t; - getStringUsageTypes(ret, class_info.get(tc.class_info).static_traits, classesOnly); - getStringUsageTypes(ret, instance_info.get(tc.class_info).instance_traits, classesOnly); - - if (instance_info.get(tc.class_info).name_index != 0) { - setStringUsageType(ret, constants.getMultiname(instance_info.get(tc.class_info).name_index).name_index, "class"); - } - if (instance_info.get(tc.class_info).super_index != 0) { - setStringUsageType(ret, constants.getMultiname(instance_info.get(tc.class_info).super_index).name_index, "class"); - } - - usageType = "class"; - } - if (t instanceof TraitMethodGetterSetter) { - TraitMethodGetterSetter tm = (TraitMethodGetterSetter) t; - usageType = "method"; - MethodBody body = findBody(tm.method_info); - if (body != null) { - getStringUsageTypes(ret, body.traits, classesOnly); - } - } - if (t instanceof TraitFunction) { - TraitFunction tf = (TraitFunction) t; - MethodBody body = findBody(tf.method_info); - if (body != null) { - getStringUsageTypes(ret, body.traits, classesOnly); - } - usageType = "function"; - } - if (t instanceof TraitSlotConst) { - TraitSlotConst ts = (TraitSlotConst) t; - if (ts.isVar()) { - usageType = "var"; - } - if (ts.isConst()) { - usageType = "const"; - } - } - if (usageType.equals("class") || (!classesOnly)) { - setStringUsageType(ret, strIndex, usageType); - } - } - } - - public void getStringUsageTypes(Map ret, boolean classesOnly) { - for (ScriptInfo script : script_info) { - getStringUsageTypes(ret, script.traits, classesOnly); - } - } - - public void renameMultiname(int multinameIndex, String newname) { - if (multinameIndex <= 0 || multinameIndex >= constants.getMultinameCount()) { - throw new IllegalArgumentException("Multiname with index " + multinameIndex + " does not exist"); - } - Set stringUsages = getStringUsages(); - Set namespaceUsages = getNsStringUsages(); - int strIndex = constants.getMultiname(multinameIndex).name_index; - if (stringUsages.contains(strIndex) || namespaceUsages.contains(strIndex)) { // name is used elsewhere as string literal - strIndex = constants.getStringId(newname, true); - constants.getMultiname(multinameIndex).name_index = strIndex; - } else { - constants.setString(strIndex, newname); - } - } - - public void deobfuscateIdentifiers(HashMap namesMap, RenameType renameType, boolean classesOnly) { - Set stringUsages = getStringUsages(); - Set namespaceUsages = getNsStringUsages(); - Map stringUsageTypes = new HashMap<>(); - informListeners("deobfuscate", "Getting usage types..."); - getStringUsageTypes(stringUsageTypes, classesOnly); - AVM2Deobfuscation deobfuscation = getDeobfuscation(); - for (int i = 0; i < instance_info.size(); i++) { - informListeners("deobfuscate", "class " + i + "/" + instance_info.size()); - InstanceInfo insti = instance_info.get(i); - if (insti.name_index != 0) { - constants.getMultiname(insti.name_index).name_index = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(insti.name_index).name_index, true, renameType); - if (constants.getMultiname(insti.name_index).namespace_index != 0) { - constants.getNamespace(constants.getMultiname(insti.name_index).namespace_index).name_index - = deobfuscation.deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, constants.getNamespace(constants.getMultiname(insti.name_index).namespace_index).name_index, renameType); - } - } - if (insti.super_index != 0) { - constants.getMultiname(insti.super_index).name_index = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(insti.super_index).name_index, true, renameType); - } - } - if (classesOnly) { - return; - } - for (int i = 1; i < constants.getMultinameCount(); i++) { - informListeners("deobfuscate", "name " + i + "/" + constants.getMultinameCount()); - constants.getMultiname(i).name_index = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(i).name_index, false, renameType); - } - for (int i = 1; i < constants.getNamespaceCount(); i++) { - informListeners("deobfuscate", "namespace " + i + "/" + constants.getNamespaceCount()); - if (constants.getNamespace(i).kind != Namespace.KIND_PACKAGE) { // only packages - continue; - } - constants.getNamespace(i).name_index = deobfuscation.deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, constants.getNamespace(i).name_index, renameType); - } - - // process reflection using getDefinitionByName too - for (MethodBody body : bodies) { - for (int ip = 0; ip < body.getCode().code.size(); ip++) { - if (body.getCode().code.get(ip).definition instanceof CallPropertyIns) { - int mIndex = body.getCode().code.get(ip).operands[0]; - if (mIndex > 0) { - Multiname m = constants.getMultiname(mIndex); - if (m.getNameWithNamespace(constants).toRawString().equals("flash.utils.getDefinitionByName")) { - if (ip > 0) { - if (body.getCode().code.get(ip - 1).definition instanceof PushStringIns) { - int strIndex = body.getCode().code.get(ip - 1).operands[0]; - String fullname = constants.getString(strIndex); - String pkg = ""; - String name = fullname; - if (fullname.contains(".")) { - pkg = fullname.substring(0, fullname.lastIndexOf('.')); - name = fullname.substring(fullname.lastIndexOf('.') + 1); - } - if (!pkg.isEmpty()) { - int pkgStrIndex = constants.getStringId(pkg, true); - pkgStrIndex = deobfuscation.deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, pkgStrIndex, renameType); - pkg = constants.getString(pkgStrIndex); - } - int nameStrIndex = constants.getStringId(name, true); - nameStrIndex = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, nameStrIndex, true, renameType); - name = constants.getString(nameStrIndex); - String fullChanged = ""; - if (!pkg.isEmpty()) { - fullChanged = pkg + "."; - } - fullChanged += name; - strIndex = constants.getStringId(fullChanged, true); - body.getCode().code.get(ip - 1).operands[0] = strIndex; - } - } - } - } - } - } - } - } - - public boolean hasDecimalSupport() { - return version.minor >= MINORwithDECIMAL; - } - - public void setDecimalSupport(boolean val) { - if (val) { - if (version.minor != MINORwithDECIMAL) { - version.minor = MINORwithDECIMAL; - ((Tag) parentTag).setModified(true); - } - } else if (version.minor == MINORwithDECIMAL) { - version.minor = MINORwithDECIMAL - 1; - ((Tag) parentTag).setModified(true); - } - } - - private boolean minVersionCheck(int minMajor, int minMinor) { - return version.compareTo(new ABCVersion(minMajor, minMinor)) >= 0; - } - - public boolean hasFloatSupport() { - return minVersionCheck(47, 16); - } - - public void setFloatSupport(boolean val) { - if (val) { - if (version.major < 47) { - version.major = 47; - ((Tag) parentTag).setModified(true); - } - } else if (version.major > 46) { - version.major = 46; - ((Tag) parentTag).setModified(true); - } - } - - public boolean hasExceptionSupport() { - return version.compareTo(new ABCVersion(46, 15)) > 0; - } - - public ABC(ABCInputStream ais, SWF swf, ABCContainerTag tag) throws IOException { - this.parentTag = tag; - int minor_version = ais.readU16("minor_version"); - int major_version = ais.readU16("major_version"); - version = new ABCVersion(major_version, minor_version); - logger.log(Level.FINE, "ABC minor_version: {0}, major_version: {1}", new Object[]{minor_version, major_version}); - - ais.newDumpLevel("constant_pool", "cpool_info"); - - // constant integers - int constant_int_pool_count = ais.readU30("int_count"); - constants.ensureIntCapacity(constant_int_pool_count); - if (constant_int_pool_count > 1) { - ais.newDumpLevel("integers", "integer[]"); - for (int i = 1; i < constant_int_pool_count; i++) { // index 0 not used. Values 1..n-1 - constants.addInt(ais.readS32("int")); - } - ais.endDumpLevel(); - } - - // constant unsigned integers - int constant_uint_pool_count = ais.readU30("uint_count"); - constants.ensureUIntCapacity(constant_uint_pool_count); - if (constant_uint_pool_count > 1) { - ais.newDumpLevel("uintegers", "uinteger[]"); - for (int i = 1; i < constant_uint_pool_count; i++) { // index 0 not used. Values 1..n-1 - constants.addUInt(ais.readU32("uint")); - } - ais.endDumpLevel(); - } - - // constant double - int constant_double_pool_count = ais.readU30("double_count"); - constants.ensureDoubleCapacity(constant_double_pool_count); - if (constant_double_pool_count > 1) { - ais.newDumpLevel("doubles", "double[]"); - for (int i = 1; i < constant_double_pool_count; i++) { // index 0 not used. Values 1..n-1 - constants.addDouble(ais.readDouble("double")); - } - ais.endDumpLevel(); - } - - // constant decimal - if (hasDecimalSupport()) { - int constant_decimal_pool_count = ais.readU30("decimal_count"); - constants.ensureDecimalCapacity(constant_decimal_pool_count); - if (constant_decimal_pool_count > 1) { - ais.newDumpLevel("decimals", "decimal[]"); - for (int i = 1; i < constant_decimal_pool_count; i++) { // index 0 not used. Values 1..n-1 - constants.addDecimal(ais.readDecimal("decimal")); - } - ais.endDumpLevel(); - } - } - - if (hasFloatSupport()) { - // constant float - int constant_float_pool_count = ais.readU30("float_count"); - if (constant_float_pool_count > 1) { - ais.newDumpLevel("floats", "float[]"); - for (int i = 1; i < constant_float_pool_count; i++) { // index 0 not used. Values 1..n-1 - constants.addFloat(ais.readFloat("float")); - } - ais.endDumpLevel(); - } - // constant float4 - int constant_float4_pool_count = ais.readU30("float4_count"); - if (constant_float4_pool_count > 1) { - ais.newDumpLevel("floats4", "float4[]"); - for (int i = 1; i < constant_float4_pool_count; i++) { // index 0 not used. Values 1..n-1 - constants.addFloat4(ais.readFloat4("float4")); - } - ais.endDumpLevel(); - } - } - - // constant string - int constant_string_pool_count = ais.readU30("string_count"); - constants.ensureStringCapacity(constant_string_pool_count); - if (constant_string_pool_count > 1) { - ais.newDumpLevel("strings", "string[]"); - for (int i = 1; i < constant_string_pool_count; i++) { // index 0 not used. Values 1..n-1 - long pos = ais.getPosition(); - constants.addString(ais.readString("string")); - } - ais.endDumpLevel(); - } - - // constant namespace - int constant_namespace_pool_count = ais.readU30("namespace_count"); - constants.ensureNamespaceCapacity(constant_namespace_pool_count); - if (constant_namespace_pool_count > 1) { - ais.newDumpLevel("namespaces", "namespace[]"); - for (int i = 1; i < constant_namespace_pool_count; i++) { // index 0 not used. Values 1..n-1 - constants.addNamespace(ais.readNamespace("namespace")); - } - ais.endDumpLevel(); - } - - // constant namespace set - int constant_namespace_set_pool_count = ais.readU30("ns_set_count"); - constants.ensureNamespaceSetCapacity(constant_namespace_set_pool_count); - if (constant_namespace_set_pool_count > 1) { - ais.newDumpLevel("ns_sets", "ns_set[]"); - for (int i = 1; i < constant_namespace_set_pool_count; i++) { // index 0 not used. Values 1..n-1 - ais.newDumpLevel("ns_set_infos", "ns_set_info[]"); - constants.addNamespaceSet(new NamespaceSet()); - int namespace_count = ais.readU30("count"); - constants.getNamespaceSet(i).namespaces = new int[namespace_count]; - for (int j = 0; j < namespace_count; j++) { - constants.getNamespaceSet(i).namespaces[j] = ais.readU30("ns"); - } - ais.endDumpLevel(); - } - ais.endDumpLevel(); - } - - // constant multiname - int constant_multiname_pool_count = ais.readU30("multiname_count"); - constants.ensureMultinameCapacity(constant_multiname_pool_count); - if (constant_multiname_pool_count > 1) { - ais.newDumpLevel("multiname", "multinames[]"); - for (int i = 1; i < constant_multiname_pool_count; i++) { // index 0 not used. Values 1..n-1 - constants.addMultiname(ais.readMultiname("multiname")); - } - ais.endDumpLevel(); - } - - ais.endDumpLevel(); // cpool_info - - // method info - int methods_count = ais.readU30("methods_count"); - method_info = new ArrayList<>(methods_count); // MethodInfo[methods_count]; - for (int i = 0; i < methods_count; i++) { - method_info.add(ais.readMethodInfo("method")); - } - - // metadata info - int metadata_count = ais.readU30("metadata_count"); - metadata_info = new ArrayList<>(metadata_count); - for (int i = 0; i < metadata_count; i++) { - int name_index = ais.readU30("name_index"); - int values_count = ais.readU30("values_count"); - int[] keys = new int[values_count]; - for (int v = 0; v < values_count; v++) { - keys[v] = ais.readU30("key"); - } - int[] values = new int[values_count]; - for (int v = 0; v < values_count; v++) { - values[v] = ais.readU30("value"); - } - metadata_info.add(new MetadataInfo(name_index, keys, values)); - } - - int class_count = ais.readU30("class_count"); - instance_info = new ArrayList<>(class_count); - for (int i = 0; i < class_count; i++) { - instance_info.add(ais.readInstanceInfo("instance")); - } - class_info = new ArrayList<>(class_count); - for (int i = 0; i < class_count; i++) { - ais.newDumpLevel("class", "class_info"); - ClassInfo ci = new ClassInfo(null); // do not create Traits in constructor - ci.cinit_index = ais.readU30("cinit_index"); - ci.static_traits = ais.readTraits("static_traits"); - class_info.add(ci); - ais.endDumpLevel(); - } - int script_count = ais.readU30("script_count"); - script_info = new ArrayList<>(script_count); - for (int i = 0; i < script_count; i++) { - ais.newDumpLevel("script", "script_info"); - ScriptInfo si = new ScriptInfo(null); // do not create Traits in constructor - si.init_index = ais.readU30("init_index"); - si.traits = ais.readTraits("traits"); - script_info.add(si); - ais.endDumpLevel(); - si.setModified(false); - } - - int bodies_count = ais.readU30("bodies_count"); - bodies = new ArrayList<>(bodies_count); - for (int i = 0; i < bodies_count; i++) { - DumpInfo di = ais.dumpInfo; - DumpInfoSpecial dis = (DumpInfoSpecial) ais.newDumpLevel("method_body", "method_body_info", DumpInfoSpecialType.ABC_METHOD_BODY); - MethodBody mb = new MethodBody(this, null, null, null); // do not create Traits in constructor - try { - mb.method_info = ais.readU30("method_info"); - if (dis != null) { - dis.specialValue = mb.method_info; - } - - mb.max_stack = ais.readU30("max_stack"); - mb.max_regs = ais.readU30("max_regs"); - mb.init_scope_depth = ais.readU30("init_scope_depth"); - mb.max_scope_depth = ais.readU30("max_scope_depth"); - int code_length = ais.readU30("code_length"); - mb.setCodeBytes(ais.readBytes(code_length, "code", DumpInfoSpecialType.ABC_CODE)); - int ex_count = ais.readU30("ex_count"); - mb.exceptions = new ABCException[ex_count]; - for (int j = 0; j < ex_count; j++) { - ABCException abce = new ABCException(); - abce.start = ais.readU30("start"); - abce.end = ais.readU30("end"); - abce.target = ais.readU30("target"); - abce.type_index = ais.readU30("type_index"); - if (hasExceptionSupport()) { - abce.name_index = ais.readU30("name_index"); - } else { - abce.name_index = 0; - } - mb.exceptions[j] = abce; - } - mb.traits = ais.readTraits("traits"); - bodies.add(mb); - ais.endDumpLevel(); - } catch (EndOfStreamException ex) { - logger.log(Level.SEVERE, "MethodBody reading: End of stream", ex); - ais.endDumpLevelUntil(di); - break; - } - - SWFDecompilerPlugin.fireMethodBodyParsed(this, mb, swf); - } - - getMethodIndexing(); - - /*for(int i=0;i()); - } catch (InterruptedException ex) { - Logger.getLogger(ABC.class.getName()).log(Level.SEVERE, null, ex); - } - System.out.println(""+t.toString()); - } - //System.exit(0);*/ - SWFDecompilerPlugin.fireAbcParsed(this, swf); - } - - public void saveToStream(OutputStream os) throws IOException { - ABCOutputStream aos = new ABCOutputStream(os); - aos.writeU16(version.minor); - aos.writeU16(version.major); - - aos.writeU30(constants.getIntCount()); - for (int i = 1; i < constants.getIntCount(); i++) { - aos.writeS32(constants.getInt(i)); - } - aos.writeU30(constants.getUIntCount()); - for (int i = 1; i < constants.getUIntCount(); i++) { - aos.writeU32(constants.getUInt(i)); - } - - aos.writeU30(constants.getDoubleCount()); - for (int i = 1; i < constants.getDoubleCount(); i++) { - aos.writeDouble(constants.getDouble(i)); - } - - if (hasDecimalSupport()) { - aos.writeU30(constants.getDecimalCount()); - for (int i = 1; i < constants.getDecimalCount(); i++) { - aos.writeDecimal(constants.getDecimal(i)); - } - } - if (hasFloatSupport()) { - aos.writeU30(constants.getFloatCount()); - for (int i = 1; i < constants.getFloatCount(); i++) { - aos.writeFloat(constants.getFloat(i)); - } - aos.writeU30(constants.getFloat4Count()); - for (int i = 1; i < constants.getFloat4Count(); i++) { - aos.writeFloat4(constants.getFloat4(i)); - } - } - - aos.writeU30(constants.getStringCount()); - for (int i = 1; i < constants.getStringCount(); i++) { - aos.writeString(constants.getString(i)); - } - - aos.writeU30(constants.getNamespaceCount()); - for (int i = 1; i < constants.getNamespaceCount(); i++) { - aos.writeNamespace(constants.getNamespace(i)); - } - - aos.writeU30(constants.getNamespaceSetCount()); - for (int i = 1; i < constants.getNamespaceSetCount(); i++) { - aos.writeU30(constants.getNamespaceSet(i).namespaces.length); - for (int j = 0; j < constants.getNamespaceSet(i).namespaces.length; j++) { - aos.writeU30(constants.getNamespaceSet(i).namespaces[j]); - } - } - - aos.writeU30(constants.getMultinameCount()); - for (int i = 1; i < constants.getMultinameCount(); i++) { - aos.writeMultiname(constants.getMultiname(i)); - } - - aos.writeU30(method_info.size()); - for (MethodInfo mi : method_info) { - aos.writeMethodInfo(mi); - } - - aos.writeU30(metadata_info.size()); - for (MetadataInfo mi : metadata_info) { - aos.writeU30(mi.name_index); - aos.writeU30(mi.values.length); - for (int j = 0; j < mi.values.length; j++) { - aos.writeU30(mi.keys[j]); - } - for (int j = 0; j < mi.values.length; j++) { - aos.writeU30(mi.values[j]); - } - } - - aos.writeU30(class_info.size()); - for (InstanceInfo ii : instance_info) { - aos.writeInstanceInfo(ii); - } - for (ClassInfo ci : class_info) { - aos.writeU30(ci.cinit_index); - aos.writeTraits(ci.static_traits); - } - aos.writeU30(script_info.size()); - for (ScriptInfo si : script_info) { - aos.writeU30(si.init_index); - aos.writeTraits(si.traits); - } - - aos.writeU30(bodies.size()); - for (MethodBody mb : bodies) { - aos.writeU30(mb.method_info); - aos.writeU30(mb.max_stack); - aos.writeU30(mb.max_regs); - aos.writeU30(mb.init_scope_depth); - aos.writeU30(mb.max_scope_depth); - byte[] codeBytes = mb.getCodeBytes(); - aos.writeU30(codeBytes.length); - aos.write(codeBytes); - aos.writeU30(mb.exceptions.length); - for (int j = 0; j < mb.exceptions.length; j++) { - aos.writeU30(mb.exceptions[j].start); - aos.writeU30(mb.exceptions[j].end); - aos.writeU30(mb.exceptions[j].target); - aos.writeU30(mb.exceptions[j].type_index); - aos.writeU30(mb.exceptions[j].name_index); - } - aos.writeTraits(mb.traits); - } - } - - public MethodBody findBody(MethodInfo methodInfo) { - return getMethodIndexing().findMethodBody(methodInfo); - } - - public MethodBody findBody(int methodInfo) { - return getMethodIndexing().findMethodBody(methodInfo); - } - - public int findBodyIndex(MethodInfo methodInfo) { - return getMethodIndexing().findMethodBodyIndex(methodInfo); - } - - public int findBodyIndex(int methodInfo) { - return getMethodIndexing().findMethodBodyIndex(methodInfo); - } - - public MethodBody findBodyClassInitializerByClass(String className) { - for (int i = 0; i < instance_info.size(); i++) { - if (className.equals(constants.getMultiname(instance_info.get(i).name_index).getName(constants, null, true))) { - MethodBody body = findBody(class_info.get(i).cinit_index); - if (body != null) { - return body; - } - } - } - - return null; - } - - public MethodBody findBodyInstanceInitializerByClass(String className) { - for (int i = 0; i < instance_info.size(); i++) { - if (className.equals(constants.getMultiname(instance_info.get(i).name_index).getName(constants, null, true))) { - MethodBody body = findBody(instance_info.get(i).iinit_index); - if (body != null) { - return body; - } - } - } - - return null; - } - - public MethodBody findBodyByClassAndName(String className, String methodName) { - for (int i = 0; i < instance_info.size(); i++) { - if (className.equals(constants.getMultiname(instance_info.get(i).name_index).getName(constants, null, true))) { - for (Trait t : instance_info.get(i).instance_traits.traits) { - if (t instanceof TraitMethodGetterSetter) { - TraitMethodGetterSetter t2 = (TraitMethodGetterSetter) t; - if (methodName.equals(t2.getName(this).getName(constants, null, true))) { - MethodBody body = findBody(t2.method_info); - if (body != null) { - return body; - } - } - } - } - - for (Trait t : class_info.get(i).static_traits.traits) { - if (t instanceof TraitMethodGetterSetter) { - TraitMethodGetterSetter t2 = (TraitMethodGetterSetter) t; - if (methodName.equals(t2.getName(this).getName(constants, null, true))) { - MethodBody body = findBody(t2.method_info); - if (body != null) { - return body; - } - } - } - } - //break; - } - } - - return null; - } - - public boolean isStaticTraitId(int classIndex, int traitId) { - if (traitId < class_info.get(classIndex).static_traits.traits.size()) { - return true; - } else if (traitId < class_info.get(classIndex).static_traits.traits.size() + instance_info.get(classIndex).instance_traits.traits.size()) { - return false; - } else { - return true; // Can be class or instance initializer - } - } - - public Trait findTraitByTraitId(int classIndex, int traitId) { - if (classIndex == -1) { - return null; - } - List staticTraits = class_info.get(classIndex).static_traits.traits; - if (traitId >= 0 && traitId < staticTraits.size()) { - return staticTraits.get(traitId); - } else { - List instanceTraits = instance_info.get(classIndex).instance_traits.traits; - if (traitId >= 0 && traitId < staticTraits.size() + instanceTraits.size()) { - traitId -= staticTraits.size(); - return instanceTraits.get(traitId); - } else { - return null; // Can be class or instance initializer - } - } - } - - public int findMethodIdByTraitId(int classIndex, int traitId) { - if (classIndex == -1) { - return -1; - } - List staticTraits = class_info.get(classIndex).static_traits.traits; - if (traitId < staticTraits.size()) { - if (staticTraits.get(traitId) instanceof TraitMethodGetterSetter) { - return ((TraitMethodGetterSetter) staticTraits.get(traitId)).method_info; - } else { - return -1; - } - } else { - List instanceTraits = instance_info.get(classIndex).instance_traits.traits; - if (traitId < staticTraits.size() + instanceTraits.size()) { - traitId -= staticTraits.size(); - if (instanceTraits.get(traitId) instanceof TraitMethodGetterSetter) { - return ((TraitMethodGetterSetter) instanceTraits.get(traitId)).method_info; - } else { - return -1; - } - } else { - traitId -= staticTraits.size() + instanceTraits.size(); - if (traitId == 0) { - return instance_info.get(classIndex).iinit_index; - } else if (traitId == 1) { - return class_info.get(classIndex).cinit_index; - } else { - return -1; - } - } - } - } - - private Map getNamespaceMap() { - if (namespaceMap == null) { - Map map = new HashMap<>(); - for (ScriptInfo si : script_info) { - for (Trait t : si.traits.traits) { - if (t instanceof TraitSlotConst) { - TraitSlotConst s = ((TraitSlotConst) t); - if (s.isNamespace()) { - String key = constants.getNamespace(s.value_index).getName(constants).toRawString(); // assume not null - DottedChain val = constants.getMultiname(s.name_index).getNameWithNamespace(constants); - map.put(key, val); - } - } - } - } - namespaceMap = map; - } - - return namespaceMap; - } - - private AVM2Deobfuscation getDeobfuscation() { - if (deobfuscation == null) { - deobfuscation = new AVM2Deobfuscation(getSwf(), constants); - } - - return deobfuscation; - } - - public final ABCMethodIndexing getMethodIndexing() { - if (abcMethodIndexing == null) { - abcMethodIndexing = new ABCMethodIndexing(this); - } - - return abcMethodIndexing; - } - - public DottedChain nsValueToName(String valueStr) { - if (valueStr == null) { - return DottedChain.EMPTY; - } - - if (getNamespaceMap().containsKey(valueStr)) { - return getNamespaceMap().get(valueStr); - } else { - DottedChain ns = getDeobfuscation().builtInNs(valueStr); - if (ns == null) { - return DottedChain.EMPTY; - } else { - return ns; - } - } - } - - public List getScriptPacks(String packagePrefix, List allAbcs) { - List ret = new ArrayList<>(); - for (int i = 0; i < script_info.size(); i++) { - ret.addAll(script_info.get(i).getPacks(this, i, packagePrefix, allAbcs)); - } - return ret; - } - - public void dump(OutputStream os) { - Utf8PrintWriter output; - output = new Utf8PrintWriter(os); - constants.dump(output); - for (int i = 0; i < method_info.size(); i++) { - output.println("MethodInfo[" + i + "]:" + method_info.get(i).toString(constants, new ArrayList<>())); - } - for (int i = 0; i < metadata_info.size(); i++) { - output.println("MetadataInfo[" + i + "]:" + metadata_info.get(i).toString(constants)); - } - for (int i = 0; i < instance_info.size(); i++) { - output.println("InstanceInfo[" + i + "]:" + instance_info.get(i).toString(this, new ArrayList<>())); - } - for (int i = 0; i < class_info.size(); i++) { - output.println("ClassInfo[" + i + "]:" + class_info.get(i).toString(this, new ArrayList<>())); - } - for (int i = 0; i < script_info.size(); i++) { - output.println("ScriptInfo[" + i + "]:" + script_info.get(i).toString(this, new ArrayList<>())); - } - for (int i = 0; i < bodies.size(); i++) { - output.println("MethodBody[" + i + "]:"); //+ bodies[i].toString(this, constants, method_info)); - } - } - - private void checkMultinameUsedInMethod(int multinameIndex, int methodInfo, List ret, int classIndex, int traitIndex, boolean isStatic, boolean isInitializer, Traits traits, int parentTraitIndex) { - for (int p = 0; p < method_info.get(methodInfo).param_types.length; p++) { - if (method_info.get(methodInfo).param_types[p] == multinameIndex) { - ret.add(new MethodParamsMultinameUsage(this, multinameIndex, classIndex, traitIndex, isStatic, isInitializer, traits, parentTraitIndex)); - break; - } - } - if (method_info.get(methodInfo).ret_type == multinameIndex) { - ret.add(new MethodReturnTypeMultinameUsage(this, multinameIndex, classIndex, traitIndex, isStatic, isInitializer, traits, parentTraitIndex)); - } - MethodBody body = findBody(methodInfo); - if (body != null) { - findMultinameUsageInTraits(body.traits, multinameIndex, isStatic, classIndex, ret, traitIndex); - for (ABCException e : body.exceptions) { - if ((e.name_index == multinameIndex) || (e.type_index == multinameIndex)) { - ret.add(new MethodBodyMultinameUsage(this, multinameIndex, classIndex, traitIndex, isStatic, 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) { - if (ins.operands[o] == multinameIndex) { - ret.add(new MethodBodyMultinameUsage(this, multinameIndex, classIndex, traitIndex, isStatic, isInitializer, traits, parentTraitIndex)); - return; - } - } - } - } - } - } - - private void findMultinameUsageInTraits(Traits traits, int multinameIndex, boolean isStatic, int classIndex, List ret, int parentTraitIndex) { - for (int t = 0; t < traits.traits.size(); t++) { - if (traits.traits.get(t) instanceof TraitSlotConst) { - TraitSlotConst tsc = (TraitSlotConst) traits.traits.get(t); - if (tsc.name_index == multinameIndex) { - ret.add(new ConstVarNameMultinameUsage(this, multinameIndex, classIndex, t, isStatic, traits, parentTraitIndex)); - } - if (tsc.type_index == multinameIndex) { - ret.add(new ConstVarTypeMultinameUsage(this, multinameIndex, classIndex, t, isStatic, traits, parentTraitIndex)); - } - } - if (traits.traits.get(t) instanceof TraitMethodGetterSetter) { - TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) traits.traits.get(t); - if (tmgs.name_index == multinameIndex) { - ret.add(new MethodNameMultinameUsage(this, multinameIndex, classIndex, t, isStatic, false, traits, parentTraitIndex)); - } - checkMultinameUsedInMethod(multinameIndex, tmgs.method_info, ret, classIndex, t, isStatic, false, traits, parentTraitIndex); - } - } - } - - public List findMultinameDefinition(int multinameIndex) { - List usages = findMultinameUsage(multinameIndex); - List ret = new ArrayList<>(); - for (MultinameUsage u : usages) { - if (u instanceof DefinitionUsage) { - ret.add(u); - } - } - return ret; - } - - public List findMultinameUsage(int multinameIndex) { - List ret = new ArrayList<>(); - if (multinameIndex == 0) { - return ret; - } - for (int c = 0; c < instance_info.size(); c++) { - if (instance_info.get(c).name_index == multinameIndex) { - ret.add(new ClassNameMultinameUsage(this, multinameIndex, c)); - } - if (instance_info.get(c).super_index == multinameIndex) { - ret.add(new ExtendsMultinameUsage(this, multinameIndex, c)); - } - for (int i = 0; i < instance_info.get(c).interfaces.length; i++) { - if (instance_info.get(c).interfaces[i] == multinameIndex) { - ret.add(new ImplementsMultinameUsage(this, multinameIndex, c)); - } - } - checkMultinameUsedInMethod(multinameIndex, instance_info.get(c).iinit_index, ret, c, 0, false, true, null, -1); - checkMultinameUsedInMethod(multinameIndex, class_info.get(c).cinit_index, ret, c, 0, true, true, null, -1); - findMultinameUsageInTraits(instance_info.get(c).instance_traits, multinameIndex, false, c, ret, -1); - findMultinameUsageInTraits(class_info.get(c).static_traits, multinameIndex, true, c, ret, -1); - } - loopm: - for (int m = 1; m < constants.getMultinameCount(); m++) { - if (constants.getMultiname(m).kind == Multiname.TYPENAME) { - if (constants.getMultiname(m).qname_index == multinameIndex) { - ret.add(new TypeNameMultinameUsage(this, m)); - continue; - } - for (int mp : constants.getMultiname(m).params) { - if (mp == multinameIndex) { - ret.add(new TypeNameMultinameUsage(this, m)); - continue loopm; - } - } - } - } - return ret; - } - - public int findMethodInfoByName(int classId, String methodName) { - if (classId > -1) { - for (Trait t : instance_info.get(classId).instance_traits.traits) { - if (t instanceof TraitMethodGetterSetter) { - if (t.getName(this).getName(constants, null, true).equals(methodName)) { - return ((TraitMethodGetterSetter) t).method_info; - } - } - } - } - return -1; - } - - public int findMethodBodyByName(int classId, String methodName) { - if (classId > -1) { - for (Trait t : instance_info.get(classId).instance_traits.traits) { - if (t instanceof TraitMethodGetterSetter) { - if (t.getName(this).getName(constants, null, true).equals(methodName)) { - return findBodyIndex(((TraitMethodGetterSetter) t).method_info); - } - } - } - } - return -1; - } - - public int findMethodBodyByName(String className, String methodName) { - int classId = findClassByName(className); - return findMethodBodyByName(classId, methodName); - } - - public int findClassByName(DottedChain name) { - String str = name == null ? null : name.toRawString(); - return findClassByName(str); - } - - public int findClassByName(String name) { - for (int c = 0; c < instance_info.size(); c++) { - DottedChain s = constants.getMultiname(instance_info.get(c).name_index).getNameWithNamespace(constants); - if (name.equals(s.toRawString())) { - return c; - } - } - return -1; - } - - public List findScriptPacksByPath(String name, List allAbcs) { - List ret = new ArrayList<>(); - List allPacks = getScriptPacks(null, allAbcs); // todo: honfika: use filter parameter - if (name.endsWith(".**") || name.equals("**") || name.endsWith(".++") || name.equals("++")) { - name = name.substring(0, name.length() - 2); - - for (ScriptPack en : allPacks) { - if (en.getClassPath().toString().startsWith(name)) { - ret.add(en); - } - } - } else if (name.endsWith(".*") || name.equals("*") || name.endsWith(".+") || name.equals("+")) { - name = name.substring(0, name.length() - 1); - for (ScriptPack en : allPacks) { - String classPathStr = en.getClassPath().toString(); - if (classPathStr.startsWith(name)) { - String rem = name.isEmpty() ? classPathStr : classPathStr.substring(name.length()); - if (!rem.contains(".")) { - ret.add(en); - } - } - } - } else { - ScriptPack p = findScriptPackByPath(name, allAbcs); - if (p != null) { - ret.add(p); - } - } - return ret; - - } - - public ScriptPack findScriptPackByPath(String name, List allAbcs) { - List packs = getScriptPacks(null, allAbcs); - for (ScriptPack en : packs) { - if (en.getClassPath().toString().equals(name)) { - return en; - } - } - return null; - } - - private void removeClassFromTraits(Traits traits, int index) { - for (Trait t : traits.traits) { - if (t instanceof TraitClass) { - TraitClass tc = (TraitClass) t; - removeClassFromTraits(instance_info.get(tc.class_info).instance_traits, index); - removeClassFromTraits(class_info.get(tc.class_info).static_traits, index); - if (tc.class_info > index) { - tc.class_info--; - } - } - } - } - - public void addClass(ClassInfo ci, InstanceInfo ii, int index) { - for (MethodBody b : bodies) { - for (AVM2Instruction ins : b.getCode().code) { - for (int i = 0; i < ins.definition.operands.length; i++) { - if (ins.definition.operands[i] == AVM2Code.DAT_CLASS_INDEX) { - if (ins.operands[i] >= index) { - ins.operands[i]++; - } - } - } - } - } - for (ScriptInfo si : script_info) { - addClassInTraits(si.traits, index); - } - for (MethodBody b : bodies) { - addClassInTraits(b.traits, index); - } - instance_info.add(index, ii); - class_info.add(index, ci); - } - - private void addClassInTraits(Traits traits, int index) { - for (Trait t : traits.traits) { - if (t instanceof TraitClass) { - TraitClass tc = (TraitClass) t; - addClassInTraits(instance_info.get(tc.class_info).instance_traits, index); - addClassInTraits(class_info.get(tc.class_info).static_traits, index); - if (tc.class_info >= index) { - tc.class_info++; - } - } - } - } - - public void removeClass(int index) { - for (MethodBody b : bodies) { - for (AVM2Instruction ins : b.getCode().code) { - for (int i = 0; i < ins.definition.operands.length; i++) { - if (ins.definition.operands[i] == AVM2Code.DAT_CLASS_INDEX) { - if (ins.operands[i] > index) { - ins.operands[i]--; - } - } - } - } - } - for (ScriptInfo si : script_info) { - removeClassFromTraits(si.traits, index); - } - for (MethodBody b : bodies) { - removeClassFromTraits(b.traits, index); - } - instance_info.remove(index); - class_info.remove(index); - } - - private void removeMethodFromTraits(Traits traits, int index) { - for (Trait t : traits.traits) { - if (t instanceof TraitClass) { - TraitClass tc = (TraitClass) t; - removeMethodFromTraits(instance_info.get(tc.class_info).instance_traits, index); - removeMethodFromTraits(class_info.get(tc.class_info).static_traits, index); - } - if (t instanceof TraitMethodGetterSetter) { - TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) t; - if (tmgs.method_info > index) { - tmgs.method_info--; - } - } - if (t instanceof TraitFunction) { - TraitFunction tf = (TraitFunction) t; - if (tf.method_info > index) { - tf.method_info--; - } - } - } - } - - public void removeMethod(int index) { - - int bindex = -1; - for (int b = 0; b < bodies.size(); b++) { - if (bodies.get(b).method_info == index) { - bodies.remove(b); - bindex = b; - b--; - } - } - - for (MethodBody b : bodies) { - if (b.method_info > index) { - b.method_info--; - } - for (AVM2Instruction ins : b.getCode().code) { - for (int i = 0; i < ins.definition.operands.length; i++) { - if (ins.definition.operands[i] == AVM2Code.DAT_METHOD_INDEX) { - if (ins.operands[i] > index) { - ins.operands[i]--; - } - } - } - } - removeMethodFromTraits(b.traits, index); - } - - for (int c = 0; c < instance_info.size(); c++) { - InstanceInfo ii = instance_info.get(c); - if (ii.iinit_index > index) { - ii.iinit_index--; - } - ClassInfo ci = class_info.get(c); - if (ci.cinit_index > index) { - ci.cinit_index--; - } - } - - for (ScriptInfo si : script_info) { - if (si.init_index > index) { - si.init_index--; - } - removeMethodFromTraits(si.traits, index); - } - - abcMethodIndexing = null; - - method_info.remove(index); - } - - public boolean replaceScriptPack(ScriptPack pack, String as) throws AVM2ParseException, CompilationException, IOException, InterruptedException { - String scriptName = pack.getPathScriptName() + ".as"; - int oldIndex = pack.scriptIndex; - int newIndex = script_info.size(); - String documentClass = getSwf().getDocumentClass(); - boolean isDocumentClass = documentClass != null && documentClass.equals(pack.getClassPath().toString()); - - boolean isSimple = pack.isSimple; - - ScriptInfo si = script_info.get(oldIndex); - if (isSimple) { - si.delete(this, true); - } else { - for (int t : pack.traitIndices) { - si.traits.traits.get(t).delete(this, true); - } - } - - int newClassIndex = instance_info.size(); - for (int t : pack.traitIndices) { - if (si.traits.traits.get(t) instanceof TraitClass) { - TraitClass tc = (TraitClass) si.traits.traits.get(t); - newClassIndex = tc.class_info + 1; - } - - } - List otherAbcs = new ArrayList<>(pack.allABCs); - otherAbcs.remove(this); - ActionScript3Parser.compile(as, this, otherAbcs, isDocumentClass, scriptName, newClassIndex, oldIndex); - - if (isSimple) { - // Move newly added script to its position - script_info.set(oldIndex, script_info.get(newIndex)); - script_info.remove(newIndex); - } else { - script_info.get(newIndex).setModified(true); - //Note: Is deleting traits safe? - List todel = new ArrayList<>(new TreeSet<>(pack.traitIndices)); - for (int i = todel.size() - 1; i >= 0; i--) { - si.traits.traits.remove((int) todel.get(i)); - } - } - - script_info.get(oldIndex).setModified(true); - pack(); // removes old classes/methods - ((Tag) parentTag).setModified(true); - return !isSimple; - } - - public void pack() { - for (int c = 0; c < instance_info.size(); c++) { - if (instance_info.get(c).deleted) { - removeClass(c); - c--; - } - } - for (int m = 0; m < method_info.size(); m++) { - if (method_info.get(m).deleted) { - removeMethod(m); - m--; - } - } - - getMethodIndexing(); - } -} +/* + * Copyright (C) 2010-2016 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; + +import com.jpexs.decompiler.flash.EndOfStreamException; +import com.jpexs.decompiler.flash.EventListener; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Deobfuscation; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallPropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushStringIns; +import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException; +import com.jpexs.decompiler.flash.abc.avm2.parser.script.ActionScript3Parser; +import com.jpexs.decompiler.flash.abc.types.ABCException; +import com.jpexs.decompiler.flash.abc.types.ClassInfo; +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.ScriptInfo; +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 com.jpexs.decompiler.flash.abc.types.traits.Traits; +import com.jpexs.decompiler.flash.abc.usages.ClassNameMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.ConstVarNameMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.ConstVarTypeMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.DefinitionUsage; +import com.jpexs.decompiler.flash.abc.usages.ExtendsMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.ImplementsMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.MethodBodyMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.MethodNameMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.MethodParamsMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.MethodReturnTypeMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.MultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.TypeNameMultinameUsage; +import com.jpexs.decompiler.flash.dumpview.DumpInfo; +import com.jpexs.decompiler.flash.dumpview.DumpInfoSpecial; +import com.jpexs.decompiler.flash.dumpview.DumpInfoSpecialType; +import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin; +import com.jpexs.decompiler.flash.importers.As3ScriptReplaceException; +import com.jpexs.decompiler.flash.importers.As3ScriptReplacerInterface; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.types.annotations.Internal; +import com.jpexs.decompiler.graph.DottedChain; +import com.jpexs.helpers.utf8.Utf8PrintWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author JPEXS + */ +public class ABC { + + public ABCVersion version = new ABCVersion(47, 16); + + public AVM2ConstantPool constants = new AVM2ConstantPool(); + + public List method_info = new ArrayList<>(); + + public List metadata_info = new ArrayList<>(); + + public List instance_info = new ArrayList<>(); + + public List class_info = new ArrayList<>(); + + public List script_info = new ArrayList<>(); + + public List bodies = new ArrayList<>(); + + private ABCMethodIndexing abcMethodIndexing; + + public static final int MINORwithDECIMAL = 17; + + protected Set listeners = new HashSet<>(); + + private static final Logger logger = Logger.getLogger(ABC.class.getName()); + + private AVM2Deobfuscation deobfuscation; + + @Internal + public ABCContainerTag parentTag; + + /* Map from multiname index of namespace value to namespace name**/ + private Map namespaceMap; + + public ABC(ABCContainerTag tag) { + this.parentTag = tag; + this.deobfuscation = null; + } + + public SWF getSwf() { + return parentTag.getSwf(); + } + + public List getAbcTags() { + return getSwf().getAbcList(); + } + + public int addMethodBody(MethodBody body) { + bodies.add(body); + abcMethodIndexing = null; + return bodies.size() - 1; + } + + public int addMethodInfo(MethodInfo mi) { + method_info.add(mi); + return method_info.size() - 1; + } + + public void deleteClass(int class_info, boolean d) { + ABC abc = this; + ClassInfo classInfo = abc.class_info.get(class_info); + classInfo.deleted = d; + InstanceInfo instanceInfo = abc.instance_info.get(class_info); + instanceInfo.deleted = d; + + classInfo.static_traits.delete(abc, d); + abc.method_info.get(classInfo.cinit_index).delete(abc, d); + + instanceInfo.instance_traits.delete(abc, d); + abc.method_info.get(instanceInfo.iinit_index).delete(abc, d); + + int protectedNS = instanceInfo.protectedNS; + if (protectedNS != 0) { + abc.constants.getNamespace(protectedNS).deleted = d; + } + } + + public TraitMethodGetterSetter addMethod(int classId, String name, boolean isStatic) { + Multiname multiname = new Multiname(); + multiname.kind = Multiname.QNAME; + multiname.name_index = constants.getStringId(name, true); + multiname.namespace_index = constants.getNamespaceId(Namespace.KIND_PACKAGE, "", 0, true); + int multinameId = constants.getMultinameId(multiname, true); + + MethodInfo methodInfo = new MethodInfo(); + int methodInfoId = addMethodInfo(methodInfo); + MethodBody methodBody = new MethodBody(); + methodBody.method_info = methodInfoId; + addMethodBody(methodBody); + + TraitMethodGetterSetter trait = new TraitMethodGetterSetter(); + trait.name_index = multinameId; + trait.kindType = Trait.TRAIT_METHOD; + if (isStatic) { + trait.kindFlags = Trait.ATTR_Final; + } + + trait.method_info = methodInfoId; + if (isStatic) { + ClassInfo classInfo = class_info.get(classId); + classInfo.static_traits.addTrait(trait); + trait.disp_id = classInfo.getNextDispId(); + } else { + InstanceInfo instanceInfo = instance_info.get(classId); + instanceInfo.instance_traits.addTrait(trait); + } + + return trait; + } + + public void addEventListener(EventListener listener) { + listeners.add(listener); + } + + public void removeEventListener(EventListener listener) { + listeners.remove(listener); + } + + protected void informListeners(String event, Object data) { + for (EventListener listener : listeners) { + listener.handleEvent(event, data); + } + } + + public int removeTraps() throws InterruptedException { + int rem = 0; + for (int s = 0; s < script_info.size(); s++) { + rem += script_info.get(s).removeTraps(s, this, ""); + } + return rem; + } + + public int removeDeadCode() throws InterruptedException { + int rem = 0; + for (MethodBody body : bodies) { + rem += body.removeDeadCode(constants, null/*FIXME*/, method_info.get(body.method_info)); + } + return rem; + } + + public Set getNsStringUsages() { + Set ret = new HashSet<>(); + for (int n = 1; n < constants.getNamespaceCount(); n++) { + ret.add(constants.getNamespace(n).name_index); + } + return ret; + } + + public Set getStringUsages() { + Set ret = new HashSet<>(); + for (MethodBody body : bodies) { + for (AVM2Instruction ins : body.getCode().code) { + for (int i = 0; i < ins.definition.operands.length; i++) { + if (ins.definition.operands[i] == AVM2Code.DAT_STRING_INDEX) { + ret.add(ins.operands[i]); + } + } + } + } + return ret; + } + + private void setStringUsageType(Map ret, int strIndex, String usageType) { + if (ret.containsKey(strIndex)) { + if (!"name".equals(usageType)) { + if (!ret.get(strIndex).equals(usageType)) { + ret.put(strIndex, "name"); + } + } + } else { + ret.put(strIndex, usageType); + } + } + + private void getStringUsageTypes(Map ret, Traits traits, boolean classesOnly) { + for (Trait t : traits.traits) { + int strIndex = constants.getMultiname(t.name_index).name_index; + String usageType = ""; + if (t instanceof TraitClass) { + TraitClass tc = (TraitClass) t; + getStringUsageTypes(ret, class_info.get(tc.class_info).static_traits, classesOnly); + getStringUsageTypes(ret, instance_info.get(tc.class_info).instance_traits, classesOnly); + + if (instance_info.get(tc.class_info).name_index != 0) { + setStringUsageType(ret, constants.getMultiname(instance_info.get(tc.class_info).name_index).name_index, "class"); + } + if (instance_info.get(tc.class_info).super_index != 0) { + setStringUsageType(ret, constants.getMultiname(instance_info.get(tc.class_info).super_index).name_index, "class"); + } + + usageType = "class"; + } + if (t instanceof TraitMethodGetterSetter) { + TraitMethodGetterSetter tm = (TraitMethodGetterSetter) t; + usageType = "method"; + MethodBody body = findBody(tm.method_info); + if (body != null) { + getStringUsageTypes(ret, body.traits, classesOnly); + } + } + if (t instanceof TraitFunction) { + TraitFunction tf = (TraitFunction) t; + MethodBody body = findBody(tf.method_info); + if (body != null) { + getStringUsageTypes(ret, body.traits, classesOnly); + } + usageType = "function"; + } + if (t instanceof TraitSlotConst) { + TraitSlotConst ts = (TraitSlotConst) t; + if (ts.isVar()) { + usageType = "var"; + } + if (ts.isConst()) { + usageType = "const"; + } + } + if (usageType.equals("class") || (!classesOnly)) { + setStringUsageType(ret, strIndex, usageType); + } + } + } + + public void getStringUsageTypes(Map ret, boolean classesOnly) { + for (ScriptInfo script : script_info) { + getStringUsageTypes(ret, script.traits, classesOnly); + } + } + + public void renameMultiname(int multinameIndex, String newname) { + if (multinameIndex <= 0 || multinameIndex >= constants.getMultinameCount()) { + throw new IllegalArgumentException("Multiname with index " + multinameIndex + " does not exist"); + } + Set stringUsages = getStringUsages(); + Set namespaceUsages = getNsStringUsages(); + int strIndex = constants.getMultiname(multinameIndex).name_index; + if (stringUsages.contains(strIndex) || namespaceUsages.contains(strIndex)) { // name is used elsewhere as string literal + strIndex = constants.getStringId(newname, true); + constants.getMultiname(multinameIndex).name_index = strIndex; + } else { + constants.setString(strIndex, newname); + } + } + + public void deobfuscateIdentifiers(HashMap namesMap, RenameType renameType, boolean classesOnly) { + Set stringUsages = getStringUsages(); + Set namespaceUsages = getNsStringUsages(); + Map stringUsageTypes = new HashMap<>(); + informListeners("deobfuscate", "Getting usage types..."); + getStringUsageTypes(stringUsageTypes, classesOnly); + AVM2Deobfuscation deobfuscation = getDeobfuscation(); + for (int i = 0; i < instance_info.size(); i++) { + informListeners("deobfuscate", "class " + i + "/" + instance_info.size()); + InstanceInfo insti = instance_info.get(i); + if (insti.name_index != 0) { + constants.getMultiname(insti.name_index).name_index = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(insti.name_index).name_index, true, renameType); + if (constants.getMultiname(insti.name_index).namespace_index != 0) { + constants.getNamespace(constants.getMultiname(insti.name_index).namespace_index).name_index + = deobfuscation.deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, constants.getNamespace(constants.getMultiname(insti.name_index).namespace_index).name_index, renameType); + } + } + if (insti.super_index != 0) { + constants.getMultiname(insti.super_index).name_index = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(insti.super_index).name_index, true, renameType); + } + } + if (classesOnly) { + return; + } + for (int i = 1; i < constants.getMultinameCount(); i++) { + informListeners("deobfuscate", "name " + i + "/" + constants.getMultinameCount()); + constants.getMultiname(i).name_index = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(i).name_index, false, renameType); + } + for (int i = 1; i < constants.getNamespaceCount(); i++) { + informListeners("deobfuscate", "namespace " + i + "/" + constants.getNamespaceCount()); + if (constants.getNamespace(i).kind != Namespace.KIND_PACKAGE) { // only packages + continue; + } + constants.getNamespace(i).name_index = deobfuscation.deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, constants.getNamespace(i).name_index, renameType); + } + + // process reflection using getDefinitionByName too + for (MethodBody body : bodies) { + for (int ip = 0; ip < body.getCode().code.size(); ip++) { + if (body.getCode().code.get(ip).definition instanceof CallPropertyIns) { + int mIndex = body.getCode().code.get(ip).operands[0]; + if (mIndex > 0) { + Multiname m = constants.getMultiname(mIndex); + if (m.getNameWithNamespace(constants).toRawString().equals("flash.utils.getDefinitionByName")) { + if (ip > 0) { + if (body.getCode().code.get(ip - 1).definition instanceof PushStringIns) { + int strIndex = body.getCode().code.get(ip - 1).operands[0]; + String fullname = constants.getString(strIndex); + String pkg = ""; + String name = fullname; + if (fullname.contains(".")) { + pkg = fullname.substring(0, fullname.lastIndexOf('.')); + name = fullname.substring(fullname.lastIndexOf('.') + 1); + } + if (!pkg.isEmpty()) { + int pkgStrIndex = constants.getStringId(pkg, true); + pkgStrIndex = deobfuscation.deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, pkgStrIndex, renameType); + pkg = constants.getString(pkgStrIndex); + } + int nameStrIndex = constants.getStringId(name, true); + nameStrIndex = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, nameStrIndex, true, renameType); + name = constants.getString(nameStrIndex); + String fullChanged = ""; + if (!pkg.isEmpty()) { + fullChanged = pkg + "."; + } + fullChanged += name; + strIndex = constants.getStringId(fullChanged, true); + body.getCode().code.get(ip - 1).operands[0] = strIndex; + } + } + } + } + } + } + } + } + + public boolean hasDecimalSupport() { + return version.minor >= MINORwithDECIMAL; + } + + public void setDecimalSupport(boolean val) { + if (val) { + if (version.minor != MINORwithDECIMAL) { + version.minor = MINORwithDECIMAL; + ((Tag) parentTag).setModified(true); + } + } else if (version.minor == MINORwithDECIMAL) { + version.minor = MINORwithDECIMAL - 1; + ((Tag) parentTag).setModified(true); + } + } + + private boolean minVersionCheck(int minMajor, int minMinor) { + return version.compareTo(new ABCVersion(minMajor, minMinor)) >= 0; + } + + public boolean hasFloatSupport() { + return minVersionCheck(47, 16); + } + + public void setFloatSupport(boolean val) { + if (val) { + if (version.major < 47) { + version.major = 47; + ((Tag) parentTag).setModified(true); + } + } else if (version.major > 46) { + version.major = 46; + ((Tag) parentTag).setModified(true); + } + } + + public boolean hasExceptionSupport() { + return version.compareTo(new ABCVersion(46, 15)) > 0; + } + + public ABC(ABCInputStream ais, SWF swf, ABCContainerTag tag) throws IOException { + this.parentTag = tag; + int minor_version = ais.readU16("minor_version"); + int major_version = ais.readU16("major_version"); + version = new ABCVersion(major_version, minor_version); + logger.log(Level.FINE, "ABC minor_version: {0}, major_version: {1}", new Object[]{minor_version, major_version}); + + ais.newDumpLevel("constant_pool", "cpool_info"); + + // constant integers + int constant_int_pool_count = ais.readU30("int_count"); + constants.ensureIntCapacity(constant_int_pool_count); + if (constant_int_pool_count > 1) { + ais.newDumpLevel("integers", "integer[]"); + for (int i = 1; i < constant_int_pool_count; i++) { // index 0 not used. Values 1..n-1 + constants.addInt(ais.readS32("int")); + } + ais.endDumpLevel(); + } + + // constant unsigned integers + int constant_uint_pool_count = ais.readU30("uint_count"); + constants.ensureUIntCapacity(constant_uint_pool_count); + if (constant_uint_pool_count > 1) { + ais.newDumpLevel("uintegers", "uinteger[]"); + for (int i = 1; i < constant_uint_pool_count; i++) { // index 0 not used. Values 1..n-1 + constants.addUInt(ais.readU32("uint")); + } + ais.endDumpLevel(); + } + + // constant double + int constant_double_pool_count = ais.readU30("double_count"); + constants.ensureDoubleCapacity(constant_double_pool_count); + if (constant_double_pool_count > 1) { + ais.newDumpLevel("doubles", "double[]"); + for (int i = 1; i < constant_double_pool_count; i++) { // index 0 not used. Values 1..n-1 + constants.addDouble(ais.readDouble("double")); + } + ais.endDumpLevel(); + } + + // constant decimal + if (hasDecimalSupport()) { + int constant_decimal_pool_count = ais.readU30("decimal_count"); + constants.ensureDecimalCapacity(constant_decimal_pool_count); + if (constant_decimal_pool_count > 1) { + ais.newDumpLevel("decimals", "decimal[]"); + for (int i = 1; i < constant_decimal_pool_count; i++) { // index 0 not used. Values 1..n-1 + constants.addDecimal(ais.readDecimal("decimal")); + } + ais.endDumpLevel(); + } + } + + if (hasFloatSupport()) { + // constant float + int constant_float_pool_count = ais.readU30("float_count"); + if (constant_float_pool_count > 1) { + ais.newDumpLevel("floats", "float[]"); + for (int i = 1; i < constant_float_pool_count; i++) { // index 0 not used. Values 1..n-1 + constants.addFloat(ais.readFloat("float")); + } + ais.endDumpLevel(); + } + // constant float4 + int constant_float4_pool_count = ais.readU30("float4_count"); + if (constant_float4_pool_count > 1) { + ais.newDumpLevel("floats4", "float4[]"); + for (int i = 1; i < constant_float4_pool_count; i++) { // index 0 not used. Values 1..n-1 + constants.addFloat4(ais.readFloat4("float4")); + } + ais.endDumpLevel(); + } + } + + // constant string + int constant_string_pool_count = ais.readU30("string_count"); + constants.ensureStringCapacity(constant_string_pool_count); + if (constant_string_pool_count > 1) { + ais.newDumpLevel("strings", "string[]"); + for (int i = 1; i < constant_string_pool_count; i++) { // index 0 not used. Values 1..n-1 + long pos = ais.getPosition(); + constants.addString(ais.readString("string")); + } + ais.endDumpLevel(); + } + + // constant namespace + int constant_namespace_pool_count = ais.readU30("namespace_count"); + constants.ensureNamespaceCapacity(constant_namespace_pool_count); + if (constant_namespace_pool_count > 1) { + ais.newDumpLevel("namespaces", "namespace[]"); + for (int i = 1; i < constant_namespace_pool_count; i++) { // index 0 not used. Values 1..n-1 + constants.addNamespace(ais.readNamespace("namespace")); + } + ais.endDumpLevel(); + } + + // constant namespace set + int constant_namespace_set_pool_count = ais.readU30("ns_set_count"); + constants.ensureNamespaceSetCapacity(constant_namespace_set_pool_count); + if (constant_namespace_set_pool_count > 1) { + ais.newDumpLevel("ns_sets", "ns_set[]"); + for (int i = 1; i < constant_namespace_set_pool_count; i++) { // index 0 not used. Values 1..n-1 + ais.newDumpLevel("ns_set_infos", "ns_set_info[]"); + constants.addNamespaceSet(new NamespaceSet()); + int namespace_count = ais.readU30("count"); + constants.getNamespaceSet(i).namespaces = new int[namespace_count]; + for (int j = 0; j < namespace_count; j++) { + constants.getNamespaceSet(i).namespaces[j] = ais.readU30("ns"); + } + ais.endDumpLevel(); + } + ais.endDumpLevel(); + } + + // constant multiname + int constant_multiname_pool_count = ais.readU30("multiname_count"); + constants.ensureMultinameCapacity(constant_multiname_pool_count); + if (constant_multiname_pool_count > 1) { + ais.newDumpLevel("multiname", "multinames[]"); + for (int i = 1; i < constant_multiname_pool_count; i++) { // index 0 not used. Values 1..n-1 + constants.addMultiname(ais.readMultiname("multiname")); + } + ais.endDumpLevel(); + } + + ais.endDumpLevel(); // cpool_info + + // method info + int methods_count = ais.readU30("methods_count"); + method_info = new ArrayList<>(methods_count); // MethodInfo[methods_count]; + for (int i = 0; i < methods_count; i++) { + method_info.add(ais.readMethodInfo("method")); + } + + // metadata info + int metadata_count = ais.readU30("metadata_count"); + metadata_info = new ArrayList<>(metadata_count); + for (int i = 0; i < metadata_count; i++) { + int name_index = ais.readU30("name_index"); + int values_count = ais.readU30("values_count"); + int[] keys = new int[values_count]; + for (int v = 0; v < values_count; v++) { + keys[v] = ais.readU30("key"); + } + int[] values = new int[values_count]; + for (int v = 0; v < values_count; v++) { + values[v] = ais.readU30("value"); + } + metadata_info.add(new MetadataInfo(name_index, keys, values)); + } + + int class_count = ais.readU30("class_count"); + instance_info = new ArrayList<>(class_count); + for (int i = 0; i < class_count; i++) { + instance_info.add(ais.readInstanceInfo("instance")); + } + class_info = new ArrayList<>(class_count); + for (int i = 0; i < class_count; i++) { + ais.newDumpLevel("class", "class_info"); + ClassInfo ci = new ClassInfo(null); // do not create Traits in constructor + ci.cinit_index = ais.readU30("cinit_index"); + ci.static_traits = ais.readTraits("static_traits"); + class_info.add(ci); + ais.endDumpLevel(); + } + int script_count = ais.readU30("script_count"); + script_info = new ArrayList<>(script_count); + for (int i = 0; i < script_count; i++) { + ais.newDumpLevel("script", "script_info"); + ScriptInfo si = new ScriptInfo(null); // do not create Traits in constructor + si.init_index = ais.readU30("init_index"); + si.traits = ais.readTraits("traits"); + script_info.add(si); + ais.endDumpLevel(); + si.setModified(false); + } + + int bodies_count = ais.readU30("bodies_count"); + bodies = new ArrayList<>(bodies_count); + for (int i = 0; i < bodies_count; i++) { + DumpInfo di = ais.dumpInfo; + DumpInfoSpecial dis = (DumpInfoSpecial) ais.newDumpLevel("method_body", "method_body_info", DumpInfoSpecialType.ABC_METHOD_BODY); + MethodBody mb = new MethodBody(this, null, null, null); // do not create Traits in constructor + try { + mb.method_info = ais.readU30("method_info"); + if (dis != null) { + dis.specialValue = mb.method_info; + } + + mb.max_stack = ais.readU30("max_stack"); + mb.max_regs = ais.readU30("max_regs"); + mb.init_scope_depth = ais.readU30("init_scope_depth"); + mb.max_scope_depth = ais.readU30("max_scope_depth"); + int code_length = ais.readU30("code_length"); + mb.setCodeBytes(ais.readBytes(code_length, "code", DumpInfoSpecialType.ABC_CODE)); + int ex_count = ais.readU30("ex_count"); + mb.exceptions = new ABCException[ex_count]; + for (int j = 0; j < ex_count; j++) { + ABCException abce = new ABCException(); + abce.start = ais.readU30("start"); + abce.end = ais.readU30("end"); + abce.target = ais.readU30("target"); + abce.type_index = ais.readU30("type_index"); + if (hasExceptionSupport()) { + abce.name_index = ais.readU30("name_index"); + } else { + abce.name_index = 0; + } + mb.exceptions[j] = abce; + } + mb.traits = ais.readTraits("traits"); + bodies.add(mb); + ais.endDumpLevel(); + } catch (EndOfStreamException ex) { + logger.log(Level.SEVERE, "MethodBody reading: End of stream", ex); + ais.endDumpLevelUntil(di); + break; + } + + SWFDecompilerPlugin.fireMethodBodyParsed(this, mb, swf); + } + + getMethodIndexing(); + + /*for(int i=0;i()); + } catch (InterruptedException ex) { + Logger.getLogger(ABC.class.getName()).log(Level.SEVERE, null, ex); + } + System.out.println(""+t.toString()); + } + //System.exit(0);*/ + SWFDecompilerPlugin.fireAbcParsed(this, swf); + } + + public void saveToStream(OutputStream os) throws IOException { + ABCOutputStream aos = new ABCOutputStream(os); + aos.writeU16(version.minor); + aos.writeU16(version.major); + + aos.writeU30(constants.getIntCount()); + for (int i = 1; i < constants.getIntCount(); i++) { + aos.writeS32(constants.getInt(i)); + } + aos.writeU30(constants.getUIntCount()); + for (int i = 1; i < constants.getUIntCount(); i++) { + aos.writeU32(constants.getUInt(i)); + } + + aos.writeU30(constants.getDoubleCount()); + for (int i = 1; i < constants.getDoubleCount(); i++) { + aos.writeDouble(constants.getDouble(i)); + } + + if (hasDecimalSupport()) { + aos.writeU30(constants.getDecimalCount()); + for (int i = 1; i < constants.getDecimalCount(); i++) { + aos.writeDecimal(constants.getDecimal(i)); + } + } + if (hasFloatSupport()) { + aos.writeU30(constants.getFloatCount()); + for (int i = 1; i < constants.getFloatCount(); i++) { + aos.writeFloat(constants.getFloat(i)); + } + aos.writeU30(constants.getFloat4Count()); + for (int i = 1; i < constants.getFloat4Count(); i++) { + aos.writeFloat4(constants.getFloat4(i)); + } + } + + aos.writeU30(constants.getStringCount()); + for (int i = 1; i < constants.getStringCount(); i++) { + aos.writeString(constants.getString(i)); + } + + aos.writeU30(constants.getNamespaceCount()); + for (int i = 1; i < constants.getNamespaceCount(); i++) { + aos.writeNamespace(constants.getNamespace(i)); + } + + aos.writeU30(constants.getNamespaceSetCount()); + for (int i = 1; i < constants.getNamespaceSetCount(); i++) { + aos.writeU30(constants.getNamespaceSet(i).namespaces.length); + for (int j = 0; j < constants.getNamespaceSet(i).namespaces.length; j++) { + aos.writeU30(constants.getNamespaceSet(i).namespaces[j]); + } + } + + aos.writeU30(constants.getMultinameCount()); + for (int i = 1; i < constants.getMultinameCount(); i++) { + aos.writeMultiname(constants.getMultiname(i)); + } + + aos.writeU30(method_info.size()); + for (MethodInfo mi : method_info) { + aos.writeMethodInfo(mi); + } + + aos.writeU30(metadata_info.size()); + for (MetadataInfo mi : metadata_info) { + aos.writeU30(mi.name_index); + aos.writeU30(mi.values.length); + for (int j = 0; j < mi.values.length; j++) { + aos.writeU30(mi.keys[j]); + } + for (int j = 0; j < mi.values.length; j++) { + aos.writeU30(mi.values[j]); + } + } + + aos.writeU30(class_info.size()); + for (InstanceInfo ii : instance_info) { + aos.writeInstanceInfo(ii); + } + for (ClassInfo ci : class_info) { + aos.writeU30(ci.cinit_index); + aos.writeTraits(ci.static_traits); + } + + aos.writeU30(script_info.size()); + for (ScriptInfo si : script_info) { + aos.writeU30(si.init_index); + aos.writeTraits(si.traits); + } + + aos.writeU30(bodies.size()); + for (MethodBody mb : bodies) { + aos.writeU30(mb.method_info); + aos.writeU30(mb.max_stack); + aos.writeU30(mb.max_regs); + aos.writeU30(mb.init_scope_depth); + aos.writeU30(mb.max_scope_depth); + byte[] codeBytes = mb.getCodeBytes(); + aos.writeU30(codeBytes.length); + aos.write(codeBytes); + aos.writeU30(mb.exceptions.length); + for (int j = 0; j < mb.exceptions.length; j++) { + aos.writeU30(mb.exceptions[j].start); + aos.writeU30(mb.exceptions[j].end); + aos.writeU30(mb.exceptions[j].target); + aos.writeU30(mb.exceptions[j].type_index); + aos.writeU30(mb.exceptions[j].name_index); + } + aos.writeTraits(mb.traits); + } + } + + public MethodBody findBody(MethodInfo methodInfo) { + return getMethodIndexing().findMethodBody(methodInfo); + } + + public MethodBody findBody(int methodInfo) { + return getMethodIndexing().findMethodBody(methodInfo); + } + + public int findBodyIndex(MethodInfo methodInfo) { + return getMethodIndexing().findMethodBodyIndex(methodInfo); + } + + public int findBodyIndex(int methodInfo) { + return getMethodIndexing().findMethodBodyIndex(methodInfo); + } + + public MethodBody findBodyClassInitializerByClass(String className) { + for (int i = 0; i < instance_info.size(); i++) { + if (className.equals(constants.getMultiname(instance_info.get(i).name_index).getName(constants, null, true))) { + MethodBody body = findBody(class_info.get(i).cinit_index); + if (body != null) { + return body; + } + } + } + + return null; + } + + public MethodBody findBodyInstanceInitializerByClass(String className) { + for (int i = 0; i < instance_info.size(); i++) { + if (className.equals(constants.getMultiname(instance_info.get(i).name_index).getName(constants, null, true))) { + MethodBody body = findBody(instance_info.get(i).iinit_index); + if (body != null) { + return body; + } + } + } + + return null; + } + + public MethodBody findBodyByClassAndName(String className, String methodName) { + for (int i = 0; i < instance_info.size(); i++) { + if (className.equals(constants.getMultiname(instance_info.get(i).name_index).getName(constants, null, true))) { + for (Trait t : instance_info.get(i).instance_traits.traits) { + if (t instanceof TraitMethodGetterSetter) { + TraitMethodGetterSetter t2 = (TraitMethodGetterSetter) t; + if (methodName.equals(t2.getName(this).getName(constants, null, true))) { + MethodBody body = findBody(t2.method_info); + if (body != null) { + return body; + } + } + } + } + + for (Trait t : class_info.get(i).static_traits.traits) { + if (t instanceof TraitMethodGetterSetter) { + TraitMethodGetterSetter t2 = (TraitMethodGetterSetter) t; + if (methodName.equals(t2.getName(this).getName(constants, null, true))) { + MethodBody body = findBody(t2.method_info); + if (body != null) { + return body; + } + } + } + } + //break; + } + } + + return null; + } + + public boolean isStaticTraitId(int classIndex, int traitId) { + if (traitId < class_info.get(classIndex).static_traits.traits.size()) { + return true; + } else if (traitId < class_info.get(classIndex).static_traits.traits.size() + instance_info.get(classIndex).instance_traits.traits.size()) { + return false; + } else { + return true; // Can be class or instance initializer + } + } + + public Trait findTraitByTraitId(int classIndex, int traitId) { + if (classIndex == -1) { + return null; + } + List staticTraits = class_info.get(classIndex).static_traits.traits; + if (traitId >= 0 && traitId < staticTraits.size()) { + return staticTraits.get(traitId); + } else { + List instanceTraits = instance_info.get(classIndex).instance_traits.traits; + if (traitId >= 0 && traitId < staticTraits.size() + instanceTraits.size()) { + traitId -= staticTraits.size(); + return instanceTraits.get(traitId); + } else { + return null; // Can be class or instance initializer + } + } + } + + public int findMethodIdByTraitId(int classIndex, int traitId) { + if (classIndex == -1) { + return -1; + } + List staticTraits = class_info.get(classIndex).static_traits.traits; + if (traitId < staticTraits.size()) { + if (staticTraits.get(traitId) instanceof TraitMethodGetterSetter) { + return ((TraitMethodGetterSetter) staticTraits.get(traitId)).method_info; + } else { + return -1; + } + } else { + List instanceTraits = instance_info.get(classIndex).instance_traits.traits; + if (traitId < staticTraits.size() + instanceTraits.size()) { + traitId -= staticTraits.size(); + if (instanceTraits.get(traitId) instanceof TraitMethodGetterSetter) { + return ((TraitMethodGetterSetter) instanceTraits.get(traitId)).method_info; + } else { + return -1; + } + } else { + traitId -= staticTraits.size() + instanceTraits.size(); + if (traitId == 0) { + return instance_info.get(classIndex).iinit_index; + } else if (traitId == 1) { + return class_info.get(classIndex).cinit_index; + } else { + return -1; + } + } + } + } + + private Map getNamespaceMap() { + if (namespaceMap == null) { + Map map = new HashMap<>(); + for (ScriptInfo si : script_info) { + for (Trait t : si.traits.traits) { + if (t instanceof TraitSlotConst) { + TraitSlotConst s = ((TraitSlotConst) t); + if (s.isNamespace()) { + String key = constants.getNamespace(s.value_index).getName(constants).toRawString(); // assume not null + DottedChain val = constants.getMultiname(s.name_index).getNameWithNamespace(constants); + map.put(key, val); + } + } + } + } + namespaceMap = map; + } + + return namespaceMap; + } + + private AVM2Deobfuscation getDeobfuscation() { + if (deobfuscation == null) { + deobfuscation = new AVM2Deobfuscation(getSwf(), constants); + } + + return deobfuscation; + } + + public final ABCMethodIndexing getMethodIndexing() { + if (abcMethodIndexing == null) { + abcMethodIndexing = new ABCMethodIndexing(this); + } + + return abcMethodIndexing; + } + + public DottedChain nsValueToName(String valueStr) { + if (valueStr == null) { + return DottedChain.EMPTY; + } + + if (getNamespaceMap().containsKey(valueStr)) { + return getNamespaceMap().get(valueStr); + } else { + DottedChain ns = getDeobfuscation().builtInNs(valueStr); + if (ns == null) { + return DottedChain.EMPTY; + } else { + return ns; + } + } + } + + public List getScriptPacks(String packagePrefix, List allAbcs) { + List ret = new ArrayList<>(); + for (int i = 0; i < script_info.size(); i++) { + if (!script_info.get(i).deleted) { + ret.addAll(script_info.get(i).getPacks(this, i, packagePrefix, allAbcs)); + } + } + return ret; + } + + public void dump(OutputStream os) { + Utf8PrintWriter output; + output = new Utf8PrintWriter(os); + constants.dump(output); + for (int i = 0; i < method_info.size(); i++) { + output.println("MethodInfo[" + i + "]:" + method_info.get(i).toString(constants, new ArrayList<>())); + } + for (int i = 0; i < metadata_info.size(); i++) { + output.println("MetadataInfo[" + i + "]:" + metadata_info.get(i).toString(constants)); + } + for (int i = 0; i < instance_info.size(); i++) { + output.println("InstanceInfo[" + i + "]:" + instance_info.get(i).toString(this, new ArrayList<>())); + } + for (int i = 0; i < class_info.size(); i++) { + output.println("ClassInfo[" + i + "]:" + class_info.get(i).toString(this, new ArrayList<>())); + } + for (int i = 0; i < script_info.size(); i++) { + output.println("ScriptInfo[" + i + "]:" + script_info.get(i).toString(this, new ArrayList<>())); + } + for (int i = 0; i < bodies.size(); i++) { + output.println("MethodBody[" + i + "]:"); //+ bodies[i].toString(this, constants, method_info)); + } + } + + private void checkMultinameUsedInMethod(int multinameIndex, int methodInfo, List ret, int classIndex, int traitIndex, boolean isStatic, boolean isInitializer, Traits traits, int parentTraitIndex) { + for (int p = 0; p < method_info.get(methodInfo).param_types.length; p++) { + if (method_info.get(methodInfo).param_types[p] == multinameIndex) { + ret.add(new MethodParamsMultinameUsage(this, multinameIndex, classIndex, traitIndex, isStatic, isInitializer, traits, parentTraitIndex)); + break; + } + } + if (method_info.get(methodInfo).ret_type == multinameIndex) { + ret.add(new MethodReturnTypeMultinameUsage(this, multinameIndex, classIndex, traitIndex, isStatic, isInitializer, traits, parentTraitIndex)); + } + MethodBody body = findBody(methodInfo); + if (body != null) { + findMultinameUsageInTraits(body.traits, multinameIndex, isStatic, classIndex, ret, traitIndex); + for (ABCException e : body.exceptions) { + if ((e.name_index == multinameIndex) || (e.type_index == multinameIndex)) { + ret.add(new MethodBodyMultinameUsage(this, multinameIndex, classIndex, traitIndex, isStatic, 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) { + if (ins.operands[o] == multinameIndex) { + ret.add(new MethodBodyMultinameUsage(this, multinameIndex, classIndex, traitIndex, isStatic, isInitializer, traits, parentTraitIndex)); + return; + } + } + } + } + } + } + + private void findMultinameUsageInTraits(Traits traits, int multinameIndex, boolean isStatic, int classIndex, List ret, int parentTraitIndex) { + for (int t = 0; t < traits.traits.size(); t++) { + if (traits.traits.get(t) instanceof TraitSlotConst) { + TraitSlotConst tsc = (TraitSlotConst) traits.traits.get(t); + if (tsc.name_index == multinameIndex) { + ret.add(new ConstVarNameMultinameUsage(this, multinameIndex, classIndex, t, isStatic, traits, parentTraitIndex)); + } + if (tsc.type_index == multinameIndex) { + ret.add(new ConstVarTypeMultinameUsage(this, multinameIndex, classIndex, t, isStatic, traits, parentTraitIndex)); + } + } + if (traits.traits.get(t) instanceof TraitMethodGetterSetter) { + TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) traits.traits.get(t); + if (tmgs.name_index == multinameIndex) { + ret.add(new MethodNameMultinameUsage(this, multinameIndex, classIndex, t, isStatic, false, traits, parentTraitIndex)); + } + checkMultinameUsedInMethod(multinameIndex, tmgs.method_info, ret, classIndex, t, isStatic, false, traits, parentTraitIndex); + } + } + } + + public List findMultinameDefinition(int multinameIndex) { + List usages = findMultinameUsage(multinameIndex); + List ret = new ArrayList<>(); + for (MultinameUsage u : usages) { + if (u instanceof DefinitionUsage) { + ret.add(u); + } + } + return ret; + } + + public List findMultinameUsage(int multinameIndex) { + List ret = new ArrayList<>(); + if (multinameIndex == 0) { + return ret; + } + for (int c = 0; c < instance_info.size(); c++) { + if (instance_info.get(c).name_index == multinameIndex) { + ret.add(new ClassNameMultinameUsage(this, multinameIndex, c)); + } + if (instance_info.get(c).super_index == multinameIndex) { + ret.add(new ExtendsMultinameUsage(this, multinameIndex, c)); + } + for (int i = 0; i < instance_info.get(c).interfaces.length; i++) { + if (instance_info.get(c).interfaces[i] == multinameIndex) { + ret.add(new ImplementsMultinameUsage(this, multinameIndex, c)); + } + } + checkMultinameUsedInMethod(multinameIndex, instance_info.get(c).iinit_index, ret, c, 0, false, true, null, -1); + checkMultinameUsedInMethod(multinameIndex, class_info.get(c).cinit_index, ret, c, 0, true, true, null, -1); + findMultinameUsageInTraits(instance_info.get(c).instance_traits, multinameIndex, false, c, ret, -1); + findMultinameUsageInTraits(class_info.get(c).static_traits, multinameIndex, true, c, ret, -1); + } + loopm: + for (int m = 1; m < constants.getMultinameCount(); m++) { + if (constants.getMultiname(m).kind == Multiname.TYPENAME) { + if (constants.getMultiname(m).qname_index == multinameIndex) { + ret.add(new TypeNameMultinameUsage(this, m)); + continue; + } + for (int mp : constants.getMultiname(m).params) { + if (mp == multinameIndex) { + ret.add(new TypeNameMultinameUsage(this, m)); + continue loopm; + } + } + } + } + return ret; + } + + public int findMethodInfoByName(int classId, String methodName) { + if (classId > -1) { + for (Trait t : instance_info.get(classId).instance_traits.traits) { + if (t instanceof TraitMethodGetterSetter) { + if (t.getName(this).getName(constants, null, true).equals(methodName)) { + return ((TraitMethodGetterSetter) t).method_info; + } + } + } + } + return -1; + } + + public int findMethodBodyByName(int classId, String methodName) { + if (classId > -1) { + for (Trait t : instance_info.get(classId).instance_traits.traits) { + if (t instanceof TraitMethodGetterSetter) { + if (t.getName(this).getName(constants, null, true).equals(methodName)) { + return findBodyIndex(((TraitMethodGetterSetter) t).method_info); + } + } + } + } + return -1; + } + + public int findMethodBodyByName(String className, String methodName) { + int classId = findClassByName(className); + return findMethodBodyByName(classId, methodName); + } + + public int findClassByName(DottedChain name) { + String str = name == null ? null : name.toRawString(); + return findClassByName(str); + } + + public int findClassByName(String name) { + for (int c = 0; c < instance_info.size(); c++) { + DottedChain s = constants.getMultiname(instance_info.get(c).name_index).getNameWithNamespace(constants); + if (name.equals(s.toRawString())) { + return c; + } + } + return -1; + } + + public List findScriptPacksByPath(String name, List allAbcs) { + List ret = new ArrayList<>(); + List allPacks = getScriptPacks(null, allAbcs); // todo: honfika: use filter parameter + if (name.endsWith(".**") || name.equals("**") || name.endsWith(".++") || name.equals("++")) { + name = name.substring(0, name.length() - 2); + + for (ScriptPack en : allPacks) { + if (en.getClassPath().toString().startsWith(name)) { + ret.add(en); + } + } + } else if (name.endsWith(".*") || name.equals("*") || name.endsWith(".+") || name.equals("+")) { + name = name.substring(0, name.length() - 1); + for (ScriptPack en : allPacks) { + String classPathStr = en.getClassPath().toString(); + if (classPathStr.startsWith(name)) { + String rem = name.isEmpty() ? classPathStr : classPathStr.substring(name.length()); + if (!rem.contains(".")) { + ret.add(en); + } + } + } + } else { + ScriptPack p = findScriptPackByPath(name, allAbcs); + if (p != null) { + ret.add(p); + } + } + return ret; + + } + + public ScriptPack findScriptPackByPath(String name, List allAbcs) { + List packs = getScriptPacks(null, allAbcs); + for (ScriptPack en : packs) { + if (en.getClassPath().toString().equals(name)) { + return en; + } + } + return null; + } + + private void removeClassFromTraits(Traits traits, int index) { + for (Trait t : traits.traits) { + if (t instanceof TraitClass) { + TraitClass tc = (TraitClass) t; + removeClassFromTraits(instance_info.get(tc.class_info).instance_traits, index); + removeClassFromTraits(class_info.get(tc.class_info).static_traits, index); + if (tc.class_info > index) { + tc.class_info--; + } + } + } + } + + public void addClass(ClassInfo ci, InstanceInfo ii, int index) { + for (MethodBody b : bodies) { + for (AVM2Instruction ins : b.getCode().code) { + for (int i = 0; i < ins.definition.operands.length; i++) { + if (ins.definition.operands[i] == AVM2Code.DAT_CLASS_INDEX) { + if (ins.operands[i] >= index) { + ins.setOperand(i, ins.operands[i] + 1, b.getCode(), b); + } + } + } + } + } + for (ScriptInfo si : script_info) { + addClassInTraits(si.traits, index); + } + for (MethodBody b : bodies) { + addClassInTraits(b.traits, index); + } + instance_info.add(index, ii); + class_info.add(index, ci); + } + + private void addClassInTraits(Traits traits, int index) { + for (Trait t : traits.traits) { + if (t instanceof TraitClass) { + TraitClass tc = (TraitClass) t; + addClassInTraits(instance_info.get(tc.class_info).instance_traits, index); + addClassInTraits(class_info.get(tc.class_info).static_traits, index); + if (tc.class_info >= index) { + tc.class_info++; + } + } + } + } + + public void reorganizeClasses(Map classIndexMap) { + for (MethodBody b : bodies) { + for (AVM2Instruction ins : b.getCode().code) { + for (int i = 0; i < ins.definition.operands.length; i++) { + if (ins.definition.operands[i] == AVM2Code.DAT_CLASS_INDEX) { + if (classIndexMap.containsKey(ins.operands[i])) { + ins.setOperand(i, classIndexMap.get(ins.operands[i]), b.getCode(), b); + } + } + } + } + } + for (ScriptInfo si : script_info) { + reorganizeClassesInTraits(si.traits, classIndexMap); + } + for (MethodBody b : bodies) { + reorganizeClassesInTraits(b.traits, classIndexMap); + } + Map backupInstanceInfos = new HashMap<>(); + Map backupClassInfos = new HashMap<>(); + for (int from : classIndexMap.keySet()) { + backupInstanceInfos.put(from, instance_info.get(from)); + backupClassInfos.put(from, class_info.get(from)); + } + for (int from : classIndexMap.keySet()) { + int to = classIndexMap.get(from); + instance_info.set(to, backupInstanceInfos.get(from)); + class_info.set(to, backupClassInfos.get(from)); + } + } + + private void reorganizeClassesInTraits(Traits traits, Map classIndexMap) { + for (Trait t : traits.traits) { + if (t instanceof TraitClass) { + TraitClass tc = (TraitClass) t; + reorganizeClassesInTraits(instance_info.get(tc.class_info).instance_traits, classIndexMap); + reorganizeClassesInTraits(class_info.get(tc.class_info).static_traits, classIndexMap); + if (classIndexMap.containsKey(tc.class_info)) { + tc.class_info = classIndexMap.get(tc.class_info); + } + } + } + } + + public void removeClass(int index) { + for (MethodBody b : bodies) { + for (AVM2Instruction ins : b.getCode().code) { + for (int i = 0; i < ins.definition.operands.length; i++) { + if (ins.definition.operands[i] == AVM2Code.DAT_CLASS_INDEX) { + if (ins.operands[i] > index) { + ins.setOperand(i, ins.operands[i] - 1, b.getCode(), b); + } + } + } + } + } + for (ScriptInfo si : script_info) { + removeClassFromTraits(si.traits, index); + } + for (MethodBody b : bodies) { + removeClassFromTraits(b.traits, index); + } + instance_info.remove(index); + class_info.remove(index); + } + + private void removeMethodFromTraits(Traits traits, int index) { + for (Trait t : traits.traits) { + if (t instanceof TraitClass) { + TraitClass tc = (TraitClass) t; + removeMethodFromTraits(instance_info.get(tc.class_info).instance_traits, index); + removeMethodFromTraits(class_info.get(tc.class_info).static_traits, index); + } + if (t instanceof TraitMethodGetterSetter) { + TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) t; + if (tmgs.method_info > index) { + tmgs.method_info--; + } + } + if (t instanceof TraitFunction) { + TraitFunction tf = (TraitFunction) t; + if (tf.method_info > index) { + tf.method_info--; + } + } + } + } + + public void removeMethod(int index) { + + int bindex = -1; + for (int b = 0; b < bodies.size(); b++) { + if (bodies.get(b).method_info == index) { + bodies.remove(b); + bindex = b; + b--; + } + } + + for (MethodBody b : bodies) { + if (b.method_info > index) { + b.method_info--; + } + for (AVM2Instruction ins : b.getCode().code) { + for (int i = 0; i < ins.definition.operands.length; i++) { + if (ins.definition.operands[i] == AVM2Code.DAT_METHOD_INDEX) { + if (ins.operands[i] > index) { + ins.setOperand(i, ins.operands[i] - 1, b.getCode(), b); + } + } + } + } + removeMethodFromTraits(b.traits, index); + } + + for (int c = 0; c < instance_info.size(); c++) { + InstanceInfo ii = instance_info.get(c); + if (ii.iinit_index > index) { + ii.iinit_index--; + } + ClassInfo ci = class_info.get(c); + if (ci.cinit_index > index) { + ci.cinit_index--; + } + } + + for (ScriptInfo si : script_info) { + if (si.init_index > index) { + si.init_index--; + } + removeMethodFromTraits(si.traits, index); + } + + abcMethodIndexing = null; + + method_info.remove(index); + } + + public boolean replaceScriptPack(As3ScriptReplacerInterface replacer, ScriptPack pack, String as) throws As3ScriptReplaceException, IOException, InterruptedException { + replacer.replaceScript(pack, as); + ((Tag) parentTag).setModified(true); + return pack.isSimple; + } + + private void packMethods() { + for (int m = 0; m < method_info.size(); m++) { + if (method_info.get(m).deleted) { + removeMethod(m); + m--; + } + } + } + + public void pack() { + packMethods(); + for (int c = 0; c < instance_info.size(); c++) { + if (instance_info.get(c).deleted) { + removeClass(c); + c--; + } + } + packMethods(); + for (int s = 0; s < script_info.size(); s++) { + if (script_info.get(s).deleted) { + script_info.remove(s); + s--; + } + } + getSwf().clearAbcListCache(); + getSwf().clearScriptCache(); + getMethodIndexing(); + } + + /** + * Merges second ABC to this one. + * + * @param secondABC + */ + public void mergeABC(ABC secondABC) { + Map mergeStringMap = new HashMap<>(); + Map mergeIntMap = new HashMap<>(); + Map mergeUIntMap = new HashMap<>(); + Map mergeDoubleMap = new HashMap<>(); + Map mergeFloatMap = new HashMap<>(); + Map mergeFloat4Map = new HashMap<>(); + Map mergeDecimalMap = new HashMap<>(); + + Map mergeNamespaceMap = new HashMap<>(); + Map mergeNamespaceSetMap = new HashMap<>(); + Map mergeMultinameMap = new HashMap<>(); + Map mergeMethodInfoMap = new HashMap<>(); + Map mergeMethodBodyMap = new HashMap<>(); + Map mergeClassIndexMap = new HashMap<>(); + Map mergeMetaDataMap = new HashMap<>(); + Map mergeScriptInfoMap = new HashMap<>(); + + mergeABC(secondABC, mergeStringMap, mergeIntMap, mergeUIntMap, mergeDoubleMap, mergeFloatMap, mergeFloat4Map, mergeDecimalMap, mergeNamespaceMap, mergeNamespaceSetMap, mergeMultinameMap, mergeMethodInfoMap, mergeMethodBodyMap, mergeClassIndexMap, mergeMetaDataMap, mergeScriptInfoMap); + } + + /** + * Merges second ABC to this one. Gets mapping of indices. + * + * @param secondABC + * @param mergeStringMap + * @param mergeIntMap + * @param mergeUIntMap + * @param mergeDoubleMap + * @param mergeFloatMap + * @param mergeFloat4Map + * @param mergeDecimalMap + * @param mergeNamespaceMap + * @param mergeNamespaceSetMap + * @param mergeMultinameMap + * @param mergeMethodInfoMap + * @param mergeMethodBodyMap + * @param mergeClassIndexMap + * @param mergeMetaDataMap + * @param mergeScriptInfoMap + */ + public void mergeABC(ABC secondABC, + Map mergeStringMap, + Map mergeIntMap, + Map mergeUIntMap, + Map mergeDoubleMap, + Map mergeFloatMap, + Map mergeFloat4Map, + Map mergeDecimalMap, + Map mergeNamespaceMap, + Map mergeNamespaceSetMap, + Map mergeMultinameMap, + Map mergeMethodInfoMap, + Map mergeMethodBodyMap, + Map mergeClassIndexMap, + Map mergeMetaDataMap, + Map mergeScriptInfoMap + ) { + + if (!version.equals(secondABC.version)) { + throw new RuntimeException("ABC versions mismatch"); + } + //Constants + constants.merge(secondABC.constants, mergeStringMap, mergeIntMap, mergeUIntMap, mergeDoubleMap, mergeFloatMap, mergeFloat4Map, mergeDecimalMap, mergeNamespaceMap, mergeNamespaceSetMap, mergeMultinameMap); + + //Metadata + for (int m = 0; m < secondABC.metadata_info.size(); m++) { + MetadataInfo secondMetadataInfo = secondABC.metadata_info.get(m); + MetadataInfo newMetadataInfo = new MetadataInfo(); + newMetadataInfo.name_index = mergeStringMap.get(secondMetadataInfo.name_index); + newMetadataInfo.keys = new int[secondMetadataInfo.keys.length]; + newMetadataInfo.values = new int[secondMetadataInfo.values.length]; + for (int i = 0; i < secondMetadataInfo.keys.length; i++) { + newMetadataInfo.keys[i] = mergeStringMap.get(secondMetadataInfo.keys[i]); + newMetadataInfo.values[i] = mergeStringMap.get(secondMetadataInfo.values[i]); + } + int newIndex = metadata_info.size(); + metadata_info.add(newMetadataInfo); + mergeMetaDataMap.put(m, newIndex); + } + + //Method Info + for (int i = 0; i < secondABC.method_info.size(); i++) { + MethodInfo secondMethodInfo = secondABC.method_info.get(i); + int newParamTypes[] = new int[secondMethodInfo.param_types.length]; + for (int t = 0; t < secondMethodInfo.param_types.length; t++) { + newParamTypes[t] = mergeMultinameMap.get(secondMethodInfo.param_types[t]); + } + int newParamNames[] = new int[secondMethodInfo.paramNames.length]; + for (int n = 0; n < secondMethodInfo.paramNames.length; n++) { + newParamNames[n] = mergeStringMap.get(secondMethodInfo.paramNames[n]); + } + int newRetType = mergeMultinameMap.get(secondMethodInfo.ret_type); + int newNameIndex = mergeStringMap.get(secondMethodInfo.name_index); + ValueKind newOptional[] = new ValueKind[secondMethodInfo.optional.length]; + for (int k = 0; k < secondMethodInfo.optional.length; k++) { + int vkind = secondMethodInfo.optional[k].value_kind; + Map valueMergeMap = null; + switch (vkind) { + case ValueKind.CONSTANT_Utf8: + valueMergeMap = mergeStringMap; + break; + case ValueKind.CONSTANT_Int: + valueMergeMap = mergeIntMap; + break; + case ValueKind.CONSTANT_UInt: + valueMergeMap = mergeUIntMap; + break; + case ValueKind.CONSTANT_Double: + valueMergeMap = mergeDoubleMap; + break; + case ValueKind.CONSTANT_DecimalOrFloat: + //assuming the second ABC has same decimal/float support + if (hasDecimalSupport()) { + valueMergeMap = mergeDecimalMap; + } else if (hasFloatSupport()) { + valueMergeMap = mergeFloatMap; + } else { + //should not happen + } + break; + case ValueKind.CONSTANT_Float4: + if (hasFloatSupport()) { + valueMergeMap = mergeFloat4Map; + } + break; + case ValueKind.CONSTANT_ExplicitNamespace: + case ValueKind.CONSTANT_Namespace: + case ValueKind.CONSTANT_PackageInternalNs: + case ValueKind.CONSTANT_PackageNamespace: + case ValueKind.CONSTANT_ProtectedNamespace: + case ValueKind.CONSTANT_PrivateNs: + case ValueKind.CONSTANT_StaticProtectedNs: + valueMergeMap = mergeNamespaceMap; + break; + } + + int newValueIndex = valueMergeMap != null ? valueMergeMap.get(secondMethodInfo.optional[k].value_index) : secondMethodInfo.optional[k].value_index; + newOptional[k] = new ValueKind(newValueIndex, vkind); + } + + MethodInfo newMethodInfo = new MethodInfo(newParamTypes, newRetType, newNameIndex, secondMethodInfo.flags, newOptional, newParamNames); + int newIndex = addMethodInfo(newMethodInfo); + mergeMethodInfoMap.put(i, newIndex); + } + + int classFirstIndex = class_info.size(); + //Class/Instance + for (int c = 0; c < secondABC.class_info.size(); c++) { + ClassInfo secondClassInfo = secondABC.class_info.get(c); + ClassInfo newClassInfo = new ClassInfo(); + int newIndex = class_info.size(); + class_info.add(newClassInfo); + mergeClassIndexMap.put(c, newIndex); + newClassInfo.cinit_index = mergeMethodInfoMap.get(secondClassInfo.cinit_index); + + newClassInfo.lastDispId = secondClassInfo.lastDispId; + InstanceInfo secondInstanceInfo = secondABC.instance_info.get(c); + InstanceInfo newInstanceInfo = new InstanceInfo(); + newInstanceInfo.iinit_index = mergeMethodInfoMap.get(secondInstanceInfo.iinit_index); + newInstanceInfo.super_index = mergeMultinameMap.get(secondInstanceInfo.super_index); + newInstanceInfo.interfaces = new int[secondInstanceInfo.interfaces.length]; + for (int i = 0; i < secondInstanceInfo.interfaces.length; i++) { + newInstanceInfo.interfaces[i] = mergeMultinameMap.get(secondInstanceInfo.interfaces[i]); + } + newInstanceInfo.protectedNS = mergeNamespaceMap.get(secondInstanceInfo.protectedNS); + newInstanceInfo.name_index = mergeMultinameMap.get(secondInstanceInfo.name_index); + newInstanceInfo.flags = secondInstanceInfo.flags; + instance_info.add(newInstanceInfo); + + //do traits in second step as some classes may depend on other classes + } + + //Class/Instance traits + for (int c = 0; c < secondABC.class_info.size(); c++) { + ClassInfo secondClassInfo = secondABC.class_info.get(c); + InstanceInfo secondInstanceInfo = secondABC.instance_info.get(c); + ClassInfo newClassInfo = class_info.get(classFirstIndex + c); + InstanceInfo newInstanceInfo = instance_info.get(classFirstIndex + c); + newClassInfo.static_traits = mergeTraits(secondClassInfo.static_traits, mergeMultinameMap, mergeStringMap, mergeIntMap, mergeUIntMap, mergeDoubleMap, mergeFloatMap, mergeFloat4Map, mergeDecimalMap, mergeNamespaceMap, mergeMetaDataMap, mergeMethodInfoMap, mergeClassIndexMap); + newInstanceInfo.instance_traits = mergeTraits(secondInstanceInfo.instance_traits, mergeMultinameMap, mergeStringMap, mergeIntMap, mergeUIntMap, mergeDoubleMap, mergeFloatMap, mergeFloat4Map, mergeDecimalMap, mergeNamespaceMap, mergeMetaDataMap, mergeMethodInfoMap, mergeClassIndexMap); + } + + //Scripts + for (int s = 0; s < secondABC.script_info.size(); s++) { + ScriptInfo secondScriptInfo = secondABC.script_info.get(s); + ScriptInfo newScriptInfo = new ScriptInfo(); + newScriptInfo.init_index = mergeMethodInfoMap.get(secondScriptInfo.init_index); + newScriptInfo.traits = mergeTraits(secondScriptInfo.traits, mergeMultinameMap, mergeStringMap, mergeIntMap, mergeUIntMap, mergeDoubleMap, mergeFloatMap, mergeFloat4Map, mergeDecimalMap, mergeNamespaceMap, mergeMetaDataMap, mergeMethodInfoMap, mergeClassIndexMap); + int newIndex = script_info.size(); + script_info.add(newScriptInfo); + mergeScriptInfoMap.put(s, newIndex); + } + + //Method bodies + for (int b = 0; b < secondABC.bodies.size(); b++) { + MethodBody secondBody = secondABC.bodies.get(b); + MethodBody newBody = secondBody.clone(true); + newBody.method_info = mergeMethodInfoMap.get(secondBody.method_info); + for (int e = 0; e < secondBody.exceptions.length; e++) { + newBody.exceptions[e].name_index = mergeMultinameMap.get(secondBody.exceptions[e].name_index); + newBody.exceptions[e].type_index = mergeMultinameMap.get(secondBody.exceptions[e].type_index); + } + AVM2Code newCode = newBody.getCode(); + for (AVM2Instruction newIns : newCode.code) { + int newOperands[] = newIns.operands == null ? null : newIns.operands.clone(); + boolean modified = false; + if (newIns.operands != null) { + for (int i = 0; i < newIns.definition.operands.length; i++) { + Map mergeMap = null; + switch (newIns.definition.operands[i]) { + case AVM2Code.DAT_CLASS_INDEX: + mergeMap = mergeClassIndexMap; + break; + case AVM2Code.DAT_STRING_INDEX: + mergeMap = mergeStringMap; + break; + case AVM2Code.DAT_INT_INDEX: + mergeMap = mergeIntMap; + break; + case AVM2Code.DAT_UINT_INDEX: + mergeMap = mergeUIntMap; + break; + case AVM2Code.DAT_DOUBLE_INDEX: + mergeMap = mergeDoubleMap; + break; + case AVM2Code.DAT_DECIMAL_INDEX: + mergeMap = mergeDecimalMap; + break; + case AVM2Code.DAT_FLOAT_INDEX: + mergeMap = mergeFloatMap; + break; + case AVM2Code.DAT_FLOAT4_INDEX: + mergeMap = mergeFloat4Map; + break; + case AVM2Code.DAT_NAMESPACE_INDEX: + mergeMap = mergeNamespaceMap; + break; + case AVM2Code.DAT_METHOD_INDEX: + mergeMap = mergeMethodInfoMap; + break; + case AVM2Code.DAT_MULTINAME_INDEX: + mergeMap = mergeMultinameMap; + break; + } + if (mergeMap != null) { + newOperands[i] = mergeMap.get(newIns.operands[i]); + modified = true; + } + } + } + if (modified) { + newIns.setOperands(newOperands, newCode, newBody); + } + newBody.traits = mergeTraits(secondBody.traits, mergeMultinameMap, mergeStringMap, mergeIntMap, mergeUIntMap, mergeDoubleMap, mergeFloatMap, mergeFloat4Map, mergeDecimalMap, mergeNamespaceMap, mergeMetaDataMap, mergeMethodInfoMap, mergeClassIndexMap); + } + newBody.setModified(); + int newIndex = bodies.size(); + bodies.add(newBody); + mergeMethodBodyMap.put(b, newIndex); + } + + //clear caches + abcMethodIndexing = null; + getSwf().clearScriptCache(); + ((Tag) parentTag).setModified(true); + } + + private Traits mergeTraits(Traits secondTraits, Map mergeMultinameMap, + Map mergeStringMap, + Map mergeIntMap, + Map mergeUIntMap, + Map mergeDoubleMap, + Map mergeFloatMap, + Map mergeFloat4Map, + Map mergeDecimalMap, + Map mergeNamespaceMap, + Map mergeMetaDataMap, + Map mergeMethodInfoMap, + Map mergeClassIndexMap) { + Traits newTraits = new Traits(); + for (Trait t : secondTraits.traits) { + Trait newTrait = null; + if (t instanceof TraitMethodGetterSetter) { + newTrait = mergeTrait((TraitMethodGetterSetter) t, mergeMultinameMap, mergeMethodInfoMap, mergeMetaDataMap); + } else if (t instanceof TraitSlotConst) { + newTrait = mergeTrait((TraitSlotConst) t, mergeMultinameMap, mergeStringMap, mergeIntMap, mergeUIntMap, mergeDoubleMap, mergeFloatMap, mergeFloat4Map, mergeDecimalMap, mergeNamespaceMap, mergeMetaDataMap); + } else if (t instanceof TraitFunction) { + newTrait = mergeTrait((TraitFunction) t, mergeMultinameMap, mergeMethodInfoMap, mergeMetaDataMap); + } else if (t instanceof TraitClass) { + newTrait = mergeTrait((TraitClass) t, mergeClassIndexMap, mergeMultinameMap, mergeMetaDataMap); + } else { + //should not happen + } + + newTraits.addTrait(newTrait); + + } + return newTraits; + } + + private TraitMethodGetterSetter mergeTrait(TraitMethodGetterSetter secondTrait, Map mergeMultinameMap, Map mergeMethodInfoMap, Map mergeMetaDataMap) { + TraitMethodGetterSetter newTrait = secondTrait.clone(); + newTrait.method_info = mergeMethodInfoMap.get(secondTrait.method_info); + newTrait.name_index = mergeMultinameMap.get(secondTrait.name_index); + for (int m = 0; m < secondTrait.metadata.length; m++) { + newTrait.metadata[m] = mergeMetaDataMap.get(secondTrait.metadata[m]); + } + return newTrait; + } + + private TraitSlotConst mergeTrait(TraitSlotConst secondTrait, Map mergeMultinameMap, + Map mergeStringMap, + Map mergeIntMap, + Map mergeUIntMap, + Map mergeDoubleMap, + Map mergeFloatMap, + Map mergeFloat4Map, + Map mergeDecimalMap, + Map mergeNamespaceMap, + Map mergeMetaDataMap + ) { + TraitSlotConst newTrait = secondTrait.clone(); + newTrait.name_index = mergeMultinameMap.get(secondTrait.name_index); + for (int m = 0; m < secondTrait.metadata.length; m++) { + newTrait.metadata[m] = mergeMetaDataMap.get(secondTrait.metadata[m]); + } + newTrait.type_index = mergeMultinameMap.get(secondTrait.type_index); + Map valueMergeMap = null; + + switch (newTrait.value_kind) { + case ValueKind.CONSTANT_Utf8: + valueMergeMap = mergeStringMap; + break; + case ValueKind.CONSTANT_Int: + valueMergeMap = mergeIntMap; + break; + case ValueKind.CONSTANT_UInt: + valueMergeMap = mergeUIntMap; + break; + case ValueKind.CONSTANT_Double: + valueMergeMap = mergeDoubleMap; + break; + case ValueKind.CONSTANT_DecimalOrFloat: + //assuming the second ABC has same decimal/float support + if (hasDecimalSupport()) { + valueMergeMap = mergeDecimalMap; + } else if (hasFloatSupport()) { + valueMergeMap = mergeFloatMap; + } else { + //should not happen + } + break; + case ValueKind.CONSTANT_Float4: + if (hasFloatSupport()) { + valueMergeMap = mergeFloat4Map; + } + break; + case ValueKind.CONSTANT_ExplicitNamespace: + case ValueKind.CONSTANT_Namespace: + case ValueKind.CONSTANT_PackageInternalNs: + case ValueKind.CONSTANT_PackageNamespace: + case ValueKind.CONSTANT_PrivateNs: + case ValueKind.CONSTANT_ProtectedNamespace: + case ValueKind.CONSTANT_StaticProtectedNs: + valueMergeMap = mergeNamespaceMap; + break; + } + if (valueMergeMap != null) { + newTrait.value_index = valueMergeMap.get(secondTrait.value_index); + } + return newTrait; //TODO + } + + private TraitFunction mergeTrait(TraitFunction secondTrait, Map mergeMultinameMap, Map mergeMethodInfoMap, Map mergeMetaDataMap) { + TraitFunction newTrait = secondTrait.clone(); + newTrait.method_info = mergeMethodInfoMap.get(secondTrait.method_info); + newTrait.name_index = mergeMultinameMap.get(secondTrait.name_index); + for (int m = 0; m < secondTrait.metadata.length; m++) { + newTrait.metadata[m] = mergeMetaDataMap.get(secondTrait.metadata[m]); + } + return newTrait; + } + + private TraitClass mergeTrait(TraitClass secondTrait, Map mergeClassMap, Map mergeMultinameMap, Map mergeMetaDataMap) { + TraitClass newTrait = secondTrait.clone(); + newTrait.class_info = mergeClassMap.get(secondTrait.class_info); + newTrait.name_index = mergeMultinameMap.get(secondTrait.name_index); + for (int m = 0; m < secondTrait.metadata.length; m++) { + newTrait.metadata[m] = mergeMetaDataMap.get(secondTrait.metadata[m]); + } + return newTrait; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABCVersion.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABCVersion.java index 8671c40b0..0e5f3fb18 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABCVersion.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABCVersion.java @@ -27,4 +27,27 @@ public class ABCVersion implements Comparable { return "" + major + "." + minor; } + @Override + public int hashCode() { + int hash = 7; + hash = 53 * hash + this.major; + hash = 53 * hash + this.minor; + 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 ABCVersion other = (ABCVersion) obj; + return true; + } + } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ScriptPack.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ScriptPack.java index 517152c1f..55317afe1 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ScriptPack.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ScriptPack.java @@ -200,12 +200,14 @@ public class ScriptPack extends AS3ClassTreeItem { //TODO: handle this better in GUI(?) writer.startTrait(GraphTextWriter.TRAIT_SCRIPT_INITIALIZER); writer.startMethod(script_init); - if (!scriptInitializerIsEmpty) { - writer.startBlock(); - abc.bodies.get(bodyIndex).toString(path +/*packageName +*/ "/.scriptinitializer", exportMode, abc, null, writer, new ArrayList<>()); - writer.endBlock(); - } else { - writer.append(" "); + if (exportMode != ScriptExportMode.AS_METHOD_STUBS) { + if (!scriptInitializerIsEmpty) { + writer.startBlock(); + abc.bodies.get(bodyIndex).toString(path +/*packageName +*/ "/.scriptinitializer", exportMode, abc, null, writer, new ArrayList<>()); + writer.endBlock(); + } else { + writer.append(" "); + } } writer.endMethod(); writer.endTrait(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java index ac413ff39..ac7f5079a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java @@ -1100,6 +1100,11 @@ public class AVM2Code implements Cloneable { } } + public void setInstructionOperand(int ip, int operandIndex, int value, MethodBody body) { + int oldVal = code.get(ip).operands[ip]; + code.get(ip).operands[ip] = value; + } + public byte[] getBytes() { return getBytes(null); } @@ -1638,7 +1643,7 @@ public class AVM2Code implements Cloneable { } }//*/ - /*if ((ip + 2 < code.size()) && (ins.definition instanceof NewCatchIns)) { // Filling local register in catch clause + /*if ((ip + 2 < code.size()) && (ins.definition instanceof NewCatchIns)) { // Filling local register in catch clause if (code.get(ip + 1).definition instanceof DupIns) { if (code.get(ip + 2).definition instanceof SetLocalTypeIns) { ins.definition.translate(isStatic, classIndex, localRegs, stack, scopeStack, constants, ins, method_info, output, body, abc, localRegNames, fullyQualifiedNames); @@ -2145,6 +2150,38 @@ public class AVM2Code implements Cloneable { return list; } + public void updateInstructionByteCountByAddr(long instructionAddress, int byteDelta, MethodBody body) { + if (byteDelta != 0) { + updateOffsets(new OffsetUpdater() { + + @Override + public long updateInstructionOffset(long address) { + if (address > instructionAddress) { + return address + byteDelta; + } + return address; + } + + @Override + public int updateOperandOffset(long insAddr, long targetAddress, int offset) { + if (targetAddress > instructionAddress && insAddr <= instructionAddress) { + return offset + byteDelta; + } + if (targetAddress <= instructionAddress && insAddr > instructionAddress) { + return offset - byteDelta; + } + return offset; + } + }, body); + body.setModified(); + } + } + + public void updateInstructionByteCount(int pos, int byteDelta, MethodBody body) { + AVM2Instruction instruction = code.get(pos); + updateInstructionByteCountByAddr(instruction.getAddress(), byteDelta, body); + } + public void updateOffsets(OffsetUpdater updater, MethodBody body) { for (int i = 0; i < code.size(); i++) { AVM2Instruction ins = code.get(i); @@ -2172,6 +2209,7 @@ public class AVM2Code implements Cloneable { } } ins.setAddress(updater.updateInstructionOffset(ins.getAddress())); + //Note: changing operands here does not change instruction byte length as offsets are always S24 (not variable length) } for (ABCException ex : body.exceptions) { 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 1303e51f5..4a51f9ff2 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 @@ -377,6 +377,14 @@ public class AVM2ConstantPool implements Cloneable { return null; } + public int getDecimalId(Decimal val, boolean add) { + int id = getDecimalId(val); + if (add && id == -1) { + id = addDecimal(val); + } + return id; + } + public Float getFloat(int index) { try { return constant_float.get(index); @@ -514,6 +522,10 @@ public class AVM2ConstantPool implements Cloneable { return constant_float4.indexOf(value); } + private int getDecimalId(Decimal value) { + return constant_decimal.indexOf(value); + } + private int getStringId(String value) { return constant_string.indexOf(value); } @@ -755,4 +767,132 @@ public class AVM2ConstantPool implements Cloneable { } return null; } + + /** + * Merges second constantpool into this one + * + * @param secondPool + * @param stringMap + * @param intMap + * @param uintMap + * @param doubleMap + * @param floatMap + * @param float4Map + * @param decimalMap + * @param namespaceMap + * @param namespaceSetMap + * @param multinameMap + */ + public void merge(AVM2ConstantPool secondPool, Map stringMap, Map intMap, Map uintMap, Map doubleMap, Map floatMap, Map float4Map, Map decimalMap, Map namespaceMap, Map namespaceSetMap, Map multinameMap) { + stringMap.put(0, 0); + for (int i = 1; i < secondPool.constant_string.size(); i++) { + String val = secondPool.constant_string.get(i); + stringMap.put(i, getStringId(val, true)); + } + intMap.put(0, 0); + for (int i = 1; i < secondPool.constant_int.size(); i++) { + Long val = secondPool.constant_int.get(i); + intMap.put(i, getIntId(val, true)); + } + uintMap.put(0, 0); + for (int i = 1; i < secondPool.constant_uint.size(); i++) { + Long val = secondPool.constant_uint.get(i); + uintMap.put(i, getUIntId(val, true)); + } + doubleMap.put(0, 0); + for (int i = 1; i < secondPool.constant_double.size(); i++) { + Double val = secondPool.constant_double.get(i); + doubleMap.put(i, getDoubleId(val, true)); + } + floatMap.put(0, 0); + for (int i = 1; i < secondPool.constant_float.size(); i++) { + Float val = secondPool.constant_float.get(i); + floatMap.put(i, getFloatId(val, true)); + } + float4Map.put(0, 0); + for (int i = 1; i < secondPool.constant_float4.size(); i++) { + Float4 val = secondPool.constant_float4.get(i); + float4Map.put(i, getFloat4Id(val, true)); + } + decimalMap.put(0, 0); + for (int i = 1; i < secondPool.constant_decimal.size(); i++) { + Decimal val = secondPool.constant_decimal.get(i); + decimalMap.put(i, getDecimalId(val, true)); + } + namespaceMap.put(0, 0); + for (int i = 1; i < secondPool.constant_namespace.size(); i++) { + Namespace secondNamespace = secondPool.constant_namespace.get(i); + int mappedId; + int newNameIndex = stringMap.get(secondNamespace.name_index); + if (secondNamespace.kind == Namespace.KIND_PRIVATE) {//always add, this does not exists in this ABC. Conflicting private namespaces can have same names. + mappedId = addNamespace(secondNamespace.kind, newNameIndex); + } else { + mappedId = getNamespaceId(secondNamespace.kind, newNameIndex, 0, true); + } + namespaceMap.put(i, mappedId); + } + namespaceSetMap.put(0, 0); + for (int i = 1; i < secondPool.constant_namespace_set.size(); i++) { + NamespaceSet secondNamespaceSet = secondPool.constant_namespace_set.get(i); + int mappedsNss[] = new int[secondNamespaceSet.namespaces.length]; + for (int n = 0; n < secondNamespaceSet.namespaces.length; n++) { + mappedsNss[n] = namespaceMap.get(secondNamespaceSet.namespaces[n]); + } + int mappedId = getNamespaceSetId(mappedsNss, true); + namespaceSetMap.put(i, mappedId); + } + multinameMap.put(0, 0); + for (int i = 1; i < secondPool.constant_multiname.size(); i++) { + Multiname secondMultiname = secondPool.constant_multiname.get(i); + Multiname newMultiname = null; + int newNameIndex = secondMultiname.name_index <= 0 ? secondMultiname.name_index : stringMap.get(secondMultiname.name_index); + int newNsIndex = secondMultiname.namespace_index <= 0 ? secondMultiname.namespace_index : namespaceMap.get(secondMultiname.namespace_index); + int newNssIndex = secondMultiname.namespace_set_index <= 0 ? secondMultiname.namespace_set_index : namespaceSetMap.get(secondMultiname.namespace_set_index); + + switch (secondMultiname.kind) { + case Multiname.MULTINAME: + newMultiname = Multiname.createMultiname(false, newNameIndex, newNssIndex); + break; + case Multiname.MULTINAMEA: + newMultiname = Multiname.createMultiname(true, newNameIndex, newNssIndex); + break; + case Multiname.MULTINAMEL: + newMultiname = Multiname.createMultinameL(false, newNssIndex); + break; + case Multiname.MULTINAMELA: + newMultiname = Multiname.createMultinameL(true, newNssIndex); + break; + case Multiname.QNAME: + newMultiname = Multiname.createQName(false, newNameIndex, newNsIndex); + break; + case Multiname.QNAMEA: + newMultiname = Multiname.createQName(true, newNameIndex, newNsIndex); + break; + case Multiname.RTQNAME: + newMultiname = Multiname.createRTQName(false, newNameIndex); + break; + case Multiname.RTQNAMEA: + newMultiname = Multiname.createRTQName(true, newNameIndex); + break; + case Multiname.RTQNAMEL: + newMultiname = Multiname.createRTQNameL(false); + break; + case Multiname.RTQNAMELA: + newMultiname = Multiname.createRTQNameL(true); + break; + case Multiname.TYPENAME: + int newQnameIndex = multinameMap.get(secondMultiname.qname_index); + int newParams[] = new int[secondMultiname.params.length]; + for (int p = 0; p < secondMultiname.params.length; p++) { + newParams[p] = multinameMap.get(secondMultiname.params[p]); + } + newMultiname = Multiname.createTypeName(newQnameIndex, newParams); + break; + } + + int mappedId = getMultinameId(newMultiname, true); + multinameMap.put(i, mappedId); + } + + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorRegisters.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorRegisters.java index 0063e899e..48ad09650 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorRegisters.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorRegisters.java @@ -220,7 +220,7 @@ public class AVM2DeobfuscatorRegisters extends AVM2DeobfuscatorSimple { AVM2Instruction ins = code.code.get(idx); InstructionDefinition def = ins.definition; - //System.err.println("" + idx + ": " + ins + " stack:" + stack.size()); + //System.err.println("" + idx + ": " + ins + " stack:" + stack.size()); // do not throw EmptyStackException, much faster int requiredStackSize = def.getStackPopCount(ins, abc); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorSimpleOld.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorSimpleOld.java index 5fadb3a3d..11ded804a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorSimpleOld.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorSimpleOld.java @@ -161,7 +161,7 @@ public class AVM2DeobfuscatorSimpleOld extends SWFDecompilerAdapter { AVM2Instruction ins = code.code.get(i); if (ins.definition instanceof JumpIns) { long address = ins.getTargetAddress(); - jumpTargets.add( code.adr2pos(address) ); + jumpTargets.add(code.adr2pos(address)); } } @@ -294,9 +294,8 @@ public class AVM2DeobfuscatorSimpleOld extends SWFDecompilerAdapter { if (inlineIns.contains(ins)) { if (def instanceof SetLocalTypeIns) { - InstructionDefinition prevDef = code.code.get(idx-1).definition; - if ((prevDef instanceof DupIns && !jumpTargets.contains(idx-2)) || !jumpTargets.contains(idx-1)) - { + InstructionDefinition prevDef = code.code.get(idx - 1).definition; + if ((prevDef instanceof DupIns && !jumpTargets.contains(idx - 2)) || !jumpTargets.contains(idx - 1)) { int regId = ((SetLocalTypeIns) def).getRegisterId(ins); staticRegs.put(regId, localData.localRegs.get(regId).getNotCoerced()); code.replaceInstruction(idx, new AVM2Instruction(0, DeobfuscatePopIns.getInstance(), null), body); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/AVM2Instruction.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/AVM2Instruction.java index f53b17d80..ce3524922 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/AVM2Instruction.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/AVM2Instruction.java @@ -27,6 +27,7 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnValueIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnVoidIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ThrowIns; import com.jpexs.decompiler.flash.abc.types.Float4; +import com.jpexs.decompiler.flash.abc.types.MethodBody; import com.jpexs.decompiler.flash.abc.types.Multiname; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.graph.DottedChain; @@ -488,4 +489,44 @@ public class AVM2Instruction implements Cloneable, GraphSourceItem { public String getFile() { return file; } + + /** + * Set operand value the right way - update offsets neccessarily. Because + * some operand types are variable length (like U30) + * + * @param operandIndex + * @param newValue + * @param code + * @param body + */ + public void setOperand(int operandIndex, int newValue, AVM2Code code, MethodBody body) { + int oldByteCount = getBytesLength(); + operands[operandIndex] = newValue; + int newByteCount = getBytesLength(); + int byteDelta = newByteCount - oldByteCount; + if (byteDelta != 0) { + code.updateInstructionByteCountByAddr(address, byteDelta, body); + } + body.setModified(); + } + + /** + * Set operand values the right way - update offsets neccessarily. Because + * some operand types are variable length (like U30) + * + * @param operands + * @param code + * @param body + */ + public void setOperands(int operands[], AVM2Code code, MethodBody body) { + int oldByteCount = getBytesLength(); + this.operands = operands; + int newByteCount = getBytesLength(); + int byteDelta = newByteCount - oldByteCount; + if (byteDelta != 0) { + code.updateInstructionByteCountByAddr(address, byteDelta, body); + } + body.setModified(); + } + } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/AVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/AVM2Item.java index 3d1159eaf..9ed4806b3 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/AVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/AVM2Item.java @@ -108,8 +108,9 @@ public abstract class AVM2Item extends GraphTargetItem { } if (propertyName instanceof FullMultinameAVM2Item) { if (((FullMultinameAVM2Item) propertyName).name != null) { - if (((FullMultinameAVM2Item) propertyName).namespace != null) + if (((FullMultinameAVM2Item) propertyName).namespace != null) { writer.append("."); + } return propertyName.toString(writer, localData); } else { writer.append("."); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/clauses/ForEachInAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/clauses/ForEachInAVM2Item.java index 433285894..0ba0873f0 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/clauses/ForEachInAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/clauses/ForEachInAVM2Item.java @@ -77,7 +77,7 @@ public class ForEachInAVM2Item extends LoopItem implements Block { */ - /* + /* if (!commands.isEmpty()) { GraphTargetItem firstAssign = commands.get(0); if (firstAssign instanceof SetTypeAVM2Item) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/pcode/Flasm3Lexer.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/pcode/Flasm3Lexer.java index 5b57f3efe..bff9f84dd 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/pcode/Flasm3Lexer.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/pcode/Flasm3Lexer.java @@ -1,6 +1,6 @@ /* The following code was generated by JFlex 1.6.0 */ -/* + /* * Copyright (C) 2010-2016 JPEXS, All rights reserved. * * This library is free software; you can redistribute it and/or @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library. */ -/* Flash assembler language lexer specification */ + /* Flash assembler language lexer specification */ package com.jpexs.decompiler.flash.abc.avm2.parser.pcode; import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException; @@ -24,8 +24,7 @@ import java.util.Stack; /** * This class is a scanner generated by - * JFlex 1.6.0 - * from the specification file + * JFlex 1.6.0 from the specification file * C:/FFDec/jpexs-decompiler/libsrc/ffdec_lib/lexers/actionscript3_pcode.flex */ public final class Flasm3Lexer { @@ -51,9 +50,8 @@ public final class Flasm3Lexer { /** * ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l - * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l - * at the beginning of a line - * l is of the form l = 2*k, k a non negative integer + * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l at the + * beginning of a line l is of the form l = 2*k, k a non negative integer */ private static final int ZZ_LEXSTATE[] = { 0, 0, 1, 1, 2, 2 @@ -272,9 +270,11 @@ public final class Flasm3Lexer { } private static int zzUnpackAction(String packed, int offset, int[] result) { - int i = 0; /* index in packed string */ + int i = 0; + /* index in packed string */ - int j = offset; /* index in unpacked array */ + int j = offset; + /* index in unpacked array */ int l = packed.length(); while (i < l) { @@ -380,9 +380,11 @@ public final class Flasm3Lexer { } private static int zzUnpackRowMap(String packed, int offset, int[] result) { - int i = 0; /* index in packed string */ + int i = 0; + /* index in packed string */ - int j = offset; /* index in unpacked array */ + int j = offset; + /* index in unpacked array */ int l = packed.length(); while (i < l) { @@ -2479,9 +2481,11 @@ public final class Flasm3Lexer { } private static int zzUnpackTrans(String packed, int offset, int[] result) { - int i = 0; /* index in packed string */ + int i = 0; + /* index in packed string */ - int j = offset; /* index in unpacked array */ + int j = offset; + /* index in unpacked array */ int l = packed.length(); while (i < l) { @@ -2550,9 +2554,11 @@ public final class Flasm3Lexer { } private static int zzUnpackAttribute(String packed, int offset, int[] result) { - int i = 0; /* index in packed string */ + int i = 0; + /* index in packed string */ - int j = offset; /* index in unpacked array */ + int j = offset; + /* index in unpacked array */ int l = packed.length(); while (i < l) { @@ -2581,8 +2587,8 @@ public final class Flasm3Lexer { private int zzLexicalState = YYINITIAL; /** - * this buffer contains the current text to be matched and is - * the source of the yytext() string + * this buffer contains the current text to be matched and is the source of + * the yytext() string */ private char zzBuffer[] = new char[ZZ_BUFFERSIZE]; @@ -2602,8 +2608,8 @@ public final class Flasm3Lexer { private int zzStartRead; /** - * endRead marks the last character in the buffer, that has been read - * from input + * endRead marks the last character in the buffer, that has been read from + * input */ private int zzEndRead; @@ -2639,10 +2645,10 @@ public final class Flasm3Lexer { private boolean zzEOFDone; /** - * The number of occupied positions in zzBuffer beyond zzEndRead. - * When a lead/high surrogate has been read from the input stream - * into the final zzBuffer position, this will have a value of 1; - * otherwise, it will have a value of 0. + * The number of occupied positions in zzBuffer beyond zzEndRead. When a + * lead/high surrogate has been read from the input stream into the final + * zzBuffer position, this will have a value of 1; otherwise, it will have a + * value of 0. */ private int zzFinalHighSurrogate = 0; @@ -2705,9 +2711,11 @@ public final class Flasm3Lexer { */ private static char[] zzUnpackCMap(String packed) { char[] map = new char[0x110000]; - int i = 0; /* index in packed string */ + int i = 0; + /* index in packed string */ - int j = 0; /* index in unpacked array */ + int j = 0; + /* index in unpacked array */ while (i < 2916) { int count = packed.charAt(i++); @@ -2766,7 +2774,8 @@ public final class Flasm3Lexer { if (totalRead > 0) { zzEndRead += totalRead; - if (totalRead == requested) { /* possibly more input available */ + if (totalRead == requested) { + /* possibly more input available */ if (Character.isHighSurrogate(zzBuffer[zzEndRead - 1])) { --zzEndRead; @@ -2784,9 +2793,11 @@ public final class Flasm3Lexer { * Closes the input stream. */ public final void yyclose() throws java.io.IOException { - zzAtEOF = true; /* indicate end of file */ + zzAtEOF = true; + /* indicate end of file */ - zzEndRead = zzStartRead; /* invalidate buffer */ + zzEndRead = zzStartRead; + /* invalidate buffer */ if (zzReader != null) { zzReader.close(); @@ -2794,12 +2805,12 @@ public final class Flasm3Lexer { } /** - * Resets the scanner to read from a new input stream. - * Does not close the old reader. + * Resets the scanner to read from a new input stream. Does not close the + * old reader. * * All internal variables are reset, the old input stream - * cannot be reused (internal buffer is discarded and lost). - * Lexical state is set to ZZ_INITIAL. + * cannot be reused (internal buffer is discarded and lost). Lexical + * state is set to ZZ_INITIAL. * * Internal scan buffer is resized down to its initial length, if it has * grown. @@ -2845,13 +2856,12 @@ public final class Flasm3Lexer { } /** - * Returns the character at position pos from the - * matched text. + * Returns the character at position pos from the matched text. * * It is equivalent to yytext().charAt(pos), but faster * - * @param pos the position of the character to fetch. - * A value from 0 to yylength()-1. + * @param pos the position of the character to fetch. A value from 0 to + * yylength()-1. * * @return the character at position pos */ @@ -2869,14 +2879,13 @@ public final class Flasm3Lexer { /** * Reports an error that occured while scanning. * - * In a wellformed scanner (no or only correct usage of - * yypushback(int) and a match-all fallback rule) this method - * will only be called with things that "Can't Possibly Happen". - * If this method is called, something is seriously wrong - * (e.g. a JFlex bug producing a faulty scanner etc.). + * In a wellformed scanner (no or only correct usage of yypushback(int) and + * a match-all fallback rule) this method will only be called with things + * that "Can't Possibly Happen". If this method is called, something is + * seriously wrong (e.g. a JFlex bug producing a faulty scanner etc.). * - * Usual syntax/scanner level error handling should be done - * in error fallback rules. + * Usual syntax/scanner level error handling should be done in error + * fallback rules. * * @param errorCode the code of the errormessage to display */ @@ -2896,8 +2905,8 @@ public final class Flasm3Lexer { * * They will be read again by then next call of the scanning method * - * @param number the number of characters to be read again. - * This number must not be greater than yylength()! + * @param number the number of characters to be read again. This number must + * not be greater than yylength()! */ public void yypushback(int number) { if (number > yylength()) { @@ -2908,8 +2917,8 @@ public final class Flasm3Lexer { } /** - * Resumes scanning until the next regular expression is matched, - * the end of input is encountered or an I/O-Error occurs. + * Resumes scanning until the next regular expression is matched, the end of + * input is encountered or an I/O-Error occurs. * * @return the next token * @exception java.io.IOException if any I/O-Error occurs 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 e7f86c494..3afe103d9 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 @@ -1,2682 +1,2693 @@ -/* - * Copyright (C) 2010-2016 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.avm2.parser.script; - -import com.jpexs.decompiler.flash.SWFInputStream; -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instructions; -import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructSuperIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.JumpIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnValueIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnVoidIns; -import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.ApplyTypeAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.BooleanAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.FloatValueAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.GetDescendantsAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.IntegerValueAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.LocalRegAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.NameValuePair; -import com.jpexs.decompiler.flash.abc.avm2.model.NanAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.NewObjectAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.NullAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.ReturnValueAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.ReturnVoidAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.StringAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.ThrowAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.UndefinedAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.WithAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.WithObjectAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ForEachInAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ForInAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.clauses.TryAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.IfCondition; -import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException; -import com.jpexs.decompiler.flash.abc.avm2.parser.script.AbcIndexing.ClassIndex; -import com.jpexs.decompiler.flash.abc.types.ABCException; -import com.jpexs.decompiler.flash.abc.types.ClassInfo; -import com.jpexs.decompiler.flash.abc.types.ConvertData; -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.ScriptInfo; -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 com.jpexs.decompiler.flash.abc.types.traits.Traits; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.ecma.EcmaScript; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.flash.helpers.NulWriter; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.DottedChain; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.Loop; -import com.jpexs.decompiler.graph.ScopeStack; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.TypeItem; -import com.jpexs.decompiler.graph.model.AndItem; -import com.jpexs.decompiler.graph.model.BreakItem; -import com.jpexs.decompiler.graph.model.CommaExpressionItem; -import com.jpexs.decompiler.graph.model.ContinueItem; -import com.jpexs.decompiler.graph.model.DefaultItem; -import com.jpexs.decompiler.graph.model.DoWhileItem; -import com.jpexs.decompiler.graph.model.DuplicateItem; -import com.jpexs.decompiler.graph.model.FalseItem; -import com.jpexs.decompiler.graph.model.ForItem; -import com.jpexs.decompiler.graph.model.IfItem; -import com.jpexs.decompiler.graph.model.LocalData; -import com.jpexs.decompiler.graph.model.NotItem; -import com.jpexs.decompiler.graph.model.OrItem; -import com.jpexs.decompiler.graph.model.PopItem; -import com.jpexs.decompiler.graph.model.PushItem; -import com.jpexs.decompiler.graph.model.SwitchItem; -import com.jpexs.decompiler.graph.model.TernarOpItem; -import com.jpexs.decompiler.graph.model.TrueItem; -import com.jpexs.decompiler.graph.model.UnboundedTypeItem; -import com.jpexs.decompiler.graph.model.WhileItem; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * - * @author JPEXS - */ -public class AVM2SourceGenerator implements SourceGenerator { - - public final AbcIndexing abcIndex; - - public static final int MARK_E_START = 0; - - public static final int MARK_E_END = 1; - - public static final int MARK_E_TARGET = 2; - - public static final int MARK_E_FINALLYPART = 3; - - private AVM2Instruction ins(int instructionCode, int... operands) { - return new AVM2Instruction(0, instructionCode, operands); - } - - private AVM2Instruction ins(InstructionDefinition def, int... operands) { - return new AVM2Instruction(0, def, operands); - } - - @Override - public List generate(SourceGeneratorLocalData localData, FalseItem item) throws CompilationException { - return GraphTargetItem.toSourceMerge(localData, this, ins(AVM2Instructions.PushFalse)); - } - - @Override - public List generate(SourceGeneratorLocalData localData, TrueItem item) throws CompilationException { - return GraphTargetItem.toSourceMerge(localData, this, ins(AVM2Instructions.PushTrue)); - } - - public List generate(SourceGeneratorLocalData localData, GetDescendantsAVM2Item item) throws CompilationException { - - AVM2ConstantPool constants = abcIndex.getSelectedAbc().constants; - int[] nssa = new int[item.openedNamespaces.size()]; - for (int i = 0; i < item.openedNamespaces.size(); i++) { - nssa[i] = item.openedNamespaces.get(i).getCpoolIndex(abcIndex); - } - - int nsset = constants.getNamespaceSetId(nssa, true); - - return GraphTargetItem.toSourceMerge(localData, this, - item.object, - ins(AVM2Instructions.GetDescendants, constants.getMultinameId(Multiname.createMultiname(false, constants.getStringId(item.nameStr, true), nsset), true)) - ); - } - - @Override - public List generate(SourceGeneratorLocalData localData, AndItem item) throws CompilationException { - List ret = new ArrayList<>(); - ret.addAll(generateToActionList(localData, item.leftSide)); - ret.add(ins(AVM2Instructions.Dup)); - if (!("" + item.leftSide.returnType()).equals("Boolean")) { - ret.add(ins(AVM2Instructions.ConvertB)); - } - List andExpr = generateToActionList(localData, item.rightSide); - andExpr.add(0, ins(AVM2Instructions.Pop)); - int andExprLen = insToBytes(andExpr).length; - ret.add(ins(AVM2Instructions.IfFalse, andExprLen)); - ret.addAll(andExpr); - return ret; - - } - - private byte[] insToBytes(List code) { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - for (AVM2Instruction instruction : code) { - - baos.write(instruction.getBytes()); - - } - return baos.toByteArray(); - } catch (IOException ex) { - Logger.getLogger(AVM2SourceGenerator.class.getName()).log(Level.SEVERE, null, ex); - } - return SWFInputStream.BYTE_ARRAY_EMPTY; - } - - @Override - public List generate(SourceGeneratorLocalData localData, OrItem item) throws CompilationException { - List ret = new ArrayList<>(); - ret.addAll(generateToActionList(localData, item.leftSide)); - ret.add(ins(AVM2Instructions.Dup)); - if (!("" + item.leftSide.returnType()).equals("Boolean")) { - ret.add(ins(AVM2Instructions.ConvertB)); - } - List orExpr = generateToActionList(localData, item.rightSide); - orExpr.add(0, ins(AVM2Instructions.Pop)); - int orExprLen = insToBytes(orExpr).length; - ret.add(ins(AVM2Instructions.IfTrue, orExprLen)); - ret.addAll(orExpr); - return ret; - } - - public ArrayList toInsList(List items) { - ArrayList ret = new ArrayList<>(); - for (GraphSourceItem s : items) { - if (s instanceof AVM2Instruction) { - ret.add((AVM2Instruction) s); - } - } - return ret; - } - - private List nonempty(List list) { - if (list == null) { - return new ArrayList<>(); - } - return list; - } - - private List condition(SourceGeneratorLocalData localData, GraphTargetItem t, int offset) throws CompilationException { - if (t instanceof IfCondition) { - IfCondition ic = (IfCondition) t; - return GraphTargetItem.toSourceMerge(localData, this, ic.getLeftSide(), ic.getRightSide(), ins(ic.getIfDefinition(), offset)); - } - return GraphTargetItem.toSourceMerge(localData, this, t, ins(AVM2Instructions.IfTrue, offset)); - } - - private List notCondition(SourceGeneratorLocalData localData, GraphTargetItem t, int offset) throws CompilationException { - if (t instanceof IfCondition) { - IfCondition ic = (IfCondition) t; - return GraphTargetItem.toSourceMerge(localData, this, ic.getLeftSide(), ic.getRightSide(), ins(ic.getIfNotDefinition(), offset)); - } - return GraphTargetItem.toSourceMerge(localData, this, t, ins(AVM2Instructions.IfFalse, offset)); - } - - private List generateIf(SourceGeneratorLocalData localData, GraphTargetItem expression, List onTrueCmds, List onFalseCmds, boolean ternar) throws CompilationException { - List ret = new ArrayList<>(); - //ret.addAll(notCondition(localData, expression)); - List onTrue; - List onFalse = null; - if (ternar) { - onTrue = toInsList(onTrueCmds.get(0).toSource(localData, this)); - } else { - onTrue = generateToInsList(localData, onTrueCmds); - } - - if (onFalseCmds != null && !onFalseCmds.isEmpty()) { - if (ternar) { - onFalse = toInsList(onFalseCmds.get(0).toSource(localData, this)); - } else { - onFalse = generateToInsList(localData, onFalseCmds); - } - } - AVM2Instruction ajmp = null; - if (onFalse != null) { - if (!((!nonempty(onTrue).isEmpty()) - && ((onTrue.get(onTrue.size() - 1).definition instanceof ContinueJumpIns) - || ((onTrue.get(onTrue.size() - 1).definition instanceof BreakJumpIns))))) { - ajmp = ins(AVM2Instructions.Jump, 0); - onTrue.add(ajmp); - } - } - - byte[] onTrueBytes = insToBytes(onTrue); - int onTrueLen = onTrueBytes.length; - - ret.addAll(notCondition(localData, expression, onTrueLen)); - ret.addAll(onTrue); - - if (onFalse != null) { - byte[] onFalseBytes = insToBytes(onFalse); - int onFalseLen = onFalseBytes.length; - if (ajmp != null) { - ajmp.operands[0] = onFalseLen; - } - ret.addAll(onFalse); - } - return ret; - } - - public List generate(SourceGeneratorLocalData localData, XMLFilterAVM2Item item) throws CompilationException { - List ret = new ArrayList<>(); - final Reference counterReg = new Reference<>(0); - final Reference collectionReg = new Reference<>(0); - final Reference xmlListReg = new Reference<>(0); - List xmlListSetTemp = AssignableAVM2Item.setTemp(localData, this, xmlListReg); - AVM2ConstantPool constants = abcIndex.getSelectedAbc().constants; - ret.addAll(GraphTargetItem.toSourceMerge(localData, this, - ins(AVM2Instructions.PushByte, 0), - AssignableAVM2Item.setTemp(localData, this, counterReg), - item.object, - ins(AVM2Instructions.CheckFilter), - NameAVM2Item.generateCoerce(localData, this, TypeItem.UNBOUNDED), - AssignableAVM2Item.setTemp(localData, this, collectionReg), - ins(AVM2Instructions.GetLex, constants.getMultinameId(Multiname.createQName(false, constants.getStringId("XMLList", true), constants.getNamespaceId(Namespace.KIND_PACKAGE, "", 0, true)), true)), - ins(AVM2Instructions.PushString, constants.getStringId("", true)), - ins(AVM2Instructions.Construct, 1), - xmlListSetTemp - )); - final Reference tempVal1 = new Reference<>(0); - final Reference tempVal2 = new Reference<>(0); - - List forBody = toInsList(GraphTargetItem.toSourceMerge(localData, this, - ins(AVM2Instructions.Label), - AssignableAVM2Item.getTemp(localData, this, collectionReg), - AssignableAVM2Item.getTemp(localData, this, counterReg), - ins(AVM2Instructions.NextValue), - AssignableAVM2Item.dupSetTemp(localData, this, tempVal1), - AssignableAVM2Item.dupSetTemp(localData, this, tempVal2), - ins(AVM2Instructions.PushWith) - )); - localData.scopeStack.add(new LocalRegAVM2Item(null, null, tempVal2.getVal(), null)); - forBody.addAll(toInsList(item.value.toSource(localData, this))); - List trueBody = new ArrayList<>(); - trueBody.addAll(toInsList(AssignableAVM2Item.getTemp(localData, this, xmlListReg))); - trueBody.addAll(toInsList(AssignableAVM2Item.getTemp(localData, this, counterReg))); - trueBody.addAll(toInsList(AssignableAVM2Item.getTemp(localData, this, tempVal1))); - trueBody.add(ins(AVM2Instructions.SetProperty, constants.getMultinameId(Multiname.createMultinameL(false, NamespaceItem.getCpoolSetIndex(abcIndex, item.openedNamespaces)), true))); - forBody.add(ins(AVM2Instructions.IfFalse, insToBytes(trueBody).length)); - forBody.addAll(trueBody); - forBody.add(ins(AVM2Instructions.PopScope)); - localData.scopeStack.remove(localData.scopeStack.size() - 1); - forBody.addAll(toInsList(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempVal2, tempVal1)))); - - int forBodyLen = insToBytes(forBody).length; - AVM2Instruction forwardJump = ins(AVM2Instructions.Jump, forBodyLen); - ret.add(forwardJump); - - List expr = new ArrayList<>(); - expr.add(ins(AVM2Instructions.HasNext2, collectionReg.getVal(), counterReg.getVal())); - AVM2Instruction backIf = ins(AVM2Instructions.IfTrue, 0); - expr.add(backIf); - - int exprLen = insToBytes(expr).length; - backIf.operands[0] = -(exprLen + forBodyLen); - - ret.addAll(forBody); - ret.addAll(expr); - ret.addAll(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(collectionReg, counterReg))); - ret.addAll(AssignableAVM2Item.getTemp(localData, this, xmlListReg)); - ret.addAll(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(xmlListReg))); - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, IfItem item) throws CompilationException { - return generateIf(localData, item.expression, item.onTrue, item.onFalse, false); - } - - private void fixSwitch(List code, int breakOffset, long loopId) { - fixLoop(code, breakOffset, Integer.MAX_VALUE, loopId); - } - - private void fixLoop(List code, int breakOffset, int continueOffset, long loopId) { - int pos = 0; - for (int a = 0; a < code.size(); a++) { - AVM2Instruction ins = code.get(a); - pos += ins.getBytesLength(); - if (ins.definition instanceof JumpIns) { - if (ins.definition instanceof ContinueJumpIns) { - if (continueOffset != Integer.MAX_VALUE) { - ins.operands[0] = (-pos + continueOffset); - ins.definition = AVM2Code.instructionSet[AVM2Instructions.Jump]; - } - } - if (ins.definition instanceof BreakJumpIns) { - ins.operands[0] = (-pos + breakOffset); - ins.definition = AVM2Code.instructionSet[AVM2Instructions.Jump]; - } - } - } - } - - @Override - public List generate(SourceGeneratorLocalData localData, TernarOpItem item) throws CompilationException { - List onTrue = new ArrayList<>(); - onTrue.add(item.onTrue); - List onFalse = new ArrayList<>(); - onFalse.add(item.onFalse); - return generateIf(localData, item.expression, onTrue, onFalse, true); - } - - @Override - public List generate(SourceGeneratorLocalData localData, WhileItem item) throws CompilationException { - List ret = new ArrayList<>(); - List whileExpr = new ArrayList<>(); - - List ex = new ArrayList<>(item.expression); - GraphTargetItem lastItem = null; - if (!ex.isEmpty()) { - lastItem = ex.remove(ex.size() - 1); - while (lastItem instanceof CommaExpressionItem) { - CommaExpressionItem cei = (CommaExpressionItem) lastItem; - ex.addAll(cei.commands); - lastItem = ex.remove(ex.size() - 1); - } - whileExpr.addAll(generateToInsList(localData, ex)); - } - List whileBody = generateToInsList(localData, item.commands); - AVM2Instruction forwardJump = ins(AVM2Instructions.Jump, 0); - ret.add(forwardJump); - whileBody.add(0, ins(AVM2Instructions.Label)); - ret.addAll(whileBody); - int whileBodyLen = insToBytes(whileBody).length; - forwardJump.operands[0] = whileBodyLen; - whileExpr.addAll(toInsList(condition(localData, lastItem, 0))); - int whileExprLen = insToBytes(whileExpr).length; - whileExpr.get(whileExpr.size() - 1).operands[0] = -(whileExprLen + whileBodyLen); //Assuming last is if instruction - ret.addAll(whileExpr); - fixLoop(whileBody, whileBodyLen + whileExprLen, whileBodyLen, item.loop.id); - return ret; - } - - public List generate(SourceGeneratorLocalData localData, ForEachInAVM2Item item) throws CompilationException { - return generateForIn(localData, item.loop, item.expression.collection, (AssignableAVM2Item) item.expression.object, item.commands, true); - } - - public List generate(SourceGeneratorLocalData localData, ForInAVM2Item item) throws CompilationException { - return generateForIn(localData, item.loop, item.expression.collection, (AssignableAVM2Item) item.expression.object, item.commands, false); - } - - public List generateForIn(SourceGeneratorLocalData localData, Loop loop, GraphTargetItem collection, AssignableAVM2Item assignable, List commands, final boolean each) throws CompilationException { - List ret = new ArrayList<>(); - final Reference counterReg = new Reference<>(0); - final Reference collectionReg = new Reference<>(0); - - if (assignable instanceof UnresolvedAVM2Item) { - assignable = (AssignableAVM2Item) ((UnresolvedAVM2Item) assignable).resolved; - } - - ret.addAll(GraphTargetItem.toSourceMerge(localData, this, - ins(AVM2Instructions.PushByte, 0), - AssignableAVM2Item.setTemp(localData, this, counterReg), - collection, - NameAVM2Item.generateCoerce(localData, this, TypeItem.UNBOUNDED), - AssignableAVM2Item.setTemp(localData, this, collectionReg) - )); - - GraphTargetItem assigned = new GraphTargetItem() { - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - return null; - } - - @Override - public boolean hasReturnValue() { - return true; - } - - @Override - public GraphTargetItem returnType() { - return TypeItem.UNBOUNDED; - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - return toSourceMerge(localData, generator, - AssignableAVM2Item.getTemp(localData, generator, collectionReg), - AssignableAVM2Item.getTemp(localData, generator, counterReg), - ins(each ? AVM2Instructions.NextValue : AVM2Instructions.NextName) - ); - } - }; - assignable.setAssignedValue(assigned); - - List forBody = toInsList(GraphTargetItem.toSourceMerge(localData, this, - ins(AVM2Instructions.Label), - assignable.toSourceIgnoreReturnValue(localData, this) - )); - - forBody.addAll(generateToInsList(localData, commands)); - int forBodyLen = insToBytes(forBody).length; - - AVM2Instruction forwardJump = ins(AVM2Instructions.Jump, forBodyLen); - ret.add(forwardJump); - - List expr = new ArrayList<>(); - expr.add(ins(AVM2Instructions.HasNext2, collectionReg.getVal(), counterReg.getVal())); - AVM2Instruction backIf = ins(AVM2Instructions.IfTrue, 0); - expr.add(backIf); - - int exprLen = insToBytes(expr).length; - backIf.operands[0] = -(exprLen + forBodyLen); - - fixLoop(forBody, forBodyLen + exprLen, forBodyLen, loop.id); - ret.addAll(forBody); - ret.addAll(expr); - ret.addAll(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(collectionReg, counterReg))); - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, DoWhileItem item) throws CompilationException { - List ret = new ArrayList<>(); - List whileExpr = new ArrayList<>(); - - List ex = new ArrayList<>(item.expression); - GraphTargetItem lastItem = null; - if (!ex.isEmpty()) { - lastItem = ex.remove(ex.size() - 1); - while (lastItem instanceof CommaExpressionItem) { - CommaExpressionItem cei = (CommaExpressionItem) lastItem; - ex.addAll(cei.commands); - lastItem = ex.remove(ex.size() - 1); - } - whileExpr.addAll(generateToInsList(localData, ex)); - } - List dowhileBody = generateToInsList(localData, item.commands); - List labelBody = new ArrayList<>(); - labelBody.add(ins(AVM2Instructions.Label)); - int labelBodyLen = insToBytes(labelBody).length; - - AVM2Instruction forwardJump = ins(AVM2Instructions.Jump, labelBodyLen); - ret.add(forwardJump); - ret.addAll(labelBody); - ret.addAll(dowhileBody); - int dowhileBodyLen = insToBytes(dowhileBody).length; - whileExpr.addAll(toInsList(condition(localData, lastItem, 0))); - int dowhileExprLen = insToBytes(whileExpr).length; - whileExpr.get(whileExpr.size() - 1).operands[0] = -(dowhileExprLen + dowhileBodyLen + labelBodyLen); //Assuming last is if instruction - ret.addAll(whileExpr); - fixLoop(dowhileBody, dowhileBodyLen + dowhileExprLen, dowhileBodyLen, item.loop.id); - return ret; - } - - public List generate(SourceGeneratorLocalData localData, WithAVM2Item item) throws CompilationException { - - List ret = new ArrayList<>(); - ret.addAll(item.scope.toSource(localData, this)); - Reference tempReg = new Reference<>(0); - ret.addAll(AssignableAVM2Item.dupSetTemp(localData, this, tempReg)); - localData.scopeStack.add(new WithObjectAVM2Item(null, null, new LocalRegAVM2Item(null, null, tempReg.getVal(), null))); - ret.add(ins(AVM2Instructions.PushWith)); - ret.addAll(generate(localData, item.items)); - ret.add(ins(AVM2Instructions.PopScope)); - ret.addAll(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempReg))); - localData.scopeStack.remove(localData.scopeStack.size() - 1); - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, ForItem item) throws CompilationException { - List ret = new ArrayList<>(); - List forExpr = new ArrayList<>(); - - List ex = new ArrayList<>(); - if (item.expression != null) { - ex.add(item.expression); - } else { - ex.add(new BooleanAVM2Item(null, null, true)); - } - GraphTargetItem lastItem = null; - if (!ex.isEmpty()) { - lastItem = ex.remove(ex.size() - 1); - while (lastItem instanceof CommaExpressionItem) { - CommaExpressionItem cei = (CommaExpressionItem) lastItem; - ex.addAll(cei.commands); - lastItem = ex.remove(ex.size() - 1); - } - forExpr.addAll(generateToInsList(localData, ex)); - } - List forBody = generateToInsList(localData, item.commands); - List forFinalCommands = generateToInsList(localData, item.finalCommands); - - ret.addAll(generateToInsList(localData, item.firstCommands)); - - AVM2Instruction forwardJump = ins(AVM2Instructions.Jump, 0); - ret.add(forwardJump); - forBody.add(0, ins(AVM2Instructions.Label)); - ret.addAll(forBody); - ret.addAll(forFinalCommands); - int forBodyLen = insToBytes(forBody).length; - int forFinalCLen = insToBytes(forFinalCommands).length; - forwardJump.operands[0] = forBodyLen + forFinalCLen; - forExpr.addAll(toInsList(condition(localData, lastItem, 0))); - int forExprLen = insToBytes(forExpr).length; - forExpr.get(forExpr.size() - 1).operands[0] = -(forExprLen + forBodyLen + forFinalCLen); //Assuming last is if instruction - ret.addAll(forExpr); - fixLoop(forBody, forBodyLen + forFinalCLen + forExprLen, forBodyLen, item.loop.id); - return ret; - } - - private long uniqLast = 0; - - public String uniqId() { - uniqLast++; - return "" + uniqLast; - } - - @Override - public List generate(SourceGeneratorLocalData localData, SwitchItem item) throws CompilationException { - List ret = new ArrayList<>(); - Reference switchedReg = new Reference<>(0); - AVM2Instruction forwardJump = ins(AVM2Instructions.Jump, 0); - ret.add(forwardJump); - - int defIndex = -1; - - for (int i = item.caseValues.size() - 1; i >= 0; i--) { - if (item.caseValues.get(i) instanceof DefaultItem) { - defIndex = i; - break; - } - } - - List cases = new ArrayList<>(); - cases.addAll(toInsList(new IntegerValueAVM2Item(null, null, (long) defIndex).toSource(localData, this))); - int cLen = insToBytes(cases).length; - List caseLast = new ArrayList<>(); - caseLast.add(0, ins(AVM2Instructions.Jump, cLen)); - caseLast.addAll(0, toInsList(new IntegerValueAVM2Item(null, null, (long) defIndex).toSource(localData, this))); - int cLastLen = insToBytes(caseLast).length; - caseLast.add(0, ins(AVM2Instructions.Jump, cLastLen)); - cases.addAll(0, caseLast); - - List preCases = new ArrayList<>(); - preCases.addAll(toInsList(item.switchedObject.toSource(localData, this))); - preCases.addAll(toInsList(AssignableAVM2Item.setTemp(localData, this, switchedReg))); - - for (int i = item.caseValues.size() - 1; i >= 0; i--) { - if (item.caseValues.get(i) instanceof DefaultItem) { - continue; - } - List sub = new ArrayList<>(); - sub.addAll(toInsList(new IntegerValueAVM2Item(null, null, (long) i).toSource(localData, this))); - sub.add(ins(AVM2Instructions.Jump, insToBytes(cases).length)); - int subLen = insToBytes(sub).length; - - cases.addAll(0, sub); - cases.add(0, ins(AVM2Instructions.IfStrictNe, subLen)); - cases.addAll(0, toInsList(AssignableAVM2Item.getTemp(localData, this, switchedReg))); - cases.addAll(0, toInsList(item.caseValues.get(i).toSource(localData, this))); - } - cases.addAll(0, preCases); - - AVM2Instruction lookupOp = new AVM2Instruction(0, AVM2Instructions.LookupSwitch, new int[item.caseValues.size() + 1 + 1]); - cases.addAll(toInsList(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(switchedReg)))); - List bodies = new ArrayList<>(); - List bodiesOffsets = new ArrayList<>(); - int defOffset; - int casesLen = insToBytes(cases).length; - bodies.add(0, ins(AVM2Instructions.Label)); - bodies.add(ins(new BreakJumpIns(item.loop.id), 0)); //There could be two breaks when default clause ends with break, but official compiler does this too, so who cares... - defOffset = -(insToBytes(bodies).length + casesLen); - for (int i = item.caseCommands.size() - 1; i >= 0; i--) { - bodies.addAll(0, generateToInsList(localData, item.caseCommands.get(i))); - bodies.add(0, ins(AVM2Instructions.Label)); - bodiesOffsets.add(0, -(insToBytes(bodies).length + casesLen)); - } - lookupOp.operands[0] = defOffset; - lookupOp.operands[1] = item.valuesMapping.size(); - for (int i = 0; i < item.valuesMapping.size(); i++) { - lookupOp.operands[2 + i] = bodiesOffsets.get(item.valuesMapping.get(i)); - } - - forwardJump.operands[0] = insToBytes(bodies).length; - ret.addAll(bodies); - ret.addAll(cases); - ret.add(lookupOp); - fixSwitch(toInsList(ret), insToBytes(toInsList(ret)).length, uniqLast); - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, NotItem item) throws CompilationException { - /*if (item.getOriginal() instanceof Inverted) { - GraphTargetItem norig = ((Inverted) item).invert(); - return norig.toSource(localData, this); - }*/ - List ret = new ArrayList<>(); - ret.addAll(item.getOriginal().toSource(localData, this)); - ret.add(ins(AVM2Instructions.Not)); - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, DuplicateItem item) { - List ret = new ArrayList<>(); - ret.add(ins(AVM2Instructions.Dup)); - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, BreakItem item) { - List ret = new ArrayList<>(); - AVM2Instruction abreak = ins(new BreakJumpIns(item.loopId), 0); - ret.add(abreak); - return ret; - } - - public List generate(SourceGeneratorLocalData localData, FunctionAVM2Item item) throws CompilationException { - List ret = new ArrayList<>(); - int scope = 0; - if (!item.functionName.isEmpty()) { - ret.add(ins(AVM2Instructions.NewObject, 0)); - ret.add(ins(AVM2Instructions.PushWith)); - scope = localData.scopeStack.size(); - localData.scopeStack.add(new PropertyAVM2Item(null, item.functionName, abcIndex, new ArrayList<>(), localData.callStack)); - } - AVM2ConstantPool constants = abcIndex.getSelectedAbc().constants; - ret.add(ins(AVM2Instructions.NewFunction, method(false, constants.getStringId(item.functionName, true), true, false, localData.callStack, localData.pkg, item.needsActivation, item.subvariables, 0 /*Set later*/, item.hasRest, item.line, localData.currentClass, null, false, localData, item.paramTypes, item.paramNames, item.paramValues, item.body, item.retType))); - if (!item.functionName.isEmpty()) { - ret.add(ins(AVM2Instructions.Dup)); - ret.add(ins(AVM2Instructions.GetScopeObject, scope)); - ret.add(ins(AVM2Instructions.Swap)); - ret.add(ins(AVM2Instructions.SetProperty, constants.getMultinameId(Multiname.createQName(false, constants.getStringId(item.functionName, true), constants.getNamespaceId(Namespace.KIND_PACKAGE, localData.pkg, 0, true)), true))); - ret.add(ins(AVM2Instructions.PopScope)); - localData.scopeStack.remove(localData.scopeStack.size() - 1); - } - return ret; - } - - private static int currentFinId = 1; - - private static int finId() { - return currentFinId++; - } - - public List generate(SourceGeneratorLocalData localData, TryAVM2Item item) throws CompilationException { - List ret = new ArrayList<>(); - - boolean newFinallyReg = false; - List newex = new ArrayList<>(); - int aloneFinallyEx = -1; - int finallyEx = -1; - for (NameAVM2Item e : item.catchExceptions2) { - ABCException aex = new ABCException(); - aex.name_index = abcIndex.getSelectedAbc().constants.getMultinameId(Multiname.createQName(false, abcIndex.getSelectedAbc().constants.getStringId(e.getVariableName(), true), abcIndex.getSelectedAbc().constants.getNamespaceId(Namespace.KIND_PACKAGE, "", 0, true)), true); - aex.type_index = typeName(localData, e.type); - newex.add(aex); - } - int finId = 0; - if (item.finallyCommands != null) { - if (item.catchExceptions2.isEmpty()) { - ABCException aex = new ABCException(); - aex.name_index = 0; - aex.type_index = 0; - newex.add(aex); - aloneFinallyEx = newex.size() - 1; - } - ABCException aex = new ABCException(); - aex.name_index = 0; - aex.type_index = 0; - newex.add(aex); - finallyEx = newex.size() - 1; - if (localData.finallyRegister == -1) { - localData.finallyRegister = getFreeRegister(localData); - killRegister(localData, localData.finallyRegister); //reuse for catches - newFinallyReg = true; - } - finId = finId(); - } - - if (finallyEx > -1) { - localData.finallyCatches.add(finId); - } - List tryCmds = generateToInsList(localData, item.tryCommands); - - //int i = firstId + item.catchCommands.size() - 1; - List catches = new ArrayList<>(); - Reference tempReg = new Reference<>(0); - - List currentExceptionIds = new ArrayList<>(); - List> catchCmds = new ArrayList<>(); - for (int c = 0; c < item.catchCommands.size(); c++) { - int i = localData.exceptions.size(); - localData.exceptions.add(newex.get(c)); - - currentExceptionIds.add(i); - - //Reference tempReg=new Reference<>(0); - List catchCmd = new ArrayList<>(); - catchCmd.add(ins(AVM2Instructions.NewCatch, i)); - catchCmd.addAll(toInsList(AssignableAVM2Item.dupSetTemp(localData, this, tempReg))); - catchCmd.add(ins(AVM2Instructions.Dup)); - catchCmd.add(ins(AVM2Instructions.PushScope)); - catchCmd.add(ins(AVM2Instructions.Swap)); - catchCmd.add(ins(AVM2Instructions.SetSlot, 1)); - - for (AssignableAVM2Item a : item.catchVariables.get(c)) { - GraphTargetItem r = a; - if (r instanceof UnresolvedAVM2Item) { - r = ((UnresolvedAVM2Item) r).resolvedRoot; - } - if (r instanceof NameAVM2Item) { - NameAVM2Item n = (NameAVM2Item) r; - if (item.catchExceptions2.get(c).getVariableName().equals(n.getVariableName())) { - n.setSlotScope(localData.scopeStack.size()); - } - } - } - localData.scopeStack.add(new LocalRegAVM2Item(null, null, tempReg.getVal(), null)); - catchCmd.addAll(generateToInsList(localData, item.catchCommands.get(c))); - localData.scopeStack.remove(localData.scopeStack.size() - 1); - catchCmd.add(ins(AVM2Instructions.PopScope)); - catchCmd.addAll(toInsList(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempReg)))); - catchCmds.add(catchCmd); - } - for (int c = item.catchCommands.size() - 1; c >= 0; c--) { - List preCatches = new ArrayList<>(); - /*preCatches.add(ins(AVM2Instructions.GetLocal0)); - preCatches.add(ins(AVM2Instructions.PushScope)); - preCatches.add(AssignableAVM2Item.generateGetLoc(localData.activationReg)); - preCatches.add(ins(AVM2Instructions.PushScope));*/ - for (GraphTargetItem s : localData.scopeStack) { - preCatches.addAll(toInsList(s.toSource(localData, this))); - if (s instanceof WithObjectAVM2Item) { - preCatches.add(ins(AVM2Instructions.PushWith)); - } else { - preCatches.add(ins(AVM2Instructions.PushScope)); - } - } - - //catchCmds.add(catchCmd); - preCatches.addAll(catchCmds.get(c)); - catches.addAll(0, preCatches); - catches.add(0, new ExceptionMarkAVM2Instruction(currentExceptionIds.get(c), MARK_E_TARGET)); - catches.add(0, ins(AVM2Instructions.Jump, insToBytes(catches).length)); - } - - if (aloneFinallyEx > -1) { - localData.exceptions.add(newex.get(aloneFinallyEx)); - aloneFinallyEx = localData.exceptions.size() - 1; - - } - if (finallyEx > -1) { - localData.exceptions.add(newex.get(finallyEx)); - finallyEx = localData.exceptions.size() - 1; - } - - for (int i : currentExceptionIds) { - ret.add(new ExceptionMarkAVM2Instruction(i, MARK_E_START)); - } - if (aloneFinallyEx > -1) { - ret.add(new ExceptionMarkAVM2Instruction(aloneFinallyEx, MARK_E_START)); - } - if (finallyEx > -1) { - ret.add(new ExceptionMarkAVM2Instruction(finallyEx, MARK_E_START)); - } - - ret.addAll(tryCmds); - - for (int i : currentExceptionIds) { - ret.add(new ExceptionMarkAVM2Instruction(i, MARK_E_END)); - } - if (aloneFinallyEx > -1) { - ret.add(new ExceptionMarkAVM2Instruction(aloneFinallyEx, MARK_E_END)); - } - - if (aloneFinallyEx > -1) { - List preCatches = new ArrayList<>(); - for (GraphTargetItem s : localData.scopeStack) { - preCatches.addAll(toInsList(s.toSource(localData, this))); - if (s instanceof WithObjectAVM2Item) { - preCatches.add(ins(AVM2Instructions.PushWith)); - } else { - preCatches.add(ins(AVM2Instructions.PushScope)); - } - } - preCatches.add(ins(AVM2Instructions.NewCatch, aloneFinallyEx)); - preCatches.addAll(toInsList(AssignableAVM2Item.dupSetTemp(localData, this, tempReg))); - preCatches.add(ins(AVM2Instructions.PushScope)); - preCatches.add(ins(AVM2Instructions.Throw)); - preCatches.add(ins(AVM2Instructions.PopScope)); - preCatches.addAll(toInsList(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempReg)))); - catches.add(ins(AVM2Instructions.Jump, insToBytes(preCatches).length)); - catches.add(new ExceptionMarkAVM2Instruction(aloneFinallyEx, MARK_E_TARGET)); - catches.addAll(preCatches); - } - AVM2Instruction finSwitch = null; - AVM2Instruction pushDefIns = ins(AVM2Instructions.PushByte, 0); - - int defPos = 0; - if (finallyEx > -1) { - List preCatches = new ArrayList<>(); - preCatches.add(0, new ExceptionMarkAVM2Instruction(finallyEx, MARK_E_TARGET)); - for (GraphTargetItem s : localData.scopeStack) { - preCatches.addAll(toInsList(s.toSource(localData, this))); - if (s instanceof WithObjectAVM2Item) { - preCatches.add(ins(AVM2Instructions.PushWith)); - } else { - preCatches.add(ins(AVM2Instructions.PushScope)); - } - } - preCatches.add(ins(AVM2Instructions.NewCatch, finallyEx)); - preCatches.addAll(toInsList(AssignableAVM2Item.dupSetTemp(localData, this, tempReg))); - preCatches.add(ins(AVM2Instructions.PushScope)); - preCatches.add(ins(AVM2Instructions.PopScope)); - Reference tempReg2 = new Reference<>(0); - preCatches.add(ins(AVM2Instructions.Kill, tempReg.getVal())); - preCatches.add(ins(AVM2Instructions.CoerceA)); - preCatches.addAll(toInsList(AssignableAVM2Item.setTemp(localData, this, tempReg2))); - preCatches.add(pushDefIns); - - List finallySwitchCmds = new ArrayList<>(); - - finSwitch = new AVM2Instruction(0, AVM2Instructions.LookupSwitch, new int[1 + 1 + 1]); - finSwitch.operands[0] = finSwitch.getBytesLength(); - finSwitch.operands[1] = 0; //switch cnt - - List preFinallySwitch = new ArrayList<>(); - preFinallySwitch.add(ins(AVM2Instructions.Label)); - preFinallySwitch.add(ins(AVM2Instructions.Pop)); - int preFinallySwitchLen = insToBytes(preFinallySwitch).length; - - finallySwitchCmds.add(ins(AVM2Instructions.Label)); - finallySwitchCmds.addAll(toInsList(AssignableAVM2Item.getTemp(localData, this, tempReg2))); - finallySwitchCmds.add(ins(AVM2Instructions.Kill, tempReg2.getVal())); - finallySwitchCmds.add(ins(AVM2Instructions.Throw)); - finallySwitchCmds.add(ins(AVM2Instructions.PushByte, 255)); - finallySwitchCmds.add(ins(AVM2Instructions.PopScope)); - finallySwitchCmds.add(ins(AVM2Instructions.Kill, tempReg.getVal())); - - int finSwitchLen = insToBytes(finallySwitchCmds).length; - - preCatches.add(ins(AVM2Instructions.Jump, preFinallySwitchLen + finSwitchLen)); - AVM2Instruction fjump = ins(AVM2Instructions.Jump, 0); - fjump.operands[0] = insToBytes(preCatches).length + preFinallySwitchLen + finSwitchLen; - - preCatches.add(0, fjump); - preCatches.add(0, new ExceptionMarkAVM2Instruction(finallyEx, MARK_E_END)); - preCatches.add(0, ins(AVM2Instructions.PushByte, 255)); - - finallySwitchCmds.add(new ExceptionMarkAVM2Instruction(finallyEx, MARK_E_FINALLYPART)); - - int oldReg = localData.finallyRegister; - localData.finallyRegister = getFreeRegister(localData); - Integer cnt = localData.finallyCounter.get(finId); - if (cnt == null) { - cnt = -1; - } - defPos = cnt; - cnt++; //Skip default clause (throw) - localData.finallyCounter.put(finId, cnt); - finallySwitchCmds.addAll(generateToInsList(localData, item.finallyCommands)); - killRegister(localData, localData.finallyRegister); - localData.finallyRegister = oldReg; - finSwitchLen = insToBytes(finallySwitchCmds).length; - - finSwitch.operands[2] = -finSwitchLen; - preCatches.addAll(preFinallySwitch); - preCatches.addAll(finallySwitchCmds); - preCatches.add(finSwitch); - - catches.addAll(preCatches); - AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempReg, tempReg2)); - } - - ret.addAll(catches); - //localData.exceptions.addAll(newex); - - if (finallyEx > -1) { - localData.finallyCatches.remove(localData.finallyCatches.size() - 1); - } - if (newFinallyReg) { - localData.finallyRegister = -1; - killRegister(localData, localData.finallyRegister); - } - int pos = 0; - int finallyPos = 0; - int switchPos = 0; - for (int s = 0; s < ret.size(); s++) { - GraphSourceItem src = ret.get(s); - if (src == finSwitch) { - switchPos = pos; - } - if (src instanceof AVM2Instruction) { - AVM2Instruction ins = (AVM2Instruction) src; - if (ins instanceof ExceptionMarkAVM2Instruction) { - ExceptionMarkAVM2Instruction em = (ExceptionMarkAVM2Instruction) ins; - if (em.exceptionId == finallyEx && em.markType == MARK_E_FINALLYPART) { - finallyPos = pos; - ret.remove(s); - s--; - continue; - } - } - pos += ins.getBytesLength(); - } - - } - - if (finSwitch != null) { - pos = 0; - int defLoc = finSwitch.operands[2]; - List switchLoc = new ArrayList<>(); - boolean wasDef = false; - for (int s = 0; s < ret.size(); s++) { - GraphSourceItem src = ret.get(s); - if (src instanceof AVM2Instruction) { - AVM2Instruction ins = (AVM2Instruction) src; - if (ins.definition instanceof FinallyJumpIns) { - FinallyJumpIns fji = (FinallyJumpIns) ins.definition; - if (fji.getClauseId() == finId) { - List bet = new ArrayList<>(); - bet.add(ins(AVM2Instructions.Label)); - bet.add(ins(AVM2Instructions.Pop)); - int betLen = insToBytes(bet).length; - if (wasDef) { - ins.operands[0] = 0; - } else { - ins.operands[0] = finallyPos - (pos + ins.getBytesLength()); - } - ins.definition = AVM2Code.instructionSet[AVM2Instructions.Jump]; - switchLoc.add(pos + ins.getBytesLength() + betLen - switchPos); - } - } - pos += ins.getBytesLength(); - } - if (defPos == switchLoc.size() - 1) { - switchLoc.add(defLoc); - wasDef = true; - } - } - finSwitch.operands = new int[1 + 1 + switchLoc.size()]; - pushDefIns.operands[0] = defPos + 1; - int afterLoc = finSwitch.getBytesLength(); - finSwitch.operands[0] = afterLoc; - finSwitch.operands[1] = switchLoc.size() - 1; - for (int j = 0; j < switchLoc.size(); j++) { - finSwitch.operands[2 + j] = switchLoc.get(j); - } - } - - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, ContinueItem item) { - List ret = new ArrayList<>(); - AVM2Instruction acontinue = ins(new ContinueJumpIns(item.loopId), 0); - ret.add(acontinue); - return ret; - } - - public List generate(SourceGeneratorLocalData localData, ReturnValueAVM2Item item) throws CompilationException { - List ret = new ArrayList<>(); - ret.addAll(item.value.toSource(localData, this)); - if (!localData.finallyCatches.isEmpty()) { - ret.add(ins(AVM2Instructions.CoerceA)); - ret.add(AssignableAVM2Item.generateSetLoc(localData.finallyRegister)); - for (int i = localData.finallyCatches.size() - 1; i >= 0; i--) { - if (i < localData.finallyCatches.size() - 1) { - ret.add(ins(AVM2Instructions.Label)); - } - int clauseId = localData.finallyCatches.get(i); - Integer cnt = localData.finallyCounter.get(clauseId); - if (cnt == null) { - cnt = -1; - } - cnt++; - localData.finallyCounter.put(clauseId, cnt); - ret.addAll(new IntegerValueAVM2Item(null, null, (long) cnt).toSource(localData, this)); - ret.add(ins(new FinallyJumpIns(clauseId), 0)); - ret.add(ins(AVM2Instructions.Label)); - ret.add(ins(AVM2Instructions.Pop)); - } - ret.add(ins(AVM2Instructions.Label)); - ret.add(AssignableAVM2Item.generateGetLoc(localData.finallyRegister)); - ret.add(ins(AVM2Instructions.Kill, localData.finallyRegister)); - } - ret.add(ins(AVM2Instructions.ReturnValue)); - return ret; - } - - public List generate(SourceGeneratorLocalData localData, ReturnVoidAVM2Item item) throws CompilationException { - List ret = new ArrayList<>(); - if (!localData.finallyCatches.isEmpty()) { - - for (int i = 0; i < localData.finallyCatches.size(); i++) { - if (i > 0) { - ret.add(ins(AVM2Instructions.Label)); - } - int clauseId = localData.finallyCatches.get(i); - Integer cnt = localData.finallyCounter.get(clauseId); - if (cnt == null) { - cnt = -1; - } - cnt++; - localData.finallyCounter.put(clauseId, cnt); - ret.addAll(new IntegerValueAVM2Item(null, null, (long) cnt).toSource(localData, this)); - ret.add(ins(new FinallyJumpIns(clauseId), 0)); - ret.add(ins(AVM2Instructions.Label)); - ret.add(ins(AVM2Instructions.Pop)); - } - ret.add(ins(AVM2Instructions.Label)); - } - ret.add(ins(AVM2Instructions.ReturnVoid)); - return ret; - } - - public List generate(SourceGeneratorLocalData localData, ThrowAVM2Item item) throws CompilationException { - List ret = new ArrayList<>(); - ret.addAll(item.value.toSource(localData, this)); - ret.add(ins(AVM2Instructions.Throw)); - return ret; - } - - private List generateToInsList(SourceGeneratorLocalData localData, List commands) throws CompilationException { - return toInsList(generate(localData, commands)); - } - - private List generateToActionList(SourceGeneratorLocalData localData, GraphTargetItem command) throws CompilationException { - return toInsList(command.toSource(localData, this)); - } - - @Override - public List generate(SourceGeneratorLocalData localData, List commands) throws CompilationException { - List ret = new ArrayList<>(); - for (GraphTargetItem item : commands) { - ret.addAll(item.toSourceIgnoreReturnValue(localData, this)); - } - return ret; - } - - public HashMap getRegisterVars(SourceGeneratorLocalData localData) { - return localData.registerVars; - } - - public void setRegisterVars(SourceGeneratorLocalData localData, HashMap value) { - localData.registerVars = value; - } - - public void setInFunction(SourceGeneratorLocalData localData, int value) { - localData.inFunction = value; - } - - public int isInFunction(SourceGeneratorLocalData localData) { - return localData.inFunction; - } - - public boolean isInMethod(SourceGeneratorLocalData localData) { - return localData.inMethod; - } - - public void setInMethod(SourceGeneratorLocalData localData, boolean value) { - localData.inMethod = value; - } - - public int getForInLevel(SourceGeneratorLocalData localData) { - return localData.forInLevel; - } - - public void setForInLevel(SourceGeneratorLocalData localData, int value) { - localData.forInLevel = value; - } - - public int getTempRegister(SourceGeneratorLocalData localData) { - HashMap registerVars = getRegisterVars(localData); - int tmpReg = 0; - for (int i = 0; i < 256; i++) { - if (!registerVars.containsValue(i)) { - tmpReg = i; - break; - } - } - return tmpReg; - } - - public AVM2SourceGenerator(AbcIndexing abc) { - this.abcIndex = abc; - } - - /*public ABC getABC() { - return abc; - }*/ - public void generateClass(List importedClasses, List cinitVariables, boolean cinitNeedsActivation, List cinit, List openedNamespaces, int namespace, int initScope, DottedChain pkg, ClassInfo classInfo, InstanceInfo instanceInfo, SourceGeneratorLocalData localData, boolean isInterface, String name, String superName, GraphTargetItem extendsVal, List implementsStr, GraphTargetItem iinit, List iinitVariables, boolean iinitNeedsActivation, List traitItems, Reference class_index) throws AVM2ParseException, CompilationException { - localData.currentClass = name; - localData.pkg = pkg; - localData.privateNs = abcIndex.getSelectedAbc().constants.getNamespaceId(Namespace.KIND_PRIVATE, pkg.toRawString() + ":" + name, 0, true); - localData.protectedNs = abcIndex.getSelectedAbc().constants.getNamespaceId(Namespace.KIND_PROTECTED, pkg.toRawString() + ":" + name, 0, true); - if (extendsVal == null && !isInterface) { - extendsVal = new TypeItem(DottedChain.OBJECT); - } - ParsedSymbol s = null; - - if (Configuration.handleSkinPartsAutomatically.get()) { - - Map skinParts = new HashMap<>(); - for (GraphTargetItem t : traitItems) { - String tname = null; - List>> tmetadata = null; - if (t instanceof MethodAVM2Item) { - tname = ((MethodAVM2Item) t).functionName; - tmetadata = ((MethodAVM2Item) t).metadata; - } else if (t instanceof SlotAVM2Item) { - tname = ((SlotAVM2Item) t).var; - tmetadata = ((SlotAVM2Item) t).metadata; - } else if (t instanceof ConstAVM2Item) { - tname = ((ConstAVM2Item) t).var; - tmetadata = ((ConstAVM2Item) t).metadata; - } - if (tname != null && tmetadata != null) { - for (Map.Entry> en : tmetadata) { - if ("SkinPart".equals(en.getKey())) { - boolean req = false; - if (en.getValue().containsKey("required")) { - if ("true".equals(en.getValue().get("required"))) { - req = true; - } - } - skinParts.put(tname, req); - } - } - } - } - if (!skinParts.isEmpty()) { - - //Merge parts from _skinParts attribute of parent class - GraphTargetItem parent = extendsVal; - if (parent instanceof UnresolvedAVM2Item) { - parent = ((UnresolvedAVM2Item) parent).resolved; - } - if (parent instanceof TypeItem) { - ClassIndex ci = abcIndex.findClass(parent); - if (ci != null) { - int mi = ci.abc.class_info.get(ci.index).cinit_index; - MethodBody pcinit = ci.abc.findBody(mi); - ConvertData d = new ConvertData(); - - List initt = new ArrayList<>(); - initt.add(ci.abc.class_info.get(ci.index).static_traits); - - try { - pcinit.convert(d, "-", ScriptExportMode.AS, true, mi, -1, ci.index, ci.abc, null, new ScopeStack(), GraphTextWriter.TRAIT_CLASS_INITIALIZER, new NulWriter(), new ArrayList<>(), initt, false); - //FIXME! Add skinparts from _skinParts attribute of parent class!!! - } catch (InterruptedException ex) { - Logger.getLogger(AVM2SourceGenerator.class.getName()).log(Level.SEVERE, "Getting parent skinparts interrupted", ex); - } - for (Trait t : ci.abc.class_info.get(ci.index).static_traits.traits) { - if (t instanceof TraitSlotConst) { - TraitSlotConst tsc = (TraitSlotConst) t; - if (tsc.kindType == Trait.TRAIT_SLOT) { - if ("_skinParts".equals(tsc.getName(ci.abc).getName(ci.abc.constants, new ArrayList<>(), true))) { - if (d.assignedValues.containsKey(tsc)) { - if (d.assignedValues.get(tsc).value instanceof NewObjectAVM2Item) { - NewObjectAVM2Item no = (NewObjectAVM2Item) d.assignedValues.get(tsc).value; - for (NameValuePair nvp : no.pairs) { - skinParts.put(EcmaScript.toString(nvp.name.getResult()), EcmaScript.toBoolean(nvp.value.getResult())); - } - } - - } - } - } - } - } - - } - } - - /* - Add - override protected function get skinParts() : Object - { - return _skinParts; - } - */ - List getterBody = new ArrayList<>(); - UnresolvedAVM2Item sp = new UnresolvedAVM2Item(new ArrayList<>(), importedClasses, false, TypeItem.UNBOUNDED, 0, new DottedChain("_skinParts"), - null, openedNamespaces); - getterBody.add(new ReturnValueAVM2Item(null, null, sp)); - List subvars = new ArrayList<>(); - subvars.add(sp); - List> allopns = new ArrayList<>(); - allopns.add(openedNamespaces); - - GetterAVM2Item getter = new GetterAVM2Item(allopns, false, false, new ArrayList<>(), new NamespaceItem(pkg.toRawString() + ":" + name, Namespace.KIND_PROTECTED), isInterface, null, false, false, 0, - true, false, false, "skinParts", new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), - getterBody, subvars, new TypeItem("Object")); - - /* - Add - private static var _skinParts = {attr1:false, attr2:true}; - */ - List pairs = new ArrayList<>(); - for (String tname : skinParts.keySet()) { - pairs.add(new NameValuePair(new StringAVM2Item(null, null, tname), skinParts.get(tname) ? new TrueItem(null, null) : new FalseItem(null, null))); - } - - NewObjectAVM2Item sltVal = new NewObjectAVM2Item(null, null, pairs); - - SlotAVM2Item slt = new SlotAVM2Item( - new ArrayList<>(), new NamespaceItem(pkg.toRawString() + ":" + name, Namespace.KIND_PRIVATE), - null, true, "_skinParts", new TypeItem("Object"), sltVal, 0); - - traitItems.add(0, slt); - traitItems.add(getter); - - } - } - - Trait[] it = generateTraitsPhase1(importedClasses, openedNamespaces, name, superName, false, localData, traitItems, instanceInfo.instance_traits, class_index); - Trait[] st = generateTraitsPhase1(importedClasses, openedNamespaces, name, superName, true, localData, traitItems, classInfo.static_traits, class_index); - generateTraitsPhase2(importedClasses, pkg, traitItems, it, openedNamespaces, localData); - generateTraitsPhase2(importedClasses, pkg, traitItems, st, openedNamespaces, localData); - abcIndex.refreshSelected(); - generateTraitsPhase3(importedClasses, initScope, isInterface, name, superName, false, localData, traitItems, instanceInfo.instance_traits, it, new HashMap<>(), class_index); - generateTraitsPhase3(importedClasses, initScope, isInterface, name, superName, true, localData, traitItems, classInfo.static_traits, st, new HashMap<>(), class_index); - int init; - if (iinit == null || isInterface) { - instanceInfo.iinit_index = init = method(false, 0, false, isInterface, new ArrayList<>(), pkg, false, new ArrayList<>(), initScope + 1, false, 0, isInterface ? null : name, extendsVal != null ? extendsVal.toString() : null, true, localData, new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), TypeItem.UNBOUNDED/*?? FIXME*/); - } else { - MethodAVM2Item m = (MethodAVM2Item) iinit; - instanceInfo.iinit_index = init = method(false, str(pkg.toRawString() + ":" + name + "/" + name), false, false, new ArrayList<>(), pkg, m.needsActivation, m.subvariables, initScope + 1, m.hasRest, m.line, name, extendsVal != null ? extendsVal.toString() : null, true, localData, m.paramTypes, m.paramNames, m.paramValues, m.body, TypeItem.UNBOUNDED/*?? FIXME*/); - } - - //Class initializer - int cinit_index = method(true, str(""), false, false, new ArrayList<>(), pkg, cinitNeedsActivation, cinitVariables, initScope + (implementsStr.isEmpty() ? 0 : 1), false, 0, isInterface ? null : name, superName, false, localData, new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), cinit, TypeItem.UNBOUNDED); - MethodBody cinitBody = abcIndex.getSelectedAbc().findBody(cinit_index); - - List sinitcode = new ArrayList<>(); - List initcode = new ArrayList<>(); - for (GraphTargetItem ti : traitItems) { - if ((ti instanceof SlotAVM2Item) || (ti instanceof ConstAVM2Item)) { - GraphTargetItem val = null; - boolean isStatic = false; - int ns = -1; - String tname = null; - boolean isConst = false; - if (ti instanceof SlotAVM2Item) { - val = ((SlotAVM2Item) ti).value; - isStatic = ((SlotAVM2Item) ti).isStatic(); - ns = genNs(importedClasses, pkg, ((SlotAVM2Item) ti).pkg, openedNamespaces, localData, ((SlotAVM2Item) ti).line); - tname = ((SlotAVM2Item) ti).var; - } - if (ti instanceof ConstAVM2Item) { - val = ((ConstAVM2Item) ti).value; - isStatic = ((ConstAVM2Item) ti).isStatic(); - ns = genNs(importedClasses, pkg, ((ConstAVM2Item) ti).pkg, openedNamespaces, localData, ((ConstAVM2Item) ti).line); - tname = ((ConstAVM2Item) ti).var; - isConst = true; - } - if (isStatic && val != null) { - sinitcode.add(ins(AVM2Instructions.FindProperty, traitName(ns, tname))); - localData.isStatic = true; - sinitcode.addAll(toInsList(val.toSource(localData, this))); - sinitcode.add(ins(isConst ? AVM2Instructions.InitProperty : AVM2Instructions.SetProperty, traitName(ns, tname))); - } - if (!isStatic && val != null) { - //do not init basic values, that can be stored in trait - if (!(val instanceof IntegerValueAVM2Item) && !(val instanceof StringAVM2Item) && !(val instanceof BooleanAVM2Item) && !(val instanceof NullAVM2Item) && !(val instanceof UndefinedAVM2Item)) { - initcode.add(ins(AVM2Instructions.GetLocal0)); - localData.isStatic = false; - initcode.addAll(toInsList(val.toSource(localData, this))); - initcode.add(ins(isConst ? AVM2Instructions.InitProperty : AVM2Instructions.SetProperty, traitName(ns, tname))); - } - } - } - } - MethodBody initBody = null; - if (!isInterface) { - initBody = abcIndex.getSelectedAbc().findBody(init); - initBody.insertAll(iinit == null ? 0 : 2, initcode);//after getlocal0,pushscope - - if (cinitBody.getCode().code.get(cinitBody.getCode().code.size() - 1).definition instanceof ReturnVoidIns) { - cinitBody.insertAll(2, sinitcode); //after getlocal0,pushscope - } - } - cinitBody.markOffsets(); - cinitBody.autoFillStats(abcIndex.getSelectedAbc(), initScope + (implementsStr.isEmpty() ? 0 : 1), true); - - classInfo.cinit_index = cinit_index; - if (initBody != null) { - initBody.autoFillStats(abcIndex.getSelectedAbc(), initScope + 1, true); - } - instanceInfo.interfaces = new int[implementsStr.size()]; - for (int i = 0; i < implementsStr.size(); i++) { - instanceInfo.interfaces[i] = superIntName(localData, implementsStr.get(i)); - } - - } - - @Override - public List generate(SourceGeneratorLocalData localData, CommaExpressionItem item) throws CompilationException { - if (item.commands.isEmpty()) { - return new ArrayList<>(); - } - - //We need to handle commands and last expression separately, otherwise last expression result will be popped - List cmds = new ArrayList<>(item.commands); - GraphTargetItem lastExpr = cmds.remove(cmds.size() - 1); - List ret = new ArrayList<>(); - ret.addAll(generate(localData, cmds)); - ret.addAll(lastExpr.toSource(localData, this)); - return ret; - } - - public int generateClass(int namespace, ClassInfo ci, InstanceInfo ii, int initScope, DottedChain pkg, SourceGeneratorLocalData localData, AVM2Item cls, Reference class_index) throws AVM2ParseException, CompilationException { - /*ClassInfo ci = new ClassInfo(); - InstanceInfo ii = new InstanceInfo(); - abc.class_info.add(ci); - abc.instance_info.add(ii); - */ - if (cls instanceof ClassAVM2Item) { - ClassAVM2Item cai = (ClassAVM2Item) cls; - //TODO: iinit variables, iinit activation - generateClass(cai.importedClasses, cai.cinitVariables, cai.cinitActivation, cai.staticInit, - cai.openedNamespaces, - namespace, - initScope, pkg, ci, ii, - localData, false, - cai.className, cai.extendsOp == null ? "Object" : cai.extendsOp.toString(), - cai.extendsOp, cai.implementsOp, cai.iinit, - cai.iinitVariables, cai.iinitActivation, cai.traits, class_index - ); - if (!cai.isDynamic) { - ii.flags |= InstanceInfo.CLASS_SEALED; - } - if (cai.isFinal) { - ii.flags |= InstanceInfo.CLASS_FINAL; - } - ii.flags |= InstanceInfo.CLASS_PROTECTEDNS; - ii.protectedNS = abcIndex.getSelectedAbc().constants.getNamespaceId(Namespace.KIND_PROTECTED, pkg.toRawString() + ":" + cai.className, 0, true); - } - if (cls instanceof InterfaceAVM2Item) { - InterfaceAVM2Item iai = (InterfaceAVM2Item) cls; - ii.flags |= InstanceInfo.CLASS_INTERFACE; - ii.flags |= InstanceInfo.CLASS_SEALED; - generateClass(iai.importedClasses, new ArrayList<>(), false, new ArrayList<>(), - iai.openedNamespaces, namespace, initScope, pkg, ci, ii, localData, true, iai.name, null, null, iai.superInterfaces, null, null, false, iai.methods, - class_index - ); - } - - return abcIndex.getSelectedAbc().instance_info.size() - 1; - } - - public int traitName(int namespace, String var) { - return abcIndex.getSelectedAbc().constants.getMultinameId(Multiname.createQName(false, str(var), namespace), true); - } - - public int typeName(SourceGeneratorLocalData localData, GraphTargetItem type) throws CompilationException { - if (type instanceof UnboundedTypeItem) { - return 0; - } - if (("" + type).equals("*")) { - return 0; - } - - return resolveType(localData, type, abcIndex); - /* - TypeItem nameItem = (TypeItem) type; - name = nameItem.fullTypeName; - if (name.contains(".")) { - pkg = name.substring(0, name.lastIndexOf('.')); - name = name.substring(name.lastIndexOf('.') + 1); - } - if (!nameItem.subtypes.isEmpty()) { //It's vector => TypeName - List params = new ArrayList<>(); - for (GraphTargetItem p : nameItem.subtypes) { - params.add(typeName(localData, p));//abc.getLastAbc().constants.getMultinameId(new Multiname(Multiname.QNAME, str(p), namespace(Namespace.KIND_PACKAGE, ppkg), 0, 0, new ArrayList()), true)); - } - int qname = abc.getLastAbc().constants.getMultinameId(new Multiname(Multiname.QNAME, str(name), namespace(Namespace.KIND_PACKAGE, pkg), 0, 0, new ArrayList()), true); - return abc.getLastAbc().constants.getMultinameId(new Multiname(Multiname.TYPENAME, 0, 0, 0, qname, params), true); - } else { - return abc.getLastAbc().constants.getMultinameId(new Multiname(Multiname.QNAME, str(name), namespace(Namespace.KIND_PACKAGE, pkg), 0, 0, new ArrayList()), true); - }*/ - } - - public int ident(GraphTargetItem name) { - if (name instanceof NameAVM2Item) { - return str(((NameAVM2Item) name).getVariableName()); - } - throw new RuntimeException("no ident"); //FIXME - } - - public int namespace(int nsKind, String name) { - return abcIndex.getSelectedAbc().constants.getNamespaceId(nsKind, str(name), 0, true); - } - - public int str(String name) { - return abcIndex.getSelectedAbc().constants.getStringId(name, true); - } - - public int propertyName(GraphTargetItem name) { - if (name instanceof NameAVM2Item) { - NameAVM2Item va = (NameAVM2Item) name; - return abcIndex.getSelectedAbc().constants.getMultinameId(Multiname.createQName(false, str(va.getVariableName()), namespace(Namespace.KIND_PACKAGE, "")), true); - } - throw new RuntimeException("no prop"); //FIXME - } - - public int getFreeRegister(SourceGeneratorLocalData localData) { - for (int i = 0;; i++) { - if (!localData.registerVars.containsValue(i)) { - localData.registerVars.put("__TEMP__" + i, i); - return i; - } - } - } - - public boolean killRegister(SourceGeneratorLocalData localData, int i) { - String key = null; - for (String k : localData.registerVars.keySet()) { - if (localData.registerVars.get(k) == i) { - key = k; - break; - } - } - if (key != null) { - localData.registerVars.remove(key); - return true; - } - return false; - } - - public int method(boolean isStatic, int name_index, boolean subMethod, boolean isInterface, List callStack, DottedChain pkg, boolean needsActivation, List subvariables, int initScope, boolean hasRest, int line, String className, String superType, boolean constructor, SourceGeneratorLocalData localData, List paramTypes, List paramNames, List paramValues, List body, GraphTargetItem retType) throws CompilationException { - //Reference hasArgs = new Reference<>(Boolean.FALSE); - //calcRegisters(localData,needsActivation,paramNames,subvariables,body, hasArgs); - SourceGeneratorLocalData newlocalData = new SourceGeneratorLocalData(new HashMap<>(), 1, true, 0); - newlocalData.currentClass = className; - newlocalData.pkg = localData.pkg; - newlocalData.callStack.addAll(localData.callStack); - newlocalData.traitUsages = localData.traitUsages; - newlocalData.currentScript = localData.currentScript; - newlocalData.documentClass = localData.documentClass; - newlocalData.privateNs = localData.privateNs; - newlocalData.protectedNs = localData.protectedNs; - newlocalData.isStatic = isStatic; - newlocalData.subMethod = subMethod; - localData = newlocalData; - - localData.activationReg = 0; - - for (int i = 0; i < subvariables.size(); i++) { - AssignableAVM2Item an = subvariables.get(i); - if (an instanceof UnresolvedAVM2Item) { - UnresolvedAVM2Item n = (UnresolvedAVM2Item) an; - if (n.resolved == null) { - String fullClass = localData.getFullClass(); - GraphTargetItem res = n.resolve(new TypeItem(fullClass), paramTypes, paramNames, abcIndex, callStack, subvariables); - if (res instanceof AssignableAVM2Item) { - subvariables.set(i, (AssignableAVM2Item) res); - } else { - subvariables.remove(i); - i--; - } - } - } - } - - for (int t = 0; t < paramTypes.size(); t++) { - GraphTargetItem an = paramTypes.get(t); - if (an instanceof UnresolvedAVM2Item) { - UnresolvedAVM2Item n = (UnresolvedAVM2Item) an; - if (n.resolved == null) { - String fullClass = localData.getFullClass(); - GraphTargetItem res = n.resolve(new TypeItem(fullClass), paramTypes, paramNames, abcIndex, callStack, subvariables); - paramTypes.set(t, res); - } - } - } - - boolean hasArguments = false; - List slotNames = new ArrayList<>(); - List slotTypes = new ArrayList<>(); - slotNames.add("--first"); - slotTypes.add("-"); - - int paramLine = 0; //? - - List registerNames = new ArrayList<>(); - List registerLines = new ArrayList<>(); - List registerTypes = new ArrayList<>(); - if (className != null) { - String fullClassName = pkg.add(className).toRawString(); - registerTypes.add(fullClassName); - localData.scopeStack.add(new LocalRegAVM2Item(null, null, registerNames.size(), null)); - registerNames.add("this"); - registerLines.add(0); //? - - } else { - registerTypes.add("global"); - registerNames.add("this"); - registerLines.add(0); //? - } - for (GraphTargetItem t : paramTypes) { - registerTypes.add(t.toString()); - slotTypes.add(t.toString()); - } - for (int i = 0; i < paramNames.size(); i++) { - registerLines.add(paramLine); - } - registerNames.addAll(paramNames); - slotNames.addAll(paramNames); - /*for (GraphTargetItem p : paramTypes) { - slotTypes.add("" + p); - }*/ - if (hasRest) { - registerTypes.add("Array"); - slotTypes.add("Array"); - } - localData.registerVars.clear(); - for (AssignableAVM2Item an : subvariables) { - if (an instanceof NameAVM2Item) { - NameAVM2Item n = (NameAVM2Item) an; - if (n.getVariableName().equals("arguments") & !n.isDefinition()) { - registerNames.add("arguments"); - registerTypes.add("Object"); - registerLines.add(0); //? - hasArguments = true; - break; - } - } - } - int paramRegCount = registerNames.size(); - - if (needsActivation) { - registerNames.add("+$activation"); - registerLines.add(0); //? - localData.activationReg = registerNames.size() - 1; - registerTypes.add("Object"); - localData.scopeStack.add(new LocalRegAVM2Item(null, null, localData.activationReg, null)); - } - - String mask = Configuration.registerNameFormat.get(); - mask = mask.replace("%d", "([0-9]+)"); - Pattern pat = Pattern.compile(mask); - - //Two rounds - for (int round = 1; round <= 2; round++) { - for (AssignableAVM2Item an : subvariables) { - if (an instanceof NameAVM2Item) { - NameAVM2Item n = (NameAVM2Item) an; - if (n.isDefinition() && !registerNames.contains(n.getVariableName())) { - if (!needsActivation || (n.getSlotScope() <= 0)) { - String varName = n.getVariableName(); - Matcher m = pat.matcher(varName); - //In first round, make all register that match standard loc_xx register - if ((round == 1) && (m.matches())) { - String regIndexStr = m.group(1); - int regIndex = Integer.parseInt(regIndexStr); - while (registerNames.size() <= regIndex + 1) { - String standardName = String.format(mask, registerNames.size() - 1); - registerNames.add(standardName); - registerTypes.add("*"); - slotNames.add(standardName); - slotTypes.add("*"); - registerLines.add(paramLine); - } - registerNames.set(regIndex, varName); - registerTypes.set(regIndex, varName); - slotNames.set(regIndex, varName); - slotTypes.set(regIndex, varName); - registerLines.set(regIndex, n.line); - } //in second round the rest - else if (round == 2 && !m.matches()) { - registerNames.add(n.getVariableName()); - registerTypes.add(n.type.toString()); - slotNames.add(n.getVariableName()); - slotTypes.add(n.type.toString()); - registerLines.add(n.line); - } - } - } - } - } - } - - int slotScope = subMethod ? 0 : 1; - - for (AssignableAVM2Item an : subvariables) { - if (an instanceof NameAVM2Item) { - NameAVM2Item n = (NameAVM2Item) an; - String variableName = n.getVariableName(); - if (variableName != null) { - boolean isThisOrSuper = variableName.equals("this") || variableName.equals("super"); - if (!isThisOrSuper && needsActivation) { - if (n.getSlotNumber() <= 0) { - n.setSlotNumber(slotNames.indexOf(variableName)); - n.setSlotScope(slotScope); - } - } else if (isThisOrSuper) { - n.setRegNumber(0); - } else { - n.setRegNumber(registerNames.indexOf(variableName)); - } - } - } - } - - for (int i = 0; i < registerNames.size(); i++) { - if (needsActivation && i > localData.activationReg) { - break; - } - localData.registerVars.put(registerNames.get(i), i); - } - List declarations = new ArrayList<>(); - loopn: - for (AssignableAVM2Item an : subvariables) { - if (an instanceof NameAVM2Item) { - NameAVM2Item n = (NameAVM2Item) an; - - if (needsActivation) { - if (n.getSlotScope() != slotScope) { - continue; - } else if (n.getSlotNumber() < paramRegCount) { - continue; - } - } - for (NameAVM2Item d : declarations) { - if (n.getVariableName() != null && n.getVariableName().equals(d.getVariableName())) { - continue loopn; - } - } - - for (GraphTargetItem it : body) { //search first level of commands - if (it instanceof NameAVM2Item) { - NameAVM2Item n2 = (NameAVM2Item) it; - if (n2.isDefinition() && n2.getAssignedValue() != null && n2.getVariableName().equals(n.getVariableName())) { - continue loopn; - } - if (!n2.isDefinition() && n2.getVariableName() != null && n2.getVariableName().equals(n.getVariableName())) { //used earlier than defined - break; - } - } - } - if (n.unresolved) { - continue; - } - if (n.redirect != null) { - continue; - } - if (n.getNs() != null) { - continue; - } - - String variableName = n.getVariableName(); - if ("this".equals(variableName) || "super".equals(variableName) || paramNames.contains(variableName) || "arguments".equals(variableName)) { - continue; - } - - NameAVM2Item d = new NameAVM2Item(n.type, n.line, n.getVariableName(), NameAVM2Item.getDefaultValue("" + n.type), true, n.openedNamespaces); - //no index - if (needsActivation) { - if (d.getSlotNumber() <= 0) { - d.setSlotNumber(n.getSlotNumber()); - d.setSlotScope(n.getSlotScope()); - } - } else { - d.setRegNumber(n.getRegNumber()); - } - declarations.add(d); - } - } - - int[] param_types = new int[paramTypes.size()]; - ValueKind[] optional = new ValueKind[paramValues.size()]; - //int[] param_names = new int[paramNames.size()]; - for (int i = 0; i < paramTypes.size(); i++) { - param_types[i] = typeName(localData, paramTypes.get(i)); - //param_names[i] = str(paramNames.get(i)); - } - - for (int i = 0; i < paramValues.size(); i++) { - optional[i] = getValueKind(Namespace.KIND_NAMESPACE/*FIXME*/, paramTypes.get(paramTypes.size() - paramValues.size() + i), paramValues.get(i)); - if (optional[i] == null) { - throw new CompilationException("Default value must be compiletime constant", line); - } - } - - MethodInfo mi = new MethodInfo(param_types, constructor ? 0 : typeName(localData, retType), name_index, 0, optional, new int[0]/*no param_names*/); - if (hasArguments) { - mi.setFlagNeed_Arguments(); - } - //No param names like in official - /* - if (!paramNames.isEmpty()) { - mi.setFlagHas_paramnames(); - }*/ - if (!paramValues.isEmpty()) { - mi.setFlagHas_optional(); - } - if (hasRest) { - mi.setFlagNeed_rest(); - } - - int mindex; - if (!isInterface) { - MethodBody mbody = new MethodBody(abcIndex.getSelectedAbc(), new Traits(), new byte[0], new ABCException[0]); - - if (needsActivation) { - int slotId = 1; - for (int i = 1; i < slotNames.size(); i++) { - TraitSlotConst tsc = new TraitSlotConst(); - tsc.slot_id = slotId++; - tsc.name_index = abcIndex.getSelectedAbc().constants.getMultinameId(Multiname.createQName(false, abcIndex.getSelectedAbc().constants.getStringId(slotNames.get(i), true), abcIndex.getSelectedAbc().constants.getNamespaceId(Namespace.KIND_PACKAGE_INTERNAL, pkg, 0, true)), true); - tsc.type_index = typeName(localData, new TypeItem(slotTypes.get(i))); - mbody.traits.traits.add(tsc); - } - for (int i = 1; i < paramRegCount; i++) { - NameAVM2Item param = new NameAVM2Item(new TypeItem(registerTypes.get(i)), 0, registerNames.get(i), null, false, new ArrayList<>()); - param.setRegNumber(i); - NameAVM2Item d = new NameAVM2Item(new TypeItem(registerTypes.get(i)), 0, registerNames.get(i), param, true, new ArrayList<>()); - d.setSlotScope(slotScope); - d.setSlotNumber(slotNames.indexOf(registerNames.get(i))); - declarations.add(d); - } - } - if (body != null) { - body.addAll(0, declarations); - } - - localData.exceptions = new ArrayList<>(); - localData.callStack.add(mbody); - List src = body == null ? new ArrayList<>() : generate(localData, body); - - mbody.method_info = abcIndex.getSelectedAbc().addMethodInfo(mi); - ArrayList mbodyCode = toInsList(src); - mbody.setCode(new AVM2Code(mbodyCode)); - - if (needsActivation) { - if (localData.traitUsages.containsKey(mbody)) { - List usages = localData.traitUsages.get(mbody); - for (int i = 0; i < mbody.traits.traits.size(); i++) { - if (usages.contains(i)) { - TraitSlotConst tsc = (TraitSlotConst) mbody.traits.traits.get(i); - GraphTargetItem type = TypeItem.UNBOUNDED; - if (tsc.type_index > 0) { - type = new TypeItem(abcIndex.getSelectedAbc().constants.getMultiname(tsc.type_index).getNameWithNamespace(abcIndex.getSelectedAbc().constants)); - } - NameAVM2Item d = new NameAVM2Item(type, 0, tsc.getName(abcIndex.getSelectedAbc()).getName(abcIndex.getSelectedAbc().constants, null, true), NameAVM2Item.getDefaultValue("" + type), true, new ArrayList<>()); - d.setSlotNumber(tsc.slot_id); - d.setSlotScope(slotScope); - mbodyCode.addAll(0, toInsList(d.toSourceIgnoreReturnValue(localData, this))); - } - } - } - - List acts = new ArrayList<>(); - acts.add(ins(AVM2Instructions.NewActivation)); - acts.add(ins(AVM2Instructions.Dup)); - acts.add(AssignableAVM2Item.generateSetLoc(localData.activationReg)); - acts.add(ins(AVM2Instructions.PushScope)); - - mbodyCode.addAll(0, acts); - } - - if (constructor) { - /* List abcs = new ArrayList<>(); - abcs.add(abc); - abcs.addAll(allABCs); - */ - int parentConstMinAC = 0; - - AbcIndexing.ClassIndex ci = abcIndex.findClass(new TypeItem(superType)); - - if (ci != null) { - MethodInfo pmi = ci.abc.method_info.get(ci.abc.instance_info.get(ci.index).iinit_index); - parentConstMinAC = pmi.param_types.length; - if (pmi.flagHas_optional()) { - parentConstMinAC -= pmi.optional.length; - } - } - int ac = -1; - for (AVM2Instruction ins : mbodyCode) { - if (ins.definition instanceof ConstructSuperIns) { - ac = ins.operands[0]; - if (parentConstMinAC > ac) { - throw new CompilationException("Parent constructor call requires different number of arguments", line); - } - - } - } - if (ac == -1) { - if (parentConstMinAC == 0) { - mbodyCode.add(0, new AVM2Instruction(0, AVM2Instructions.GetLocal0, null)); - mbodyCode.add(1, new AVM2Instruction(0, AVM2Instructions.ConstructSuper, new int[]{0})); - - } else { - throw new CompilationException("Parent constructor must be called", line); - } - } - } - for (int i = 1; i < registerNames.size(); i++) { - mbodyCode.add(i - 1, ins(AVM2Instructions.Debug, 1, str(registerNames.get(i)), i - 1, (int) registerLines.get(i))); - } - if (!subMethod) { - mbodyCode.add(0, new AVM2Instruction(0, AVM2Instructions.GetLocal0, null)); - mbodyCode.add(1, new AVM2Instruction(0, AVM2Instructions.PushScope, null)); - } - boolean addRet = false; - if (!mbodyCode.isEmpty()) { - InstructionDefinition lastDef = mbodyCode.get(mbodyCode.size() - 1).definition; - if (!((lastDef instanceof ReturnVoidIns) || (lastDef instanceof ReturnValueIns))) { - addRet = true; - } - } else { - addRet = true; - } - if (addRet) { - if (retType.toString().equals("*") || retType.toString().equals("void") || constructor) { - mbodyCode.add(new AVM2Instruction(0, AVM2Instructions.ReturnVoid, null)); - } else { - mbodyCode.add(new AVM2Instruction(0, AVM2Instructions.PushUndefined, null)); - mbodyCode.add(new AVM2Instruction(0, AVM2Instructions.ReturnValue, null)); - } - } - mbody.exceptions = localData.exceptions.toArray(new ABCException[localData.exceptions.size()]); - int offset = 0; - for (int i = 0; i < mbodyCode.size(); i++) { - AVM2Instruction ins = mbodyCode.get(i); - if (ins instanceof ExceptionMarkAVM2Instruction) { - ExceptionMarkAVM2Instruction m = (ExceptionMarkAVM2Instruction) ins; - switch (m.markType) { - case MARK_E_START: - mbody.exceptions[m.exceptionId].start = offset; - break; - case MARK_E_END: - mbody.exceptions[m.exceptionId].end = offset; - break; - case MARK_E_TARGET: - mbody.exceptions[m.exceptionId].target = offset; - break; - } - mbodyCode.remove(i); - i--; - continue; - } - offset += ins.getBytesLength(); - } - - mbody.markOffsets(); - mbody.autoFillStats(abcIndex.getSelectedAbc(), initScope, className != null); - abcIndex.getSelectedAbc().addMethodBody(mbody); - mindex = mbody.method_info; - } else { - mindex = abcIndex.getSelectedAbc().addMethodInfo(mi); - } - - return mindex; - } - - public ValueKind getValueKind(int ns, GraphTargetItem type, GraphTargetItem val) { - - if (val instanceof BooleanAVM2Item) { - BooleanAVM2Item bi = (BooleanAVM2Item) val; - if (bi.value) { - return new ValueKind(0, ValueKind.CONSTANT_True); - } else { - return new ValueKind(0, ValueKind.CONSTANT_False); - } - } - - boolean isNs = false; - if (type instanceof NameAVM2Item) { - if (((NameAVM2Item) type).getVariableName().equals("namespace")) { - isNs = true; - } - } - - if ((type instanceof TypeItem) && (((TypeItem) type).fullTypeName.equals(DottedChain.NAMESPACE))) { - isNs = true; - } - - if (val instanceof StringAVM2Item) { - StringAVM2Item sval = (StringAVM2Item) val; - if (isNs) { - return new ValueKind(namespace(Namespace.KIND_NAMESPACE, sval.getValue()), ValueKind.CONSTANT_Namespace); - } else { - return new ValueKind(str(sval.getValue()), ValueKind.CONSTANT_Utf8); - } - } - if (val instanceof IntegerValueAVM2Item) { - return new ValueKind(abcIndex.getSelectedAbc().constants.getIntId(((IntegerValueAVM2Item) val).value, true), ValueKind.CONSTANT_Int); - } - if (val instanceof FloatValueAVM2Item) { - return new ValueKind(abcIndex.getSelectedAbc().constants.getDoubleId(((FloatValueAVM2Item) val).value, true), ValueKind.CONSTANT_Double); - } - if (val instanceof NanAVM2Item) { - return new ValueKind(abcIndex.getSelectedAbc().constants.getDoubleId(Double.NaN, true), ValueKind.CONSTANT_Double); - } - if (val instanceof NullAVM2Item) { - return new ValueKind(0, ValueKind.CONSTANT_Null); - } - if (val instanceof UndefinedAVM2Item) { - return new ValueKind(0, ValueKind.CONSTANT_Undefined); - } - return null; - } - - private int genNs(List importedClasses, DottedChain pkg, NamespaceItem ns, List openedNamespaces, SourceGeneratorLocalData localData, int line) throws CompilationException { - ns.resolveCustomNs(abcIndex, importedClasses, pkg, openedNamespaces, localData); - return ns.getCpoolIndex(abcIndex); - } - - public void generateTraitsPhase2(List importedClasses, DottedChain pkg, List items, Trait[] traits, List openedNamespaces, SourceGeneratorLocalData localData) throws CompilationException { - for (int k = 0; k < items.size(); k++) { - GraphTargetItem item = items.get(k); - if (traits[k] == null) { - - } else if (item instanceof InterfaceAVM2Item) { - traits[k].name_index = traitName(((InterfaceAVM2Item) item).pkg == null ? 0 : ((InterfaceAVM2Item) item).pkg.getCpoolIndex(abcIndex), ((InterfaceAVM2Item) item).name); - } else if (item instanceof ClassAVM2Item) { - traits[k].name_index = traitName(((ClassAVM2Item) item).pkg == null ? 0 : ((ClassAVM2Item) item).pkg.getCpoolIndex(abcIndex), ((ClassAVM2Item) item).className); - } else if ((item instanceof MethodAVM2Item) || (item instanceof GetterAVM2Item) || (item instanceof SetterAVM2Item)) { - traits[k].name_index = traitName(genNs(importedClasses, pkg, ((MethodAVM2Item) item).pkg, openedNamespaces, localData, ((MethodAVM2Item) item).line), ((MethodAVM2Item) item).functionName); - } else if (item instanceof FunctionAVM2Item) { - traits[k].name_index = traitName(((FunctionAVM2Item) item).pkg == null ? 0 : ((FunctionAVM2Item) item).pkg.getCpoolIndex(abcIndex), ((FunctionAVM2Item) item).functionName); - } else if (item instanceof ConstAVM2Item) { - traits[k].name_index = traitName(genNs(importedClasses, pkg, ((ConstAVM2Item) item).pkg, openedNamespaces, localData, ((ConstAVM2Item) item).line), ((ConstAVM2Item) item).var); - } else if (item instanceof SlotAVM2Item) { - traits[k].name_index = traitName(genNs(importedClasses, pkg, ((SlotAVM2Item) item).pkg, openedNamespaces, localData, ((SlotAVM2Item) item).line), ((SlotAVM2Item) item).var); - } - } - - for (int k = 0; k < items.size(); k++) { - GraphTargetItem item = items.get(k); - if (traits[k] == null) { - continue; - } - if (item instanceof ClassAVM2Item) { - - InstanceInfo instanceInfo = abcIndex.getSelectedAbc().instance_info.get(((TraitClass) traits[k]).class_info); - instanceInfo.name_index = abcIndex.getSelectedAbc().constants.getMultinameId( - Multiname.createQName( - false, - abcIndex.getSelectedAbc().constants.getStringId(((ClassAVM2Item) item).className, true), - ((ClassAVM2Item) item).pkg.getCpoolIndex(abcIndex)), true); - - if (((ClassAVM2Item) item).extendsOp != null) { - instanceInfo.super_index = typeName(localData, ((ClassAVM2Item) item).extendsOp); - } else { - instanceInfo.super_index = abcIndex.getSelectedAbc().constants.getMultinameId(Multiname.createQName(false, str("Object"), namespace(Namespace.KIND_PACKAGE, "")), true); - } - instanceInfo.interfaces = new int[((ClassAVM2Item) item).implementsOp.size()]; - for (int i = 0; i < ((ClassAVM2Item) item).implementsOp.size(); i++) { - instanceInfo.interfaces[i] = superIntName(localData, ((ClassAVM2Item) item).implementsOp.get(i)); - } - } - if (item instanceof InterfaceAVM2Item) { - ABC abc = abcIndex.getSelectedAbc(); - AVM2ConstantPool constants = abc.constants; - InstanceInfo instanceInfo = abc.instance_info.get(((TraitClass) traits[k]).class_info); - instanceInfo.name_index = constants.getMultinameId(Multiname.createQName(false, constants.getStringId(((InterfaceAVM2Item) item).name, true), - ((InterfaceAVM2Item) item).pkg.getCpoolIndex(abcIndex)), true); - - instanceInfo.interfaces = new int[((InterfaceAVM2Item) item).superInterfaces.size()]; - for (int i = 0; i < ((InterfaceAVM2Item) item).superInterfaces.size(); i++) { - GraphTargetItem un = ((InterfaceAVM2Item) item).superInterfaces.get(i); - instanceInfo.interfaces[i] = superIntName(localData, un); - } - } - } - } - - public int superIntName(SourceGeneratorLocalData localData, GraphTargetItem un) throws CompilationException { - if (un instanceof UnresolvedAVM2Item) { - ((UnresolvedAVM2Item) un).resolve(null, new ArrayList<>(), new ArrayList<>(), abcIndex, new ArrayList<>(), new ArrayList<>()); - un = ((UnresolvedAVM2Item) un).resolved; - } - if (!(un instanceof TypeItem)) { //not applyType - throw new CompilationException("Invalid type", 0); - } - TypeItem sup = (TypeItem) un; - int propId = resolveType(localData, sup, abcIndex); - int[] nss = new int[]{abcIndex.getSelectedAbc().constants.getMultiname(propId).namespace_index}; - return abcIndex.getSelectedAbc().constants.getMultinameId(Multiname.createMultiname(false, abcIndex.getSelectedAbc().constants.getMultiname(propId).name_index, abcIndex.getSelectedAbc().constants.getNamespaceSetId(nss, true)), true); - - } - - public int[] generateMetadata(List>> metadata) { - int[] ret = new int[metadata.size()]; - for (int i = 0; i < metadata.size(); i++) { - Map.Entry> en = metadata.get(i); - int[] keys = new int[en.getValue().size()]; - int[] values = new int[en.getValue().size()]; - int j = 0; - for (String key : en.getValue().keySet()) { - keys[j] = abcIndex.getSelectedAbc().constants.getStringId(key, true); - values[j] = abcIndex.getSelectedAbc().constants.getStringId(en.getValue().get(key), true); - j++; - } - MetadataInfo mi = new MetadataInfo(abcIndex.getSelectedAbc().constants.getStringId(en.getKey(), true), keys, values); - ret[i] = abcIndex.getSelectedAbc().metadata_info.size(); - abcIndex.getSelectedAbc().metadata_info.add(mi); - } - return ret; - } - - public void generateTraitsPhase3(List importedClasses, int methodInitScope, boolean isInterface, String className, String superName, boolean generateStatic, SourceGeneratorLocalData localData, List items, Traits ts, Trait[] traits, Map initScopes, Reference class_index) throws AVM2ParseException, CompilationException { - - //Note: Names must be generated first before accesed in inner subs - for (int k = 0; k < items.size(); k++) { - GraphTargetItem item = items.get(k); - if (traits[k] == null) { - continue; - } - if (item instanceof InterfaceAVM2Item) { - generateClass(((InterfaceAVM2Item) item).pkg.getCpoolIndex(abcIndex), abcIndex.getSelectedAbc().class_info.get(((TraitClass) traits[k]).class_info), abcIndex.getSelectedAbc().instance_info.get(((TraitClass) traits[k]).class_info), initScopes.get(traits[k]), ((InterfaceAVM2Item) item).pkg.name, localData, (InterfaceAVM2Item) item, class_index); - } - - if (item instanceof ClassAVM2Item) { - generateClass(((ClassAVM2Item) item).pkg.getCpoolIndex(abcIndex), abcIndex.getSelectedAbc().class_info.get(((TraitClass) traits[k]).class_info), abcIndex.getSelectedAbc().instance_info.get(((TraitClass) traits[k]).class_info), initScopes.get(traits[k]), ((ClassAVM2Item) item).pkg.name, localData, (ClassAVM2Item) item, class_index); - } - if ((item instanceof MethodAVM2Item) || (item instanceof GetterAVM2Item) || (item instanceof SetterAVM2Item)) { - MethodAVM2Item mai = (MethodAVM2Item) item; - if (mai.isStatic() != generateStatic) { - continue; - } - for (List ln : mai.allOpenedNamespaces) { - for (NamespaceItem n : ln) { - n.resolveCustomNs(abcIndex, importedClasses, localData.pkg, ln, localData); - } - } - String suffix = null; - if (item instanceof GetterAVM2Item) { - suffix = "get"; - } - if (item instanceof SetterAVM2Item) { - suffix = "set"; - } - - ((TraitMethodGetterSetter) traits[k]).method_info = method(mai.isStatic(), methodName(mai.outsidePackage, localData.pkg, mai.functionName, mai.pkg, className, mai.customNamespace, suffix), false, isInterface, new ArrayList<>(), localData.pkg, mai.needsActivation, mai.subvariables, methodInitScope + (mai.isStatic() ? 0 : 1), mai.hasRest, mai.line, className, superName, false, localData, mai.paramTypes, mai.paramNames, mai.paramValues, mai.body, mai.retType); - } else if (item instanceof FunctionAVM2Item) { - FunctionAVM2Item fai = (FunctionAVM2Item) item; - ((TraitFunction) traits[k]).method_info = method(false, methodName(false/*?*/, localData.pkg, fai.functionName, fai.pkg, null, null, ""), false, isInterface, new ArrayList<>(), localData.pkg, fai.needsActivation, fai.subvariables, methodInitScope, fai.hasRest, fai.line, className, superName, false, localData, fai.paramTypes, fai.paramNames, fai.paramValues, fai.body, fai.retType); - } - } - } - - private int methodName(boolean outsidePkg, DottedChain pkg, String methodName, NamespaceItem ns, String className, String customNs, String typeSuffix) { - StringBuilder sb = new StringBuilder(); - /*if (ns != null) { - sb.append(ns.name.toRawString()); - }*/ - if (className != null) { - if (pkg != null && !pkg.isEmpty() && !pkg.isTopLevel()) { - sb.append(pkg.toRawString()); - sb.append(":"); - } - sb.append(className); - } - if (customNs != null) { - sb.append(customNs); - } else if (ns != null) { - switch (ns.kind) { - case Namespace.KIND_PACKAGE_INTERNAL: - sb.append(pkg == null ? "" /*?*/ : pkg.toRawString()); - break; - case Namespace.KIND_PRIVATE: - - if (!outsidePkg) { - sb.append("/private"); - } - break; - case Namespace.KIND_PROTECTED: - case Namespace.KIND_STATIC_PROTECTED: - sb.append("/protected"); - break; - } - } - sb.append(":"); - sb.append(methodName); - if (typeSuffix != null && !typeSuffix.isEmpty()) { - sb.append("/"); - sb.append(typeSuffix); - } - return abcIndex.getSelectedAbc().constants.getStringId(sb.toString(), true); - } - - public Trait[] generateTraitsPhase1(List importedClasses, List openedNamespaces, String className, String superName, boolean generateStatic, SourceGeneratorLocalData localData, List items, Traits ts, Reference classIndex) throws AVM2ParseException, CompilationException { - Trait[] traits = new Trait[items.size()]; - int slot_id = 1; - int disp_id = 3; //1 and 2 are for constructor - for (int k = 0; k < items.size(); k++) { - GraphTargetItem item = items.get(k); - if (item instanceof InterfaceAVM2Item) { - TraitClass tc = new TraitClass(); - ClassInfo ci = new ClassInfo(); - InstanceInfo ii = new InstanceInfo(); - /*abc.class_info.add(ci); - abc.instance_info.add(ii);*/ - tc.class_info = classIndex.getVal(); - abcIndex.getSelectedAbc().addClass(ci, ii, classIndex.getVal()); - classIndex.setVal(classIndex.getVal() + 1); - ii.flags |= InstanceInfo.CLASS_INTERFACE; - //ii.name_index = traitName(((InterfaceAVM2Item) item).namespace, ((InterfaceAVM2Item) item).name); - //tc.class_info = abc.instance_info.size() - 1; - tc.kindType = Trait.TRAIT_CLASS; - //tc.name_index = traitName(((InterfaceAVM2Item) item).namespace, ((InterfaceAVM2Item) item).name); - tc.slot_id = 0; //? - ts.traits.add(tc); - traits[k] = tc; - traits[k].metadata = generateMetadata(((InterfaceAVM2Item) item).metadata); - } - - if (item instanceof ClassAVM2Item) { - TraitClass tc = new TraitClass(); - ClassInfo ci = new ClassInfo(); - InstanceInfo ii = new InstanceInfo(); - //ii.name_index = traitName(((ClassAVM2Item) item).namespace, ((ClassAVM2Item) item).className); - /*abc.class_info.add(ci); - abc.instance_info.add(instanceInfo);*/ - tc.class_info = classIndex.getVal(); - abcIndex.getSelectedAbc().addClass(ci, ii, classIndex.getVal()); - classIndex.setVal(classIndex.getVal() + 1); - tc.kindType = Trait.TRAIT_CLASS; - // tc.name_index = traitName(((ClassAVM2Item) item).namespace, ((ClassAVM2Item) item).className); - tc.slot_id = slot_id++; - ts.traits.add(tc); - traits[k] = tc; - traits[k].metadata = generateMetadata(((ClassAVM2Item) item).metadata); - - } - if ((item instanceof SlotAVM2Item) || (item instanceof ConstAVM2Item)) { - TraitSlotConst tsc = new TraitSlotConst(); - tsc.kindType = (item instanceof SlotAVM2Item) ? Trait.TRAIT_SLOT : Trait.TRAIT_CONST; - String var = null; - GraphTargetItem val = null; - GraphTargetItem type = null; - boolean isNamespace = false; - int namespace = 0; - boolean isStatic = false; - int[] metadata = new int[0]; - if (item instanceof SlotAVM2Item) { - SlotAVM2Item sai = (SlotAVM2Item) item; - if (sai.isStatic() != generateStatic) { - continue; - } - var = sai.var; - val = sai.value; - type = sai.type; - isStatic = sai.isStatic(); - if (sai.pkg != null) { - sai.pkg.resolveCustomNs(abcIndex, importedClasses, localData.pkg, openedNamespaces, localData); - } - namespace = sai.pkg == null ? 0 : sai.pkg.getCpoolIndex(abcIndex); - metadata = generateMetadata(((SlotAVM2Item) item).metadata); - } - if (item instanceof ConstAVM2Item) { - ConstAVM2Item cai = (ConstAVM2Item) item; - if (cai.isStatic() != generateStatic) { - continue; - } - var = cai.var; - val = cai.value; - type = cai.type; - if (cai.pkg != null) { - cai.pkg.resolveCustomNs(abcIndex, importedClasses, localData.pkg, openedNamespaces, localData); - } - namespace = cai.pkg == null ? 0 : cai.pkg.getCpoolIndex(abcIndex); - isNamespace = type.toString().equals("Namespace"); - isStatic = cai.isStatic(); - metadata = generateMetadata(((ConstAVM2Item) item).metadata); - } - if (isNamespace) { - tsc.name_index = traitName(namespace, var); - } - tsc.type_index = isNamespace ? 0 : (type == null ? 0 : typeName(localData, type)); - - ValueKind vk = getValueKind(namespace, type, val); - if (vk == null) { - tsc.value_kind = ValueKind.CONSTANT_Undefined; - } else { - tsc.value_kind = vk.value_kind; - tsc.value_index = vk.value_index; - } - tsc.slot_id = isStatic ? slot_id++ : 0; - ts.traits.add(tsc); - traits[k] = tsc; - traits[k].metadata = metadata; - } - if ((item instanceof MethodAVM2Item) || (item instanceof GetterAVM2Item) || (item instanceof SetterAVM2Item)) { - MethodAVM2Item mai = (MethodAVM2Item) item; - if (mai.isStatic() != generateStatic) { - continue; - } - TraitMethodGetterSetter tmgs = new TraitMethodGetterSetter(); - tmgs.kindType = (item instanceof GetterAVM2Item) ? Trait.TRAIT_GETTER : ((item instanceof SetterAVM2Item) ? Trait.TRAIT_SETTER : Trait.TRAIT_METHOD); - tmgs.disp_id = mai.isStatic() ? disp_id++ : 0; //For a reason, there is disp_id only for static methods (or not?) - if (mai.isFinal() || (className != null && mai.isStatic())) { - tmgs.kindFlags |= Trait.ATTR_Final; - } - if (mai.isOverride()) { - tmgs.kindFlags |= Trait.ATTR_Override; - } - ts.traits.add(tmgs); - - traits[k] = tmgs; - traits[k].metadata = generateMetadata(((MethodAVM2Item) item).metadata); - } - /*else if (item instanceof FunctionAVM2Item) { - TraitFunction tf = new TraitFunction(); - tf.slot_id = slot_id++; - tf.kindType = Trait.TRAIT_FUNCTION; - //tf.name_index = traitName(((FunctionAVM2Item) item).namespace, ((FunctionAVM2Item) item).functionName); - ts.traits.add(tf); - traits[k] = tf; - traits[k].metadata = generateMetadata(((FunctionAVM2Item) item).metadata); - }*/ - - } - - return traits; - } - - public ScriptInfo generateScriptInfo(List> allOpenedNamespaces, SourceGeneratorLocalData localData, List commands, int classPos) throws AVM2ParseException, CompilationException { - Reference class_index = new Reference<>(classPos); - ScriptInfo si = new ScriptInfo(); - localData.currentScript = si; - Trait[] traitArr = generateTraitsPhase1(new ArrayList<>(), new ArrayList<>(), null, null, true, localData, commands, si.traits, class_index); - generateTraitsPhase2(new ArrayList<>(), null/*FIXME*/, commands, traitArr, new ArrayList<>(), localData); - - abcIndex.refreshSelected(); - - ABC abc = abcIndex.getSelectedAbc(); - AVM2ConstantPool constants = abc.constants; - MethodInfo mi = new MethodInfo(new int[0], 0, constants.getStringId("", true), 0, new ValueKind[0], new int[0]); - MethodBody mb = new MethodBody(abc, new Traits(), new byte[0], new ABCException[0]); - mb.method_info = abc.addMethodInfo(mi); - mb.setCode(new AVM2Code()); - List mbCode = mb.getCode().code; - mbCode.add(ins(AVM2Instructions.GetLocal0)); - mbCode.add(ins(AVM2Instructions.PushScope)); - - int traitScope = 2; - - Map initScopes = new HashMap<>(); - - for (Trait t : si.traits.traits) { - if (t instanceof TraitClass) { - TraitClass tc = (TraitClass) t; - List parents = new ArrayList<>(); - if (localData.documentClass) { - mbCode.add(ins(AVM2Instructions.GetScopeObject, 0)); - traitScope++; - } else { - int[] nsset = new int[]{constants.getMultiname(tc.name_index).namespace_index}; - mbCode.add(ins(AVM2Instructions.FindPropertyStrict, constants.getMultinameId(Multiname.createMultiname(false, constants.getMultiname(tc.name_index).name_index, constants.getNamespaceSetId(nsset, true)), true))); - } - if (abc.instance_info.get(tc.class_info).isInterface()) { - mbCode.add(ins(AVM2Instructions.PushNull)); - } else { - - AbcIndexing.ClassIndex ci = abcIndex.findClass(AbcIndexing.multinameToType(abc.instance_info.get(tc.class_info).name_index, constants)); - while (ci != null && ci.parent != null) { - ci = ci.parent; - Multiname origM = ci.abc.constants.getMultiname(ci.abc.instance_info.get(ci.index).name_index); - Namespace origNs = ci.abc.constants.getNamespace(origM.namespace_index); - if (origM.kind == Multiname.QNAME || origM.kind == Multiname.QNAMEA) { - parents.add(constants.getMultinameId( - Multiname.createQName(origM.kind == Multiname.QNAMEA, - constants.getStringId(ci.abc.constants.getString(origM.name_index), true), - constants.getNamespaceId(origNs.kind, - ci.abc.constants.getString(origNs.name_index), 0, true)), true)); - } - } - - //add all parent objects to scopestack - for (int i = parents.size() - 1; i >= 0; i--) { - mbCode.add(ins(AVM2Instructions.GetLex, parents.get(i))); - mbCode.add(ins(AVM2Instructions.PushScope)); - traitScope++; - } - //direct parent class to new_class instruction - if (!parents.isEmpty()) { //NON EXISTING PARENT CLASS - TODO: handle as error! - mbCode.add(ins(AVM2Instructions.GetLex, parents.get(0))); - } - } - mbCode.add(ins(AVM2Instructions.NewClass, tc.class_info)); - for (int i = 0; i < parents.size(); i++) { - mbCode.add(ins(AVM2Instructions.PopScope)); - } - - mbCode.add(ins(AVM2Instructions.InitProperty, tc.name_index)); - initScopes.put(t, traitScope); - traitScope = 1; - } - } - - abc.addMethodBody(mb); - si.init_index = mb.method_info; - localData.pkg = DottedChain.EMPTY; - generateTraitsPhase3(new ArrayList<>(), 1/*??*/, false, null, null, true, localData, commands, si.traits, traitArr, initScopes, class_index); - - int maxSlotId = 0; - for (int k = 0; k < si.traits.traits.size(); k++) { - if (si.traits.traits.get(k) instanceof TraitSlotConst) { - TraitSlotConst ti = (TraitSlotConst) si.traits.traits.get(k); - if (ti.slot_id > maxSlotId) { - maxSlotId = ti.slot_id; - } - } - } - for (int k = 0; k < si.traits.traits.size(); k++) { - if ((si.traits.traits.get(k) instanceof TraitMethodGetterSetter) && (commands.get(k) instanceof MethodAVM2Item)) { - MethodAVM2Item mai = (MethodAVM2Item) commands.get(k); - if (mai.outsidePackage) { - TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) si.traits.traits.get(k); - TraitSlotConst nts = new TraitSlotConst(); - nts.name_index = si.traits.traits.get(k).name_index; - nts.metadata = si.traits.traits.get(k).metadata; - - nts.slot_id = maxSlotId + 1; - maxSlotId++; - nts.type_index = abcIndex.getSelectedAbc().constants.getQnameId("Function", Namespace.KIND_PACKAGE, "", true); - nts.value_index = 0; - nts.value_kind = 0; - int methodinfo = tmgs.method_info; - si.traits.traits.set(k, nts); - mbCode.add(ins(AVM2Instructions.NewFunction, methodinfo)); - mbCode.add(ins(AVM2Instructions.InitProperty, nts.name_index)); - } - } - } - - mbCode.add(ins(AVM2Instructions.ReturnVoid)); - mb.autoFillStats(abc, 1, false); - - return si; - } - - public static void parentNamesAddNames(AbcIndexing abc, int name_index, List indices, List names, List namespaces) { - List cindices = new ArrayList<>(); - - List outABCs = new ArrayList<>(); - parentNames(abc, name_index, cindices, names, namespaces, outABCs); - for (int i = 0; i < cindices.size(); i++) { - ABC a = outABCs.get(i); - int m = cindices.get(i); - if (a == abc.getSelectedAbc()) { - indices.add(m); - continue; - } - Multiname superName = a.constants.getMultiname(m); - indices.add( - abc.getSelectedAbc().constants.getMultinameId( - Multiname.createQName(false, - abc.getSelectedAbc().constants.getStringId(superName.getName(a.constants, null, true), true), - abc.getSelectedAbc().constants.getNamespaceId(superName.getNamespace(a.constants).kind, superName.getNamespace(a.constants).getName(a.constants), 0, true)), true) - ); - } - } - - public static GraphTargetItem getTraitReturnType(AbcIndexing abc, Trait t) { - if (t instanceof TraitSlotConst) { - TraitSlotConst tsc = (TraitSlotConst) t; - if (tsc.type_index == 0) { - return TypeItem.UNBOUNDED; - } - return PropertyAVM2Item.multinameToType(tsc.type_index, abc.getSelectedAbc().constants); - } - if (t instanceof TraitMethodGetterSetter) { - TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) t; - if (tmgs.kindType == Trait.TRAIT_GETTER) { - return PropertyAVM2Item.multinameToType(abc.getSelectedAbc().method_info.get(tmgs.method_info).ret_type, abc.getSelectedAbc().constants); - } - if (tmgs.kindType == Trait.TRAIT_SETTER) { - if (abc.getSelectedAbc().method_info.get(tmgs.method_info).param_types.length > 0) { - return PropertyAVM2Item.multinameToType(abc.getSelectedAbc().method_info.get(tmgs.method_info).param_types[0], abc.getSelectedAbc().constants); - } else { - return TypeItem.UNBOUNDED; - } - } - } - if (t instanceof TraitFunction) { - return new TypeItem(DottedChain.FUNCTION); - } - return TypeItem.UNBOUNDED; - } - - public static boolean searchPrototypeChain(List otherNs, int privateNs, int protectedNs, boolean instanceOnly, AbcIndexing abc, DottedChain pkg, String obj, String propertyName, Reference outName, Reference outNs, Reference outPropNs, Reference outPropNsKind, Reference outPropNsIndex, Reference outPropType, Reference outPropValue, Reference outPropValueAbc) { - for (int ns : otherNs) { - if (searchPrototypeChain(ns, instanceOnly, abc, pkg, obj, propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue, outPropValueAbc)) { - return true; - } - } - - if (searchPrototypeChain(privateNs, instanceOnly, abc, pkg, obj, propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue, outPropValueAbc)) { - return true; - } - if (searchPrototypeChain(protectedNs, instanceOnly, abc, pkg, obj, propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue, outPropValueAbc)) { - return true; - } - return searchPrototypeChain(0, instanceOnly, abc, pkg, obj, propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue, outPropValueAbc); - } - - private static boolean searchPrototypeChain(int selectedNs, boolean instanceOnly, AbcIndexing abc, DottedChain pkg, String obj, String propertyName, Reference outName, Reference outNs, Reference outPropNs, Reference outPropNsKind, Reference outPropNsIndex, Reference outPropType, Reference outPropValue, Reference outPropValueAbc) { - - AbcIndexing.TraitIndex sp = abc.findScriptProperty(pkg.add(propertyName)); - if (sp == null) { - sp = abc.findProperty(new AbcIndexing.PropertyDef(propertyName, new TypeItem(pkg.add(obj)), abc.getSelectedAbc(), selectedNs), !instanceOnly, true); - } - if (sp != null) { - if (sp.objType instanceof TypeItem) { - outName.setVal(((TypeItem) sp.objType).fullTypeName.getLast()); - outNs.setVal(((TypeItem) sp.objType).fullTypeName.getWithoutLast()); - } else { - //FIXME? Vector? - } - outPropNs.setVal(sp.trait.getName(sp.abc).getNamespace(sp.abc.constants).getName(sp.abc.constants)); - outPropNsKind.setVal(sp.trait.getName(sp.abc).getNamespace(sp.abc.constants).kind); - int nsi = sp.trait.getName(sp.abc).namespace_index; - outPropNsIndex.setVal(sp.abc == abc.getSelectedAbc() ? sp.abc.constants.getNamespaceSubIndex(nsi) : 0); - outPropType.setVal(sp.returnType); - outPropValue.setVal(sp.value); - outPropValueAbc.setVal(sp.abc); - return true; - } - return false; - } - - public static void parentNames(AbcIndexing abc, int name_index, List indices, List names, List namespaces, List outABCs) { - AbcIndexing.ClassIndex ci = abc.findClass(new TypeItem(abc.getSelectedAbc().constants.getMultiname(name_index).getNameWithNamespace(abc.getSelectedAbc().constants))); - while (ci != null) { - int ni = ci.abc.instance_info.get(ci.index).name_index; - indices.add(ni); - outABCs.add(ci.abc); - names.add(ci.abc.constants.getMultiname(ni).getName(ci.abc.constants, null, true)); - namespaces.add(ci.abc.constants.getMultiname(ni).getNamespace(ci.abc.constants).getName(ci.abc.constants).toRawString()); - ci = ci.parent; - } - } - - /* public void calcRegisters(Reference activationReg, SourceGeneratorLocalData localData, boolean needsActivation, List funParamNames,List funSubVariables,List funBody, Reference hasArguments) throws ParseException { - - }*/ - /*public int resolveType(String objType) { - if (objType.equals("*")) { - return 0; - } - List abcs = new ArrayList<>(); - abcs.add(abc); - abcs.addAll(allABCs); - for (ABC a : abcs) { - int ci = a.findClassByName(objType); - if (ci != -1) { - Multiname tname = a.instance_info.get(ci).getName(a.constants); - return abc.getLastAbc().constants.getMultinameId(new Multiname(tname.kind, - abc.getLastAbc().constants.getStringId(tname.getName(a.constants, new ArrayList<>()), true), - abc.getLastAbc().constants.getNamespaceId(new Namespace(tname.getNamespace(a.constants).kind, abc.getLastAbc().constants.getStringId(tname.getNamespace(a.constants).getName(a.constants), true)), 0, true), 0, 0, new ArrayList()), true); - } - } - return 0; - }*/ - @Override - public List generate(SourceGeneratorLocalData localData, TypeItem item) throws CompilationException { - String currentFullClassName = localData.getFullClass(); - - if (localData.documentClass && item.toString().equals(currentFullClassName)) { - int slotId = 0; - int c = abcIndex.getSelectedAbc().findClassByName(currentFullClassName); - for (Trait t : localData.currentScript.traits.traits) { - if (t instanceof TraitClass) { - TraitClass tc = (TraitClass) t; - if (tc.class_info == c) { - slotId = tc.slot_id; - break; - } - } - } - return GraphTargetItem.toSourceMerge(localData, this, ins(AVM2Instructions.GetGlobalScope), ins(AVM2Instructions.GetSlot, slotId)); - } else { - return GraphTargetItem.toSourceMerge(localData, this, ins(AVM2Instructions.GetLex, resolveType(localData, item, abcIndex))); - } - } - - public static int resolveType(SourceGeneratorLocalData localData, GraphTargetItem item, AbcIndexing abcIndex) throws CompilationException { - int name_index = 0; - GraphTargetItem typeItem = null; - - if (item instanceof UnresolvedAVM2Item) { - String fullClass = localData.getFullClass(); - item = ((UnresolvedAVM2Item) item).resolve(new TypeItem(fullClass), new ArrayList<>(), new ArrayList<>(), abcIndex, new ArrayList<>(), new ArrayList<>()); - } - if (item instanceof TypeItem) { - typeItem = item; - } else if (item instanceof ApplyTypeAVM2Item) { - typeItem = ((ApplyTypeAVM2Item) item).object; - } else { - throw new CompilationException("Invalid type:" + item + " (" + item.getClass().getName() + ")", 0/*??*/); - } - if (typeItem instanceof UnresolvedAVM2Item) { - String fullClass = localData.getFullClass(); - typeItem = ((UnresolvedAVM2Item) typeItem).resolve(new TypeItem(fullClass), new ArrayList<>(), new ArrayList<>(), abcIndex, new ArrayList<>(), new ArrayList<>()); - } - - if (!(typeItem instanceof TypeItem)) { - throw new CompilationException("Invalid type", 0/*??*/); - } - - TypeItem type = (TypeItem) typeItem; - - DottedChain dname = type.fullTypeName; - DottedChain pkg = dname.getWithoutLast(); - String name = dname.getLast(); - /*for (InstanceInfo ii : abc.getSelectedAbc().instance_info) { - Multiname mname = abc.getSelectedAbc().constants.constant_multiname.get(ii.name_index); - if (mname != null && name.equals(mname.getName(abc.getSelectedAbc().constants, null, true))) { - Namespace ns = mname.getNamespace(abc.getSelectedAbc().constants); - if (ns != null && ns.hasName(pkg, abc.getSelectedAbc().constants)) { - name_index = ii.name_index; - break; - } - } - }*/ - ABC abc = abcIndex.getSelectedAbc(); - AVM2ConstantPool constants = abc.constants; - AbcIndexing.ClassIndex ci = abcIndex.findClass(new TypeItem(dname)); - if (ci != null) { - Multiname m = ci.abc.instance_info.get(ci.index).getName(ci.abc.constants); - 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); - String nsn = ns == null ? null : ns.getName(ci.abc.constants).toRawString(); - name_index = constants.getQnameId( - n, - ns == null ? Namespace.KIND_PACKAGE : ns.kind, - nsn, true); - } - } - - for (int i = 1; i < constants.getMultinameCount(); i++) { - Multiname mname = constants.getMultiname(i); - if (mname != null && name.equals(mname.getName(constants, null, true))) { - if (mname.getNamespace(constants) != null && pkg.equals(mname.getNamespace(constants).getName(constants))) { - name_index = i; - break; - } - } - } - if (name_index == 0) { - if (pkg.isEmpty() && localData.currentScript != null /*FIXME!*/) { - for (Trait t : localData.currentScript.traits.traits) { - if (t.getName(abc).getName(constants, null, true).equals(name)) { - name_index = t.name_index; - break; - } - } - } - if (name_index == 0) { - name_index = constants.getMultinameId(Multiname.createQName(false, constants.getStringId(name, true), constants.getNamespaceId(Namespace.KIND_PACKAGE, pkg, 0, true)), true); - } - } - - if (item instanceof ApplyTypeAVM2Item) { - ApplyTypeAVM2Item atype = (ApplyTypeAVM2Item) item; - int[] params = new int[atype.params.size()]; - int i = 0; - for (GraphTargetItem s : atype.params) { - params[i++] = resolveType(localData, s, abcIndex); - } - return constants.getMultinameId(Multiname.createTypeName(name_index, params), true); - } - - return name_index; - } - - @Override - public List generateDiscardValue(SourceGeneratorLocalData localData, GraphTargetItem item) throws CompilationException { - List ret = item.toSource(localData, this); - ret.add(ins(AVM2Instructions.Pop)); - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, PushItem item) throws CompilationException { - return item.value.toSource(localData, this); - } - - @Override - public List generate(SourceGeneratorLocalData localData, PopItem item) throws CompilationException { - List ret = new ArrayList<>(); - return ret; - } - -} +/* + * Copyright (C) 2010-2016 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.avm2.parser.script; + +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instructions; +import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructSuperIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.JumpIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnValueIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnVoidIns; +import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ApplyTypeAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.BooleanAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.FloatValueAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.GetDescendantsAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.IntegerValueAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.LocalRegAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.NameValuePair; +import com.jpexs.decompiler.flash.abc.avm2.model.NanAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.NewObjectAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.NullAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ReturnValueAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ReturnVoidAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.StringAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ThrowAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.UndefinedAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.WithAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.WithObjectAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ForEachInAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ForInAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.clauses.TryAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.IfCondition; +import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException; +import com.jpexs.decompiler.flash.abc.avm2.parser.script.AbcIndexing.ClassIndex; +import com.jpexs.decompiler.flash.abc.types.ABCException; +import com.jpexs.decompiler.flash.abc.types.ClassInfo; +import com.jpexs.decompiler.flash.abc.types.ConvertData; +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.ScriptInfo; +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 com.jpexs.decompiler.flash.abc.types.traits.Traits; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.ecma.EcmaScript; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.flash.helpers.NulWriter; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.DottedChain; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.Loop; +import com.jpexs.decompiler.graph.ScopeStack; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.TypeItem; +import com.jpexs.decompiler.graph.model.AndItem; +import com.jpexs.decompiler.graph.model.BreakItem; +import com.jpexs.decompiler.graph.model.CommaExpressionItem; +import com.jpexs.decompiler.graph.model.ContinueItem; +import com.jpexs.decompiler.graph.model.DefaultItem; +import com.jpexs.decompiler.graph.model.DoWhileItem; +import com.jpexs.decompiler.graph.model.DuplicateItem; +import com.jpexs.decompiler.graph.model.FalseItem; +import com.jpexs.decompiler.graph.model.ForItem; +import com.jpexs.decompiler.graph.model.IfItem; +import com.jpexs.decompiler.graph.model.LocalData; +import com.jpexs.decompiler.graph.model.NotItem; +import com.jpexs.decompiler.graph.model.OrItem; +import com.jpexs.decompiler.graph.model.PopItem; +import com.jpexs.decompiler.graph.model.PushItem; +import com.jpexs.decompiler.graph.model.SwitchItem; +import com.jpexs.decompiler.graph.model.TernarOpItem; +import com.jpexs.decompiler.graph.model.TrueItem; +import com.jpexs.decompiler.graph.model.UnboundedTypeItem; +import com.jpexs.decompiler.graph.model.WhileItem; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * + * @author JPEXS + */ +public class AVM2SourceGenerator implements SourceGenerator { + + public final AbcIndexing abcIndex; + + public static final int MARK_E_START = 0; + + public static final int MARK_E_END = 1; + + public static final int MARK_E_TARGET = 2; + + public static final int MARK_E_FINALLYPART = 3; + + private AVM2Instruction ins(int instructionCode, int... operands) { + return new AVM2Instruction(0, instructionCode, operands); + } + + private AVM2Instruction ins(InstructionDefinition def, int... operands) { + return new AVM2Instruction(0, def, operands); + } + + @Override + public List generate(SourceGeneratorLocalData localData, FalseItem item) throws CompilationException { + return GraphTargetItem.toSourceMerge(localData, this, ins(AVM2Instructions.PushFalse)); + } + + @Override + public List generate(SourceGeneratorLocalData localData, TrueItem item) throws CompilationException { + return GraphTargetItem.toSourceMerge(localData, this, ins(AVM2Instructions.PushTrue)); + } + + public List generate(SourceGeneratorLocalData localData, GetDescendantsAVM2Item item) throws CompilationException { + + AVM2ConstantPool constants = abcIndex.getSelectedAbc().constants; + int[] nssa = new int[item.openedNamespaces.size()]; + for (int i = 0; i < item.openedNamespaces.size(); i++) { + nssa[i] = item.openedNamespaces.get(i).getCpoolIndex(abcIndex); + } + + int nsset = constants.getNamespaceSetId(nssa, true); + + return GraphTargetItem.toSourceMerge(localData, this, + item.object, + ins(AVM2Instructions.GetDescendants, constants.getMultinameId(Multiname.createMultiname(false, constants.getStringId(item.nameStr, true), nsset), true)) + ); + } + + @Override + public List generate(SourceGeneratorLocalData localData, AndItem item) throws CompilationException { + List ret = new ArrayList<>(); + ret.addAll(generateToActionList(localData, item.leftSide)); + ret.add(ins(AVM2Instructions.Dup)); + if (!("" + item.leftSide.returnType()).equals("Boolean")) { + ret.add(ins(AVM2Instructions.ConvertB)); + } + List andExpr = generateToActionList(localData, item.rightSide); + andExpr.add(0, ins(AVM2Instructions.Pop)); + int andExprLen = insToBytes(andExpr).length; + ret.add(ins(AVM2Instructions.IfFalse, andExprLen)); + ret.addAll(andExpr); + return ret; + + } + + private byte[] insToBytes(List code) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + for (AVM2Instruction instruction : code) { + + baos.write(instruction.getBytes()); + + } + return baos.toByteArray(); + } catch (IOException ex) { + Logger.getLogger(AVM2SourceGenerator.class.getName()).log(Level.SEVERE, null, ex); + } + return SWFInputStream.BYTE_ARRAY_EMPTY; + } + + @Override + public List generate(SourceGeneratorLocalData localData, OrItem item) throws CompilationException { + List ret = new ArrayList<>(); + ret.addAll(generateToActionList(localData, item.leftSide)); + ret.add(ins(AVM2Instructions.Dup)); + if (!("" + item.leftSide.returnType()).equals("Boolean")) { + ret.add(ins(AVM2Instructions.ConvertB)); + } + List orExpr = generateToActionList(localData, item.rightSide); + orExpr.add(0, ins(AVM2Instructions.Pop)); + int orExprLen = insToBytes(orExpr).length; + ret.add(ins(AVM2Instructions.IfTrue, orExprLen)); + ret.addAll(orExpr); + return ret; + } + + public ArrayList toInsList(List items) { + ArrayList ret = new ArrayList<>(); + for (GraphSourceItem s : items) { + if (s instanceof AVM2Instruction) { + ret.add((AVM2Instruction) s); + } + } + return ret; + } + + private List nonempty(List list) { + if (list == null) { + return new ArrayList<>(); + } + return list; + } + + private List condition(SourceGeneratorLocalData localData, GraphTargetItem t, int offset) throws CompilationException { + if (t instanceof IfCondition) { + IfCondition ic = (IfCondition) t; + return GraphTargetItem.toSourceMerge(localData, this, ic.getLeftSide(), ic.getRightSide(), ins(ic.getIfDefinition(), offset)); + } + return GraphTargetItem.toSourceMerge(localData, this, t, ins(AVM2Instructions.IfTrue, offset)); + } + + private List notCondition(SourceGeneratorLocalData localData, GraphTargetItem t, int offset) throws CompilationException { + if (t instanceof IfCondition) { + IfCondition ic = (IfCondition) t; + return GraphTargetItem.toSourceMerge(localData, this, ic.getLeftSide(), ic.getRightSide(), ins(ic.getIfNotDefinition(), offset)); + } + return GraphTargetItem.toSourceMerge(localData, this, t, ins(AVM2Instructions.IfFalse, offset)); + } + + private List generateIf(SourceGeneratorLocalData localData, GraphTargetItem expression, List onTrueCmds, List onFalseCmds, boolean ternar) throws CompilationException { + List ret = new ArrayList<>(); + //ret.addAll(notCondition(localData, expression)); + List onTrue; + List onFalse = null; + if (ternar) { + onTrue = toInsList(onTrueCmds.get(0).toSource(localData, this)); + } else { + onTrue = generateToInsList(localData, onTrueCmds); + } + + if (onFalseCmds != null && !onFalseCmds.isEmpty()) { + if (ternar) { + onFalse = toInsList(onFalseCmds.get(0).toSource(localData, this)); + } else { + onFalse = generateToInsList(localData, onFalseCmds); + } + } + AVM2Instruction ajmp = null; + if (onFalse != null) { + if (!((!nonempty(onTrue).isEmpty()) + && ((onTrue.get(onTrue.size() - 1).definition instanceof ContinueJumpIns) + || ((onTrue.get(onTrue.size() - 1).definition instanceof BreakJumpIns))))) { + ajmp = ins(AVM2Instructions.Jump, 0); + onTrue.add(ajmp); + } + } + + byte[] onTrueBytes = insToBytes(onTrue); + int onTrueLen = onTrueBytes.length; + + ret.addAll(notCondition(localData, expression, onTrueLen)); + ret.addAll(onTrue); + + if (onFalse != null) { + byte[] onFalseBytes = insToBytes(onFalse); + int onFalseLen = onFalseBytes.length; + if (ajmp != null) { + ajmp.operands[0] = onFalseLen; + } + ret.addAll(onFalse); + } + return ret; + } + + public List generate(SourceGeneratorLocalData localData, XMLFilterAVM2Item item) throws CompilationException { + List ret = new ArrayList<>(); + final Reference counterReg = new Reference<>(0); + final Reference collectionReg = new Reference<>(0); + final Reference xmlListReg = new Reference<>(0); + List xmlListSetTemp = AssignableAVM2Item.setTemp(localData, this, xmlListReg); + AVM2ConstantPool constants = abcIndex.getSelectedAbc().constants; + ret.addAll(GraphTargetItem.toSourceMerge(localData, this, + ins(AVM2Instructions.PushByte, 0), + AssignableAVM2Item.setTemp(localData, this, counterReg), + item.object, + ins(AVM2Instructions.CheckFilter), + NameAVM2Item.generateCoerce(localData, this, TypeItem.UNBOUNDED), + AssignableAVM2Item.setTemp(localData, this, collectionReg), + ins(AVM2Instructions.GetLex, constants.getMultinameId(Multiname.createQName(false, constants.getStringId("XMLList", true), constants.getNamespaceId(Namespace.KIND_PACKAGE, "", 0, true)), true)), + ins(AVM2Instructions.PushString, constants.getStringId("", true)), + ins(AVM2Instructions.Construct, 1), + xmlListSetTemp + )); + final Reference tempVal1 = new Reference<>(0); + final Reference tempVal2 = new Reference<>(0); + + List forBody = toInsList(GraphTargetItem.toSourceMerge(localData, this, + ins(AVM2Instructions.Label), + AssignableAVM2Item.getTemp(localData, this, collectionReg), + AssignableAVM2Item.getTemp(localData, this, counterReg), + ins(AVM2Instructions.NextValue), + AssignableAVM2Item.dupSetTemp(localData, this, tempVal1), + AssignableAVM2Item.dupSetTemp(localData, this, tempVal2), + ins(AVM2Instructions.PushWith) + )); + localData.scopeStack.add(new LocalRegAVM2Item(null, null, tempVal2.getVal(), null)); + forBody.addAll(toInsList(item.value.toSource(localData, this))); + List trueBody = new ArrayList<>(); + trueBody.addAll(toInsList(AssignableAVM2Item.getTemp(localData, this, xmlListReg))); + trueBody.addAll(toInsList(AssignableAVM2Item.getTemp(localData, this, counterReg))); + trueBody.addAll(toInsList(AssignableAVM2Item.getTemp(localData, this, tempVal1))); + trueBody.add(ins(AVM2Instructions.SetProperty, constants.getMultinameId(Multiname.createMultinameL(false, NamespaceItem.getCpoolSetIndex(abcIndex, item.openedNamespaces)), true))); + forBody.add(ins(AVM2Instructions.IfFalse, insToBytes(trueBody).length)); + forBody.addAll(trueBody); + forBody.add(ins(AVM2Instructions.PopScope)); + localData.scopeStack.remove(localData.scopeStack.size() - 1); + forBody.addAll(toInsList(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempVal2, tempVal1)))); + + int forBodyLen = insToBytes(forBody).length; + AVM2Instruction forwardJump = ins(AVM2Instructions.Jump, forBodyLen); + ret.add(forwardJump); + + List expr = new ArrayList<>(); + expr.add(ins(AVM2Instructions.HasNext2, collectionReg.getVal(), counterReg.getVal())); + AVM2Instruction backIf = ins(AVM2Instructions.IfTrue, 0); + expr.add(backIf); + + int exprLen = insToBytes(expr).length; + backIf.operands[0] = -(exprLen + forBodyLen); + + ret.addAll(forBody); + ret.addAll(expr); + ret.addAll(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(collectionReg, counterReg))); + ret.addAll(AssignableAVM2Item.getTemp(localData, this, xmlListReg)); + ret.addAll(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(xmlListReg))); + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, IfItem item) throws CompilationException { + return generateIf(localData, item.expression, item.onTrue, item.onFalse, false); + } + + private void fixSwitch(List code, int breakOffset, long loopId) { + fixLoop(code, breakOffset, Integer.MAX_VALUE, loopId); + } + + private void fixLoop(List code, int breakOffset, int continueOffset, long loopId) { + int pos = 0; + for (int a = 0; a < code.size(); a++) { + AVM2Instruction ins = code.get(a); + pos += ins.getBytesLength(); + if (ins.definition instanceof JumpIns) { + if (ins.definition instanceof ContinueJumpIns) { + if (continueOffset != Integer.MAX_VALUE) { + ins.operands[0] = (-pos + continueOffset); + ins.definition = AVM2Code.instructionSet[AVM2Instructions.Jump]; + } + } + if (ins.definition instanceof BreakJumpIns) { + ins.operands[0] = (-pos + breakOffset); + ins.definition = AVM2Code.instructionSet[AVM2Instructions.Jump]; + } + } + } + } + + @Override + public List generate(SourceGeneratorLocalData localData, TernarOpItem item) throws CompilationException { + List onTrue = new ArrayList<>(); + onTrue.add(item.onTrue); + List onFalse = new ArrayList<>(); + onFalse.add(item.onFalse); + return generateIf(localData, item.expression, onTrue, onFalse, true); + } + + @Override + public List generate(SourceGeneratorLocalData localData, WhileItem item) throws CompilationException { + List ret = new ArrayList<>(); + List whileExpr = new ArrayList<>(); + + List ex = new ArrayList<>(item.expression); + GraphTargetItem lastItem = null; + if (!ex.isEmpty()) { + lastItem = ex.remove(ex.size() - 1); + while (lastItem instanceof CommaExpressionItem) { + CommaExpressionItem cei = (CommaExpressionItem) lastItem; + ex.addAll(cei.commands); + lastItem = ex.remove(ex.size() - 1); + } + whileExpr.addAll(generateToInsList(localData, ex)); + } + List whileBody = generateToInsList(localData, item.commands); + AVM2Instruction forwardJump = ins(AVM2Instructions.Jump, 0); + ret.add(forwardJump); + whileBody.add(0, ins(AVM2Instructions.Label)); + ret.addAll(whileBody); + int whileBodyLen = insToBytes(whileBody).length; + forwardJump.operands[0] = whileBodyLen; + whileExpr.addAll(toInsList(condition(localData, lastItem, 0))); + int whileExprLen = insToBytes(whileExpr).length; + whileExpr.get(whileExpr.size() - 1).operands[0] = -(whileExprLen + whileBodyLen); //Assuming last is if instruction + ret.addAll(whileExpr); + fixLoop(whileBody, whileBodyLen + whileExprLen, whileBodyLen, item.loop.id); + return ret; + } + + public List generate(SourceGeneratorLocalData localData, ForEachInAVM2Item item) throws CompilationException { + return generateForIn(localData, item.loop, item.expression.collection, (AssignableAVM2Item) item.expression.object, item.commands, true); + } + + public List generate(SourceGeneratorLocalData localData, ForInAVM2Item item) throws CompilationException { + return generateForIn(localData, item.loop, item.expression.collection, (AssignableAVM2Item) item.expression.object, item.commands, false); + } + + public List generateForIn(SourceGeneratorLocalData localData, Loop loop, GraphTargetItem collection, AssignableAVM2Item assignable, List commands, final boolean each) throws CompilationException { + List ret = new ArrayList<>(); + final Reference counterReg = new Reference<>(0); + final Reference collectionReg = new Reference<>(0); + + if (assignable instanceof UnresolvedAVM2Item) { + assignable = (AssignableAVM2Item) ((UnresolvedAVM2Item) assignable).resolved; + } + + ret.addAll(GraphTargetItem.toSourceMerge(localData, this, + ins(AVM2Instructions.PushByte, 0), + AssignableAVM2Item.setTemp(localData, this, counterReg), + collection, + NameAVM2Item.generateCoerce(localData, this, TypeItem.UNBOUNDED), + AssignableAVM2Item.setTemp(localData, this, collectionReg) + )); + + GraphTargetItem assigned = new GraphTargetItem() { + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + return null; + } + + @Override + public boolean hasReturnValue() { + return true; + } + + @Override + public GraphTargetItem returnType() { + return TypeItem.UNBOUNDED; + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + return toSourceMerge(localData, generator, + AssignableAVM2Item.getTemp(localData, generator, collectionReg), + AssignableAVM2Item.getTemp(localData, generator, counterReg), + ins(each ? AVM2Instructions.NextValue : AVM2Instructions.NextName) + ); + } + }; + assignable.setAssignedValue(assigned); + + List forBody = toInsList(GraphTargetItem.toSourceMerge(localData, this, + ins(AVM2Instructions.Label), + assignable.toSourceIgnoreReturnValue(localData, this) + )); + + forBody.addAll(generateToInsList(localData, commands)); + int forBodyLen = insToBytes(forBody).length; + + AVM2Instruction forwardJump = ins(AVM2Instructions.Jump, forBodyLen); + ret.add(forwardJump); + + List expr = new ArrayList<>(); + expr.add(ins(AVM2Instructions.HasNext2, collectionReg.getVal(), counterReg.getVal())); + AVM2Instruction backIf = ins(AVM2Instructions.IfTrue, 0); + expr.add(backIf); + + int exprLen = insToBytes(expr).length; + backIf.operands[0] = -(exprLen + forBodyLen); + + fixLoop(forBody, forBodyLen + exprLen, forBodyLen, loop.id); + ret.addAll(forBody); + ret.addAll(expr); + ret.addAll(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(collectionReg, counterReg))); + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, DoWhileItem item) throws CompilationException { + List ret = new ArrayList<>(); + List whileExpr = new ArrayList<>(); + + List ex = new ArrayList<>(item.expression); + GraphTargetItem lastItem = null; + if (!ex.isEmpty()) { + lastItem = ex.remove(ex.size() - 1); + while (lastItem instanceof CommaExpressionItem) { + CommaExpressionItem cei = (CommaExpressionItem) lastItem; + ex.addAll(cei.commands); + lastItem = ex.remove(ex.size() - 1); + } + whileExpr.addAll(generateToInsList(localData, ex)); + } + List dowhileBody = generateToInsList(localData, item.commands); + List labelBody = new ArrayList<>(); + labelBody.add(ins(AVM2Instructions.Label)); + int labelBodyLen = insToBytes(labelBody).length; + + AVM2Instruction forwardJump = ins(AVM2Instructions.Jump, labelBodyLen); + ret.add(forwardJump); + ret.addAll(labelBody); + ret.addAll(dowhileBody); + int dowhileBodyLen = insToBytes(dowhileBody).length; + whileExpr.addAll(toInsList(condition(localData, lastItem, 0))); + int dowhileExprLen = insToBytes(whileExpr).length; + whileExpr.get(whileExpr.size() - 1).operands[0] = -(dowhileExprLen + dowhileBodyLen + labelBodyLen); //Assuming last is if instruction + ret.addAll(whileExpr); + fixLoop(dowhileBody, dowhileBodyLen + dowhileExprLen, dowhileBodyLen, item.loop.id); + return ret; + } + + public List generate(SourceGeneratorLocalData localData, WithAVM2Item item) throws CompilationException { + + List ret = new ArrayList<>(); + ret.addAll(item.scope.toSource(localData, this)); + Reference tempReg = new Reference<>(0); + ret.addAll(AssignableAVM2Item.dupSetTemp(localData, this, tempReg)); + localData.scopeStack.add(new WithObjectAVM2Item(null, null, new LocalRegAVM2Item(null, null, tempReg.getVal(), null))); + ret.add(ins(AVM2Instructions.PushWith)); + ret.addAll(generate(localData, item.items)); + ret.add(ins(AVM2Instructions.PopScope)); + ret.addAll(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempReg))); + localData.scopeStack.remove(localData.scopeStack.size() - 1); + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, ForItem item) throws CompilationException { + List ret = new ArrayList<>(); + List forExpr = new ArrayList<>(); + + List ex = new ArrayList<>(); + if (item.expression != null) { + ex.add(item.expression); + } else { + ex.add(new BooleanAVM2Item(null, null, true)); + } + GraphTargetItem lastItem = null; + if (!ex.isEmpty()) { + lastItem = ex.remove(ex.size() - 1); + while (lastItem instanceof CommaExpressionItem) { + CommaExpressionItem cei = (CommaExpressionItem) lastItem; + ex.addAll(cei.commands); + lastItem = ex.remove(ex.size() - 1); + } + forExpr.addAll(generateToInsList(localData, ex)); + } + List forBody = generateToInsList(localData, item.commands); + List forFinalCommands = generateToInsList(localData, item.finalCommands); + + ret.addAll(generateToInsList(localData, item.firstCommands)); + + AVM2Instruction forwardJump = ins(AVM2Instructions.Jump, 0); + ret.add(forwardJump); + forBody.add(0, ins(AVM2Instructions.Label)); + ret.addAll(forBody); + ret.addAll(forFinalCommands); + int forBodyLen = insToBytes(forBody).length; + int forFinalCLen = insToBytes(forFinalCommands).length; + forwardJump.operands[0] = forBodyLen + forFinalCLen; + forExpr.addAll(toInsList(condition(localData, lastItem, 0))); + int forExprLen = insToBytes(forExpr).length; + forExpr.get(forExpr.size() - 1).operands[0] = -(forExprLen + forBodyLen + forFinalCLen); //Assuming last is if instruction + ret.addAll(forExpr); + fixLoop(forBody, forBodyLen + forFinalCLen + forExprLen, forBodyLen, item.loop.id); + return ret; + } + + private long uniqLast = 0; + + public String uniqId() { + uniqLast++; + return "" + uniqLast; + } + + @Override + public List generate(SourceGeneratorLocalData localData, SwitchItem item) throws CompilationException { + List ret = new ArrayList<>(); + Reference switchedReg = new Reference<>(0); + AVM2Instruction forwardJump = ins(AVM2Instructions.Jump, 0); + ret.add(forwardJump); + + int defIndex = -1; + + for (int i = item.caseValues.size() - 1; i >= 0; i--) { + if (item.caseValues.get(i) instanceof DefaultItem) { + defIndex = i; + break; + } + } + + List cases = new ArrayList<>(); + cases.addAll(toInsList(new IntegerValueAVM2Item(null, null, (long) defIndex).toSource(localData, this))); + int cLen = insToBytes(cases).length; + List caseLast = new ArrayList<>(); + caseLast.add(0, ins(AVM2Instructions.Jump, cLen)); + caseLast.addAll(0, toInsList(new IntegerValueAVM2Item(null, null, (long) defIndex).toSource(localData, this))); + int cLastLen = insToBytes(caseLast).length; + caseLast.add(0, ins(AVM2Instructions.Jump, cLastLen)); + cases.addAll(0, caseLast); + + List preCases = new ArrayList<>(); + preCases.addAll(toInsList(item.switchedObject.toSource(localData, this))); + preCases.addAll(toInsList(AssignableAVM2Item.setTemp(localData, this, switchedReg))); + + for (int i = item.caseValues.size() - 1; i >= 0; i--) { + if (item.caseValues.get(i) instanceof DefaultItem) { + continue; + } + List sub = new ArrayList<>(); + sub.addAll(toInsList(new IntegerValueAVM2Item(null, null, (long) i).toSource(localData, this))); + sub.add(ins(AVM2Instructions.Jump, insToBytes(cases).length)); + int subLen = insToBytes(sub).length; + + cases.addAll(0, sub); + cases.add(0, ins(AVM2Instructions.IfStrictNe, subLen)); + cases.addAll(0, toInsList(AssignableAVM2Item.getTemp(localData, this, switchedReg))); + cases.addAll(0, toInsList(item.caseValues.get(i).toSource(localData, this))); + } + cases.addAll(0, preCases); + + AVM2Instruction lookupOp = new AVM2Instruction(0, AVM2Instructions.LookupSwitch, new int[item.caseValues.size() + 1 + 1]); + cases.addAll(toInsList(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(switchedReg)))); + List bodies = new ArrayList<>(); + List bodiesOffsets = new ArrayList<>(); + int defOffset; + int casesLen = insToBytes(cases).length; + bodies.add(0, ins(AVM2Instructions.Label)); + bodies.add(ins(new BreakJumpIns(item.loop.id), 0)); //There could be two breaks when default clause ends with break, but official compiler does this too, so who cares... + defOffset = -(insToBytes(bodies).length + casesLen); + for (int i = item.caseCommands.size() - 1; i >= 0; i--) { + bodies.addAll(0, generateToInsList(localData, item.caseCommands.get(i))); + bodies.add(0, ins(AVM2Instructions.Label)); + bodiesOffsets.add(0, -(insToBytes(bodies).length + casesLen)); + } + lookupOp.operands[0] = defOffset; + lookupOp.operands[1] = item.valuesMapping.size(); + for (int i = 0; i < item.valuesMapping.size(); i++) { + lookupOp.operands[2 + i] = bodiesOffsets.get(item.valuesMapping.get(i)); + } + + forwardJump.operands[0] = insToBytes(bodies).length; + ret.addAll(bodies); + ret.addAll(cases); + ret.add(lookupOp); + fixSwitch(toInsList(ret), insToBytes(toInsList(ret)).length, uniqLast); + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, NotItem item) throws CompilationException { + /*if (item.getOriginal() instanceof Inverted) { + GraphTargetItem norig = ((Inverted) item).invert(); + return norig.toSource(localData, this); + }*/ + List ret = new ArrayList<>(); + ret.addAll(item.getOriginal().toSource(localData, this)); + ret.add(ins(AVM2Instructions.Not)); + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, DuplicateItem item) { + List ret = new ArrayList<>(); + ret.add(ins(AVM2Instructions.Dup)); + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, BreakItem item) { + List ret = new ArrayList<>(); + AVM2Instruction abreak = ins(new BreakJumpIns(item.loopId), 0); + ret.add(abreak); + return ret; + } + + public List generate(SourceGeneratorLocalData localData, FunctionAVM2Item item) throws CompilationException { + List ret = new ArrayList<>(); + int scope = 0; + if (!item.functionName.isEmpty()) { + ret.add(ins(AVM2Instructions.NewObject, 0)); + ret.add(ins(AVM2Instructions.PushWith)); + scope = localData.scopeStack.size(); + localData.scopeStack.add(new PropertyAVM2Item(null, item.functionName, abcIndex, new ArrayList<>(), localData.callStack)); + } + AVM2ConstantPool constants = abcIndex.getSelectedAbc().constants; + ret.add(ins(AVM2Instructions.NewFunction, method(false, constants.getStringId(item.functionName, true), true, false, localData.callStack, localData.pkg, item.needsActivation, item.subvariables, 0 /*Set later*/, item.hasRest, item.line, localData.currentClass, null, false, localData, item.paramTypes, item.paramNames, item.paramValues, item.body, item.retType))); + if (!item.functionName.isEmpty()) { + ret.add(ins(AVM2Instructions.Dup)); + ret.add(ins(AVM2Instructions.GetScopeObject, scope)); + ret.add(ins(AVM2Instructions.Swap)); + ret.add(ins(AVM2Instructions.SetProperty, constants.getMultinameId(Multiname.createQName(false, constants.getStringId(item.functionName, true), constants.getNamespaceId(Namespace.KIND_PACKAGE, localData.pkg, 0, true)), true))); + ret.add(ins(AVM2Instructions.PopScope)); + localData.scopeStack.remove(localData.scopeStack.size() - 1); + } + return ret; + } + + private static int currentFinId = 1; + + private static int finId() { + return currentFinId++; + } + + public List generate(SourceGeneratorLocalData localData, TryAVM2Item item) throws CompilationException { + List ret = new ArrayList<>(); + + boolean newFinallyReg = false; + List newex = new ArrayList<>(); + int aloneFinallyEx = -1; + int finallyEx = -1; + for (NameAVM2Item e : item.catchExceptions2) { + ABCException aex = new ABCException(); + aex.name_index = abcIndex.getSelectedAbc().constants.getMultinameId(Multiname.createQName(false, abcIndex.getSelectedAbc().constants.getStringId(e.getVariableName(), true), abcIndex.getSelectedAbc().constants.getNamespaceId(Namespace.KIND_PACKAGE, "", 0, true)), true); + aex.type_index = typeName(localData, e.type); + newex.add(aex); + } + int finId = 0; + if (item.finallyCommands != null) { + if (item.catchExceptions2.isEmpty()) { + ABCException aex = new ABCException(); + aex.name_index = 0; + aex.type_index = 0; + newex.add(aex); + aloneFinallyEx = newex.size() - 1; + } + ABCException aex = new ABCException(); + aex.name_index = 0; + aex.type_index = 0; + newex.add(aex); + finallyEx = newex.size() - 1; + if (localData.finallyRegister == -1) { + localData.finallyRegister = getFreeRegister(localData); + killRegister(localData, localData.finallyRegister); //reuse for catches + newFinallyReg = true; + } + finId = finId(); + } + + if (finallyEx > -1) { + localData.finallyCatches.add(finId); + } + List tryCmds = generateToInsList(localData, item.tryCommands); + + //int i = firstId + item.catchCommands.size() - 1; + List catches = new ArrayList<>(); + Reference tempReg = new Reference<>(0); + + List currentExceptionIds = new ArrayList<>(); + List> catchCmds = new ArrayList<>(); + for (int c = 0; c < item.catchCommands.size(); c++) { + int i = localData.exceptions.size(); + localData.exceptions.add(newex.get(c)); + + currentExceptionIds.add(i); + + //Reference tempReg=new Reference<>(0); + List catchCmd = new ArrayList<>(); + catchCmd.add(ins(AVM2Instructions.NewCatch, i)); + catchCmd.addAll(toInsList(AssignableAVM2Item.dupSetTemp(localData, this, tempReg))); + catchCmd.add(ins(AVM2Instructions.Dup)); + catchCmd.add(ins(AVM2Instructions.PushScope)); + catchCmd.add(ins(AVM2Instructions.Swap)); + catchCmd.add(ins(AVM2Instructions.SetSlot, 1)); + + for (AssignableAVM2Item a : item.catchVariables.get(c)) { + GraphTargetItem r = a; + if (r instanceof UnresolvedAVM2Item) { + r = ((UnresolvedAVM2Item) r).resolvedRoot; + } + if (r instanceof NameAVM2Item) { + NameAVM2Item n = (NameAVM2Item) r; + if (item.catchExceptions2.get(c).getVariableName().equals(n.getVariableName())) { + n.setSlotScope(localData.scopeStack.size()); + } + } + } + localData.scopeStack.add(new LocalRegAVM2Item(null, null, tempReg.getVal(), null)); + catchCmd.addAll(generateToInsList(localData, item.catchCommands.get(c))); + localData.scopeStack.remove(localData.scopeStack.size() - 1); + catchCmd.add(ins(AVM2Instructions.PopScope)); + catchCmd.addAll(toInsList(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempReg)))); + catchCmds.add(catchCmd); + } + for (int c = item.catchCommands.size() - 1; c >= 0; c--) { + List preCatches = new ArrayList<>(); + /*preCatches.add(ins(AVM2Instructions.GetLocal0)); + preCatches.add(ins(AVM2Instructions.PushScope)); + preCatches.add(AssignableAVM2Item.generateGetLoc(localData.activationReg)); + preCatches.add(ins(AVM2Instructions.PushScope));*/ + for (GraphTargetItem s : localData.scopeStack) { + preCatches.addAll(toInsList(s.toSource(localData, this))); + if (s instanceof WithObjectAVM2Item) { + preCatches.add(ins(AVM2Instructions.PushWith)); + } else { + preCatches.add(ins(AVM2Instructions.PushScope)); + } + } + + //catchCmds.add(catchCmd); + preCatches.addAll(catchCmds.get(c)); + catches.addAll(0, preCatches); + catches.add(0, new ExceptionMarkAVM2Instruction(currentExceptionIds.get(c), MARK_E_TARGET)); + catches.add(0, ins(AVM2Instructions.Jump, insToBytes(catches).length)); + } + + if (aloneFinallyEx > -1) { + localData.exceptions.add(newex.get(aloneFinallyEx)); + aloneFinallyEx = localData.exceptions.size() - 1; + + } + if (finallyEx > -1) { + localData.exceptions.add(newex.get(finallyEx)); + finallyEx = localData.exceptions.size() - 1; + } + + for (int i : currentExceptionIds) { + ret.add(new ExceptionMarkAVM2Instruction(i, MARK_E_START)); + } + if (aloneFinallyEx > -1) { + ret.add(new ExceptionMarkAVM2Instruction(aloneFinallyEx, MARK_E_START)); + } + if (finallyEx > -1) { + ret.add(new ExceptionMarkAVM2Instruction(finallyEx, MARK_E_START)); + } + + ret.addAll(tryCmds); + + for (int i : currentExceptionIds) { + ret.add(new ExceptionMarkAVM2Instruction(i, MARK_E_END)); + } + if (aloneFinallyEx > -1) { + ret.add(new ExceptionMarkAVM2Instruction(aloneFinallyEx, MARK_E_END)); + } + + if (aloneFinallyEx > -1) { + List preCatches = new ArrayList<>(); + for (GraphTargetItem s : localData.scopeStack) { + preCatches.addAll(toInsList(s.toSource(localData, this))); + if (s instanceof WithObjectAVM2Item) { + preCatches.add(ins(AVM2Instructions.PushWith)); + } else { + preCatches.add(ins(AVM2Instructions.PushScope)); + } + } + preCatches.add(ins(AVM2Instructions.NewCatch, aloneFinallyEx)); + preCatches.addAll(toInsList(AssignableAVM2Item.dupSetTemp(localData, this, tempReg))); + preCatches.add(ins(AVM2Instructions.PushScope)); + preCatches.add(ins(AVM2Instructions.Throw)); + preCatches.add(ins(AVM2Instructions.PopScope)); + preCatches.addAll(toInsList(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempReg)))); + catches.add(ins(AVM2Instructions.Jump, insToBytes(preCatches).length)); + catches.add(new ExceptionMarkAVM2Instruction(aloneFinallyEx, MARK_E_TARGET)); + catches.addAll(preCatches); + } + AVM2Instruction finSwitch = null; + AVM2Instruction pushDefIns = ins(AVM2Instructions.PushByte, 0); + + int defPos = 0; + if (finallyEx > -1) { + List preCatches = new ArrayList<>(); + preCatches.add(0, new ExceptionMarkAVM2Instruction(finallyEx, MARK_E_TARGET)); + for (GraphTargetItem s : localData.scopeStack) { + preCatches.addAll(toInsList(s.toSource(localData, this))); + if (s instanceof WithObjectAVM2Item) { + preCatches.add(ins(AVM2Instructions.PushWith)); + } else { + preCatches.add(ins(AVM2Instructions.PushScope)); + } + } + preCatches.add(ins(AVM2Instructions.NewCatch, finallyEx)); + preCatches.addAll(toInsList(AssignableAVM2Item.dupSetTemp(localData, this, tempReg))); + preCatches.add(ins(AVM2Instructions.PushScope)); + preCatches.add(ins(AVM2Instructions.PopScope)); + Reference tempReg2 = new Reference<>(0); + preCatches.add(ins(AVM2Instructions.Kill, tempReg.getVal())); + preCatches.add(ins(AVM2Instructions.CoerceA)); + preCatches.addAll(toInsList(AssignableAVM2Item.setTemp(localData, this, tempReg2))); + preCatches.add(pushDefIns); + + List finallySwitchCmds = new ArrayList<>(); + + finSwitch = new AVM2Instruction(0, AVM2Instructions.LookupSwitch, new int[1 + 1 + 1]); + finSwitch.operands[0] = finSwitch.getBytesLength(); + finSwitch.operands[1] = 0; //switch cnt + + List preFinallySwitch = new ArrayList<>(); + preFinallySwitch.add(ins(AVM2Instructions.Label)); + preFinallySwitch.add(ins(AVM2Instructions.Pop)); + int preFinallySwitchLen = insToBytes(preFinallySwitch).length; + + finallySwitchCmds.add(ins(AVM2Instructions.Label)); + finallySwitchCmds.addAll(toInsList(AssignableAVM2Item.getTemp(localData, this, tempReg2))); + finallySwitchCmds.add(ins(AVM2Instructions.Kill, tempReg2.getVal())); + finallySwitchCmds.add(ins(AVM2Instructions.Throw)); + finallySwitchCmds.add(ins(AVM2Instructions.PushByte, 255)); + finallySwitchCmds.add(ins(AVM2Instructions.PopScope)); + finallySwitchCmds.add(ins(AVM2Instructions.Kill, tempReg.getVal())); + + int finSwitchLen = insToBytes(finallySwitchCmds).length; + + preCatches.add(ins(AVM2Instructions.Jump, preFinallySwitchLen + finSwitchLen)); + AVM2Instruction fjump = ins(AVM2Instructions.Jump, 0); + fjump.operands[0] = insToBytes(preCatches).length + preFinallySwitchLen + finSwitchLen; + + preCatches.add(0, fjump); + preCatches.add(0, new ExceptionMarkAVM2Instruction(finallyEx, MARK_E_END)); + preCatches.add(0, ins(AVM2Instructions.PushByte, 255)); + + finallySwitchCmds.add(new ExceptionMarkAVM2Instruction(finallyEx, MARK_E_FINALLYPART)); + + int oldReg = localData.finallyRegister; + localData.finallyRegister = getFreeRegister(localData); + Integer cnt = localData.finallyCounter.get(finId); + if (cnt == null) { + cnt = -1; + } + defPos = cnt; + cnt++; //Skip default clause (throw) + localData.finallyCounter.put(finId, cnt); + finallySwitchCmds.addAll(generateToInsList(localData, item.finallyCommands)); + killRegister(localData, localData.finallyRegister); + localData.finallyRegister = oldReg; + finSwitchLen = insToBytes(finallySwitchCmds).length; + + finSwitch.operands[2] = -finSwitchLen; + preCatches.addAll(preFinallySwitch); + preCatches.addAll(finallySwitchCmds); + preCatches.add(finSwitch); + + catches.addAll(preCatches); + AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempReg, tempReg2)); + } + + ret.addAll(catches); + //localData.exceptions.addAll(newex); + + if (finallyEx > -1) { + localData.finallyCatches.remove(localData.finallyCatches.size() - 1); + } + if (newFinallyReg) { + localData.finallyRegister = -1; + killRegister(localData, localData.finallyRegister); + } + int pos = 0; + int finallyPos = 0; + int switchPos = 0; + for (int s = 0; s < ret.size(); s++) { + GraphSourceItem src = ret.get(s); + if (src == finSwitch) { + switchPos = pos; + } + if (src instanceof AVM2Instruction) { + AVM2Instruction ins = (AVM2Instruction) src; + if (ins instanceof ExceptionMarkAVM2Instruction) { + ExceptionMarkAVM2Instruction em = (ExceptionMarkAVM2Instruction) ins; + if (em.exceptionId == finallyEx && em.markType == MARK_E_FINALLYPART) { + finallyPos = pos; + ret.remove(s); + s--; + continue; + } + } + pos += ins.getBytesLength(); + } + + } + + if (finSwitch != null) { + pos = 0; + int defLoc = finSwitch.operands[2]; + List switchLoc = new ArrayList<>(); + boolean wasDef = false; + for (int s = 0; s < ret.size(); s++) { + GraphSourceItem src = ret.get(s); + if (src instanceof AVM2Instruction) { + AVM2Instruction ins = (AVM2Instruction) src; + if (ins.definition instanceof FinallyJumpIns) { + FinallyJumpIns fji = (FinallyJumpIns) ins.definition; + if (fji.getClauseId() == finId) { + List bet = new ArrayList<>(); + bet.add(ins(AVM2Instructions.Label)); + bet.add(ins(AVM2Instructions.Pop)); + int betLen = insToBytes(bet).length; + if (wasDef) { + ins.operands[0] = 0; + } else { + ins.operands[0] = finallyPos - (pos + ins.getBytesLength()); + } + ins.definition = AVM2Code.instructionSet[AVM2Instructions.Jump]; + switchLoc.add(pos + ins.getBytesLength() + betLen - switchPos); + } + } + pos += ins.getBytesLength(); + } + if (defPos == switchLoc.size() - 1) { + switchLoc.add(defLoc); + wasDef = true; + } + } + finSwitch.operands = new int[1 + 1 + switchLoc.size()]; + pushDefIns.operands[0] = defPos + 1; + int afterLoc = finSwitch.getBytesLength(); + finSwitch.operands[0] = afterLoc; + finSwitch.operands[1] = switchLoc.size() - 1; + for (int j = 0; j < switchLoc.size(); j++) { + finSwitch.operands[2 + j] = switchLoc.get(j); + } + } + + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, ContinueItem item) { + List ret = new ArrayList<>(); + AVM2Instruction acontinue = ins(new ContinueJumpIns(item.loopId), 0); + ret.add(acontinue); + return ret; + } + + public List generate(SourceGeneratorLocalData localData, ReturnValueAVM2Item item) throws CompilationException { + List ret = new ArrayList<>(); + ret.addAll(item.value.toSource(localData, this)); + if (!localData.finallyCatches.isEmpty()) { + ret.add(ins(AVM2Instructions.CoerceA)); + ret.add(AssignableAVM2Item.generateSetLoc(localData.finallyRegister)); + for (int i = localData.finallyCatches.size() - 1; i >= 0; i--) { + if (i < localData.finallyCatches.size() - 1) { + ret.add(ins(AVM2Instructions.Label)); + } + int clauseId = localData.finallyCatches.get(i); + Integer cnt = localData.finallyCounter.get(clauseId); + if (cnt == null) { + cnt = -1; + } + cnt++; + localData.finallyCounter.put(clauseId, cnt); + ret.addAll(new IntegerValueAVM2Item(null, null, (long) cnt).toSource(localData, this)); + ret.add(ins(new FinallyJumpIns(clauseId), 0)); + ret.add(ins(AVM2Instructions.Label)); + ret.add(ins(AVM2Instructions.Pop)); + } + ret.add(ins(AVM2Instructions.Label)); + ret.add(AssignableAVM2Item.generateGetLoc(localData.finallyRegister)); + ret.add(ins(AVM2Instructions.Kill, localData.finallyRegister)); + } + ret.add(ins(AVM2Instructions.ReturnValue)); + return ret; + } + + public List generate(SourceGeneratorLocalData localData, ReturnVoidAVM2Item item) throws CompilationException { + List ret = new ArrayList<>(); + if (!localData.finallyCatches.isEmpty()) { + + for (int i = 0; i < localData.finallyCatches.size(); i++) { + if (i > 0) { + ret.add(ins(AVM2Instructions.Label)); + } + int clauseId = localData.finallyCatches.get(i); + Integer cnt = localData.finallyCounter.get(clauseId); + if (cnt == null) { + cnt = -1; + } + cnt++; + localData.finallyCounter.put(clauseId, cnt); + ret.addAll(new IntegerValueAVM2Item(null, null, (long) cnt).toSource(localData, this)); + ret.add(ins(new FinallyJumpIns(clauseId), 0)); + ret.add(ins(AVM2Instructions.Label)); + ret.add(ins(AVM2Instructions.Pop)); + } + ret.add(ins(AVM2Instructions.Label)); + } + ret.add(ins(AVM2Instructions.ReturnVoid)); + return ret; + } + + public List generate(SourceGeneratorLocalData localData, ThrowAVM2Item item) throws CompilationException { + List ret = new ArrayList<>(); + ret.addAll(item.value.toSource(localData, this)); + ret.add(ins(AVM2Instructions.Throw)); + return ret; + } + + private List generateToInsList(SourceGeneratorLocalData localData, List commands) throws CompilationException { + return toInsList(generate(localData, commands)); + } + + private List generateToActionList(SourceGeneratorLocalData localData, GraphTargetItem command) throws CompilationException { + return toInsList(command.toSource(localData, this)); + } + + @Override + public List generate(SourceGeneratorLocalData localData, List commands) throws CompilationException { + List ret = new ArrayList<>(); + for (GraphTargetItem item : commands) { + ret.addAll(item.toSourceIgnoreReturnValue(localData, this)); + } + return ret; + } + + public HashMap getRegisterVars(SourceGeneratorLocalData localData) { + return localData.registerVars; + } + + public void setRegisterVars(SourceGeneratorLocalData localData, HashMap value) { + localData.registerVars = value; + } + + public void setInFunction(SourceGeneratorLocalData localData, int value) { + localData.inFunction = value; + } + + public int isInFunction(SourceGeneratorLocalData localData) { + return localData.inFunction; + } + + public boolean isInMethod(SourceGeneratorLocalData localData) { + return localData.inMethod; + } + + public void setInMethod(SourceGeneratorLocalData localData, boolean value) { + localData.inMethod = value; + } + + public int getForInLevel(SourceGeneratorLocalData localData) { + return localData.forInLevel; + } + + public void setForInLevel(SourceGeneratorLocalData localData, int value) { + localData.forInLevel = value; + } + + public int getTempRegister(SourceGeneratorLocalData localData) { + HashMap registerVars = getRegisterVars(localData); + int tmpReg = 0; + for (int i = 0; i < 256; i++) { + if (!registerVars.containsValue(i)) { + tmpReg = i; + break; + } + } + return tmpReg; + } + + public AVM2SourceGenerator(AbcIndexing abc) { + this.abcIndex = abc; + } + + /*public ABC getABC() { + return abc; + }*/ + public void generateClass(List importedClasses, List cinitVariables, boolean cinitNeedsActivation, List cinit, List openedNamespaces, int namespace, int initScope, DottedChain pkg, ClassInfo classInfo, InstanceInfo instanceInfo, SourceGeneratorLocalData localData, boolean isInterface, String name, String superName, GraphTargetItem extendsVal, List implementsStr, GraphTargetItem iinit, List iinitVariables, boolean iinitNeedsActivation, List traitItems, Reference class_index) throws AVM2ParseException, CompilationException { + localData.currentClass = name; + localData.pkg = pkg; + localData.privateNs = abcIndex.getSelectedAbc().constants.getNamespaceId(Namespace.KIND_PRIVATE, pkg.toRawString() + ":" + name, 0, true); + localData.protectedNs = abcIndex.getSelectedAbc().constants.getNamespaceId(Namespace.KIND_PROTECTED, pkg.toRawString() + ":" + name, 0, true); + if (extendsVal == null && !isInterface) { + extendsVal = new TypeItem(DottedChain.OBJECT); + } + ParsedSymbol s = null; + + if (Configuration.handleSkinPartsAutomatically.get()) { + + Map skinParts = new HashMap<>(); + for (GraphTargetItem t : traitItems) { + String tname = null; + List>> tmetadata = null; + if (t instanceof MethodAVM2Item) { + tname = ((MethodAVM2Item) t).functionName; + tmetadata = ((MethodAVM2Item) t).metadata; + } else if (t instanceof SlotAVM2Item) { + tname = ((SlotAVM2Item) t).var; + tmetadata = ((SlotAVM2Item) t).metadata; + } else if (t instanceof ConstAVM2Item) { + tname = ((ConstAVM2Item) t).var; + tmetadata = ((ConstAVM2Item) t).metadata; + } + if (tname != null && tmetadata != null) { + for (Map.Entry> en : tmetadata) { + if ("SkinPart".equals(en.getKey())) { + boolean req = false; + if (en.getValue().containsKey("required")) { + if ("true".equals(en.getValue().get("required"))) { + req = true; + } + } + skinParts.put(tname, req); + } + } + } + } + if (!skinParts.isEmpty()) { + + //Merge parts from _skinParts attribute of parent class + GraphTargetItem parent = extendsVal; + if (parent instanceof UnresolvedAVM2Item) { + parent = ((UnresolvedAVM2Item) parent).resolved; + } + if (parent instanceof TypeItem) { + ClassIndex ci = abcIndex.findClass(parent); + if (ci != null) { + int mi = ci.abc.class_info.get(ci.index).cinit_index; + MethodBody pcinit = ci.abc.findBody(mi); + ConvertData d = new ConvertData(); + + List initt = new ArrayList<>(); + initt.add(ci.abc.class_info.get(ci.index).static_traits); + + try { + pcinit.convert(d, "-", ScriptExportMode.AS, true, mi, -1, ci.index, ci.abc, null, new ScopeStack(), GraphTextWriter.TRAIT_CLASS_INITIALIZER, new NulWriter(), new ArrayList<>(), initt, false); + //FIXME! Add skinparts from _skinParts attribute of parent class!!! + } catch (InterruptedException ex) { + Logger.getLogger(AVM2SourceGenerator.class.getName()).log(Level.SEVERE, "Getting parent skinparts interrupted", ex); + } + for (Trait t : ci.abc.class_info.get(ci.index).static_traits.traits) { + if (t instanceof TraitSlotConst) { + TraitSlotConst tsc = (TraitSlotConst) t; + if (tsc.kindType == Trait.TRAIT_SLOT) { + if ("_skinParts".equals(tsc.getName(ci.abc).getName(ci.abc.constants, new ArrayList<>(), true))) { + if (d.assignedValues.containsKey(tsc)) { + if (d.assignedValues.get(tsc).value instanceof NewObjectAVM2Item) { + NewObjectAVM2Item no = (NewObjectAVM2Item) d.assignedValues.get(tsc).value; + for (NameValuePair nvp : no.pairs) { + skinParts.put(EcmaScript.toString(nvp.name.getResult()), EcmaScript.toBoolean(nvp.value.getResult())); + } + } + + } + } + } + } + } + + } + } + + /* + Add + override protected function get skinParts() : Object + { + return _skinParts; + } + */ + List getterBody = new ArrayList<>(); + UnresolvedAVM2Item sp = new UnresolvedAVM2Item(new ArrayList<>(), importedClasses, false, TypeItem.UNBOUNDED, 0, new DottedChain("_skinParts"), + null, openedNamespaces); + getterBody.add(new ReturnValueAVM2Item(null, null, sp)); + List subvars = new ArrayList<>(); + subvars.add(sp); + List> allopns = new ArrayList<>(); + allopns.add(openedNamespaces); + + GetterAVM2Item getter = new GetterAVM2Item(allopns, false, false, new ArrayList<>(), new NamespaceItem(pkg.toRawString() + ":" + name, Namespace.KIND_PROTECTED), isInterface, null, false, false, 0, + true, false, false, "skinParts", new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), + getterBody, subvars, new TypeItem("Object")); + + /* + Add + private static var _skinParts = {attr1:false, attr2:true}; + */ + List pairs = new ArrayList<>(); + for (String tname : skinParts.keySet()) { + pairs.add(new NameValuePair(new StringAVM2Item(null, null, tname), skinParts.get(tname) ? new TrueItem(null, null) : new FalseItem(null, null))); + } + + NewObjectAVM2Item sltVal = new NewObjectAVM2Item(null, null, pairs); + + SlotAVM2Item slt = new SlotAVM2Item( + new ArrayList<>(), new NamespaceItem(pkg.toRawString() + ":" + name, Namespace.KIND_PRIVATE), + null, true, "_skinParts", new TypeItem("Object"), sltVal, 0); + + traitItems.add(0, slt); + traitItems.add(getter); + + } + } + + Trait[] it = generateTraitsPhase1(importedClasses, openedNamespaces, name, superName, false, localData, traitItems, instanceInfo.instance_traits, class_index); + Trait[] st = generateTraitsPhase1(importedClasses, openedNamespaces, name, superName, true, localData, traitItems, classInfo.static_traits, class_index); + generateTraitsPhase2(importedClasses, pkg, traitItems, it, openedNamespaces, localData); + generateTraitsPhase2(importedClasses, pkg, traitItems, st, openedNamespaces, localData); + abcIndex.refreshSelected(); + generateTraitsPhase3(importedClasses, initScope, isInterface, name, superName, false, localData, traitItems, instanceInfo.instance_traits, it, new HashMap<>(), class_index); + generateTraitsPhase3(importedClasses, initScope, isInterface, name, superName, true, localData, traitItems, classInfo.static_traits, st, new HashMap<>(), class_index); + int init; + if (iinit == null || isInterface) { + instanceInfo.iinit_index = init = method(false, 0, false, isInterface, new ArrayList<>(), pkg, false, new ArrayList<>(), initScope + 1, false, 0, isInterface ? null : name, extendsVal != null ? extendsVal.toString() : null, true, localData, new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), TypeItem.UNBOUNDED/*?? FIXME*/); + } else { + MethodAVM2Item m = (MethodAVM2Item) iinit; + instanceInfo.iinit_index = init = method(false, str(pkg.toRawString() + ":" + name + "/" + name), false, false, new ArrayList<>(), pkg, m.needsActivation, m.subvariables, initScope + 1, m.hasRest, m.line, name, extendsVal != null ? extendsVal.toString() : null, true, localData, m.paramTypes, m.paramNames, m.paramValues, m.body, TypeItem.UNBOUNDED/*?? FIXME*/); + } + + //Class initializer + int cinit_index = method(true, str(""), false, false, new ArrayList<>(), pkg, cinitNeedsActivation, cinitVariables, initScope + (implementsStr.isEmpty() ? 0 : 1), false, 0, isInterface ? null : name, superName, false, localData, new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), cinit, TypeItem.UNBOUNDED); + MethodBody cinitBody = abcIndex.getSelectedAbc().findBody(cinit_index); + + List sinitcode = new ArrayList<>(); + List initcode = new ArrayList<>(); + for (GraphTargetItem ti : traitItems) { + if ((ti instanceof SlotAVM2Item) || (ti instanceof ConstAVM2Item)) { + GraphTargetItem val = null; + boolean isStatic = false; + int ns = -1; + String tname = null; + boolean isConst = false; + if (ti instanceof SlotAVM2Item) { + val = ((SlotAVM2Item) ti).value; + isStatic = ((SlotAVM2Item) ti).isStatic(); + ns = genNs(importedClasses, pkg, ((SlotAVM2Item) ti).pkg, openedNamespaces, localData, ((SlotAVM2Item) ti).line); + tname = ((SlotAVM2Item) ti).var; + } + if (ti instanceof ConstAVM2Item) { + val = ((ConstAVM2Item) ti).value; + isStatic = ((ConstAVM2Item) ti).isStatic(); + ns = genNs(importedClasses, pkg, ((ConstAVM2Item) ti).pkg, openedNamespaces, localData, ((ConstAVM2Item) ti).line); + tname = ((ConstAVM2Item) ti).var; + isConst = true; + } + if (isStatic && val != null) { + sinitcode.add(ins(AVM2Instructions.FindProperty, traitName(ns, tname))); + localData.isStatic = true; + sinitcode.addAll(toInsList(val.toSource(localData, this))); + sinitcode.add(ins(isConst ? AVM2Instructions.InitProperty : AVM2Instructions.SetProperty, traitName(ns, tname))); + } + if (!isStatic && val != null) { + //do not init basic values, that can be stored in trait + if (!(val instanceof IntegerValueAVM2Item) && !(val instanceof StringAVM2Item) && !(val instanceof BooleanAVM2Item) && !(val instanceof NullAVM2Item) && !(val instanceof UndefinedAVM2Item)) { + initcode.add(ins(AVM2Instructions.GetLocal0)); + localData.isStatic = false; + initcode.addAll(toInsList(val.toSource(localData, this))); + initcode.add(ins(isConst ? AVM2Instructions.InitProperty : AVM2Instructions.SetProperty, traitName(ns, tname))); + } + } + } + } + MethodBody initBody = null; + if (!isInterface) { + initBody = abcIndex.getSelectedAbc().findBody(init); + initBody.insertAll(iinit == null ? 0 : 2, initcode);//after getlocal0,pushscope + + if (cinitBody.getCode().code.get(cinitBody.getCode().code.size() - 1).definition instanceof ReturnVoidIns) { + cinitBody.insertAll(2, sinitcode); //after getlocal0,pushscope + } + } + cinitBody.markOffsets(); + cinitBody.autoFillStats(abcIndex.getSelectedAbc(), initScope + (implementsStr.isEmpty() ? 0 : 1), true); + + classInfo.cinit_index = cinit_index; + if (initBody != null) { + initBody.autoFillStats(abcIndex.getSelectedAbc(), initScope + 1, true); + } + instanceInfo.interfaces = new int[implementsStr.size()]; + for (int i = 0; i < implementsStr.size(); i++) { + instanceInfo.interfaces[i] = superIntName(localData, implementsStr.get(i)); + } + + } + + @Override + public List generate(SourceGeneratorLocalData localData, CommaExpressionItem item) throws CompilationException { + if (item.commands.isEmpty()) { + return new ArrayList<>(); + } + + //We need to handle commands and last expression separately, otherwise last expression result will be popped + List cmds = new ArrayList<>(item.commands); + GraphTargetItem lastExpr = cmds.remove(cmds.size() - 1); + List ret = new ArrayList<>(); + ret.addAll(generate(localData, cmds)); + ret.addAll(lastExpr.toSource(localData, this)); + return ret; + } + + public int generateClass(int namespace, ClassInfo ci, InstanceInfo ii, int initScope, DottedChain pkg, SourceGeneratorLocalData localData, AVM2Item cls, Reference class_index) throws AVM2ParseException, CompilationException { + /*ClassInfo ci = new ClassInfo(); + InstanceInfo ii = new InstanceInfo(); + abc.class_info.add(ci); + abc.instance_info.add(ii); + */ + if (cls instanceof ClassAVM2Item) { + ClassAVM2Item cai = (ClassAVM2Item) cls; + //TODO: iinit variables, iinit activation + generateClass(cai.importedClasses, cai.cinitVariables, cai.cinitActivation, cai.staticInit, + cai.openedNamespaces, + namespace, + initScope, pkg, ci, ii, + localData, false, + cai.className, cai.extendsOp == null ? "Object" : cai.extendsOp.toString(), + cai.extendsOp, cai.implementsOp, cai.iinit, + cai.iinitVariables, cai.iinitActivation, cai.traits, class_index + ); + if (!cai.isDynamic) { + ii.flags |= InstanceInfo.CLASS_SEALED; + } + if (cai.isFinal) { + ii.flags |= InstanceInfo.CLASS_FINAL; + } + ii.flags |= InstanceInfo.CLASS_PROTECTEDNS; + ii.protectedNS = abcIndex.getSelectedAbc().constants.getNamespaceId(Namespace.KIND_PROTECTED, pkg.toRawString() + ":" + cai.className, 0, true); + } + if (cls instanceof InterfaceAVM2Item) { + InterfaceAVM2Item iai = (InterfaceAVM2Item) cls; + ii.flags |= InstanceInfo.CLASS_INTERFACE; + ii.flags |= InstanceInfo.CLASS_SEALED; + generateClass(iai.importedClasses, new ArrayList<>(), false, new ArrayList<>(), + iai.openedNamespaces, namespace, initScope, pkg, ci, ii, localData, true, iai.name, null, null, iai.superInterfaces, null, null, false, iai.methods, + class_index + ); + } + + return abcIndex.getSelectedAbc().instance_info.size() - 1; + } + + public int traitName(int namespace, String var) { + return abcIndex.getSelectedAbc().constants.getMultinameId(Multiname.createQName(false, str(var), namespace), true); + } + + public int typeName(SourceGeneratorLocalData localData, GraphTargetItem type) throws CompilationException { + if (type instanceof UnboundedTypeItem) { + return 0; + } + if (("" + type).equals("*")) { + return 0; + } + + return resolveType(localData, type, abcIndex); + /* + TypeItem nameItem = (TypeItem) type; + name = nameItem.fullTypeName; + if (name.contains(".")) { + pkg = name.substring(0, name.lastIndexOf('.')); + name = name.substring(name.lastIndexOf('.') + 1); + } + if (!nameItem.subtypes.isEmpty()) { //It's vector => TypeName + List params = new ArrayList<>(); + for (GraphTargetItem p : nameItem.subtypes) { + params.add(typeName(localData, p));//abc.getLastAbc().constants.getMultinameId(new Multiname(Multiname.QNAME, str(p), namespace(Namespace.KIND_PACKAGE, ppkg), 0, 0, new ArrayList()), true)); + } + int qname = abc.getLastAbc().constants.getMultinameId(new Multiname(Multiname.QNAME, str(name), namespace(Namespace.KIND_PACKAGE, pkg), 0, 0, new ArrayList()), true); + return abc.getLastAbc().constants.getMultinameId(new Multiname(Multiname.TYPENAME, 0, 0, 0, qname, params), true); + } else { + return abc.getLastAbc().constants.getMultinameId(new Multiname(Multiname.QNAME, str(name), namespace(Namespace.KIND_PACKAGE, pkg), 0, 0, new ArrayList()), true); + }*/ + } + + public int ident(GraphTargetItem name) { + if (name instanceof NameAVM2Item) { + return str(((NameAVM2Item) name).getVariableName()); + } + throw new RuntimeException("no ident"); //FIXME + } + + public int namespace(int nsKind, String name) { + return abcIndex.getSelectedAbc().constants.getNamespaceId(nsKind, str(name), 0, true); + } + + public int str(String name) { + return abcIndex.getSelectedAbc().constants.getStringId(name, true); + } + + public int propertyName(GraphTargetItem name) { + if (name instanceof NameAVM2Item) { + NameAVM2Item va = (NameAVM2Item) name; + return abcIndex.getSelectedAbc().constants.getMultinameId(Multiname.createQName(false, str(va.getVariableName()), namespace(Namespace.KIND_PACKAGE, "")), true); + } + throw new RuntimeException("no prop"); //FIXME + } + + public int getFreeRegister(SourceGeneratorLocalData localData) { + for (int i = 0;; i++) { + if (!localData.registerVars.containsValue(i)) { + localData.registerVars.put("__TEMP__" + i, i); + return i; + } + } + } + + public boolean killRegister(SourceGeneratorLocalData localData, int i) { + String key = null; + for (String k : localData.registerVars.keySet()) { + if (localData.registerVars.get(k) == i) { + key = k; + break; + } + } + if (key != null) { + localData.registerVars.remove(key); + return true; + } + return false; + } + + public int method(boolean isStatic, int name_index, boolean subMethod, boolean isInterface, List callStack, DottedChain pkg, boolean needsActivation, List subvariables, int initScope, boolean hasRest, int line, String className, String superType, boolean constructor, SourceGeneratorLocalData localData, List paramTypes, List paramNames, List paramValues, List body, GraphTargetItem retType) throws CompilationException { + //Reference hasArgs = new Reference<>(Boolean.FALSE); + //calcRegisters(localData,needsActivation,paramNames,subvariables,body, hasArgs); + SourceGeneratorLocalData newlocalData = new SourceGeneratorLocalData(new HashMap<>(), 1, true, 0); + newlocalData.currentClass = className; + newlocalData.pkg = localData.pkg; + newlocalData.callStack.addAll(localData.callStack); + newlocalData.traitUsages = localData.traitUsages; + newlocalData.currentScript = localData.currentScript; + newlocalData.documentClass = localData.documentClass; + newlocalData.privateNs = localData.privateNs; + newlocalData.protectedNs = localData.protectedNs; + newlocalData.isStatic = isStatic; + newlocalData.subMethod = subMethod; + localData = newlocalData; + + localData.activationReg = 0; + + for (int i = 0; i < subvariables.size(); i++) { + AssignableAVM2Item an = subvariables.get(i); + if (an instanceof UnresolvedAVM2Item) { + UnresolvedAVM2Item n = (UnresolvedAVM2Item) an; + if (n.resolved == null) { + String fullClass = localData.getFullClass(); + GraphTargetItem res = n.resolve(new TypeItem(fullClass), paramTypes, paramNames, abcIndex, callStack, subvariables); + if (res instanceof AssignableAVM2Item) { + subvariables.set(i, (AssignableAVM2Item) res); + } else { + subvariables.remove(i); + i--; + } + } + } + } + + for (int t = 0; t < paramTypes.size(); t++) { + GraphTargetItem an = paramTypes.get(t); + if (an instanceof UnresolvedAVM2Item) { + UnresolvedAVM2Item n = (UnresolvedAVM2Item) an; + if (n.resolved == null) { + String fullClass = localData.getFullClass(); + GraphTargetItem res = n.resolve(new TypeItem(fullClass), paramTypes, paramNames, abcIndex, callStack, subvariables); + paramTypes.set(t, res); + } + } + } + + boolean hasArguments = false; + List slotNames = new ArrayList<>(); + List slotTypes = new ArrayList<>(); + slotNames.add("--first"); + slotTypes.add("-"); + + int paramLine = 0; //? + + List registerNames = new ArrayList<>(); + List registerLines = new ArrayList<>(); + List registerTypes = new ArrayList<>(); + if (className != null) { + String fullClassName = pkg.add(className).toRawString(); + registerTypes.add(fullClassName); + localData.scopeStack.add(new LocalRegAVM2Item(null, null, registerNames.size(), null)); + registerNames.add("this"); + registerLines.add(0); //? + + } else { + registerTypes.add("global"); + registerNames.add("this"); + registerLines.add(0); //? + } + for (GraphTargetItem t : paramTypes) { + registerTypes.add(t.toString()); + slotTypes.add(t.toString()); + } + for (int i = 0; i < paramNames.size(); i++) { + registerLines.add(paramLine); + } + registerNames.addAll(paramNames); + slotNames.addAll(paramNames); + /*for (GraphTargetItem p : paramTypes) { + slotTypes.add("" + p); + }*/ + if (hasRest) { + registerTypes.add("Array"); + slotTypes.add("Array"); + } + localData.registerVars.clear(); + for (AssignableAVM2Item an : subvariables) { + if (an instanceof NameAVM2Item) { + NameAVM2Item n = (NameAVM2Item) an; + if (n.getVariableName().equals("arguments") & !n.isDefinition()) { + registerNames.add("arguments"); + registerTypes.add("Object"); + registerLines.add(0); //? + hasArguments = true; + break; + } + } + } + int paramRegCount = registerNames.size(); + + if (needsActivation) { + registerNames.add("+$activation"); + registerLines.add(0); //? + localData.activationReg = registerNames.size() - 1; + registerTypes.add("Object"); + localData.scopeStack.add(new LocalRegAVM2Item(null, null, localData.activationReg, null)); + } + + String mask = Configuration.registerNameFormat.get(); + mask = mask.replace("%d", "([0-9]+)"); + Pattern pat = Pattern.compile(mask); + + //Two rounds + for (int round = 1; round <= 2; round++) { + for (AssignableAVM2Item an : subvariables) { + if (an instanceof NameAVM2Item) { + NameAVM2Item n = (NameAVM2Item) an; + if (n.isDefinition() && !registerNames.contains(n.getVariableName())) { + if (!needsActivation || (n.getSlotScope() <= 0)) { + String varName = n.getVariableName(); + Matcher m = pat.matcher(varName); + //In first round, make all register that match standard loc_xx register + if ((round == 1) && (m.matches())) { + String regIndexStr = m.group(1); + int regIndex = Integer.parseInt(regIndexStr); + while (registerNames.size() <= regIndex + 1) { + String standardName = String.format(mask, registerNames.size() - 1); + registerNames.add(standardName); + registerTypes.add("*"); + slotNames.add(standardName); + slotTypes.add("*"); + registerLines.add(paramLine); + } + registerNames.set(regIndex, varName); + registerTypes.set(regIndex, varName); + slotNames.set(regIndex, varName); + slotTypes.set(regIndex, varName); + registerLines.set(regIndex, n.line); + } //in second round the rest + else if (round == 2 && !m.matches()) { + registerNames.add(n.getVariableName()); + registerTypes.add(n.type.toString()); + slotNames.add(n.getVariableName()); + slotTypes.add(n.type.toString()); + registerLines.add(n.line); + } + } + } + } + } + } + + int slotScope = subMethod ? 0 : 1; + + for (AssignableAVM2Item an : subvariables) { + if (an instanceof NameAVM2Item) { + NameAVM2Item n = (NameAVM2Item) an; + String variableName = n.getVariableName(); + if (variableName != null) { + boolean isThisOrSuper = variableName.equals("this") || variableName.equals("super"); + if (!isThisOrSuper && needsActivation) { + if (n.getSlotNumber() <= 0) { + n.setSlotNumber(slotNames.indexOf(variableName)); + n.setSlotScope(slotScope); + } + } else if (isThisOrSuper) { + n.setRegNumber(0); + } else { + n.setRegNumber(registerNames.indexOf(variableName)); + } + } + } + } + + for (int i = 0; i < registerNames.size(); i++) { + if (needsActivation && i > localData.activationReg) { + break; + } + localData.registerVars.put(registerNames.get(i), i); + } + List declarations = new ArrayList<>(); + loopn: + for (AssignableAVM2Item an : subvariables) { + if (an instanceof NameAVM2Item) { + NameAVM2Item n = (NameAVM2Item) an; + + if (needsActivation) { + if (n.getSlotScope() != slotScope) { + continue; + } else if (n.getSlotNumber() < paramRegCount) { + continue; + } + } + for (NameAVM2Item d : declarations) { + if (n.getVariableName() != null && n.getVariableName().equals(d.getVariableName())) { + continue loopn; + } + } + + for (GraphTargetItem it : body) { //search first level of commands + if (it instanceof NameAVM2Item) { + NameAVM2Item n2 = (NameAVM2Item) it; + if (n2.isDefinition() && n2.getAssignedValue() != null && n2.getVariableName().equals(n.getVariableName())) { + continue loopn; + } + if (!n2.isDefinition() && n2.getVariableName() != null && n2.getVariableName().equals(n.getVariableName())) { //used earlier than defined + break; + } + } + } + if (n.unresolved) { + continue; + } + if (n.redirect != null) { + continue; + } + if (n.getNs() != null) { + continue; + } + + String variableName = n.getVariableName(); + if ("this".equals(variableName) || "super".equals(variableName) || paramNames.contains(variableName) || "arguments".equals(variableName)) { + continue; + } + + NameAVM2Item d = new NameAVM2Item(n.type, n.line, n.getVariableName(), NameAVM2Item.getDefaultValue("" + n.type), true, n.openedNamespaces); + //no index + if (needsActivation) { + if (d.getSlotNumber() <= 0) { + d.setSlotNumber(n.getSlotNumber()); + d.setSlotScope(n.getSlotScope()); + } + } else { + d.setRegNumber(n.getRegNumber()); + } + declarations.add(d); + } + } + + int[] param_types = new int[paramTypes.size()]; + ValueKind[] optional = new ValueKind[paramValues.size()]; + //int[] param_names = new int[paramNames.size()]; + for (int i = 0; i < paramTypes.size(); i++) { + param_types[i] = typeName(localData, paramTypes.get(i)); + //param_names[i] = str(paramNames.get(i)); + } + + for (int i = 0; i < paramValues.size(); i++) { + optional[i] = getValueKind(Namespace.KIND_NAMESPACE/*FIXME*/, paramTypes.get(paramTypes.size() - paramValues.size() + i), paramValues.get(i)); + if (optional[i] == null) { + throw new CompilationException("Default value must be compiletime constant", line); + } + } + + MethodInfo mi = new MethodInfo(param_types, constructor ? 0 : typeName(localData, retType), name_index, 0, optional, new int[0]/*no param_names*/); + if (hasArguments) { + mi.setFlagNeed_Arguments(); + } + //No param names like in official + /* + if (!paramNames.isEmpty()) { + mi.setFlagHas_paramnames(); + }*/ + if (!paramValues.isEmpty()) { + mi.setFlagHas_optional(); + } + if (hasRest) { + mi.setFlagNeed_rest(); + } + + int mindex; + if (!isInterface) { + MethodBody mbody = new MethodBody(abcIndex.getSelectedAbc(), new Traits(), new byte[0], new ABCException[0]); + + if (needsActivation) { + int slotId = 1; + for (int i = 1; i < slotNames.size(); i++) { + TraitSlotConst tsc = new TraitSlotConst(); + tsc.slot_id = slotId++; + tsc.name_index = abcIndex.getSelectedAbc().constants.getMultinameId(Multiname.createQName(false, abcIndex.getSelectedAbc().constants.getStringId(slotNames.get(i), true), abcIndex.getSelectedAbc().constants.getNamespaceId(Namespace.KIND_PACKAGE_INTERNAL, pkg, 0, true)), true); + tsc.type_index = typeName(localData, new TypeItem(slotTypes.get(i))); + mbody.traits.traits.add(tsc); + } + for (int i = 1; i < paramRegCount; i++) { + NameAVM2Item param = new NameAVM2Item(new TypeItem(registerTypes.get(i)), 0, registerNames.get(i), null, false, new ArrayList<>()); + param.setRegNumber(i); + NameAVM2Item d = new NameAVM2Item(new TypeItem(registerTypes.get(i)), 0, registerNames.get(i), param, true, new ArrayList<>()); + d.setSlotScope(slotScope); + d.setSlotNumber(slotNames.indexOf(registerNames.get(i))); + declarations.add(d); + } + } + if (body != null) { + body.addAll(0, declarations); + } + + localData.exceptions = new ArrayList<>(); + localData.callStack.add(mbody); + List src = body == null ? new ArrayList<>() : generate(localData, body); + + mbody.method_info = abcIndex.getSelectedAbc().addMethodInfo(mi); + ArrayList mbodyCode = toInsList(src); + mbody.setCode(new AVM2Code(mbodyCode)); + + if (needsActivation) { + if (localData.traitUsages.containsKey(mbody)) { + List usages = localData.traitUsages.get(mbody); + for (int i = 0; i < mbody.traits.traits.size(); i++) { + if (usages.contains(i)) { + TraitSlotConst tsc = (TraitSlotConst) mbody.traits.traits.get(i); + GraphTargetItem type = TypeItem.UNBOUNDED; + if (tsc.type_index > 0) { + type = new TypeItem(abcIndex.getSelectedAbc().constants.getMultiname(tsc.type_index).getNameWithNamespace(abcIndex.getSelectedAbc().constants)); + } + NameAVM2Item d = new NameAVM2Item(type, 0, tsc.getName(abcIndex.getSelectedAbc()).getName(abcIndex.getSelectedAbc().constants, null, true), NameAVM2Item.getDefaultValue("" + type), true, new ArrayList<>()); + d.setSlotNumber(tsc.slot_id); + d.setSlotScope(slotScope); + mbodyCode.addAll(0, toInsList(d.toSourceIgnoreReturnValue(localData, this))); + } + } + } + + List acts = new ArrayList<>(); + acts.add(ins(AVM2Instructions.NewActivation)); + acts.add(ins(AVM2Instructions.Dup)); + acts.add(AssignableAVM2Item.generateSetLoc(localData.activationReg)); + acts.add(ins(AVM2Instructions.PushScope)); + + mbodyCode.addAll(0, acts); + } + + if (constructor) { + /* List abcs = new ArrayList<>(); + abcs.add(abc); + abcs.addAll(allABCs); + */ + int parentConstMinAC = 0; + + AbcIndexing.ClassIndex ci = abcIndex.findClass(new TypeItem(superType)); + + if (ci != null) { + MethodInfo pmi = ci.abc.method_info.get(ci.abc.instance_info.get(ci.index).iinit_index); + parentConstMinAC = pmi.param_types.length; + if (pmi.flagHas_optional()) { + parentConstMinAC -= pmi.optional.length; + } + } + int ac = -1; + for (AVM2Instruction ins : mbodyCode) { + if (ins.definition instanceof ConstructSuperIns) { + ac = ins.operands[0]; + if (parentConstMinAC > ac) { + throw new CompilationException("Parent constructor call requires different number of arguments", line); + } + + } + } + if (ac == -1) { + if (parentConstMinAC == 0) { + mbodyCode.add(0, new AVM2Instruction(0, AVM2Instructions.GetLocal0, null)); + mbodyCode.add(1, new AVM2Instruction(0, AVM2Instructions.ConstructSuper, new int[]{0})); + + } else { + throw new CompilationException("Parent constructor must be called", line); + } + } + } + for (int i = 1; i < registerNames.size(); i++) { + mbodyCode.add(i - 1, ins(AVM2Instructions.Debug, 1, str(registerNames.get(i)), i - 1, (int) registerLines.get(i))); + } + if (!subMethod) { + mbodyCode.add(0, new AVM2Instruction(0, AVM2Instructions.GetLocal0, null)); + mbodyCode.add(1, new AVM2Instruction(0, AVM2Instructions.PushScope, null)); + } + boolean addRet = false; + if (!mbodyCode.isEmpty()) { + InstructionDefinition lastDef = mbodyCode.get(mbodyCode.size() - 1).definition; + if (!((lastDef instanceof ReturnVoidIns) || (lastDef instanceof ReturnValueIns))) { + addRet = true; + } + } else { + addRet = true; + } + if (addRet) { + if (retType.toString().equals("*") || retType.toString().equals("void") || constructor) { + mbodyCode.add(new AVM2Instruction(0, AVM2Instructions.ReturnVoid, null)); + } else { + mbodyCode.add(new AVM2Instruction(0, AVM2Instructions.PushUndefined, null)); + mbodyCode.add(new AVM2Instruction(0, AVM2Instructions.ReturnValue, null)); + } + } + mbody.exceptions = localData.exceptions.toArray(new ABCException[localData.exceptions.size()]); + int offset = 0; + for (int i = 0; i < mbodyCode.size(); i++) { + AVM2Instruction ins = mbodyCode.get(i); + if (ins instanceof ExceptionMarkAVM2Instruction) { + ExceptionMarkAVM2Instruction m = (ExceptionMarkAVM2Instruction) ins; + switch (m.markType) { + case MARK_E_START: + mbody.exceptions[m.exceptionId].start = offset; + break; + case MARK_E_END: + mbody.exceptions[m.exceptionId].end = offset; + break; + case MARK_E_TARGET: + mbody.exceptions[m.exceptionId].target = offset; + break; + } + mbodyCode.remove(i); + i--; + continue; + } + offset += ins.getBytesLength(); + } + + mbody.markOffsets(); + mbody.autoFillStats(abcIndex.getSelectedAbc(), initScope, className != null); + abcIndex.getSelectedAbc().addMethodBody(mbody); + mindex = mbody.method_info; + } else { + mindex = abcIndex.getSelectedAbc().addMethodInfo(mi); + } + + return mindex; + } + + public ValueKind getValueKind(int ns, GraphTargetItem type, GraphTargetItem val) { + + if (val instanceof BooleanAVM2Item) { + BooleanAVM2Item bi = (BooleanAVM2Item) val; + if (bi.value) { + return new ValueKind(0, ValueKind.CONSTANT_True); + } else { + return new ValueKind(0, ValueKind.CONSTANT_False); + } + } + + boolean isNs = false; + if (type instanceof NameAVM2Item) { + if (((NameAVM2Item) type).getVariableName().equals("namespace")) { + isNs = true; + } + } + + if ((type instanceof TypeItem) && (((TypeItem) type).fullTypeName.equals(DottedChain.NAMESPACE))) { + isNs = true; + } + + if (val instanceof StringAVM2Item) { + StringAVM2Item sval = (StringAVM2Item) val; + if (isNs) { + return new ValueKind(namespace(Namespace.KIND_NAMESPACE, sval.getValue()), ValueKind.CONSTANT_Namespace); + } else { + return new ValueKind(str(sval.getValue()), ValueKind.CONSTANT_Utf8); + } + } + if (val instanceof IntegerValueAVM2Item) { + return new ValueKind(abcIndex.getSelectedAbc().constants.getIntId(((IntegerValueAVM2Item) val).value, true), ValueKind.CONSTANT_Int); + } + if (val instanceof FloatValueAVM2Item) { + return new ValueKind(abcIndex.getSelectedAbc().constants.getDoubleId(((FloatValueAVM2Item) val).value, true), ValueKind.CONSTANT_Double); + } + if (val instanceof NanAVM2Item) { + return new ValueKind(abcIndex.getSelectedAbc().constants.getDoubleId(Double.NaN, true), ValueKind.CONSTANT_Double); + } + if (val instanceof NullAVM2Item) { + return new ValueKind(0, ValueKind.CONSTANT_Null); + } + if (val instanceof UndefinedAVM2Item) { + return new ValueKind(0, ValueKind.CONSTANT_Undefined); + } + return null; + } + + private int genNs(List importedClasses, DottedChain pkg, NamespaceItem ns, List openedNamespaces, SourceGeneratorLocalData localData, int line) throws CompilationException { + ns.resolveCustomNs(abcIndex, importedClasses, pkg, openedNamespaces, localData); + return ns.getCpoolIndex(abcIndex); + } + + public void generateTraitsPhase2(List importedClasses, DottedChain pkg, List items, Trait[] traits, List openedNamespaces, SourceGeneratorLocalData localData) throws CompilationException { + for (int k = 0; k < items.size(); k++) { + GraphTargetItem item = items.get(k); + if (traits[k] == null) { + + } else if (item instanceof InterfaceAVM2Item) { + traits[k].name_index = traitName(((InterfaceAVM2Item) item).pkg == null ? 0 : ((InterfaceAVM2Item) item).pkg.getCpoolIndex(abcIndex), ((InterfaceAVM2Item) item).name); + } else if (item instanceof ClassAVM2Item) { + traits[k].name_index = traitName(((ClassAVM2Item) item).pkg == null ? 0 : ((ClassAVM2Item) item).pkg.getCpoolIndex(abcIndex), ((ClassAVM2Item) item).className); + } else if ((item instanceof MethodAVM2Item) || (item instanceof GetterAVM2Item) || (item instanceof SetterAVM2Item)) { + traits[k].name_index = traitName(genNs(importedClasses, pkg, ((MethodAVM2Item) item).pkg, openedNamespaces, localData, ((MethodAVM2Item) item).line), ((MethodAVM2Item) item).functionName); + } else if (item instanceof FunctionAVM2Item) { + traits[k].name_index = traitName(((FunctionAVM2Item) item).pkg == null ? 0 : ((FunctionAVM2Item) item).pkg.getCpoolIndex(abcIndex), ((FunctionAVM2Item) item).functionName); + } else if (item instanceof ConstAVM2Item) { + traits[k].name_index = traitName(genNs(importedClasses, pkg, ((ConstAVM2Item) item).pkg, openedNamespaces, localData, ((ConstAVM2Item) item).line), ((ConstAVM2Item) item).var); + } else if (item instanceof SlotAVM2Item) { + traits[k].name_index = traitName(genNs(importedClasses, pkg, ((SlotAVM2Item) item).pkg, openedNamespaces, localData, ((SlotAVM2Item) item).line), ((SlotAVM2Item) item).var); + } + } + + for (int k = 0; k < items.size(); k++) { + GraphTargetItem item = items.get(k); + if (traits[k] == null) { + continue; + } + if (item instanceof ClassAVM2Item) { + + InstanceInfo instanceInfo = abcIndex.getSelectedAbc().instance_info.get(((TraitClass) traits[k]).class_info); + instanceInfo.name_index = abcIndex.getSelectedAbc().constants.getMultinameId( + Multiname.createQName( + false, + abcIndex.getSelectedAbc().constants.getStringId(((ClassAVM2Item) item).className, true), + ((ClassAVM2Item) item).pkg.getCpoolIndex(abcIndex)), true); + + if (((ClassAVM2Item) item).extendsOp != null) { + instanceInfo.super_index = typeName(localData, ((ClassAVM2Item) item).extendsOp); + } else { + instanceInfo.super_index = abcIndex.getSelectedAbc().constants.getMultinameId(Multiname.createQName(false, str("Object"), namespace(Namespace.KIND_PACKAGE, "")), true); + } + instanceInfo.interfaces = new int[((ClassAVM2Item) item).implementsOp.size()]; + for (int i = 0; i < ((ClassAVM2Item) item).implementsOp.size(); i++) { + instanceInfo.interfaces[i] = superIntName(localData, ((ClassAVM2Item) item).implementsOp.get(i)); + } + } + if (item instanceof InterfaceAVM2Item) { + ABC abc = abcIndex.getSelectedAbc(); + AVM2ConstantPool constants = abc.constants; + InstanceInfo instanceInfo = abc.instance_info.get(((TraitClass) traits[k]).class_info); + instanceInfo.name_index = constants.getMultinameId(Multiname.createQName(false, constants.getStringId(((InterfaceAVM2Item) item).name, true), + ((InterfaceAVM2Item) item).pkg.getCpoolIndex(abcIndex)), true); + + instanceInfo.interfaces = new int[((InterfaceAVM2Item) item).superInterfaces.size()]; + for (int i = 0; i < ((InterfaceAVM2Item) item).superInterfaces.size(); i++) { + GraphTargetItem un = ((InterfaceAVM2Item) item).superInterfaces.get(i); + instanceInfo.interfaces[i] = superIntName(localData, un); + } + } + } + } + + public int superIntName(SourceGeneratorLocalData localData, GraphTargetItem un) throws CompilationException { + if (un instanceof UnresolvedAVM2Item) { + ((UnresolvedAVM2Item) un).resolve(null, new ArrayList<>(), new ArrayList<>(), abcIndex, new ArrayList<>(), new ArrayList<>()); + un = ((UnresolvedAVM2Item) un).resolved; + } + if (!(un instanceof TypeItem)) { //not applyType + throw new CompilationException("Invalid type", 0); + } + TypeItem sup = (TypeItem) un; + int propId = resolveType(localData, sup, abcIndex); + int[] nss = new int[]{abcIndex.getSelectedAbc().constants.getMultiname(propId).namespace_index}; + return abcIndex.getSelectedAbc().constants.getMultinameId(Multiname.createMultiname(false, abcIndex.getSelectedAbc().constants.getMultiname(propId).name_index, abcIndex.getSelectedAbc().constants.getNamespaceSetId(nss, true)), true); + + } + + public int[] generateMetadata(List>> metadata) { + int[] ret = new int[metadata.size()]; + for (int i = 0; i < metadata.size(); i++) { + Map.Entry> en = metadata.get(i); + int[] keys = new int[en.getValue().size()]; + int[] values = new int[en.getValue().size()]; + int j = 0; + for (String key : en.getValue().keySet()) { + keys[j] = abcIndex.getSelectedAbc().constants.getStringId(key, true); + values[j] = abcIndex.getSelectedAbc().constants.getStringId(en.getValue().get(key), true); + j++; + } + MetadataInfo mi = new MetadataInfo(abcIndex.getSelectedAbc().constants.getStringId(en.getKey(), true), keys, values); + ret[i] = abcIndex.getSelectedAbc().metadata_info.size(); + abcIndex.getSelectedAbc().metadata_info.add(mi); + } + return ret; + } + + public void generateTraitsPhase3(List importedClasses, int methodInitScope, boolean isInterface, String className, String superName, boolean generateStatic, SourceGeneratorLocalData localData, List items, Traits ts, Trait[] traits, Map initScopes, Reference class_index) throws AVM2ParseException, CompilationException { + + //Note: Names must be generated first before accesed in inner subs + for (int k = 0; k < items.size(); k++) { + GraphTargetItem item = items.get(k); + if (traits[k] == null) { + continue; + } + if (item instanceof InterfaceAVM2Item) { + generateClass(((InterfaceAVM2Item) item).pkg.getCpoolIndex(abcIndex), abcIndex.getSelectedAbc().class_info.get(((TraitClass) traits[k]).class_info), abcIndex.getSelectedAbc().instance_info.get(((TraitClass) traits[k]).class_info), initScopes.get(traits[k]), ((InterfaceAVM2Item) item).pkg.name, localData, (InterfaceAVM2Item) item, class_index); + } + + if (item instanceof ClassAVM2Item) { + generateClass(((ClassAVM2Item) item).pkg.getCpoolIndex(abcIndex), abcIndex.getSelectedAbc().class_info.get(((TraitClass) traits[k]).class_info), abcIndex.getSelectedAbc().instance_info.get(((TraitClass) traits[k]).class_info), initScopes.get(traits[k]), ((ClassAVM2Item) item).pkg.name, localData, (ClassAVM2Item) item, class_index); + } + if ((item instanceof MethodAVM2Item) || (item instanceof GetterAVM2Item) || (item instanceof SetterAVM2Item)) { + MethodAVM2Item mai = (MethodAVM2Item) item; + if (mai.isStatic() != generateStatic) { + continue; + } + for (List ln : mai.allOpenedNamespaces) { + for (NamespaceItem n : ln) { + n.resolveCustomNs(abcIndex, importedClasses, localData.pkg, ln, localData); + } + } + String suffix = null; + if (item instanceof GetterAVM2Item) { + suffix = "get"; + } + if (item instanceof SetterAVM2Item) { + suffix = "set"; + } + + ((TraitMethodGetterSetter) traits[k]).method_info = method(mai.isStatic(), methodName(mai.outsidePackage, localData.pkg, mai.functionName, mai.pkg, className, mai.customNamespace, suffix), false, isInterface, new ArrayList<>(), localData.pkg, mai.needsActivation, mai.subvariables, methodInitScope + (mai.isStatic() ? 0 : 1), mai.hasRest, mai.line, className, superName, false, localData, mai.paramTypes, mai.paramNames, mai.paramValues, mai.body, mai.retType); + } else if (item instanceof FunctionAVM2Item) { + FunctionAVM2Item fai = (FunctionAVM2Item) item; + ((TraitFunction) traits[k]).method_info = method(false, methodName(false/*?*/, localData.pkg, fai.functionName, fai.pkg, null, null, ""), false, isInterface, new ArrayList<>(), localData.pkg, fai.needsActivation, fai.subvariables, methodInitScope, fai.hasRest, fai.line, className, superName, false, localData, fai.paramTypes, fai.paramNames, fai.paramValues, fai.body, fai.retType); + } + } + } + + private int methodName(boolean outsidePkg, DottedChain pkg, String methodName, NamespaceItem ns, String className, String customNs, String typeSuffix) { + StringBuilder sb = new StringBuilder(); + /*if (ns != null) { + sb.append(ns.name.toRawString()); + }*/ + if (className != null) { + if (pkg != null && !pkg.isEmpty() && !pkg.isTopLevel()) { + sb.append(pkg.toRawString()); + sb.append(":"); + } + sb.append(className); + } + if (customNs != null) { + sb.append(customNs); + } else if (ns != null) { + switch (ns.kind) { + case Namespace.KIND_PACKAGE_INTERNAL: + sb.append(pkg == null ? "" /*?*/ : pkg.toRawString()); + break; + case Namespace.KIND_PRIVATE: + + if (!outsidePkg) { + sb.append("/private"); + } + break; + case Namespace.KIND_PROTECTED: + case Namespace.KIND_STATIC_PROTECTED: + sb.append("/protected"); + break; + } + } + sb.append(":"); + sb.append(methodName); + if (typeSuffix != null && !typeSuffix.isEmpty()) { + sb.append("/"); + sb.append(typeSuffix); + } + return abcIndex.getSelectedAbc().constants.getStringId(sb.toString(), true); + } + + public Trait[] generateTraitsPhase1(List importedClasses, List openedNamespaces, String className, String superName, boolean generateStatic, SourceGeneratorLocalData localData, List items, Traits ts, Reference classIndex) throws AVM2ParseException, CompilationException { + Trait[] traits = new Trait[items.size()]; + int slot_id = 1; + int disp_id = 3; //1 and 2 are for constructor + for (int k = 0; k < items.size(); k++) { + GraphTargetItem item = items.get(k); + if (item instanceof InterfaceAVM2Item) { + TraitClass tc = new TraitClass(); + ClassInfo ci = new ClassInfo(); + InstanceInfo ii = new InstanceInfo(); + /*abc.class_info.add(ci); + abc.instance_info.add(ii);*/ + tc.class_info = classIndex.getVal(); + abcIndex.getSelectedAbc().addClass(ci, ii, classIndex.getVal()); + classIndex.setVal(classIndex.getVal() + 1); + ii.flags |= InstanceInfo.CLASS_INTERFACE; + //ii.name_index = traitName(((InterfaceAVM2Item) item).namespace, ((InterfaceAVM2Item) item).name); + //tc.class_info = abc.instance_info.size() - 1; + tc.kindType = Trait.TRAIT_CLASS; + //tc.name_index = traitName(((InterfaceAVM2Item) item).namespace, ((InterfaceAVM2Item) item).name); + tc.slot_id = 0; //? + ts.traits.add(tc); + traits[k] = tc; + traits[k].metadata = generateMetadata(((InterfaceAVM2Item) item).metadata); + if (traits[k].metadata.length > 0) { + traits[k].kindFlags |= Trait.ATTR_Metadata; + } + } + + if (item instanceof ClassAVM2Item) { + TraitClass tc = new TraitClass(); + ClassInfo ci = new ClassInfo(); + InstanceInfo ii = new InstanceInfo(); + //ii.name_index = traitName(((ClassAVM2Item) item).namespace, ((ClassAVM2Item) item).className); + /*abc.class_info.add(ci); + abc.instance_info.add(instanceInfo);*/ + tc.class_info = classIndex.getVal(); + abcIndex.getSelectedAbc().addClass(ci, ii, classIndex.getVal()); + classIndex.setVal(classIndex.getVal() + 1); + tc.kindType = Trait.TRAIT_CLASS; + // tc.name_index = traitName(((ClassAVM2Item) item).namespace, ((ClassAVM2Item) item).className); + tc.slot_id = slot_id++; + ts.traits.add(tc); + traits[k] = tc; + traits[k].metadata = generateMetadata(((ClassAVM2Item) item).metadata); + if (traits[k].metadata.length > 0) { + traits[k].kindFlags |= Trait.ATTR_Metadata; + } + } + if ((item instanceof SlotAVM2Item) || (item instanceof ConstAVM2Item)) { + TraitSlotConst tsc = new TraitSlotConst(); + tsc.kindType = (item instanceof SlotAVM2Item) ? Trait.TRAIT_SLOT : Trait.TRAIT_CONST; + String var = null; + GraphTargetItem val = null; + GraphTargetItem type = null; + boolean isNamespace = false; + int namespace = 0; + boolean isStatic = false; + int[] metadata = new int[0]; + if (item instanceof SlotAVM2Item) { + SlotAVM2Item sai = (SlotAVM2Item) item; + if (sai.isStatic() != generateStatic) { + continue; + } + var = sai.var; + val = sai.value; + type = sai.type; + isStatic = sai.isStatic(); + if (sai.pkg != null) { + sai.pkg.resolveCustomNs(abcIndex, importedClasses, localData.pkg, openedNamespaces, localData); + } + namespace = sai.pkg == null ? 0 : sai.pkg.getCpoolIndex(abcIndex); + metadata = generateMetadata(((SlotAVM2Item) item).metadata); + } + if (item instanceof ConstAVM2Item) { + ConstAVM2Item cai = (ConstAVM2Item) item; + if (cai.isStatic() != generateStatic) { + continue; + } + var = cai.var; + val = cai.value; + type = cai.type; + if (cai.pkg != null) { + cai.pkg.resolveCustomNs(abcIndex, importedClasses, localData.pkg, openedNamespaces, localData); + } + namespace = cai.pkg == null ? 0 : cai.pkg.getCpoolIndex(abcIndex); + isNamespace = type.toString().equals("Namespace"); + isStatic = cai.isStatic(); + metadata = generateMetadata(((ConstAVM2Item) item).metadata); + } + if (isNamespace) { + tsc.name_index = traitName(namespace, var); + } + tsc.type_index = isNamespace ? 0 : (type == null ? 0 : typeName(localData, type)); + + ValueKind vk = getValueKind(namespace, type, val); + if (vk == null) { + tsc.value_kind = ValueKind.CONSTANT_Undefined; + } else { + tsc.value_kind = vk.value_kind; + tsc.value_index = vk.value_index; + } + tsc.slot_id = isStatic ? slot_id++ : 0; + ts.traits.add(tsc); + traits[k] = tsc; + traits[k].metadata = metadata; + if (traits[k].metadata.length > 0) { + traits[k].kindFlags |= Trait.ATTR_Metadata; + } + } + if ((item instanceof MethodAVM2Item) || (item instanceof GetterAVM2Item) || (item instanceof SetterAVM2Item)) { + MethodAVM2Item mai = (MethodAVM2Item) item; + if (mai.isStatic() != generateStatic) { + continue; + } + TraitMethodGetterSetter tmgs = new TraitMethodGetterSetter(); + tmgs.kindType = (item instanceof GetterAVM2Item) ? Trait.TRAIT_GETTER : ((item instanceof SetterAVM2Item) ? Trait.TRAIT_SETTER : Trait.TRAIT_METHOD); + tmgs.disp_id = mai.isStatic() ? disp_id++ : 0; //For a reason, there is disp_id only for static methods (or not?) + if (mai.isFinal() || (className != null && mai.isStatic())) { + tmgs.kindFlags |= Trait.ATTR_Final; + } + if (mai.isOverride()) { + tmgs.kindFlags |= Trait.ATTR_Override; + } + ts.traits.add(tmgs); + + traits[k] = tmgs; + traits[k].metadata = generateMetadata(((MethodAVM2Item) item).metadata); + if (traits[k].metadata.length > 0) { + traits[k].kindFlags |= Trait.ATTR_Metadata; + } + } + /*else if (item instanceof FunctionAVM2Item) { + TraitFunction tf = new TraitFunction(); + tf.slot_id = slot_id++; + tf.kindType = Trait.TRAIT_FUNCTION; + //tf.name_index = traitName(((FunctionAVM2Item) item).namespace, ((FunctionAVM2Item) item).functionName); + ts.traits.add(tf); + traits[k] = tf; + traits[k].metadata = generateMetadata(((FunctionAVM2Item) item).metadata); + }*/ + + } + + return traits; + } + + public ScriptInfo generateScriptInfo(List> allOpenedNamespaces, SourceGeneratorLocalData localData, List commands, int classPos) throws AVM2ParseException, CompilationException { + Reference class_index = new Reference<>(classPos); + ScriptInfo si = new ScriptInfo(); + localData.currentScript = si; + Trait[] traitArr = generateTraitsPhase1(new ArrayList<>(), new ArrayList<>(), null, null, true, localData, commands, si.traits, class_index); + generateTraitsPhase2(new ArrayList<>(), null/*FIXME*/, commands, traitArr, new ArrayList<>(), localData); + + abcIndex.refreshSelected(); + + ABC abc = abcIndex.getSelectedAbc(); + AVM2ConstantPool constants = abc.constants; + MethodInfo mi = new MethodInfo(new int[0], 0, constants.getStringId("", true), 0, new ValueKind[0], new int[0]); + MethodBody mb = new MethodBody(abc, new Traits(), new byte[0], new ABCException[0]); + mb.method_info = abc.addMethodInfo(mi); + mb.setCode(new AVM2Code()); + List mbCode = mb.getCode().code; + mbCode.add(ins(AVM2Instructions.GetLocal0)); + mbCode.add(ins(AVM2Instructions.PushScope)); + + int traitScope = 2; + + Map initScopes = new HashMap<>(); + + for (Trait t : si.traits.traits) { + if (t instanceof TraitClass) { + TraitClass tc = (TraitClass) t; + List parents = new ArrayList<>(); + if (localData.documentClass) { + mbCode.add(ins(AVM2Instructions.GetScopeObject, 0)); + traitScope++; + } else { + int[] nsset = new int[]{constants.getMultiname(tc.name_index).namespace_index}; + mbCode.add(ins(AVM2Instructions.FindPropertyStrict, constants.getMultinameId(Multiname.createMultiname(false, constants.getMultiname(tc.name_index).name_index, constants.getNamespaceSetId(nsset, true)), true))); + } + if (abc.instance_info.get(tc.class_info).isInterface()) { + mbCode.add(ins(AVM2Instructions.PushNull)); + } else { + + AbcIndexing.ClassIndex ci = abcIndex.findClass(AbcIndexing.multinameToType(abc.instance_info.get(tc.class_info).name_index, constants)); + while (ci != null && ci.parent != null) { + ci = ci.parent; + Multiname origM = ci.abc.constants.getMultiname(ci.abc.instance_info.get(ci.index).name_index); + Namespace origNs = ci.abc.constants.getNamespace(origM.namespace_index); + if (origM.kind == Multiname.QNAME || origM.kind == Multiname.QNAMEA) { + parents.add(constants.getMultinameId( + Multiname.createQName(origM.kind == Multiname.QNAMEA, + constants.getStringId(ci.abc.constants.getString(origM.name_index), true), + constants.getNamespaceId(origNs.kind, + ci.abc.constants.getString(origNs.name_index), 0, true)), true)); + } + } + + //add all parent objects to scopestack + for (int i = parents.size() - 1; i >= 0; i--) { + mbCode.add(ins(AVM2Instructions.GetLex, parents.get(i))); + mbCode.add(ins(AVM2Instructions.PushScope)); + traitScope++; + } + //direct parent class to new_class instruction + if (!parents.isEmpty()) { //NON EXISTING PARENT CLASS - TODO: handle as error! + mbCode.add(ins(AVM2Instructions.GetLex, parents.get(0))); + } + } + mbCode.add(ins(AVM2Instructions.NewClass, tc.class_info)); + for (int i = 0; i < parents.size(); i++) { + mbCode.add(ins(AVM2Instructions.PopScope)); + } + + mbCode.add(ins(AVM2Instructions.InitProperty, tc.name_index)); + initScopes.put(t, traitScope); + traitScope = 1; + } + } + + abc.addMethodBody(mb); + si.init_index = mb.method_info; + localData.pkg = DottedChain.EMPTY; + generateTraitsPhase3(new ArrayList<>(), 1/*??*/, false, null, null, true, localData, commands, si.traits, traitArr, initScopes, class_index); + + int maxSlotId = 0; + for (int k = 0; k < si.traits.traits.size(); k++) { + if (si.traits.traits.get(k) instanceof TraitSlotConst) { + TraitSlotConst ti = (TraitSlotConst) si.traits.traits.get(k); + if (ti.slot_id > maxSlotId) { + maxSlotId = ti.slot_id; + } + } + } + for (int k = 0; k < si.traits.traits.size(); k++) { + if ((si.traits.traits.get(k) instanceof TraitMethodGetterSetter) && (commands.get(k) instanceof MethodAVM2Item)) { + MethodAVM2Item mai = (MethodAVM2Item) commands.get(k); + if (mai.outsidePackage) { + TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) si.traits.traits.get(k); + TraitSlotConst nts = new TraitSlotConst(); + nts.name_index = si.traits.traits.get(k).name_index; + nts.metadata = si.traits.traits.get(k).metadata; + + nts.slot_id = maxSlotId + 1; + maxSlotId++; + nts.type_index = abcIndex.getSelectedAbc().constants.getQnameId("Function", Namespace.KIND_PACKAGE, "", true); + nts.value_index = 0; + nts.value_kind = 0; + int methodinfo = tmgs.method_info; + si.traits.traits.set(k, nts); + mbCode.add(ins(AVM2Instructions.NewFunction, methodinfo)); + mbCode.add(ins(AVM2Instructions.InitProperty, nts.name_index)); + } + } + } + + mbCode.add(ins(AVM2Instructions.ReturnVoid)); + mb.autoFillStats(abc, 1, false); + + return si; + } + + public static void parentNamesAddNames(AbcIndexing abc, int name_index, List indices, List names, List namespaces) { + List cindices = new ArrayList<>(); + + List outABCs = new ArrayList<>(); + parentNames(abc, name_index, cindices, names, namespaces, outABCs); + for (int i = 0; i < cindices.size(); i++) { + ABC a = outABCs.get(i); + int m = cindices.get(i); + if (a == abc.getSelectedAbc()) { + indices.add(m); + continue; + } + Multiname superName = a.constants.getMultiname(m); + indices.add( + abc.getSelectedAbc().constants.getMultinameId( + Multiname.createQName(false, + abc.getSelectedAbc().constants.getStringId(superName.getName(a.constants, null, true), true), + abc.getSelectedAbc().constants.getNamespaceId(superName.getNamespace(a.constants).kind, superName.getNamespace(a.constants).getName(a.constants), 0, true)), true) + ); + } + } + + public static GraphTargetItem getTraitReturnType(AbcIndexing abc, Trait t) { + if (t instanceof TraitSlotConst) { + TraitSlotConst tsc = (TraitSlotConst) t; + if (tsc.type_index == 0) { + return TypeItem.UNBOUNDED; + } + return PropertyAVM2Item.multinameToType(tsc.type_index, abc.getSelectedAbc().constants); + } + if (t instanceof TraitMethodGetterSetter) { + TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) t; + if (tmgs.kindType == Trait.TRAIT_GETTER) { + return PropertyAVM2Item.multinameToType(abc.getSelectedAbc().method_info.get(tmgs.method_info).ret_type, abc.getSelectedAbc().constants); + } + if (tmgs.kindType == Trait.TRAIT_SETTER) { + if (abc.getSelectedAbc().method_info.get(tmgs.method_info).param_types.length > 0) { + return PropertyAVM2Item.multinameToType(abc.getSelectedAbc().method_info.get(tmgs.method_info).param_types[0], abc.getSelectedAbc().constants); + } else { + return TypeItem.UNBOUNDED; + } + } + } + if (t instanceof TraitFunction) { + return new TypeItem(DottedChain.FUNCTION); + } + return TypeItem.UNBOUNDED; + } + + public static boolean searchPrototypeChain(List otherNs, int privateNs, int protectedNs, boolean instanceOnly, AbcIndexing abc, DottedChain pkg, String obj, String propertyName, Reference outName, Reference outNs, Reference outPropNs, Reference outPropNsKind, Reference outPropNsIndex, Reference outPropType, Reference outPropValue, Reference outPropValueAbc) { + for (int ns : otherNs) { + if (searchPrototypeChain(ns, instanceOnly, abc, pkg, obj, propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue, outPropValueAbc)) { + return true; + } + } + + if (searchPrototypeChain(privateNs, instanceOnly, abc, pkg, obj, propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue, outPropValueAbc)) { + return true; + } + if (searchPrototypeChain(protectedNs, instanceOnly, abc, pkg, obj, propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue, outPropValueAbc)) { + return true; + } + return searchPrototypeChain(0, instanceOnly, abc, pkg, obj, propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue, outPropValueAbc); + } + + private static boolean searchPrototypeChain(int selectedNs, boolean instanceOnly, AbcIndexing abc, DottedChain pkg, String obj, String propertyName, Reference outName, Reference outNs, Reference outPropNs, Reference outPropNsKind, Reference outPropNsIndex, Reference outPropType, Reference outPropValue, Reference outPropValueAbc) { + + AbcIndexing.TraitIndex sp = abc.findScriptProperty(pkg.add(propertyName)); + if (sp == null) { + sp = abc.findProperty(new AbcIndexing.PropertyDef(propertyName, new TypeItem(pkg.add(obj)), abc.getSelectedAbc(), selectedNs), !instanceOnly, true); + } + if (sp != null) { + if (sp.objType instanceof TypeItem) { + outName.setVal(((TypeItem) sp.objType).fullTypeName.getLast()); + outNs.setVal(((TypeItem) sp.objType).fullTypeName.getWithoutLast()); + } else { + //FIXME? Vector? + } + outPropNs.setVal(sp.trait.getName(sp.abc).getNamespace(sp.abc.constants).getName(sp.abc.constants)); + outPropNsKind.setVal(sp.trait.getName(sp.abc).getNamespace(sp.abc.constants).kind); + int nsi = sp.trait.getName(sp.abc).namespace_index; + outPropNsIndex.setVal(sp.abc == abc.getSelectedAbc() ? sp.abc.constants.getNamespaceSubIndex(nsi) : 0); + outPropType.setVal(sp.returnType); + outPropValue.setVal(sp.value); + outPropValueAbc.setVal(sp.abc); + return true; + } + return false; + } + + public static void parentNames(AbcIndexing abc, int name_index, List indices, List names, List namespaces, List outABCs) { + AbcIndexing.ClassIndex ci = abc.findClass(new TypeItem(abc.getSelectedAbc().constants.getMultiname(name_index).getNameWithNamespace(abc.getSelectedAbc().constants))); + while (ci != null) { + int ni = ci.abc.instance_info.get(ci.index).name_index; + indices.add(ni); + outABCs.add(ci.abc); + names.add(ci.abc.constants.getMultiname(ni).getName(ci.abc.constants, null, true)); + namespaces.add(ci.abc.constants.getMultiname(ni).getNamespace(ci.abc.constants).getName(ci.abc.constants).toRawString()); + ci = ci.parent; + } + } + + /* public void calcRegisters(Reference activationReg, SourceGeneratorLocalData localData, boolean needsActivation, List funParamNames,List funSubVariables,List funBody, Reference hasArguments) throws ParseException { + + }*/ + /*public int resolveType(String objType) { + if (objType.equals("*")) { + return 0; + } + List abcs = new ArrayList<>(); + abcs.add(abc); + abcs.addAll(allABCs); + for (ABC a : abcs) { + int ci = a.findClassByName(objType); + if (ci != -1) { + Multiname tname = a.instance_info.get(ci).getName(a.constants); + return abc.getLastAbc().constants.getMultinameId(new Multiname(tname.kind, + abc.getLastAbc().constants.getStringId(tname.getName(a.constants, new ArrayList<>()), true), + abc.getLastAbc().constants.getNamespaceId(new Namespace(tname.getNamespace(a.constants).kind, abc.getLastAbc().constants.getStringId(tname.getNamespace(a.constants).getName(a.constants), true)), 0, true), 0, 0, new ArrayList()), true); + } + } + return 0; + }*/ + @Override + public List generate(SourceGeneratorLocalData localData, TypeItem item) throws CompilationException { + String currentFullClassName = localData.getFullClass(); + + if (localData.documentClass && item.toString().equals(currentFullClassName)) { + int slotId = 0; + int c = abcIndex.getSelectedAbc().findClassByName(currentFullClassName); + for (Trait t : localData.currentScript.traits.traits) { + if (t instanceof TraitClass) { + TraitClass tc = (TraitClass) t; + if (tc.class_info == c) { + slotId = tc.slot_id; + break; + } + } + } + return GraphTargetItem.toSourceMerge(localData, this, ins(AVM2Instructions.GetGlobalScope), ins(AVM2Instructions.GetSlot, slotId)); + } else { + return GraphTargetItem.toSourceMerge(localData, this, ins(AVM2Instructions.GetLex, resolveType(localData, item, abcIndex))); + } + } + + public static int resolveType(SourceGeneratorLocalData localData, GraphTargetItem item, AbcIndexing abcIndex) throws CompilationException { + int name_index = 0; + GraphTargetItem typeItem = null; + + if (item instanceof UnresolvedAVM2Item) { + String fullClass = localData.getFullClass(); + item = ((UnresolvedAVM2Item) item).resolve(new TypeItem(fullClass), new ArrayList<>(), new ArrayList<>(), abcIndex, new ArrayList<>(), new ArrayList<>()); + } + if (item instanceof TypeItem) { + typeItem = item; + } else if (item instanceof ApplyTypeAVM2Item) { + typeItem = ((ApplyTypeAVM2Item) item).object; + } else { + throw new CompilationException("Invalid type:" + item + " (" + item.getClass().getName() + ")", 0/*??*/); + } + if (typeItem instanceof UnresolvedAVM2Item) { + String fullClass = localData.getFullClass(); + typeItem = ((UnresolvedAVM2Item) typeItem).resolve(new TypeItem(fullClass), new ArrayList<>(), new ArrayList<>(), abcIndex, new ArrayList<>(), new ArrayList<>()); + } + + if (!(typeItem instanceof TypeItem)) { + throw new CompilationException("Invalid type", 0/*??*/); + } + + TypeItem type = (TypeItem) typeItem; + + DottedChain dname = type.fullTypeName; + DottedChain pkg = dname.getWithoutLast(); + String name = dname.getLast(); + /*for (InstanceInfo ii : abc.getSelectedAbc().instance_info) { + Multiname mname = abc.getSelectedAbc().constants.constant_multiname.get(ii.name_index); + if (mname != null && name.equals(mname.getName(abc.getSelectedAbc().constants, null, true))) { + Namespace ns = mname.getNamespace(abc.getSelectedAbc().constants); + if (ns != null && ns.hasName(pkg, abc.getSelectedAbc().constants)) { + name_index = ii.name_index; + break; + } + } + }*/ + ABC abc = abcIndex.getSelectedAbc(); + AVM2ConstantPool constants = abc.constants; + AbcIndexing.ClassIndex ci = abcIndex.findClass(new TypeItem(dname)); + if (ci != null) { + Multiname m = ci.abc.instance_info.get(ci.index).getName(ci.abc.constants); + 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); + String nsn = ns == null ? null : ns.getName(ci.abc.constants).toRawString(); + name_index = constants.getQnameId( + n, + ns == null ? Namespace.KIND_PACKAGE : ns.kind, + nsn, true); + } + } + + for (int i = 1; i < constants.getMultinameCount(); i++) { + Multiname mname = constants.getMultiname(i); + if (mname != null && name.equals(mname.getName(constants, null, true))) { + if (mname.getNamespace(constants) != null && pkg.equals(mname.getNamespace(constants).getName(constants))) { + name_index = i; + break; + } + } + } + if (name_index == 0) { + if (pkg.isEmpty() && localData.currentScript != null /*FIXME!*/) { + for (Trait t : localData.currentScript.traits.traits) { + if (t.getName(abc).getName(constants, null, true).equals(name)) { + name_index = t.name_index; + break; + } + } + } + if (name_index == 0) { + name_index = constants.getMultinameId(Multiname.createQName(false, constants.getStringId(name, true), constants.getNamespaceId(Namespace.KIND_PACKAGE, pkg, 0, true)), true); + } + } + + if (item instanceof ApplyTypeAVM2Item) { + ApplyTypeAVM2Item atype = (ApplyTypeAVM2Item) item; + int[] params = new int[atype.params.size()]; + int i = 0; + for (GraphTargetItem s : atype.params) { + params[i++] = resolveType(localData, s, abcIndex); + } + return constants.getMultinameId(Multiname.createTypeName(name_index, params), true); + } + + return name_index; + } + + @Override + public List generateDiscardValue(SourceGeneratorLocalData localData, GraphTargetItem item) throws CompilationException { + List ret = item.toSource(localData, this); + ret.add(ins(AVM2Instructions.Pop)); + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, PushItem item) throws CompilationException { + return item.value.toSource(localData, this); + } + + @Override + public List generate(SourceGeneratorLocalData localData, PopItem item) throws CompilationException { + List ret = new ArrayList<>(); + return ret; + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScriptLexer.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScriptLexer.java index 6edf3e8a3..4bd05fca1 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScriptLexer.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScriptLexer.java @@ -1,6 +1,6 @@ /* The following code was generated by JFlex 1.6.0 */ -/* + /* * Copyright (C) 2010-2016 JPEXS, All rights reserved. * * This library is free software; you can redistribute it and/or @@ -26,8 +26,7 @@ import java.util.Stack; /** * This class is a scanner generated by - * JFlex 1.6.0 - * from the specification file + * JFlex 1.6.0 from the specification file * C:/FFDec/jpexs-decompiler/libsrc/ffdec_lib/lexers/actionscript3_script.flex */ public final class ActionScriptLexer { @@ -69,9 +68,8 @@ public final class ActionScriptLexer { /** * ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l - * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l - * at the beginning of a line - * l is of the form l = 2*k, k a non negative integer + * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l at the + * beginning of a line l is of the form l = 2*k, k a non negative integer */ private static final int ZZ_LEXSTATE[] = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, @@ -295,9 +293,11 @@ public final class ActionScriptLexer { } private static int zzUnpackAction(String packed, int offset, int[] result) { - int i = 0; /* index in packed string */ + int i = 0; + /* index in packed string */ - int j = offset; /* index in unpacked array */ + int j = offset; + /* index in unpacked array */ int l = packed.length(); while (i < l) { @@ -383,9 +383,11 @@ public final class ActionScriptLexer { } private static int zzUnpackRowMap(String packed, int offset, int[] result) { - int i = 0; /* index in packed string */ + int i = 0; + /* index in packed string */ - int j = offset; /* index in unpacked array */ + int j = offset; + /* index in unpacked array */ int l = packed.length(); while (i < l) { @@ -833,9 +835,11 @@ public final class ActionScriptLexer { } private static int zzUnpackTrans(String packed, int offset, int[] result) { - int i = 0; /* index in packed string */ + int i = 0; + /* index in packed string */ - int j = offset; /* index in unpacked array */ + int j = offset; + /* index in unpacked array */ int l = packed.length(); while (i < l) { @@ -894,9 +898,11 @@ public final class ActionScriptLexer { } private static int zzUnpackAttribute(String packed, int offset, int[] result) { - int i = 0; /* index in packed string */ + int i = 0; + /* index in packed string */ - int j = offset; /* index in unpacked array */ + int j = offset; + /* index in unpacked array */ int l = packed.length(); while (i < l) { @@ -925,8 +931,8 @@ public final class ActionScriptLexer { private int zzLexicalState = YYINITIAL; /** - * this buffer contains the current text to be matched and is - * the source of the yytext() string + * this buffer contains the current text to be matched and is the source of + * the yytext() string */ private char zzBuffer[] = new char[ZZ_BUFFERSIZE]; @@ -946,8 +952,8 @@ public final class ActionScriptLexer { private int zzStartRead; /** - * endRead marks the last character in the buffer, that has been read - * from input + * endRead marks the last character in the buffer, that has been read from + * input */ private int zzEndRead; @@ -983,10 +989,10 @@ public final class ActionScriptLexer { private boolean zzEOFDone; /** - * The number of occupied positions in zzBuffer beyond zzEndRead. - * When a lead/high surrogate has been read from the input stream - * into the final zzBuffer position, this will have a value of 1; - * otherwise, it will have a value of 0. + * The number of occupied positions in zzBuffer beyond zzEndRead. When a + * lead/high surrogate has been read from the input stream into the final + * zzBuffer position, this will have a value of 1; otherwise, it will have a + * value of 0. */ private int zzFinalHighSurrogate = 0; @@ -1080,9 +1086,11 @@ public final class ActionScriptLexer { */ private static char[] zzUnpackCMap(String packed) { char[] map = new char[0x110000]; - int i = 0; /* index in packed string */ + int i = 0; + /* index in packed string */ - int j = 0; /* index in unpacked array */ + int j = 0; + /* index in unpacked array */ while (i < 3136) { int count = packed.charAt(i++); @@ -1141,7 +1149,8 @@ public final class ActionScriptLexer { if (totalRead > 0) { zzEndRead += totalRead; - if (totalRead == requested) { /* possibly more input available */ + if (totalRead == requested) { + /* possibly more input available */ if (Character.isHighSurrogate(zzBuffer[zzEndRead - 1])) { --zzEndRead; @@ -1159,9 +1168,11 @@ public final class ActionScriptLexer { * Closes the input stream. */ public final void yyclose() throws java.io.IOException { - zzAtEOF = true; /* indicate end of file */ + zzAtEOF = true; + /* indicate end of file */ - zzEndRead = zzStartRead; /* invalidate buffer */ + zzEndRead = zzStartRead; + /* invalidate buffer */ if (zzReader != null) { zzReader.close(); @@ -1169,12 +1180,12 @@ public final class ActionScriptLexer { } /** - * Resets the scanner to read from a new input stream. - * Does not close the old reader. + * Resets the scanner to read from a new input stream. Does not close the + * old reader. * * All internal variables are reset, the old input stream - * cannot be reused (internal buffer is discarded and lost). - * Lexical state is set to ZZ_INITIAL. + * cannot be reused (internal buffer is discarded and lost). Lexical + * state is set to ZZ_INITIAL. * * Internal scan buffer is resized down to its initial length, if it has * grown. @@ -1220,13 +1231,12 @@ public final class ActionScriptLexer { } /** - * Returns the character at position pos from the - * matched text. + * Returns the character at position pos from the matched text. * * It is equivalent to yytext().charAt(pos), but faster * - * @param pos the position of the character to fetch. - * A value from 0 to yylength()-1. + * @param pos the position of the character to fetch. A value from 0 to + * yylength()-1. * * @return the character at position pos */ @@ -1244,14 +1254,13 @@ public final class ActionScriptLexer { /** * Reports an error that occured while scanning. * - * In a wellformed scanner (no or only correct usage of - * yypushback(int) and a match-all fallback rule) this method - * will only be called with things that "Can't Possibly Happen". - * If this method is called, something is seriously wrong - * (e.g. a JFlex bug producing a faulty scanner etc.). + * In a wellformed scanner (no or only correct usage of yypushback(int) and + * a match-all fallback rule) this method will only be called with things + * that "Can't Possibly Happen". If this method is called, something is + * seriously wrong (e.g. a JFlex bug producing a faulty scanner etc.). * - * Usual syntax/scanner level error handling should be done - * in error fallback rules. + * Usual syntax/scanner level error handling should be done in error + * fallback rules. * * @param errorCode the code of the errormessage to display */ @@ -1271,8 +1280,8 @@ public final class ActionScriptLexer { * * They will be read again by then next call of the scanning method * - * @param number the number of characters to be read again. - * This number must not be greater than yylength()! + * @param number the number of characters to be read again. This number must + * not be greater than yylength()! */ public void yypushback(int number) { if (number > yylength()) { @@ -1283,8 +1292,8 @@ public final class ActionScriptLexer { } /** - * Resumes scanning until the next regular expression is matched, - * the end of input is encountered or an I/O-Error occurs. + * Resumes scanning until the next regular expression is matched, the end of + * input is encountered or an I/O-Error occurs. * * @return the next token * @exception java.io.IOException if any I/O-Error occurs @@ -1380,7 +1389,8 @@ public final class ActionScriptLexer { } case 167: break; - case 3: { /*ignore*/ + case 3: { + /*ignore*/ } case 168: @@ -1794,7 +1804,8 @@ public final class ActionScriptLexer { } case 242: break; - case 78: { /* ignore illegal character escape */ + case 78: { + /* ignore illegal character escape */ } case 243: diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/NameAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/NameAVM2Item.java index fe36c4106..e59836843 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/NameAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/NameAVM2Item.java @@ -220,9 +220,9 @@ public class NameAVM2Item extends AssignableAVM2Item { return toSourceMerge(localData, generator, ins(AVM2Instructions.GetScopeObject, slotScope), assignedValue, !(("" + assignedValue.returnType()).equals("" + type) && (basicTypes.contains("" + type))) ? generateCoerce(localData, generator, type) : null, needsReturn - ? dupSetTemp(localData, generator, ret_temp) : null, generateSetLoc(regNumber), slotNumber > -1 - ? ins(AVM2Instructions.SetSlot, slotNumber) - : null, + ? dupSetTemp(localData, generator, ret_temp) : null, generateSetLoc(regNumber), slotNumber > -1 + ? ins(AVM2Instructions.SetSlot, slotNumber) + : null, needsReturn ? getTemp(localData, generator, ret_temp) : null, killTemp(localData, generator, Arrays.asList(ret_temp))); } else { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/NamespacedAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/NamespacedAVM2Item.java index c2db4f69e..8b0f08214 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/NamespacedAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/NamespacedAVM2Item.java @@ -180,30 +180,28 @@ public class NamespacedAVM2Item extends AssignableAVM2Item { killTemp(localData, generator, Arrays.asList(obj_temp)) ); } + } else if (assignedValue != null) { + return toSourceMerge(localData, generator, + obj == null ? ns : null, obj == null ? NameAVM2Item.generateCoerce(localData, generator, new TypeItem(DottedChain.NAMESPACE)) : null, obj != null ? obj : ins(AVM2Instructions.FindPropertyStrict, constants.getMultinameId(Multiname.createRTQName(attr, constants.getStringId(name, true)), true)), + ns, NameAVM2Item.generateCoerce(localData, generator, new TypeItem(DottedChain.NAMESPACE)), assignedValue, + needsReturn ? dupSetTemp(localData, generator, ret_temp) : null, + ins(AVM2Instructions.SetProperty, constants.getMultinameId(Multiname.createRTQName(attr, constants.getStringId(name, true)), true)), + needsReturn ? getTemp(localData, generator, ret_temp) : null, + killTemp(localData, generator, Arrays.asList(ns_temp, index_temp, ret_temp)) + ); } else { - if (assignedValue != null) { - return toSourceMerge(localData, generator, - obj == null ? ns : null, obj == null ? NameAVM2Item.generateCoerce(localData, generator, new TypeItem(DottedChain.NAMESPACE)) : null, obj != null ? obj : ins(AVM2Instructions.FindPropertyStrict, constants.getMultinameId(Multiname.createRTQName(attr, constants.getStringId(name, true)), true)), - ns, NameAVM2Item.generateCoerce(localData, generator, new TypeItem(DottedChain.NAMESPACE)), assignedValue, - needsReturn ? dupSetTemp(localData, generator, ret_temp) : null, - ins(AVM2Instructions.SetProperty, constants.getMultinameId(Multiname.createRTQName(attr, constants.getStringId(name, true)), true)), - needsReturn ? getTemp(localData, generator, ret_temp) : null, - killTemp(localData, generator, Arrays.asList(ns_temp, index_temp, ret_temp)) - ); - } else { - return toSourceMerge(localData, generator, - obj == null ? ns : null, obj == null ? NameAVM2Item.generateCoerce(localData, generator, new TypeItem(DottedChain.NAMESPACE)) : null, obj != null ? obj : ins(AVM2Instructions.FindPropertyStrict, constants.getMultinameId(Multiname.createRTQName(attr, constants.getStringId(name, true)), true)), - call ? dupSetTemp(localData, generator, obj_temp) : null, - ns, NameAVM2Item.generateCoerce(localData, generator, new TypeItem(DottedChain.NAMESPACE)), - construct ? callargs : null, - ins(construct ? AVM2Instructions.ConstructProp : delete ? AVM2Instructions.DeleteProperty : AVM2Instructions.GetProperty, constants.getMultinameId(Multiname.createRTQName(attr, constants.getStringId(name, true)), true), construct ? callargs.size() : null), - call ? getTemp(localData, generator, obj_temp) : null, - call ? callargs : null, - call ? ins(AVM2Instructions.Call, callargs.size()) : null, - needsReturn ? null : ins(AVM2Instructions.Pop), - killTemp(localData, generator, Arrays.asList(obj_temp)) - ); - } + return toSourceMerge(localData, generator, + obj == null ? ns : null, obj == null ? NameAVM2Item.generateCoerce(localData, generator, new TypeItem(DottedChain.NAMESPACE)) : null, obj != null ? obj : ins(AVM2Instructions.FindPropertyStrict, constants.getMultinameId(Multiname.createRTQName(attr, constants.getStringId(name, true)), true)), + call ? dupSetTemp(localData, generator, obj_temp) : null, + ns, NameAVM2Item.generateCoerce(localData, generator, new TypeItem(DottedChain.NAMESPACE)), + construct ? callargs : null, + ins(construct ? AVM2Instructions.ConstructProp : delete ? AVM2Instructions.DeleteProperty : AVM2Instructions.GetProperty, constants.getMultinameId(Multiname.createRTQName(attr, constants.getStringId(name, true)), true), construct ? callargs.size() : null), + call ? getTemp(localData, generator, obj_temp) : null, + call ? callargs : null, + call ? ins(AVM2Instructions.Call, callargs.size()) : null, + needsReturn ? null : ins(AVM2Instructions.Pop), + killTemp(localData, generator, Arrays.asList(obj_temp)) + ); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/UnresolvedAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/UnresolvedAVM2Item.java index c40c93260..83ee345a5 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/UnresolvedAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/UnresolvedAVM2Item.java @@ -116,12 +116,12 @@ public class UnresolvedAVM2Item extends AssignableAVM2Item { } return -1; } + /* public GraphTargetItem getNs() { return ns; } */ - public void appendName(String name) { this.name = this.name.add(name); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/methodinfo_parser/MethodInfoLexer.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/methodinfo_parser/MethodInfoLexer.java index 540e9d239..0531753b8 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/methodinfo_parser/MethodInfoLexer.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/methodinfo_parser/MethodInfoLexer.java @@ -1,6 +1,6 @@ /* The following code was generated by JFlex 1.6.0 */ -/* + /* * Copyright (C) 2010-2016 JPEXS, All rights reserved. * * This library is free software; you can redistribute it and/or @@ -16,13 +16,12 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library. */ -/* Method info lexer specification */ + /* Method info lexer specification */ package com.jpexs.decompiler.flash.abc.methodinfo_parser; /** * This class is a scanner generated by - * JFlex 1.6.0 - * from the specification file + * JFlex 1.6.0 from the specification file * C:/FFDec/jpexs-decompiler/libsrc/ffdec_lib/lexers/actionscript3_methodinfo.flex */ public final class MethodInfoLexer { @@ -46,9 +45,8 @@ public final class MethodInfoLexer { /** * ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l - * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l - * at the beginning of a line - * l is of the form l = 2*k, k a non negative integer + * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l at the + * beginning of a line l is of the form l = 2*k, k a non negative integer */ private static final int ZZ_LEXSTATE[] = { 0, 0, 1, 1 @@ -231,9 +229,11 @@ public final class MethodInfoLexer { } private static int zzUnpackAction(String packed, int offset, int[] result) { - int i = 0; /* index in packed string */ + int i = 0; + /* index in packed string */ - int j = offset; /* index in unpacked array */ + int j = offset; + /* index in unpacked array */ int l = packed.length(); while (i < l) { @@ -279,9 +279,11 @@ public final class MethodInfoLexer { } private static int zzUnpackRowMap(String packed, int offset, int[] result) { - int i = 0; /* index in packed string */ + int i = 0; + /* index in packed string */ - int j = offset; /* index in unpacked array */ + int j = offset; + /* index in unpacked array */ int l = packed.length(); while (i < l) { @@ -466,9 +468,11 @@ public final class MethodInfoLexer { } private static int zzUnpackTrans(String packed, int offset, int[] result) { - int i = 0; /* index in packed string */ + int i = 0; + /* index in packed string */ - int j = offset; /* index in unpacked array */ + int j = offset; + /* index in unpacked array */ int l = packed.length(); while (i < l) { @@ -518,9 +522,11 @@ public final class MethodInfoLexer { } private static int zzUnpackAttribute(String packed, int offset, int[] result) { - int i = 0; /* index in packed string */ + int i = 0; + /* index in packed string */ - int j = offset; /* index in unpacked array */ + int j = offset; + /* index in unpacked array */ int l = packed.length(); while (i < l) { @@ -549,8 +555,8 @@ public final class MethodInfoLexer { private int zzLexicalState = YYINITIAL; /** - * this buffer contains the current text to be matched and is - * the source of the yytext() string + * this buffer contains the current text to be matched and is the source of + * the yytext() string */ private char zzBuffer[] = new char[ZZ_BUFFERSIZE]; @@ -570,8 +576,8 @@ public final class MethodInfoLexer { private int zzStartRead; /** - * endRead marks the last character in the buffer, that has been read - * from input + * endRead marks the last character in the buffer, that has been read from + * input */ private int zzEndRead; @@ -607,10 +613,10 @@ public final class MethodInfoLexer { private boolean zzEOFDone; /** - * The number of occupied positions in zzBuffer beyond zzEndRead. - * When a lead/high surrogate has been read from the input stream - * into the final zzBuffer position, this will have a value of 1; - * otherwise, it will have a value of 0. + * The number of occupied positions in zzBuffer beyond zzEndRead. When a + * lead/high surrogate has been read from the input stream into the final + * zzBuffer position, this will have a value of 1; otherwise, it will have a + * value of 0. */ private int zzFinalHighSurrogate = 0; @@ -654,9 +660,11 @@ public final class MethodInfoLexer { */ private static char[] zzUnpackCMap(String packed) { char[] map = new char[0x110000]; - int i = 0; /* index in packed string */ + int i = 0; + /* index in packed string */ - int j = 0; /* index in unpacked array */ + int j = 0; + /* index in unpacked array */ while (i < 2868) { int count = packed.charAt(i++); @@ -715,7 +723,8 @@ public final class MethodInfoLexer { if (totalRead > 0) { zzEndRead += totalRead; - if (totalRead == requested) { /* possibly more input available */ + if (totalRead == requested) { + /* possibly more input available */ if (Character.isHighSurrogate(zzBuffer[zzEndRead - 1])) { --zzEndRead; @@ -733,9 +742,11 @@ public final class MethodInfoLexer { * Closes the input stream. */ public final void yyclose() throws java.io.IOException { - zzAtEOF = true; /* indicate end of file */ + zzAtEOF = true; + /* indicate end of file */ - zzEndRead = zzStartRead; /* invalidate buffer */ + zzEndRead = zzStartRead; + /* invalidate buffer */ if (zzReader != null) { zzReader.close(); @@ -743,12 +754,12 @@ public final class MethodInfoLexer { } /** - * Resets the scanner to read from a new input stream. - * Does not close the old reader. + * Resets the scanner to read from a new input stream. Does not close the + * old reader. * * All internal variables are reset, the old input stream - * cannot be reused (internal buffer is discarded and lost). - * Lexical state is set to ZZ_INITIAL. + * cannot be reused (internal buffer is discarded and lost). Lexical + * state is set to ZZ_INITIAL. * * Internal scan buffer is resized down to its initial length, if it has * grown. @@ -794,13 +805,12 @@ public final class MethodInfoLexer { } /** - * Returns the character at position pos from the - * matched text. + * Returns the character at position pos from the matched text. * * It is equivalent to yytext().charAt(pos), but faster * - * @param pos the position of the character to fetch. - * A value from 0 to yylength()-1. + * @param pos the position of the character to fetch. A value from 0 to + * yylength()-1. * * @return the character at position pos */ @@ -818,14 +828,13 @@ public final class MethodInfoLexer { /** * Reports an error that occured while scanning. * - * In a wellformed scanner (no or only correct usage of - * yypushback(int) and a match-all fallback rule) this method - * will only be called with things that "Can't Possibly Happen". - * If this method is called, something is seriously wrong - * (e.g. a JFlex bug producing a faulty scanner etc.). + * In a wellformed scanner (no or only correct usage of yypushback(int) and + * a match-all fallback rule) this method will only be called with things + * that "Can't Possibly Happen". If this method is called, something is + * seriously wrong (e.g. a JFlex bug producing a faulty scanner etc.). * - * Usual syntax/scanner level error handling should be done - * in error fallback rules. + * Usual syntax/scanner level error handling should be done in error + * fallback rules. * * @param errorCode the code of the errormessage to display */ @@ -845,8 +854,8 @@ public final class MethodInfoLexer { * * They will be read again by then next call of the scanning method * - * @param number the number of characters to be read again. - * This number must not be greater than yylength()! + * @param number the number of characters to be read again. This number must + * not be greater than yylength()! */ public void yypushback(int number) { if (number > yylength()) { @@ -857,8 +866,8 @@ public final class MethodInfoLexer { } /** - * Resumes scanning until the next regular expression is matched, - * the end of input is encountered or an I/O-Error occurs. + * Resumes scanning until the next regular expression is matched, the end of + * input is encountered or an I/O-Error occurs. * * @return the next token * @exception java.io.IOException if any I/O-Error occurs diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java index dcfde7578..de2f8c1da 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java @@ -424,6 +424,10 @@ public final class MethodBody implements Cloneable { @Override public MethodBody clone() { + return clone(false); + } + + public MethodBody clone(boolean deepTraits) { try { MethodBody ret = (MethodBody) super.clone(); if (code != null) { @@ -437,10 +441,9 @@ public final class MethodBody implements Cloneable { } } - // maybe deep clone traits - /*if (traits != null) { - ret.traits = traits.clone(); - }*/ + if (deepTraits && traits != null) { + ret.traits = traits.clone(); + } ret.convertedItems = null; ret.convertException = null; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodInfo.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodInfo.java index 022b3dd2f..20f504f49 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodInfo.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodInfo.java @@ -386,4 +386,17 @@ public class MethodInfo { } return writer.hilightSpecial(rname, HighlightSpecialType.RETURNS); } + + public String getReturnTypeRaw(AVM2ConstantPool constants, List fullyQualifiedNames) { + String rname = "*"; + if (ret_type > 0) { + Multiname multiname = constants.getMultiname(ret_type); + if (multiname.kind != Multiname.TYPENAME && multiname.name_index > 0 && constants.getString(multiname.name_index).equals("void")) { + rname = "void"; + } else { + rname = multiname.getName(constants, fullyQualifiedNames, false); + } + } + return rname; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/ValueKind.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/ValueKind.java index 98a4cdc45..3e27a2714 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/ValueKind.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/ValueKind.java @@ -25,7 +25,7 @@ import com.jpexs.helpers.Helper; */ public class ValueKind { - public static final int CONSTANT_Decimal = 0x02; //decimal + public static final int CONSTANT_DecimalOrFloat = 0x02; //decimal or float depending on ABC version public static final int CONSTANT_Int = 0x03;// integer @@ -57,6 +57,8 @@ public class ValueKind { public static final int CONSTANT_PrivateNs = 0x05;// namespace + public static final int CONSTANT_Float4 = 0x1E;// float4 + private static final int[] optionalKinds = new int[]{0x03, 0x04, 0x06, 0x02, 0x01, 0x0B, 0x0A, 0x0C, 0x00, 0x08, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x05}; private static final String[] optionalKindNames = new String[]{"Int", "UInt", "Double", "Decimal", "Utf8", "True", "False", "Null", "Undefined", "Namespace", "PackageNamespace", "PackageInternalNs", "ProtectedNamespace", "ExplicitNamespace", "StaticProtectedNs", "PrivateNamespace"}; @@ -137,7 +139,7 @@ public class ValueKind { case CONSTANT_Double: ret = "" + constants.getDouble(value_index); break; - case CONSTANT_Decimal: + case CONSTANT_DecimalOrFloat: ret = "" + constants.getDecimal(value_index); break; case CONSTANT_Utf8: @@ -179,7 +181,7 @@ public class ValueKind { case CONSTANT_Double: ret = "Double(" + constants.getDouble(value_index) + ")"; break; - case CONSTANT_Decimal: + case CONSTANT_DecimalOrFloat: ret = "Decimal(" + constants.getDecimal(value_index) + ")"; break; case CONSTANT_Utf8: diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/Trait.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/Trait.java index c161303b6..6c93a773d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/Trait.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/Trait.java @@ -42,6 +42,9 @@ import com.jpexs.decompiler.flash.abc.types.NamespaceSet; import com.jpexs.decompiler.flash.abc.types.ScriptInfo; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.exporters.script.Dependency; +import com.jpexs.decompiler.flash.exporters.script.DependencyParser; +import com.jpexs.decompiler.flash.exporters.script.DependencyType; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.helpers.NulWriter; import com.jpexs.decompiler.flash.tags.ABCContainerTag; @@ -50,6 +53,8 @@ import com.jpexs.helpers.Helper; import java.io.Serializable; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -169,129 +174,18 @@ public abstract class Trait implements Cloneable, Serializable { return ret; } - protected void parseImportsUsagesFromMultiname(String ignoredCustom, ABC abc, List imports, List uses, Multiname m, DottedChain ignorePackage, List fullyQualifiedNames) { - if (m != null) { - if (m.kind == Multiname.TYPENAME) { - if (m.qname_index != 0) { - parseImportsUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(m.qname_index), ignorePackage, fullyQualifiedNames); - } - for (Integer i : m.params) { - if (i != 0) { - parseImportsUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(i), ignorePackage, fullyQualifiedNames); - } - } - return; - } - Namespace ns = m.getNamespace(abc.constants); - String name = m.getName(abc.constants, fullyQualifiedNames, true); - NamespaceSet nss = m.getNamespaceSet(abc.constants); - if (ns != null) { - parseImportsUsagesFromNS(ignoredCustom, abc, imports, uses, m.namespace_index, ignorePackage, name); - } - if (nss != null) { - for (int n : nss.namespaces) { - parseImportsUsagesFromNS(ignoredCustom, abc, imports, uses, n, ignorePackage, nss.namespaces.length > 1 ? "" : name); - } - } - } - } - - private boolean parseUsagesFromNS(String ignoredCustom, ABC abc, List imports, List uses, int namespace_index, DottedChain ignorePackage, String name) { - Namespace ns = abc.constants.getNamespace(namespace_index); - - if (ns.kind == Namespace.KIND_NAMESPACE) { - String nsVal = ns.getName(abc.constants).toRawString(); - for (ABCContainerTag abcTag : abc.getAbcTags()) { - DottedChain nsimport = abcTag.getABC().nsValueToName(nsVal); - if (nsimport.equals(AVM2Deobfuscation.BUILTIN)) { - return true; //handled, but import/use not added - } - if (!nsimport.isEmpty()) { - - if (!nsimport.getWithoutLast().equals(ignorePackage) && !imports.contains(nsimport)) { - imports.add(nsimport); - } - if (ignoredCustom != null && nsVal.equals(ignoredCustom)) { - return true; - } - if (!uses.contains(nsimport.getLast())) { - uses.add(nsimport.getLast()); - } - return true; - } - } - } - return false; - } - - protected void parseImportsUsagesFromNS(String ignoredCustom, ABC abc, List imports, List uses, int namespace_index, DottedChain ignorePackage, String name) { - Namespace ns = abc.constants.getNamespace(namespace_index); - if (name.isEmpty()) { - name = "*"; - } - DottedChain newimport = ns.getName(abc.constants); - - if (parseUsagesFromNS(ignoredCustom, abc, imports, uses, namespace_index, ignorePackage, name)) { - return; - } else if ((ns.kind != Namespace.KIND_PACKAGE) && (ns.kind != Namespace.KIND_PACKAGE_INTERNAL)) { - return; - } - newimport = newimport.add(name); - if (!imports.contains(newimport)) { - DottedChain pkg = newimport.getWithoutLast(); //.substring(0, newimport.lastIndexOf('.')); - if (pkg.equals(InitVectorAVM2Item.VECTOR_PACKAGE)) { //special case - is imported always - return; - } - if (!pkg.equals(ignorePackage)) { - imports.add(newimport); - } - } - //} - } - - protected void parseUsagesFromMultiname(String ignoredCustom, ABC abc, List imports, List uses, Multiname m, DottedChain ignorePackage, List fullyQualifiedNames) { - if (m != null) { - if (m.kind == Multiname.TYPENAME) { - if (m.qname_index != 0) { - parseUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(m.qname_index), ignorePackage, fullyQualifiedNames); - } - for (Integer i : m.params) { - if (i != 0) { - parseUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(i), ignorePackage, fullyQualifiedNames); - } - } - return; - } - Namespace ns = m.getNamespace(abc.constants); - String name = m.getName(abc.constants, fullyQualifiedNames, false); - NamespaceSet nss = m.getNamespaceSet(abc.constants); - if (ns != null) { - parseUsagesFromNS(ignoredCustom, abc, imports, uses, m.namespace_index, ignorePackage, name); - } - if (nss != null) { - if (nss.namespaces.length == 1) { - parseUsagesFromNS(ignoredCustom, abc, imports, uses, nss.namespaces[0], ignorePackage, name); - } else { - for (int n : nss.namespaces) { - parseUsagesFromNS(ignoredCustom, abc, imports, uses, n, ignorePackage, ""); - } - } - } - } - } - protected DottedChain getPackage(ABC abc) { return getName(abc).getNamespace(abc.constants).getName(abc.constants); } - public void getImportsUsages(String ignoredCustom, ABC abc, List imports, List uses, DottedChain ignorePackage, List fullyQualifiedNames) { + public void getDependencies(String ignoredCustom, ABC abc, List dependencies, List uses, DottedChain ignorePackage, List fullyQualifiedNames) { if (ignoredCustom == null) { Namespace n = getName(abc).getNamespace(abc.constants); if (n.kind == Namespace.KIND_NAMESPACE) { ignoredCustom = n.getName(abc.constants).toRawString(); } } - parseUsagesFromMultiname(ignoredCustom, abc, imports, uses, getName(abc), ignorePackage, fullyQualifiedNames); + DependencyParser.parseUsagesFromMultiname(ignoredCustom, abc, dependencies, uses, getName(abc), ignorePackage, fullyQualifiedNames, DependencyType.NAMESPACE); } private static final String[] builtInClasses = {"ArgumentError", "arguments", "Array", "Boolean", "Class", "Date", "DefinitionError", "Error", "EvalError", "Function", "int", "JSON", "Math", "Namespace", "Number", "Object", "QName", "RangeError", "ReferenceError", "RegExp", "SecurityError", "String", "SyntaxError", "TypeError", "uint", "URIError", "VerifyError", "XML", "XMLList"}; @@ -320,21 +214,34 @@ public abstract class Trait implements Cloneable, Serializable { } //imports - List imports = new ArrayList<>(); + List dependencies = new ArrayList<>(); List uses = new ArrayList<>(); String customNs = null; Namespace ns = getName(abc).getNamespace(abc.constants); if (ns.kind == Namespace.KIND_NAMESPACE) { customNs = ns.getName(abc.constants).toRawString(); } - getImportsUsages(customNs, abc, imports, uses, ignorePackage, new ArrayList<>()); + getDependencies(customNs, abc, dependencies, uses, ignorePackage, new ArrayList<>()); + + List imports = new ArrayList<>(); + for (Dependency d : dependencies) { + if (!imports.contains(d.getId())) { + imports.add(d.getId()); + } + } List importnames = new ArrayList<>(); importnames.addAll(namesInThisPackage); + importnames.addAll(Arrays.asList(builtInClasses)); for (int i = 0; i < imports.size(); i++) { DottedChain ipath = imports.get(i); + if (ipath.getWithoutLast().equals(ignorePackage)) { //do not check classes from same package, they are imported automatically + imports.remove(i); + i--; + continue; + } String name = ipath.getLast(); - if (importnames.contains(name) || isBuiltInClass(name)) { + if (importnames.contains(name)) { imports.remove(i); i--; fullyQualifiedNames.add(new DottedChain(name)); @@ -358,6 +265,7 @@ public abstract class Trait implements Cloneable, Serializable { } boolean hasImport = false; + Collections.sort(imports); for (DottedChain imp : imports) { if (imp.size() > 1) { //No imports from root package writer.appendNoHilight("import " + imp.toPrintableString(true) + ";").newLine(); @@ -375,64 +283,6 @@ public abstract class Trait implements Cloneable, Serializable { } } - protected void parseImportsUsagesFromMethodInfo(String ignoredCustom, ABC abc, int method_index, List imports, List uses, DottedChain ignorePackage, List fullyQualifiedNames, List visitedMethods) { - if ((method_index < 0) || (method_index >= abc.method_info.size())) { - return; - } - visitedMethods.add(method_index); - if (abc.method_info.get(method_index).ret_type != 0) { - parseImportsUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(abc.method_info.get(method_index).ret_type), ignorePackage, fullyQualifiedNames); - } - for (int t : abc.method_info.get(method_index).param_types) { - if (t != 0) { - parseImportsUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(t), ignorePackage, fullyQualifiedNames); - } - } - MethodBody body = abc.findBody(method_index); - if (body != null) { - body.traits.getImportsUsages(ignoredCustom, abc, imports, uses, ignorePackage, fullyQualifiedNames); - for (ABCException ex : body.exceptions) { - parseImportsUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(ex.type_index), ignorePackage, fullyQualifiedNames); - } - for (AVM2Instruction ins : body.getCode().code) { - if (ins.definition instanceof AlchemyTypeIns) { - DottedChain nimport = AlchemyTypeIns.ALCHEMY_PACKAGE.add(ins.definition.instructionName); - if (!imports.contains(nimport)) { - imports.add(nimport); - } - } - if (ins.definition instanceof NewFunctionIns) { - if (ins.operands[0] != method_index) { - if (!visitedMethods.contains(ins.operands[0])) { - parseImportsUsagesFromMethodInfo(ignoredCustom, abc, ins.operands[0], imports, uses, ignorePackage, fullyQualifiedNames, visitedMethods); - } - } - } - if ((ins.definition instanceof FindPropertyStrictIns) - || (ins.definition instanceof FindPropertyIns) - || (ins.definition instanceof GetLexIns) - || (ins.definition instanceof CoerceIns) - || (ins.definition instanceof AsTypeIns)) { - int m = ins.operands[0]; - if (m != 0) { - if (m < abc.constants.getMultinameCount()) { - parseImportsUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(m), ignorePackage, fullyQualifiedNames); - } - } - } else { - for (int k = 0; k < ins.definition.operands.length; k++) { - if (ins.definition.operands[k] == AVM2Code.DAT_MULTINAME_INDEX) { - int multinameIndex = ins.operands[k]; - if (multinameIndex < abc.constants.getMultinameCount()) { - parseUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(multinameIndex), ignorePackage, fullyQualifiedNames); - } - } - } - } - } - } - } - public final GraphTextWriter getMetaData(Trait parent, ConvertData convertData, ABC abc, GraphTextWriter writer) { List>> md = getMetaDataTable(parent, convertData, abc); for (Entry> en : md) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java index d21b8a12d..f21028ecc 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java @@ -25,6 +25,9 @@ import com.jpexs.decompiler.flash.abc.types.MethodBody; import com.jpexs.decompiler.flash.abc.types.Multiname; import com.jpexs.decompiler.flash.abc.types.Namespace; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.exporters.script.Dependency; +import com.jpexs.decompiler.flash.exporters.script.DependencyParser; +import com.jpexs.decompiler.flash.exporters.script.DependencyType; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.helpers.NulWriter; import com.jpexs.decompiler.graph.DottedChain; @@ -48,22 +51,7 @@ public class TraitClass extends Trait implements TraitWithSlot { @Override public void delete(ABC abc, boolean d) { - ClassInfo classInfo = abc.class_info.get(class_info); - classInfo.deleted = d; - InstanceInfo instanceInfo = abc.instance_info.get(class_info); - instanceInfo.deleted = d; - - classInfo.static_traits.delete(abc, d); - abc.method_info.get(classInfo.cinit_index).delete(abc, d); - - instanceInfo.instance_traits.delete(abc, d); - abc.method_info.get(instanceInfo.iinit_index).delete(abc, d); - - int protectedNS = instanceInfo.protectedNS; - if (protectedNS != 0) { - abc.constants.getNamespace(protectedNS).deleted = d; - } - + abc.deleteClass(class_info, d); abc.constants.getMultiname(name_index).deleted = d; } @@ -78,32 +66,31 @@ public class TraitClass extends Trait implements TraitWithSlot { } @Override - public void getImportsUsages(String customNs, ABC abc, List imports, List uses, DottedChain ignorePackage, List fullyQualifiedNames) { - super.getImportsUsages(customNs, abc, imports, uses, ignorePackage == null ? getPackage(abc) : ignorePackage, fullyQualifiedNames); + public void getDependencies(String customNs, ABC abc, List dependencies, List uses, DottedChain ignorePackage, List fullyQualifiedNames) { + super.getDependencies(customNs, abc, dependencies, uses, ignorePackage == null ? getPackage(abc) : ignorePackage, fullyQualifiedNames); ClassInfo classInfo = abc.class_info.get(class_info); InstanceInfo instanceInfo = abc.instance_info.get(class_info); DottedChain packageName = instanceInfo.getName(abc.constants).getNamespace(abc.constants).getName(abc.constants); //assume not null name - parseImportsUsagesFromMultiname(customNs, abc, imports, uses, abc.constants.getMultiname(instanceInfo.name_index), packageName, fullyQualifiedNames); - + //DependencyParser.parseDependenciesFromMultiname(customNs, abc, dependencies, uses, abc.constants.getMultiname(instanceInfo.name_index), packageName, fullyQualifiedNames); if (instanceInfo.super_index > 0) { - parseImportsUsagesFromMultiname(customNs, abc, imports, uses, abc.constants.getMultiname(instanceInfo.super_index), packageName, fullyQualifiedNames); + DependencyParser.parseDependenciesFromMultiname(customNs, abc, dependencies, uses, abc.constants.getMultiname(instanceInfo.super_index), packageName, fullyQualifiedNames, DependencyType.INHERITANCE); } for (int i : instanceInfo.interfaces) { - parseImportsUsagesFromMultiname(customNs, abc, imports, uses, abc.constants.getMultiname(i), packageName, fullyQualifiedNames); + DependencyParser.parseDependenciesFromMultiname(customNs, abc, dependencies, uses, abc.constants.getMultiname(i), packageName, fullyQualifiedNames, DependencyType.INHERITANCE); } //static - classInfo.static_traits.getImportsUsages(customNs, abc, imports, uses, packageName, fullyQualifiedNames); + classInfo.static_traits.getDependencies(customNs, abc, dependencies, uses, packageName, fullyQualifiedNames); //static initializer - parseImportsUsagesFromMethodInfo(customNs, abc, classInfo.cinit_index, imports, uses, packageName, fullyQualifiedNames, new ArrayList<>()); + DependencyParser.parseDependenciesFromMethodInfo(customNs, abc, classInfo.cinit_index, dependencies, uses, packageName, fullyQualifiedNames, new ArrayList<>()); //instance - instanceInfo.instance_traits.getImportsUsages(customNs, abc, imports, uses, packageName, fullyQualifiedNames); + instanceInfo.instance_traits.getDependencies(customNs, abc, dependencies, uses, packageName, fullyQualifiedNames); //instance initializer - parseImportsUsagesFromMethodInfo(customNs, abc, instanceInfo.iinit_index, imports, uses, packageName, fullyQualifiedNames, new ArrayList<>()); + DependencyParser.parseDependenciesFromMethodInfo(customNs, abc, instanceInfo.iinit_index, dependencies, uses, packageName, fullyQualifiedNames, new ArrayList<>()); } @Override @@ -144,14 +131,16 @@ public class TraitClass extends Trait implements TraitWithSlot { if (bodyIndex != -1) { writer.startTrait(GraphTextWriter.TRAIT_CLASS_INITIALIZER); writer.startMethod(classInfo.cinit_index); - if (!classInitializerIsEmpty) { - writer.startBlock(); - abc.bodies.get(bodyIndex).toString(path +/*packageName +*/ "/" + instanceInfoName + ".staticinitializer", exportMode, abc, this, writer, fullyQualifiedNames); - writer.endBlock(); - } else { - //Note: There must be trait/method highlight even if the initializer is empty to TraitList in GUI to work correctly - //TODO: handle this better in GUI(?) - writer.append(" ").newLine(); + if (exportMode != ScriptExportMode.AS_METHOD_STUBS) { + if (!classInitializerIsEmpty) { + writer.startBlock(); + abc.bodies.get(bodyIndex).toString(path +/*packageName +*/ "/" + instanceInfoName + ".staticinitializer", exportMode, abc, this, writer, fullyQualifiedNames); + writer.endBlock(); + } else { + //Note: There must be trait/method highlight even if the initializer is empty to TraitList in GUI to work correctly + //TODO: handle this better in GUI(?) + writer.append(" ").newLine(); + } } writer.endMethod(); writer.endTrait(); @@ -191,8 +180,10 @@ public class TraitClass extends Trait implements TraitWithSlot { MethodBody body = bodyIndex == -1 ? null : abc.bodies.get(bodyIndex); abc.method_info.get(instanceInfo.iinit_index).getParamStr(writer, abc.constants, body, abc, fullyQualifiedNames); writer.appendNoHilight(")").startBlock(); - if (body != null) { - body.toString(path +/*packageName +*/ "/" + instanceInfoName + ".initializer", exportMode, abc, this, writer, fullyQualifiedNames); + if (exportMode != ScriptExportMode.AS_METHOD_STUBS) { + if (body != null) { + body.toString(path +/*packageName +*/ "/" + instanceInfoName + ".initializer", exportMode, abc, this, writer, fullyQualifiedNames); + } } writer.endBlock().newLine(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitFunction.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitFunction.java index 887971236..b02f88e61 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitFunction.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitFunction.java @@ -20,6 +20,8 @@ import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.types.ConvertData; import com.jpexs.decompiler.flash.abc.types.MethodBody; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.exporters.script.Dependency; +import com.jpexs.decompiler.flash.exporters.script.DependencyParser; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.helpers.NulWriter; import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; @@ -123,14 +125,14 @@ public class TraitFunction extends Trait implements TraitWithSlot { } @Override - public void getImportsUsages(String customNs, ABC abc, List imports, List uses, DottedChain ignorePackage, List fullyQualifiedNames) { + public void getDependencies(String customNs, ABC abc, List dependencies, List uses, DottedChain ignorePackage, List fullyQualifiedNames) { if (ignorePackage == null) { ignorePackage = getPackage(abc); } - super.getImportsUsages(customNs, abc, imports, uses, ignorePackage, fullyQualifiedNames); + super.getDependencies(customNs, abc, dependencies, uses, ignorePackage, fullyQualifiedNames); //if (method_info != 0) { - parseImportsUsagesFromMethodInfo(customNs, abc, method_info, imports, uses, ignorePackage, fullyQualifiedNames, new ArrayList<>()); + DependencyParser.parseDependenciesFromMethodInfo(customNs, abc, method_info, dependencies, uses, ignorePackage, fullyQualifiedNames, new ArrayList<>()); } } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitMethodGetterSetter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitMethodGetterSetter.java index cc836a0ae..1ed3656a0 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitMethodGetterSetter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitMethodGetterSetter.java @@ -23,8 +23,11 @@ import com.jpexs.decompiler.flash.abc.types.MethodInfo; import com.jpexs.decompiler.flash.abc.types.Namespace; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.exporters.script.Dependency; +import com.jpexs.decompiler.flash.exporters.script.DependencyParser; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.helpers.NulWriter; +import com.jpexs.decompiler.flash.helpers.hilight.HighlightData; import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.decompiler.graph.ScopeStack; @@ -58,11 +61,11 @@ public class TraitMethodGetterSetter extends Trait { } @Override - public void getImportsUsages(String customNs, ABC abc, List imports, List uses, DottedChain ignorePackage, List fullyQualifiedNames) { + public void getDependencies(String customNs, ABC abc, List dependencies, List uses, DottedChain ignorePackage, List fullyQualifiedNames) { if (ignorePackage == null) { ignorePackage = getPackage(abc); } - super.getImportsUsages(customNs, abc, imports, uses, ignorePackage, fullyQualifiedNames); + super.getDependencies(customNs, abc, dependencies, uses, ignorePackage, fullyQualifiedNames); if (customNs == null) { Namespace n = getName(abc).getNamespace(abc.constants); @@ -72,7 +75,7 @@ public class TraitMethodGetterSetter extends Trait { } //if (method_info != 0) { - parseImportsUsagesFromMethodInfo(customNs, abc, method_info, imports, uses, ignorePackage, fullyQualifiedNames, new ArrayList<>()); + DependencyParser.parseDependenciesFromMethodInfo(customNs, abc, method_info, dependencies, uses, ignorePackage, fullyQualifiedNames, new ArrayList<>()); } } @@ -110,9 +113,11 @@ public class TraitMethodGetterSetter extends Trait { path = path + "." + getName(abc).getName(abc.constants, fullyQualifiedNames, false); convertHeader(parent, convertData, path, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); int bodyIndex = abc.findBodyIndex(method_info); - if (!(classIndex != -1 && abc.instance_info.get(classIndex).isInterface() || bodyIndex == -1)) { - if (bodyIndex != -1) { - abc.bodies.get(bodyIndex).convert(convertData, path, exportMode, isStatic, method_info, scriptIndex, classIndex, abc, this, new ScopeStack(), 0, writer, fullyQualifiedNames, null, true); + if (exportMode != ScriptExportMode.AS_METHOD_STUBS) { + if (!(classIndex != -1 && abc.instance_info.get(classIndex).isInterface() || bodyIndex == -1)) { + if (bodyIndex != -1) { + abc.bodies.get(bodyIndex).convert(convertData, path, exportMode, isStatic, method_info, scriptIndex, classIndex, abc, this, new ScopeStack(), 0, writer, fullyQualifiedNames, null, true); + } } } writer.endMethod(); @@ -133,8 +138,30 @@ public class TraitMethodGetterSetter extends Trait { writer.appendNoHilight(";"); } else { writer.startBlock(); - if (bodyIndex != -1) { - abc.bodies.get(bodyIndex).toString(path, exportMode, abc, this, writer, fullyQualifiedNames); + if (exportMode != ScriptExportMode.AS_METHOD_STUBS) { + if (bodyIndex != -1) { + abc.bodies.get(bodyIndex).toString(path, exportMode, abc, this, writer, fullyQualifiedNames); + } + } else { + String retTypeRaw = abc.method_info.get(method_info).getReturnTypeRaw(abc.constants, fullyQualifiedNames); + switch (retTypeRaw) { + case "void": + break; + case "int": + case "uint": + writer.append("return 0; //autogenerated").newLine(); + break; + case "double": + case "float": + writer.append("return 0.0; //autogenerated").newLine(); + break; + case "String": + writer.append("return \"\"; //autogenerated").newLine(); + break; + default: + writer.append("return null; //autogenerated").newLine(); + break; + } } writer.endBlock(); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitSlotConst.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitSlotConst.java index 6971590a8..29337bd30 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitSlotConst.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitSlotConst.java @@ -26,6 +26,9 @@ import com.jpexs.decompiler.flash.abc.types.Namespace; import com.jpexs.decompiler.flash.abc.types.ValueKind; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.exporters.script.Dependency; +import com.jpexs.decompiler.flash.exporters.script.DependencyParser; +import com.jpexs.decompiler.flash.exporters.script.DependencyType; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.helpers.NulWriter; import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; @@ -103,7 +106,7 @@ public class TraitSlotConst extends Trait implements TraitWithSlot { return writer; } - public void getValueStr(Trait parent, ConvertData convertData, GraphTextWriter writer, ABC abc, List fullyQualifiedNames) throws InterruptedException { + public void getValueStr(ScriptExportMode exportMode, Trait parent, ConvertData convertData, GraphTextWriter writer, ABC abc, List fullyQualifiedNames) throws InterruptedException { if (convertData.assignedValues.containsKey(this)) { AssignedValue assignment = convertData.assignedValues.get(this); @@ -116,8 +119,9 @@ public class TraitSlotConst extends Trait implements TraitWithSlot { writer.appendNoHilight(assignment.method); writer.newLine(); } - - assignment.value.toString(writer, LocalData.create(abc.constants, new HashMap<>(), fullyQualifiedNames)); + if (exportMode != ScriptExportMode.AS_METHOD_STUBS) { + assignment.value.toString(writer, LocalData.create(abc.constants, new HashMap<>(), fullyQualifiedNames)); + } writer.endMethod(); writer.endTrait(); return; @@ -162,7 +166,7 @@ public class TraitSlotConst extends Trait implements TraitWithSlot { getNameStr(writer, abc, fullyQualifiedNames); if (value_kind != 0 || convertData.assignedValues.containsKey(this)) { writer.appendNoHilight(" = "); - getValueStr(parent, convertData, writer, abc, fullyQualifiedNames); + getValueStr(exportMode, parent, convertData, writer, abc, fullyQualifiedNames); } return writer.appendNoHilight(";").newLine(); } @@ -171,7 +175,7 @@ public class TraitSlotConst extends Trait implements TraitWithSlot { public void convert(Trait parent, ConvertData convertData, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { getNameStr(writer, abc, fullyQualifiedNames); if (value_kind != 0 || convertData.assignedValues.containsKey(this)) { - getValueStr(parent, convertData, writer, abc, fullyQualifiedNames); + getValueStr(exportMode, parent, convertData, writer, abc, fullyQualifiedNames); } } @@ -196,12 +200,12 @@ public class TraitSlotConst extends Trait implements TraitWithSlot { } @Override - public void getImportsUsages(String customNs, ABC abc, List imports, List uses, DottedChain ignorePackage, List fullyQualifiedNames) { + public void getDependencies(String customNs, ABC abc, List dependencies, List uses, DottedChain ignorePackage, List fullyQualifiedNames) { if (ignorePackage == null) { ignorePackage = getPackage(abc); } - super.getImportsUsages(customNs, abc, imports, uses, ignorePackage, fullyQualifiedNames); - parseImportsUsagesFromMultiname(customNs, abc, imports, uses, abc.constants.getMultiname(type_index), getPackage(abc), fullyQualifiedNames); + super.getDependencies(customNs, abc, dependencies, uses, ignorePackage, fullyQualifiedNames); + DependencyParser.parseDependenciesFromMultiname(customNs, abc, dependencies, uses, abc.constants.getMultiname(type_index), getPackage(abc), fullyQualifiedNames, DependencyType.SIGNATURE); } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/Traits.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/Traits.java index ad3ed16dc..b863ed879 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/Traits.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/Traits.java @@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.types.ConvertData; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.exporters.script.Dependency; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.helpers.NulWriter; import com.jpexs.decompiler.graph.DottedChain; @@ -244,9 +245,9 @@ public class Traits implements Cloneable, Serializable { } } - public void getImportsUsages(String customNs, ABC abc, List imports, List uses, DottedChain ignorePackage, List fullyQualifiedNames) { + public void getDependencies(String customNs, ABC abc, List dependencies, List uses, DottedChain ignorePackage, List fullyQualifiedNames) { for (Trait t : traits) { - t.getImportsUsages(customNs, abc, imports, uses, ignorePackage, fullyQualifiedNames); + t.getDependencies(customNs, abc, dependencies, uses, ignorePackage, fullyQualifiedNames); } } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/Action.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/Action.java index 89a9cefc8..53b0bc1bc 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/Action.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/Action.java @@ -568,7 +568,7 @@ public abstract class Action implements GraphSourceItem { writer.appendNoHilight(Helper.byteToHex(fileData[(int) (fileOffset + i)])); } - + writer.appendNoHilight(")"); } @@ -1114,7 +1114,7 @@ public abstract class Action implements GraphSourceItem { } /*ActionJump && ActionIf removed*/ - /*if ((action instanceof ActionEnumerate2) || (action instanceof ActionEnumerate)) { + /*if ((action instanceof ActionEnumerate2) || (action instanceof ActionEnumerate)) { loopStart = ip + 1; isForIn = true; ip += 4; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/FunctionActionItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/FunctionActionItem.java index b970960e7..93cb192c3 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/FunctionActionItem.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/FunctionActionItem.java @@ -308,16 +308,12 @@ public class FunctionActionItem extends ActionItem { } else { v.setBoxedValue(new DirectValueActionItem(new RegisterNumber(registerNames.indexOf(varName), varName))); } + } else if (v.isDefinition()) { + v.setBoxedValue(new DefineLocalActionItem(null, null, ((ActionSourceGenerator) generator).pushConstTargetItem(varName), stored)); + } else if (stored != null) { + v.setBoxedValue(new SetVariableActionItem(null, null, ((ActionSourceGenerator) generator).pushConstTargetItem(varName), stored)); } else { - if (v.isDefinition()) { - v.setBoxedValue(new DefineLocalActionItem(null, null, ((ActionSourceGenerator) generator).pushConstTargetItem(varName), stored)); - } else { - if (stored != null) { - v.setBoxedValue(new SetVariableActionItem(null, null, ((ActionSourceGenerator) generator).pushConstTargetItem(varName), stored)); - } else { - v.setBoxedValue(new GetVariableActionItem(null, null, ((ActionSourceGenerator) generator).pushConstTargetItem(varName))); - } - } + v.setBoxedValue(new GetVariableActionItem(null, null, ((ActionSourceGenerator) generator).pushConstTargetItem(varName))); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/parser/pcode/FlasmLexer.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/parser/pcode/FlasmLexer.java index b9e8ba77d..81b68d4e9 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/parser/pcode/FlasmLexer.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/parser/pcode/FlasmLexer.java @@ -1,6 +1,6 @@ /* The following code was generated by JFlex 1.6.0 */ -/* + /* * Copyright (C) 2010-2016 JPEXS, All rights reserved. * * This library is free software; you can redistribute it and/or @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library. */ -/* Flash assembler language lexer specification */ + /* Flash assembler language lexer specification */ package com.jpexs.decompiler.flash.action.parser.pcode; import com.jpexs.decompiler.flash.action.parser.ActionParseException; @@ -27,8 +27,7 @@ import com.jpexs.decompiler.flash.ecma.Undefined; /** * This class is a scanner generated by - * JFlex 1.6.0 - * from the specification file + * JFlex 1.6.0 from the specification file * C:/Dropbox/Programovani/JavaSE/FFDec/libsrc/ffdec_lib/lexers/actionscript_pcode.flex */ public final class FlasmLexer { @@ -54,9 +53,8 @@ public final class FlasmLexer { /** * ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l - * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l - * at the beginning of a line - * l is of the form l = 2*k, k a non negative integer + * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l at the + * beginning of a line l is of the form l = 2*k, k a non negative integer */ private static final int ZZ_LEXSTATE[] = { 0, 0, 1, 1, 2, 2 @@ -238,9 +236,11 @@ public final class FlasmLexer { } private static int zzUnpackAction(String packed, int offset, int[] result) { - int i = 0; /* index in packed string */ + int i = 0; + /* index in packed string */ - int j = offset; /* index in unpacked array */ + int j = offset; + /* index in unpacked array */ int l = packed.length(); while (i < l) { @@ -282,9 +282,11 @@ public final class FlasmLexer { } private static int zzUnpackRowMap(String packed, int offset, int[] result) { - int i = 0; /* index in packed string */ + int i = 0; + /* index in packed string */ - int j = offset; /* index in unpacked array */ + int j = offset; + /* index in unpacked array */ int l = packed.length(); while (i < l) { @@ -458,9 +460,11 @@ public final class FlasmLexer { } private static int zzUnpackTrans(String packed, int offset, int[] result) { - int i = 0; /* index in packed string */ + int i = 0; + /* index in packed string */ - int j = offset; /* index in unpacked array */ + int j = offset; + /* index in unpacked array */ int l = packed.length(); while (i < l) { @@ -510,9 +514,11 @@ public final class FlasmLexer { } private static int zzUnpackAttribute(String packed, int offset, int[] result) { - int i = 0; /* index in packed string */ + int i = 0; + /* index in packed string */ - int j = offset; /* index in unpacked array */ + int j = offset; + /* index in unpacked array */ int l = packed.length(); while (i < l) { @@ -541,8 +547,8 @@ public final class FlasmLexer { private int zzLexicalState = YYINITIAL; /** - * this buffer contains the current text to be matched and is - * the source of the yytext() string + * this buffer contains the current text to be matched and is the source of + * the yytext() string */ private char zzBuffer[] = new char[ZZ_BUFFERSIZE]; @@ -562,8 +568,8 @@ public final class FlasmLexer { private int zzStartRead; /** - * endRead marks the last character in the buffer, that has been read - * from input + * endRead marks the last character in the buffer, that has been read from + * input */ private int zzEndRead; @@ -599,10 +605,10 @@ public final class FlasmLexer { private boolean zzEOFDone; /** - * The number of occupied positions in zzBuffer beyond zzEndRead. - * When a lead/high surrogate has been read from the input stream - * into the final zzBuffer position, this will have a value of 1; - * otherwise, it will have a value of 0. + * The number of occupied positions in zzBuffer beyond zzEndRead. When a + * lead/high surrogate has been read from the input stream into the final + * zzBuffer position, this will have a value of 1; otherwise, it will have a + * value of 0. */ private int zzFinalHighSurrogate = 0; @@ -642,9 +648,11 @@ public final class FlasmLexer { */ private static char[] zzUnpackCMap(String packed) { char[] map = new char[0x110000]; - int i = 0; /* index in packed string */ + int i = 0; + /* index in packed string */ - int j = 0; /* index in unpacked array */ + int j = 0; + /* index in unpacked array */ while (i < 2854) { int count = packed.charAt(i++); @@ -703,7 +711,8 @@ public final class FlasmLexer { if (totalRead > 0) { zzEndRead += totalRead; - if (totalRead == requested) { /* possibly more input available */ + if (totalRead == requested) { + /* possibly more input available */ if (Character.isHighSurrogate(zzBuffer[zzEndRead - 1])) { --zzEndRead; @@ -721,9 +730,11 @@ public final class FlasmLexer { * Closes the input stream. */ public final void yyclose() throws java.io.IOException { - zzAtEOF = true; /* indicate end of file */ + zzAtEOF = true; + /* indicate end of file */ - zzEndRead = zzStartRead; /* invalidate buffer */ + zzEndRead = zzStartRead; + /* invalidate buffer */ if (zzReader != null) { zzReader.close(); @@ -731,12 +742,12 @@ public final class FlasmLexer { } /** - * Resets the scanner to read from a new input stream. - * Does not close the old reader. + * Resets the scanner to read from a new input stream. Does not close the + * old reader. * * All internal variables are reset, the old input stream - * cannot be reused (internal buffer is discarded and lost). - * Lexical state is set to ZZ_INITIAL. + * cannot be reused (internal buffer is discarded and lost). Lexical + * state is set to ZZ_INITIAL. * * Internal scan buffer is resized down to its initial length, if it has * grown. @@ -782,13 +793,12 @@ public final class FlasmLexer { } /** - * Returns the character at position pos from the - * matched text. + * Returns the character at position pos from the matched text. * * It is equivalent to yytext().charAt(pos), but faster * - * @param pos the position of the character to fetch. - * A value from 0 to yylength()-1. + * @param pos the position of the character to fetch. A value from 0 to + * yylength()-1. * * @return the character at position pos */ @@ -806,14 +816,13 @@ public final class FlasmLexer { /** * Reports an error that occured while scanning. * - * In a wellformed scanner (no or only correct usage of - * yypushback(int) and a match-all fallback rule) this method - * will only be called with things that "Can't Possibly Happen". - * If this method is called, something is seriously wrong - * (e.g. a JFlex bug producing a faulty scanner etc.). + * In a wellformed scanner (no or only correct usage of yypushback(int) and + * a match-all fallback rule) this method will only be called with things + * that "Can't Possibly Happen". If this method is called, something is + * seriously wrong (e.g. a JFlex bug producing a faulty scanner etc.). * - * Usual syntax/scanner level error handling should be done - * in error fallback rules. + * Usual syntax/scanner level error handling should be done in error + * fallback rules. * * @param errorCode the code of the errormessage to display */ @@ -833,8 +842,8 @@ public final class FlasmLexer { * * They will be read again by then next call of the scanning method * - * @param number the number of characters to be read again. - * This number must not be greater than yylength()! + * @param number the number of characters to be read again. This number must + * not be greater than yylength()! */ public void yypushback(int number) { if (number > yylength()) { @@ -845,8 +854,8 @@ public final class FlasmLexer { } /** - * Resumes scanning until the next regular expression is matched, - * the end of input is encountered or an I/O-Error occurs. + * Resumes scanning until the next regular expression is matched, the end of + * input is encountered or an I/O-Error occurs. * * @return the next token * @exception java.io.IOException if any I/O-Error occurs diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/parser/script/ActionScriptLexer.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/parser/script/ActionScriptLexer.java index 29d96d52e..21ebb8b7b 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/parser/script/ActionScriptLexer.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/parser/script/ActionScriptLexer.java @@ -1,6 +1,6 @@ /* The following code was generated by JFlex 1.6.0 */ -/* + /* * Copyright (C) 2010-2016 JPEXS, All rights reserved. * * This library is free software; you can redistribute it and/or @@ -25,8 +25,7 @@ import java.util.Stack; /** * This class is a scanner generated by - * JFlex 1.6.0 - * from the specification file + * JFlex 1.6.0 from the specification file * D:/Dropbox/Programovani/JavaSE/FFDec/libsrc/ffdec_lib/lexers/actionscript_script.flex */ public final class ActionScriptLexer { @@ -58,9 +57,8 @@ public final class ActionScriptLexer { /** * ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l - * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l - * at the beginning of a line - * l is of the form l = 2*k, k a non negative integer + * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l at the + * beginning of a line l is of the form l = 2*k, k a non negative integer */ private static final int ZZ_LEXSTATE[] = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5 @@ -278,9 +276,11 @@ public final class ActionScriptLexer { } private static int zzUnpackAction(String packed, int offset, int[] result) { - int i = 0; /* index in packed string */ + int i = 0; + /* index in packed string */ - int j = offset; /* index in unpacked array */ + int j = offset; + /* index in unpacked array */ int l = packed.length(); while (i < l) { @@ -395,9 +395,11 @@ public final class ActionScriptLexer { } private static int zzUnpackRowMap(String packed, int offset, int[] result) { - int i = 0; /* index in packed string */ + int i = 0; + /* index in packed string */ - int j = offset; /* index in unpacked array */ + int j = offset; + /* index in unpacked array */ int l = packed.length(); while (i < l) { @@ -1100,9 +1102,11 @@ public final class ActionScriptLexer { } private static int zzUnpackTrans(String packed, int offset, int[] result) { - int i = 0; /* index in packed string */ + int i = 0; + /* index in packed string */ - int j = offset; /* index in unpacked array */ + int j = offset; + /* index in unpacked array */ int l = packed.length(); while (i < l) { @@ -1157,9 +1161,11 @@ public final class ActionScriptLexer { } private static int zzUnpackAttribute(String packed, int offset, int[] result) { - int i = 0; /* index in packed string */ + int i = 0; + /* index in packed string */ - int j = offset; /* index in unpacked array */ + int j = offset; + /* index in unpacked array */ int l = packed.length(); while (i < l) { @@ -1188,8 +1194,8 @@ public final class ActionScriptLexer { private int zzLexicalState = YYINITIAL; /** - * this buffer contains the current text to be matched and is - * the source of the yytext() string + * this buffer contains the current text to be matched and is the source of + * the yytext() string */ private char zzBuffer[] = new char[ZZ_BUFFERSIZE]; @@ -1209,8 +1215,8 @@ public final class ActionScriptLexer { private int zzStartRead; /** - * endRead marks the last character in the buffer, that has been read - * from input + * endRead marks the last character in the buffer, that has been read from + * input */ private int zzEndRead; @@ -1246,10 +1252,10 @@ public final class ActionScriptLexer { private boolean zzEOFDone; /** - * The number of occupied positions in zzBuffer beyond zzEndRead. - * When a lead/high surrogate has been read from the input stream - * into the final zzBuffer position, this will have a value of 1; - * otherwise, it will have a value of 0. + * The number of occupied positions in zzBuffer beyond zzEndRead. When a + * lead/high surrogate has been read from the input stream into the final + * zzBuffer position, this will have a value of 1; otherwise, it will have a + * value of 0. */ private int zzFinalHighSurrogate = 0; @@ -1326,9 +1332,11 @@ public final class ActionScriptLexer { */ private static char[] zzUnpackCMap(String packed) { char[] map = new char[0x110000]; - int i = 0; /* index in packed string */ + int i = 0; + /* index in packed string */ - int j = 0; /* index in unpacked array */ + int j = 0; + /* index in unpacked array */ while (i < 2916) { int count = packed.charAt(i++); @@ -1387,7 +1395,8 @@ public final class ActionScriptLexer { if (totalRead > 0) { zzEndRead += totalRead; - if (totalRead == requested) { /* possibly more input available */ + if (totalRead == requested) { + /* possibly more input available */ if (Character.isHighSurrogate(zzBuffer[zzEndRead - 1])) { --zzEndRead; @@ -1405,9 +1414,11 @@ public final class ActionScriptLexer { * Closes the input stream. */ public final void yyclose() throws java.io.IOException { - zzAtEOF = true; /* indicate end of file */ + zzAtEOF = true; + /* indicate end of file */ - zzEndRead = zzStartRead; /* invalidate buffer */ + zzEndRead = zzStartRead; + /* invalidate buffer */ if (zzReader != null) { zzReader.close(); @@ -1415,12 +1426,12 @@ public final class ActionScriptLexer { } /** - * Resets the scanner to read from a new input stream. - * Does not close the old reader. + * Resets the scanner to read from a new input stream. Does not close the + * old reader. * * All internal variables are reset, the old input stream - * cannot be reused (internal buffer is discarded and lost). - * Lexical state is set to ZZ_INITIAL. + * cannot be reused (internal buffer is discarded and lost). Lexical + * state is set to ZZ_INITIAL. * * Internal scan buffer is resized down to its initial length, if it has * grown. @@ -1466,13 +1477,12 @@ public final class ActionScriptLexer { } /** - * Returns the character at position pos from the - * matched text. + * Returns the character at position pos from the matched text. * * It is equivalent to yytext().charAt(pos), but faster * - * @param pos the position of the character to fetch. - * A value from 0 to yylength()-1. + * @param pos the position of the character to fetch. A value from 0 to + * yylength()-1. * * @return the character at position pos */ @@ -1490,14 +1500,13 @@ public final class ActionScriptLexer { /** * Reports an error that occured while scanning. * - * In a wellformed scanner (no or only correct usage of - * yypushback(int) and a match-all fallback rule) this method - * will only be called with things that "Can't Possibly Happen". - * If this method is called, something is seriously wrong - * (e.g. a JFlex bug producing a faulty scanner etc.). + * In a wellformed scanner (no or only correct usage of yypushback(int) and + * a match-all fallback rule) this method will only be called with things + * that "Can't Possibly Happen". If this method is called, something is + * seriously wrong (e.g. a JFlex bug producing a faulty scanner etc.). * - * Usual syntax/scanner level error handling should be done - * in error fallback rules. + * Usual syntax/scanner level error handling should be done in error + * fallback rules. * * @param errorCode the code of the errormessage to display */ @@ -1517,8 +1526,8 @@ public final class ActionScriptLexer { * * They will be read again by then next call of the scanning method * - * @param number the number of characters to be read again. - * This number must not be greater than yylength()! + * @param number the number of characters to be read again. This number must + * not be greater than yylength()! */ public void yypushback(int number) { if (number > yylength()) { @@ -1529,8 +1538,8 @@ public final class ActionScriptLexer { } /** - * Resumes scanning until the next regular expression is matched, - * the end of input is encountered or an I/O-Error occurs. + * Resumes scanning until the next regular expression is matched, the end of + * input is encountered or an I/O-Error occurs. * * @return the next token * @exception java.io.IOException if any I/O-Error occurs @@ -1626,7 +1635,8 @@ public final class ActionScriptLexer { } case 185: break; - case 3: { /*ignore*/ + case 3: { + /*ignore*/ } case 186: @@ -1963,7 +1973,8 @@ public final class ActionScriptLexer { case 250: break; case 68: { - string.append('\\'); /*illegal escape sequence*/ + string.append('\\'); + /*illegal escape sequence*/ } case 251: diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java index 36cd7e4bb..b23c62052 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java @@ -539,6 +539,11 @@ public class Configuration { @ConfigurationFile(".*\\.swc$") public static final ConfigurationItem playerLibLocation = null; + @ConfigurationDefaultString("") + @ConfigurationCategory("paths") + @ConfigurationDirectory + public static final ConfigurationItem flexSdkLocation = null; + @ConfigurationDefaultDouble(0.7) @ConfigurationName("gui.avm2.splitPane.vars.dividerLocationPercent") @ConfigurationInternal @@ -608,6 +613,10 @@ public class Configuration { @ConfigurationInternal public static final ConfigurationItem guiAvm2DocsSplitPaneDividerLocationPercent = null; + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("script") + public static final ConfigurationItem useFlexAs3Compiler = null; + private enum OSId { WINDOWS, OSX, UNIX diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/docs/docs.js b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/docs/docs.js index beeac0b56..d0f438957 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/docs/docs.js +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/docs/docs.js @@ -1,152 +1,152 @@ function bind(el, evt, func) { - if (el.addEventListener){ + if (el.addEventListener) { el.addEventListener(evt, func, false); } else if (el.attachEvent) { el.attachEvent('on' + evt, func); } } -function showhide(e,show){ - var cls = " "+e.className+" "; - if(show && cls.indexOf(" hidden ") > -1){ - cls = cls.replace(" hidden "," "); - }else if(!show && cls.indexOf(" hidden ") == -1 ){ - cls = cls + "hidden "; - } - cls = cls.trim(); - e.className = cls; -} - -function hideNoName(name){ - var lis = document.getElementsByTagName("li"); - loopi:for(var i=0;i