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 5d7900a0c..4d9a9e159 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 @@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.abc.types.Decimal; 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.types.annotations.Internal; import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.helpers.utf8.Utf8PrintWriter; import java.util.ArrayList; @@ -50,6 +51,7 @@ public class AVM2ConstantPool implements Cloneable { public List constant_multiname = new ArrayList<>(); + @Internal public Map dottedChainCache = new HashMap<>(); public synchronized int addInt(long value) { 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 047a85998..fbf1eff99 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 @@ -692,7 +692,7 @@ public class AVM2SourceGenerator implements SourceGenerator { scope = localData.scopeStack.size(); localData.scopeStack.add(new PropertyAVM2Item(null, item.functionName, abc, allABCs, new ArrayList<>(), localData.callStack)); } - ret.add(ins(AVM2Instructions.NewFunction, method(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))); + ret.add(ins(AVM2Instructions.NewFunction, method(abc.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)); @@ -1178,14 +1178,14 @@ public class AVM2SourceGenerator implements SourceGenerator { generateTraitsPhase3(initScope, isInterface, name, superName, true, localData, traitItems, classInfo.static_traits, st, new HashMap<>(), class_index); int init = 0; if (constructor == null || isInterface) { - instanceInfo.iinit_index = init = method(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*/); + instanceInfo.iinit_index = init = method(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) constructor; - instanceInfo.iinit_index = init = method(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*/); + instanceInfo.iinit_index = init = method(0, 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 staticMi = method(false, false, new ArrayList<>(), pkg, staticNeedsActivation, sinitVariables, initScope + (implementsStr.isEmpty() ? 0 : 1), false, 0, isInterface ? null : name, superName, false, localData, new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), staticInit, TypeItem.UNBOUNDED); + int staticMi = method(0, false, false, new ArrayList<>(), pkg, staticNeedsActivation, sinitVariables, initScope + (implementsStr.isEmpty() ? 0 : 1), false, 0, isInterface ? null : name, superName, false, localData, new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), staticInit, TypeItem.UNBOUNDED); MethodBody sinitBody = abc.findBody(staticMi); List sinitcode = new ArrayList<>(); @@ -1369,7 +1369,7 @@ public class AVM2SourceGenerator implements SourceGenerator { return false; } - public int method(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 { + public int method(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); @@ -1611,7 +1611,7 @@ public class AVM2SourceGenerator implements SourceGenerator { } } - MethodInfo mi = new MethodInfo(param_types, constructor ? 0 : typeName(localData, retType), 0/*name_index*/, 0, optional, new int[0]/*no param_names*/); + 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(); } @@ -1629,7 +1629,7 @@ public class AVM2SourceGenerator implements SourceGenerator { int mindex; if (!isInterface) { - MethodBody mbody = new MethodBody(abc); + MethodBody mbody = new MethodBody(abc, new Traits(), new byte[0], new ABCException[0]); if (needsActivation) { int slotId = 1; @@ -1984,10 +1984,10 @@ public class AVM2SourceGenerator implements SourceGenerator { if (mai.isStatic() != generateStatic) { continue; } - ((TraitMethodGetterSetter) traits[k]).method_info = method(false, isInterface, new ArrayList<>(), mai.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); + ((TraitMethodGetterSetter) traits[k]).method_info = method(abc.constants.getStringId(mai.functionName, true), false, isInterface, new ArrayList<>(), mai.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, isInterface, new ArrayList<>(), fai.pkg, fai.needsActivation, fai.subvariables, methodInitScope, fai.hasRest, fai.line, className, superName, false, localData, fai.paramTypes, fai.paramNames, fai.paramValues, fai.body, fai.retType); + ((TraitFunction) traits[k]).method_info = method(abc.constants.getStringId(fai.functionName, true), false, isInterface, new ArrayList<>(), fai.pkg, fai.needsActivation, fai.subvariables, methodInitScope, fai.hasRest, fai.line, className, superName, false, localData, fai.paramTypes, fai.paramNames, fai.paramValues, fai.body, fai.retType); } } } @@ -2137,7 +2137,7 @@ public class AVM2SourceGenerator implements SourceGenerator { Trait[] traitArr = generateTraitsPhase1(null, null, true, localData, commands, si.traits, class_index); generateTraitsPhase2(new ArrayList<>(), null/*FIXME*/, commands, traitArr, new ArrayList<>(), localData); MethodInfo mi = new MethodInfo(new int[0], 0, 0, 0, new ValueKind[0], new int[0]); - MethodBody mb = new MethodBody(abc); + 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; @@ -2162,21 +2162,23 @@ public class AVM2SourceGenerator implements SourceGenerator { if (abc.instance_info.get(tc.class_info).isInterface()) { mbCode.add(ins(AVM2Instructions.PushNull)); } else { - parentNamesAddNames(abc, allABCs, abc.instance_info.get(tc.class_info).name_index, parents, new ArrayList<>(), new ArrayList<>()); + parents.remove(0); //remove this class + + //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 mbCode.add(ins(AVM2Instructions.GetLex, parents.get(0))); } mbCode.add(ins(AVM2Instructions.NewClass, tc.class_info)); - if (!abc.instance_info.get(tc.class_info).isInterface()) { - for (int i = parents.size() - 1; i >= 1; i--) { - mbCode.add(ins(AVM2Instructions.PopScope)); - } + 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; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MetadataInfo.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MetadataInfo.java index 5ff880362..657d6b69a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MetadataInfo.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MetadataInfo.java @@ -27,6 +27,12 @@ public class MetadataInfo { public int[] values; + public MetadataInfo() { + this.name_index = 0; + this.keys = new int[0]; + this.values = new int[0]; + } + public MetadataInfo(int name_index, int[] keys, int[] values) { this.name_index = name_index; this.keys = keys; 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 eafea2014..0a928deec 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 @@ -94,10 +94,14 @@ public final class MethodBody implements Cloneable { @Internal private ABC abc; - public MethodBody(ABC abc) { + public MethodBody() { this.traits = new Traits(); this.codeBytes = SWFInputStream.BYTE_ARRAY_EMPTY; this.exceptions = new ABCException[0]; + this.abc = null; + } + + public void setAbc(ABC abc) { this.abc = abc; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/SwfXmlImporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/SwfXmlImporter.java index dc954bd88..7cc28ea68 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/SwfXmlImporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/SwfXmlImporter.java @@ -23,6 +23,7 @@ import com.jpexs.decompiler.flash.abc.types.ABCException; import com.jpexs.decompiler.flash.abc.types.ClassInfo; import com.jpexs.decompiler.flash.abc.types.Decimal; 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; @@ -102,6 +103,7 @@ import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -156,6 +158,28 @@ public class SwfXmlImporter { return field; } + private static void setFieldValue(Field field, Object obj, Object value) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException { + Field modifiersField = Field.class.getDeclaredField("modifiers"); + modifiersField.setAccessible(true); + + //Remove final attribute temporary (For example Multiname.namespace_set_index + int originalModifiers = field.getModifiers(); + if ((originalModifiers & Modifier.FINAL) > 0) { + modifiersField.setInt(field, originalModifiers & ~Modifier.FINAL); + } + + field.setAccessible(true); + + int newModifiers = field.getModifiers(); + + field.set(obj, value); + + //Put final back in + if (originalModifiers != newModifiers) { + modifiersField.setInt(field, originalModifiers); + } + } + private void processElement(Element element, Object obj, SWF swf, Tag tag) { Class cls = obj.getClass(); for (int i = 0; i < element.getAttributes().getLength(); i++) { @@ -165,7 +189,7 @@ public class SwfXmlImporter { try { Field field = getField(cls, name); String attrValue = attr.getValue(); - field.set(obj, getAs(field.getType(), attrValue)); + setFieldValue(field, obj, getAs(field.getType(), attrValue)); } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) { logger.log(Level.SEVERE, null, ex); } @@ -191,7 +215,7 @@ public class SwfXmlImporter { } } - field.set(obj, list); + setFieldValue(field, obj, list); } else if (childCls.isArray()) { List list = new ArrayList(); for (int j = 0; j < child.getChildNodes().getLength(); j++) { @@ -208,10 +232,10 @@ public class SwfXmlImporter { Array.set(array, j, list.get(j)); } - field.set(obj, array); + setFieldValue(field, obj, array); } else { Object childObj = processObject(child, null, swf, tag); - field.set(obj, childObj); + setFieldValue(field, obj, childObj); } } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException | NoSuchMethodException | InstantiationException | InvocationTargetException ex) { logger.log(Level.SEVERE, null, ex); @@ -274,7 +298,7 @@ public class SwfXmlImporter { CurvedEdgeRecord.class, EndShapeRecord.class, StraightEdgeRecord.class, StyleChangeRecord.class, BEVELFILTER.class, BLURFILTER.class, COLORMATRIXFILTER.class, CONVOLUTIONFILTER.class, DROPSHADOWFILTER.class, GLOWFILTER.class, GRADIENTBEVELFILTER.class, GRADIENTGLOWFILTER.class, - AVM2ConstantPool.class, Decimal.class, Namespace.class, NamespaceSet.class, Multiname.class, MethodInfo.class, + AVM2ConstantPool.class, Decimal.class, Namespace.class, NamespaceSet.class, Multiname.class, MethodInfo.class, MetadataInfo.class, ValueKind.class, InstanceInfo.class, Traits.class, TraitClass.class, TraitFunction.class, TraitMethodGetterSetter.class, TraitSlotConst.class, ClassInfo.class, ScriptInfo.class, MethodBody.class, ABCException.class}; diff --git a/libsrc/ffdec_lib/testdata/as3/as3.swf b/libsrc/ffdec_lib/testdata/as3/as3.swf index 9deac05bc..f8cf0bd98 100644 Binary files a/libsrc/ffdec_lib/testdata/as3/as3.swf and b/libsrc/ffdec_lib/testdata/as3/as3.swf differ diff --git a/libsrc/ffdec_lib/testdata/as3/classes/MyInterface.as b/libsrc/ffdec_lib/testdata/as3/classes/MyInterface.as new file mode 100644 index 000000000..e819c1137 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/as3/classes/MyInterface.as @@ -0,0 +1,9 @@ +package classes { + + public interface MyInterface { + + function interfaceMethod(a:int):int; + + } + +} diff --git a/libsrc/ffdec_lib/testdata/as3/classes/MyInterface2.as b/libsrc/ffdec_lib/testdata/as3/classes/MyInterface2.as new file mode 100644 index 000000000..e63aea9f5 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/as3/classes/MyInterface2.as @@ -0,0 +1,9 @@ +package classes { + + public interface MyInterface2 extends MyInterface{ + + function interface2Method(a:int,b:int):int; + + } + +} diff --git a/libsrc/ffdec_lib/testdata/as3/classes/Test.as b/libsrc/ffdec_lib/testdata/as3/classes/Test.as index 3881438c8..71c913284 100644 --- a/libsrc/ffdec_lib/testdata/as3/classes/Test.as +++ b/libsrc/ffdec_lib/testdata/as3/classes/Test.as @@ -5,7 +5,7 @@ import classes.myInternal; import flash.errors.EOFError; import flash.events.Event; - public class Test + public class Test implements MyInterface2 { private var testPriv:int=5; protected var testProt:int=9; @@ -17,6 +17,14 @@ { trace("hello"); } + + public function interfaceMethod(a:int):int { + return a+1; + } + + public function interface2Method(a:int,b:int):int { + return a + b; + } public function testIncDec() {