diff --git a/CHANGELOG.md b/CHANGELOG.md index 3862a128c..7d9fc7d47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ All notable changes to this project will be documented in this file. - [#2331] AS1/2 lite - support for fscommand2, `#strict` directive - [#2332] Imported fonts by class name not available in texts - [#2330] Windows EXE launcher (+ CLI) does not respect current working directory +- [#2335] AS3 direct editation - Type coercion of `&&` and `||` operators ## [21.1.0] - 2024-09-23 ### Added @@ -3609,6 +3610,7 @@ Major version of SWF to XML export changed to 2. [#2331]: https://www.free-decompiler.com/flash/issues/2331 [#2332]: https://www.free-decompiler.com/flash/issues/2332 [#2330]: https://www.free-decompiler.com/flash/issues/2330 +[#2335]: https://www.free-decompiler.com/flash/issues/2335 [#943]: https://www.free-decompiler.com/flash/issues/943 [#1812]: https://www.free-decompiler.com/flash/issues/1812 [#2287]: https://www.free-decompiler.com/flash/issues/2287 diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SourceGeneratorLocalData.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SourceGeneratorLocalData.java index c71f3a259..392e61439 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SourceGeneratorLocalData.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SourceGeneratorLocalData.java @@ -190,6 +190,11 @@ public class SourceGeneratorLocalData implements Serializable { * Opened namespaces */ public List openedNamespaces = new ArrayList<>(); + + /** + * Current method return type + */ + public GraphTargetItem returnType = null; /** * Gets full class name. 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 d317e7346..2749d95d1 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 @@ -31,6 +31,7 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopScopeIns; 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.CoerceAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.DecimalValueAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.DoubleValueAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.Float4ValueAVM2Item; @@ -86,6 +87,7 @@ 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.BinaryOpItem; import com.jpexs.decompiler.graph.model.BreakItem; import com.jpexs.decompiler.graph.model.CommaExpressionItem; import com.jpexs.decompiler.graph.model.ContinueItem; @@ -104,6 +106,7 @@ 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.WhileItem; +import com.jpexs.helpers.Helper; import com.jpexs.helpers.Reference; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -779,17 +782,17 @@ public class AVM2SourceGenerator implements SourceGenerator { generateTraitsPhase4(importedClasses, openedNamespaces, initScope, isInterface, baseClassName, superName, true, localData, traitItems, classInfo.static_traits, st, new HashMap<>(), class_index, false); int init; if (iinit == null || isInterface) { - instanceInfo.iinit_index = init = method(false, 0, false, isInterface, false, new ArrayList<>(), pkg, false, new ArrayList<>(), initScope + 1, false, 0, isInterface ? null : baseClassName, extendsVal != null ? extendsVal.toString() : null, true, localData, new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), TypeItem.UNBOUNDED/*?? FIXME*/); + instanceInfo.iinit_index = init = method(null, false, 0, false, isInterface, false, new ArrayList<>(), pkg, false, new ArrayList<>(), initScope + 1, false, 0, isInterface ? null : baseClassName, extendsVal != null ? extendsVal.toString() : null, true, localData, new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), TypeItem.UNBOUNDED/*?? FIXME*/); } else { MethodAVM2Item m = (MethodAVM2Item) iinit; List allIinitVariables = new ArrayList<>(); allIinitVariables.addAll(iinitVariables); allIinitVariables.addAll(m.subvariables); - instanceInfo.iinit_index = init = method(false, pkg.toRawString().isEmpty() ? str(baseClassName + "/" + baseClassName) : str(pkg.toRawString() + ":" + baseClassName + "/" + baseClassName), false, false, false, new ArrayList<>(), pkg, m.needsActivation, allIinitVariables, initScope + 1, m.hasRest, m.line, baseClassName, extendsVal != null ? extendsVal.toString() : null, true, localData, m.paramTypes, m.paramNames, m.paramValues, m.body, TypeItem.UNBOUNDED/*?? FIXME*/); + instanceInfo.iinit_index = init = method(null, false, pkg.toRawString().isEmpty() ? str(baseClassName + "/" + baseClassName) : str(pkg.toRawString() + ":" + baseClassName + "/" + baseClassName), false, false, false, new ArrayList<>(), pkg, m.needsActivation, allIinitVariables, initScope + 1, m.hasRest, m.line, baseClassName, 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, false, new ArrayList<>(), pkg, cinitNeedsActivation, cinitVariables, initScope + (implementsStr.isEmpty() ? 0 : 1), false, 0, isInterface ? null : baseClassName, superName, false, localData, new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), commands, TypeItem.UNBOUNDED); + int cinit_index = method(null, true, str(""), false, false, false, new ArrayList<>(), pkg, cinitNeedsActivation, cinitVariables, initScope + (implementsStr.isEmpty() ? 0 : 1), false, 0, isInterface ? null : baseClassName, superName, false, localData, new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), commands, TypeItem.UNBOUNDED); MethodBody cinitBody = abcIndex.getSelectedAbc().findBody(cinit_index); @@ -821,11 +824,13 @@ public class AVM2SourceGenerator implements SourceGenerator { int ns = -1; String tname = null; boolean isConst = false; + GraphTargetItem type = null; 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; + type = ((SlotAVM2Item) ti).type; } if (ti instanceof ConstAVM2Item) { val = ((ConstAVM2Item) ti).value; @@ -833,6 +838,7 @@ public class AVM2SourceGenerator implements SourceGenerator { ns = genNs(importedClasses, pkg, ((ConstAVM2Item) ti).pkg, openedNamespaces, localData, ((ConstAVM2Item) ti).line); tname = ((ConstAVM2Item) ti).var; isConst = true; + type = ((ConstAVM2Item) ti).type; if (((ConstAVM2Item) ti).type.toString().equals("Namespace")) { continue; } @@ -853,6 +859,7 @@ public class AVM2SourceGenerator implements SourceGenerator { && !(val instanceof NullAVM2Item) && !(val instanceof UndefinedAVM2Item) ) { + val = handleAndOrCoerce(val, type); initcode.add(ins(AVM2Instructions.GetLocal0)); localData.isStatic = false; initcode.addAll(toInsList(val.toSource(localData, this))); @@ -875,6 +882,7 @@ public class AVM2SourceGenerator implements SourceGenerator { classInfo.cinit_index = cinit_index; if (initBody != null) { + initBody.markOffsets(); initBody.autoFillStats(abcIndex.getSelectedAbc(), initScope + 1, true); } instanceInfo.interfaces = new int[implementsStr.size()]; @@ -1068,8 +1076,47 @@ public class AVM2SourceGenerator implements SourceGenerator { return false; } + public int methodHeader(boolean isStatic, int name_index, boolean subMethod, boolean isInterface, boolean isNative, List callStack, DottedChain pkg, boolean needsActivation, List subvariables, int initScope, boolean hasRest, int line, String classBaseName, 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.currentClassBaseName = classBaseName; + newlocalData.pkg = localData.pkg; + newlocalData.callStack.addAll(localData.callStack); + newlocalData.traitUsages = localData.traitUsages; + newlocalData.currentScript = localData.currentScript; + newlocalData.scriptIndex = localData.scriptIndex; + newlocalData.documentClass = localData.documentClass; + newlocalData.privateNs = localData.privateNs; + newlocalData.protectedNs = localData.protectedNs; + newlocalData.isStatic = isStatic; + newlocalData.subMethod = subMethod; + newlocalData.numberContext = localData.numberContext; + newlocalData.returnType = retType; + localData = newlocalData; + + localData.activationReg = 0; 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), false); + 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*/); + return abcIndex.getSelectedAbc().addMethodInfo(mi); + } + /** * Gets method. + * @param replaceMethodIndex Which method info to replace. Null = create new. * @param isStatic Is static * @param name_index Name index * @param subMethod Sub method @@ -1094,7 +1141,7 @@ public class AVM2SourceGenerator implements SourceGenerator { * @return Method index * @throws CompilationException On compilation error */ - public int method(boolean isStatic, int name_index, boolean subMethod, boolean isInterface, boolean isNative, List callStack, DottedChain pkg, boolean needsActivation, List subvariables, int initScope, boolean hasRest, int line, String classBaseName, String superType, boolean constructor, SourceGeneratorLocalData localData, List paramTypes, List paramNames, List paramValues, List body, GraphTargetItem retType) throws CompilationException { + public int method(Integer replaceMethodIndex, boolean isStatic, int name_index, boolean subMethod, boolean isInterface, boolean isNative, List callStack, DottedChain pkg, boolean needsActivation, List subvariables, int initScope, boolean hasRest, int line, String classBaseName, 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); @@ -1110,6 +1157,7 @@ public class AVM2SourceGenerator implements SourceGenerator { newlocalData.isStatic = isStatic; newlocalData.subMethod = subMethod; newlocalData.numberContext = localData.numberContext; + newlocalData.returnType = retType; localData = newlocalData; localData.activationReg = 0; @@ -1393,6 +1441,11 @@ public class AVM2SourceGenerator implements SourceGenerator { } MethodInfo mi = new MethodInfo(param_types, constructor ? 0 : typeName(localData, retType), name_index, 0, optional, new int[0]/*no param_names*/); + + if (replaceMethodIndex != null) { + mi = abcIndex.getSelectedAbc().method_info.get(replaceMethodIndex); + } + if (hasArguments) { mi.setFlagNeed_Arguments(); } @@ -1446,7 +1499,7 @@ public class AVM2SourceGenerator implements SourceGenerator { localData.callStack.add(mbody); List src = body == null ? new ArrayList<>() : generate(localData, body); - mbody.method_info = abcIndex.getSelectedAbc().addMethodInfo(mi); + mbody.method_info = replaceMethodIndex != null ? replaceMethodIndex : abcIndex.getSelectedAbc().addMethodInfo(mi); ArrayList mbodyCode = toInsList(src); mbody.setCode(new AVM2Code(mbodyCode)); @@ -1567,7 +1620,11 @@ public class AVM2SourceGenerator implements SourceGenerator { abcIndex.getSelectedAbc().addMethodBody(mbody); mindex = mbody.method_info; } else { - mindex = abcIndex.getSelectedAbc().addMethodInfo(mi); + if (replaceMethodIndex != null) { + mindex = replaceMethodIndex; + } else { + mindex = abcIndex.getSelectedAbc().addMethodInfo(mi); + } } return mindex; @@ -1941,10 +1998,42 @@ public class AVM2SourceGenerator implements SourceGenerator { 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, mai.isNative, 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); + ((TraitMethodGetterSetter) traits[k]).method_info = methodHeader(mai.isStatic(), methodName(mai.outsidePackage, localData.pkg, mai.functionName, mai.pkg, className, mai.customNamespace, suffix), false, isInterface, mai.isNative, 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, fai.isNative, 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); + ((TraitFunction) traits[k]).method_info = methodHeader(false, methodName(false/*?*/, localData.pkg, fai.functionName, fai.pkg, null, null, ""), false, isInterface, fai.isNative, 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); + } + } + + abcIndex.refreshSelected(); + + for (int k = 0; k < items.size(); k++) { + GraphTargetItem item = items.get(k); + if (traits[k] == null) { + continue; + } + if ((item instanceof MethodAVM2Item) || (item instanceof GetterAVM2Item) || (item instanceof SetterAVM2Item)) { + MethodAVM2Item mai = (MethodAVM2Item) item; + if (!isScriptTraits && 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(((TraitMethodGetterSetter) traits[k]).method_info, mai.isStatic(), methodName(mai.outsidePackage, localData.pkg, mai.functionName, mai.pkg, className, mai.customNamespace, suffix), false, isInterface, mai.isNative, 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(((TraitFunction) traits[k]).method_info, false, methodName(false/*?*/, localData.pkg, fai.functionName, fai.pkg, null, null, ""), false, isInterface, fai.isNative, 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); } } } @@ -2203,7 +2292,7 @@ public class AVM2SourceGenerator implements SourceGenerator { localData.importedClasses = importedClasses; localData.openedNamespaces = openedNamespaces; - int sinit_index = method(true, str(""), false, false, false, new ArrayList<>(), null, sinitNeedsActivation, sinitVariables, 0, false, 0, null, null, false, localData, new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), commands, TypeItem.UNBOUNDED); + int sinit_index = method(null, true, str(""), false, false, false, new ArrayList<>(), null, sinitNeedsActivation, sinitVariables, 0, false, 0, null, null, false, localData, new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), commands, TypeItem.UNBOUNDED); mb = abcIndex.getSelectedAbc().findBody(sinit_index); @@ -2373,30 +2462,30 @@ public class AVM2SourceGenerator implements SourceGenerator { * @param isType Is type * @return True if found */ - public static boolean searchPrototypeChain(Integer namespaceSuffix, 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, Reference isType) { + public static boolean searchPrototypeChain(Integer namespaceSuffix, 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, Reference isType, Reference outPropTrait) { // private and protected namespaces first so we find overriding functions before overridden functions if (namespaceSuffix != null) { - if (searchPrototypeChain(namespaceSuffix, instanceOnly, abc, pkg, obj, propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue, outPropValueAbc, isType)) { + if (searchPrototypeChain(namespaceSuffix, instanceOnly, abc, pkg, obj, propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue, outPropValueAbc, isType, outPropTrait)) { return true; } } - if (searchPrototypeChain(privateNs, instanceOnly, abc, pkg, obj, propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue, outPropValueAbc, isType)) { + if (searchPrototypeChain(privateNs, instanceOnly, abc, pkg, obj, propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue, outPropValueAbc, isType, outPropTrait)) { return true; } - if (searchPrototypeChain(protectedNs, instanceOnly, abc, pkg, obj, propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue, outPropValueAbc, isType)) { + if (searchPrototypeChain(protectedNs, instanceOnly, abc, pkg, obj, propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue, outPropValueAbc, isType, outPropTrait)) { return true; } for (int ns : otherNs) { - if (searchPrototypeChain(ns, instanceOnly, abc, pkg, obj, propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue, outPropValueAbc, isType)) { + if (searchPrototypeChain(ns, instanceOnly, abc, pkg, obj, propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue, outPropValueAbc, isType, outPropTrait)) { return true; } } - return searchPrototypeChain(0, instanceOnly, abc, pkg, obj, propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue, outPropValueAbc, isType); + return searchPrototypeChain(0, instanceOnly, abc, pkg, obj, propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue, outPropValueAbc, isType, outPropTrait); } - 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, Reference isType) { + 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, Reference isType, Reference outPropTrait) { isType.setVal(false); AbcIndexing.TraitIndex sp = abc.findScriptProperty(pkg.addWithSuffix(propertyName)); if (sp == null) { @@ -2419,6 +2508,7 @@ public class AVM2SourceGenerator implements SourceGenerator { outPropType.setVal(sp.returnType); outPropValue.setVal(sp.value); outPropValueAbc.setVal(sp.abc); + outPropTrait.setVal(sp.trait); return true; } return false; @@ -2902,7 +2992,7 @@ public class AVM2SourceGenerator implements SourceGenerator { localData.scopeStack.add(new PropertyAVM2Item(null, false, 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, false, localData.callStack, localData.pkg, item.needsActivation, item.subvariables, 0 /*Set later*/, item.hasRest, item.line, localData.currentClassBaseName, null, false, localData, item.paramTypes, item.paramNames, item.paramValues, item.body, item.retType))); + ret.add(ins(AVM2Instructions.NewFunction, method(null, false, constants.getStringId(item.functionName, true), true, false, false, localData.callStack, localData.pkg, item.needsActivation, item.subvariables, 0 /*Set later*/, item.hasRest, item.line, localData.currentClassBaseName, 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)); @@ -3288,7 +3378,9 @@ public class AVM2SourceGenerator implements SourceGenerator { */ public List generate(SourceGeneratorLocalData localData, ReturnValueAVM2Item item) throws CompilationException { List ret = new ArrayList<>(); - ret.addAll(item.value.toSource(localData, this)); + GraphTargetItem value = item.value; + value = handleAndOrCoerce(value, localData.returnType); + ret.addAll(value.toSource(localData, this)); if (!localData.finallyCatches.isEmpty()) { ret.add(ins(AVM2Instructions.CoerceA)); ret.add(AssignableAVM2Item.generateSetLoc(localData.finallyRegister)); @@ -3423,4 +3515,31 @@ public class AVM2SourceGenerator implements SourceGenerator { List ret = new ArrayList<>(); return ret; } + + /** + * For some strange reasons, && and || operators must have coerced both sides + * when there is specific type expected. + * @param item Item to check. If it's && or ||, it will be handled, otherwise ignored + * @param type Coerced type + * @return Original value or coerced item + */ + public static GraphTargetItem handleAndOrCoerce(GraphTargetItem item, GraphTargetItem type) { + if (type == null) { + return item; + } + if ((item instanceof OrItem)||(item instanceof AndItem)) { + BinaryOpItem bo = (BinaryOpItem) item; + if ((bo.leftSide instanceof OrItem)||(bo.leftSide instanceof AndItem)) { + bo.leftSide = handleAndOrCoerce(bo.leftSide, type); + } else { + bo.leftSide = new CoerceAVM2Item(null, null, bo.leftSide, type); + } + if ((bo.rightSide instanceof OrItem)||(bo.rightSide instanceof AndItem)) { + bo.rightSide = handleAndOrCoerce(bo.rightSide, type); + } else { + bo.rightSide = new CoerceAVM2Item(null, null, bo.rightSide, type); + } + } + return item; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/CallAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/CallAVM2Item.java index 3b18cff9c..6b9bed180 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/CallAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/CallAVM2Item.java @@ -21,7 +21,10 @@ import com.jpexs.decompiler.flash.abc.ABC; 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.model.AVM2Item; +import com.jpexs.decompiler.flash.abc.types.MethodInfo; 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.TraitMethodGetterSetter; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.graph.CompilationException; import com.jpexs.decompiler.graph.DottedChain; @@ -150,6 +153,33 @@ public class CallAVM2Item extends AVM2Item { if (callable instanceof PropertyAVM2Item) { PropertyAVM2Item prop = (PropertyAVM2Item) callable; + + Reference objType = new Reference<>(null); + Reference propType = new Reference<>(null); + Reference propIndexX = new Reference<>(0); + Reference outPropValue = new Reference<>(null); + Reference outPropValueAbc = new Reference<>(null); + Reference isType = new Reference<>(false); + Reference outPropTrait = new Reference<>(null); + + prop.resolve(false, localData, isType, objType, propType, propIndexX, outPropValue, outPropValueAbc, outPropTrait); + + if (outPropTrait.getVal() != null) { + Trait t = (Trait) outPropTrait.getVal(); + if (t instanceof TraitMethodGetterSetter) { + TraitMethodGetterSetter tm = (TraitMethodGetterSetter) t; + ABC abc = outPropValueAbc.getVal(); + MethodInfo mi = abc.method_info.get(tm.method_info); + for (int i = 0; i < mi.param_types.length; i++) { + if (i >= arguments.size()) { + break; + } + TypeItem type = new TypeItem(abc.constants.getMultiname(mi.param_types[i]).getNameWithNamespace(abc.constants, true /*??*/)); + arguments.set(i, AVM2SourceGenerator.handleAndOrCoerce(arguments.get(i), type)); + } + } + } + obj = prop.object; //For using this when appropriate: (Non ASC2 approach) /*if (obj == null) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ConstAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ConstAVM2Item.java index d82d01eb8..de8f75905 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ConstAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ConstAVM2Item.java @@ -142,9 +142,10 @@ public class ConstAVM2Item extends AVM2Item { List ret = new ArrayList<>(); if (value != null) { + GraphTargetItem value2 = AVM2SourceGenerator.handleAndOrCoerce(value, type); ret.add(ins(AVM2Instructions.FindProperty, agen.traitName(ns, var))); localData.isStatic = true; - ret.addAll(agen.toInsList(value.toSource(localData, agen))); + ret.addAll(agen.toInsList(value2.toSource(localData, agen))); ret.add(ins(AVM2Instructions.InitProperty, agen.traitName(ns, var))); } return ret; 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 e29bdba38..c7eedf683 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 @@ -249,6 +249,10 @@ public class NameAVM2Item extends AssignableAVM2Item { */ public NameAVM2Item(GraphTargetItem type, int line, boolean attribute, String variableName, String namespaceSuffix, GraphTargetItem storeValue, boolean definition, List openedNamespaces, AbcIndexing abcIndex, boolean isConst) { super(storeValue); + + if (storeValue != null && type != null) { + storeValue = AVM2SourceGenerator.handleAndOrCoerce(storeValue, type); + } this.attribute = attribute; this.variableName = variableName; this.namespaceSuffix = namespaceSuffix; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/NamespaceItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/NamespaceItem.java index 041ef17ba..da9436bf3 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/NamespaceItem.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/NamespaceItem.java @@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.SourceGeneratorLocalData; import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.types.Namespace; 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.TraitSlotConst; import com.jpexs.decompiler.graph.CompilationException; import com.jpexs.decompiler.graph.DottedChain; @@ -139,8 +140,9 @@ public class NamespaceItem { Reference value = new Reference<>(null); Reference outAbc = new Reference<>(null); Reference isType = new Reference<>(false); + Reference outPropTrait = new Reference<>(null); - prop.resolve(true, localData, isType, new Reference<>(null), new Reference<>(null), new Reference<>(0), value, outAbc); + prop.resolve(true, localData, isType, new Reference<>(null), new Reference<>(null), new Reference<>(0), value, outAbc, outPropTrait); boolean resolved = true; if (value.getVal() == null) { resolved = false; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/PropertyAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/PropertyAVM2Item.java index 9028989f2..2119e9655 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/PropertyAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/PropertyAVM2Item.java @@ -149,7 +149,7 @@ public class PropertyAVM2Item extends AssignableAVM2Item { * @param propertyValueABC Property value ABC * @throws CompilationException On compilation error */ - public void resolve(boolean mustExist, SourceGeneratorLocalData localData, Reference isType, Reference objectType, Reference propertyType, Reference propertyIndex, Reference propertyValue, Reference propertyValueABC) throws CompilationException { + public void resolve(boolean mustExist, SourceGeneratorLocalData localData, Reference isType, Reference objectType, Reference propertyType, Reference propertyIndex, Reference propertyValue, Reference propertyValueABC, Reference propertyTrait) throws CompilationException { Integer namespaceSuffixInt = null; if (!"".equals(namespaceSuffix)) { namespaceSuffixInt = Integer.parseInt(namespaceSuffix.substring(1)); @@ -177,6 +177,7 @@ public class PropertyAVM2Item extends AssignableAVM2Item { } } GraphTargetItem propType = null; + Trait propTrait = null; int propIndex = 0; ABC abc = abcIndex.getSelectedAbc(); AVM2ConstantPool constants = abc.constants; @@ -214,6 +215,7 @@ public class PropertyAVM2Item extends AssignableAVM2Item { Reference outPropType = new Reference<>(null); Reference outPropValue = new Reference<>(null); Reference outPropValueAbc = new Reference<>(null); + Reference outPropTrait = new Reference<>(null); List otherNs = new ArrayList<>(); for (NamespaceItem n : openedNamespaces) { if (n.isResolved()) { @@ -237,9 +239,10 @@ public class PropertyAVM2Item extends AssignableAVM2Item { propType = sp.returnType; propValue = sp.value; propValueAbc = sp.abc; + propTrait = sp.trait; } } - if (propType == null && AVM2SourceGenerator.searchPrototypeChain(namespaceSuffixInt, otherNs, localData.privateNs, localData.protectedNs, false, abcIndex, ftn.getWithoutLast(), ftn.getLast(), propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue, outPropValueAbc, isType)) { + if (propType == null && AVM2SourceGenerator.searchPrototypeChain(namespaceSuffixInt, otherNs, localData.privateNs, localData.protectedNs, false, abcIndex, ftn.getWithoutLast(), ftn.getLast(), propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue, outPropValueAbc, isType, outPropTrait)) { objType = new TypeItem(outNs.getVal().addWithSuffix(outName.getVal())); propType = outPropType.getVal(); propIndex = constants.getMultinameId(Multiname.createQName(false, @@ -248,6 +251,7 @@ public class PropertyAVM2Item extends AssignableAVM2Item { ); propValue = outPropValue.getVal(); propValueAbc = outPropValueAbc.getVal(); + propTrait = outPropTrait.getVal(); } } @@ -345,6 +349,7 @@ public class PropertyAVM2Item extends AssignableAVM2Item { TraitSlotConst tsc = (TraitSlotConst) t; propValue = new ValueKind(tsc.value_index, tsc.value_kind); propValueAbc = abc; + propTrait = t; } break loopobjType; } @@ -362,6 +367,7 @@ public class PropertyAVM2Item extends AssignableAVM2Item { Reference outPropType = new Reference<>(null); Reference outPropValue = new Reference<>(null); Reference outPropValueAbc = new Reference<>(null); + Reference outPropTrait = new Reference<>(null); if (p != null && (p.objType instanceof TypeItem)) { List otherns = new ArrayList<>(); for (NamespaceItem n : openedNamespaces) { @@ -369,7 +375,7 @@ public class PropertyAVM2Item extends AssignableAVM2Item { otherns.add(n.getCpoolIndex(abcIndex)); } } - if (AVM2SourceGenerator.searchPrototypeChain(namespaceSuffixInt, otherns, localData.privateNs, localData.protectedNs, false, abcIndex, nsname, (((TypeItem) p.objType).fullTypeName.getLast()), propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue, outPropValueAbc, isType)) { + if (AVM2SourceGenerator.searchPrototypeChain(namespaceSuffixInt, otherns, localData.privateNs, localData.protectedNs, false, abcIndex, nsname, (((TypeItem) p.objType).fullTypeName.getLast()), propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue, outPropValueAbc, isType, outPropTrait)) { objType = new TypeItem(outNs.getVal().addWithSuffix(outName.getVal())); propType = p.returnType; propIndex = constants.getMultinameId(Multiname.createQName(false, @@ -378,6 +384,7 @@ public class PropertyAVM2Item extends AssignableAVM2Item { ); propValue = p.value; propValueAbc = outPropValueAbc.getVal(); + propTrait = outPropTrait.getVal(); break loopobjType; } } @@ -410,6 +417,7 @@ public class PropertyAVM2Item extends AssignableAVM2Item { propertyIndex.setVal(propIndex); propertyType.setVal(propType); objectType.setVal(objType); + propertyTrait.setVal(propTrait); } /** @@ -425,7 +433,8 @@ public class PropertyAVM2Item extends AssignableAVM2Item { Reference outPropValue = new Reference<>(null); Reference outPropValueAbc = new Reference<>(null); Reference isType = new Reference<>(false); - resolve(false, localData, isType, objType, propType, propIndex, outPropValue, outPropValueAbc); + Reference outPropTrait = new Reference<>(null); + resolve(false, localData, isType, objType, propType, propIndex, outPropValue, outPropValueAbc, outPropTrait); return propIndex.getVal(); } @@ -438,8 +447,9 @@ public class PropertyAVM2Item extends AssignableAVM2Item { Reference outPropValue = new Reference<>(null); Reference outPropValueAbc = new Reference<>(null); Reference isType = new Reference<>(false); + Reference outPropTrait = new Reference<>(null); try { - resolve(false, new SourceGeneratorLocalData(new HashMap<>(), 0, false, 0)/*???*/, isType, objType, propType, propIndex, outPropValue, outPropValueAbc); + resolve(false, new SourceGeneratorLocalData(new HashMap<>(), 0, false, 0)/*???*/, isType, objType, propType, propIndex, outPropValue, outPropValueAbc, outPropTrait); return propType.getVal(); } catch (CompilationException ex) { @@ -464,14 +474,18 @@ public class PropertyAVM2Item extends AssignableAVM2Item { Reference outPropValue = new Reference<>(null); Reference outPropValueAbc = new Reference<>(null); Reference isType = new Reference<>(false); - - resolve(false, localData, isType, objType, propType, propIndex, outPropValue, outPropValueAbc); + Reference outPropTrait = new Reference<>(null); + + resolve(false, localData, isType, objType, propType, propIndex, outPropValue, outPropValueAbc, outPropTrait); int propertyId = propIndex.getVal(); Object obj = resolveObject(localData, generator, assignedValue == null); Reference ret_temp = new Reference<>(-1); boolean isSuper = (obj instanceof NameAVM2Item) && "super".equals(((NameAVM2Item) obj).getVariableName()); if (assignedValue != null) { + + assignedValue = AVM2SourceGenerator.handleAndOrCoerce(assignedValue, propType.getVal()); + GraphTargetItem targetType = propType.getVal(); String srcType = assignedValue.returnType().toString(); GraphTargetItem coerced = assignedValue; @@ -571,8 +585,9 @@ public class PropertyAVM2Item extends AssignableAVM2Item { Reference propIndex = new Reference<>(0); Reference propValue = new Reference<>(null); Reference propValueAbc = new Reference<>(null); + Reference propTrait = new Reference<>(null); - resolve(false, localData, isType, objType, propType, propIndex, outPropValue, propValueAbc); + resolve(false, localData, isType, objType, propType, propIndex, propValue, propValueAbc, propTrait); obj = ins(mustExist ? AVM2Instructions.FindPropertyStrict : AVM2Instructions.FindProperty, propIndex.getVal()); //} } @@ -588,8 +603,9 @@ public class PropertyAVM2Item extends AssignableAVM2Item { Reference outPropValue = new Reference<>(null); Reference outPropValueAbc = new Reference<>(null); Reference isType = new Reference<>(false); + Reference outPropTrait = new Reference<>(null); - resolve(false, localData, isType, objType, propType, propIndex, outPropValue, outPropValueAbc); + resolve(false, localData, isType, objType, propType, propIndex, outPropValue, outPropValueAbc, outPropTrait); int propertyId = propIndex.getVal(); Object obj = resolveObject(localData, generator, false); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/SlotAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/SlotAVM2Item.java index d440dd25d..bab2cff48 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/SlotAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/SlotAVM2Item.java @@ -130,9 +130,10 @@ public class SlotAVM2Item extends AVM2Item { List ret = new ArrayList<>(); if (value != null) { + GraphTargetItem value2 = AVM2SourceGenerator.handleAndOrCoerce(value, type); ret.add(ins(AVM2Instructions.FindProperty, agen.traitName(ns, var))); localData.isStatic = true; - ret.addAll(agen.toInsList(value.toSource(localData, agen))); + ret.addAll(agen.toInsList(value2.toSource(localData, agen))); ret.add(ins(AVM2Instructions.SetProperty, agen.traitName(ns, var))); } return ret; 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 f9f3db543..aefb89019 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 @@ -29,6 +29,7 @@ import com.jpexs.decompiler.flash.abc.avm2.model.UndefinedAVM2Item; import com.jpexs.decompiler.flash.abc.types.MethodBody; import com.jpexs.decompiler.flash.abc.types.Namespace; 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.TraitSlotConst; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.graph.CompilationException; @@ -466,8 +467,9 @@ public class UnresolvedAVM2Item extends AssignableAVM2Item { Reference propertyValue = new Reference<>(null); Reference propertyValueABC = new Reference<>(null); Reference isType = new Reference<>(false); + Reference outPropTrait = new Reference<>(null); - resolvedx.resolve(true, localData, isType, objectType, propertyType, propertyIndex, propertyValue, propertyValueABC); + resolvedx.resolve(true, localData, isType, objectType, propertyType, propertyIndex, propertyValue, propertyValueABC, outPropTrait); if (objectType.getVal() != null && !isType.getVal()) { isProperty = true; diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicAirDecompileTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicAirDecompileTest.java index 6864b3faf..5143efcbb 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicAirDecompileTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicAirDecompileTest.java @@ -45,6 +45,19 @@ public class ActionScript3ClassicAirDecompileTest extends ActionScript3Decompile false); } + @Test + public void testAndOrCoercion() { + decompileMethod("classic_air", "testAndOrCoercion", "var x:TestInterface = ti || (ti = new TestClass()) && (ti = new TestClass());\r\n" + + "var y:TestInterface = ti && (ti = new TestClass());\r\n" + + "var z:TestClass = tc || (tc = new TestClass());\r\n" + + "this.ti = ti && (ti = new TestClass());\r\n" + + "var a:* = ti && (ti = new TestClass());\r\n" + + "var b:int = 1 + (i || j);\r\n" + + "test(ti && (ti = new TestClass()));\r\n" + + "return ti && (ti = new TestClass());\r\n", + false); + } + @Test public void testArguments() { decompileMethod("classic_air", "testArguments", "return arguments[0];\r\n", diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicDecompileTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicDecompileTest.java index 245d696f7..a0a727ba1 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicDecompileTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicDecompileTest.java @@ -45,6 +45,19 @@ public class ActionScript3ClassicDecompileTest extends ActionScript3DecompileTes false); } + @Test + public void testAndOrCoercion() { + decompileMethod("classic", "testAndOrCoercion", "var x:TestInterface = this.ti || (this.ti = new TestClass()) && (this.ti = new TestClass());\r\n" + + "var y:TestInterface = this.ti && (this.ti = new TestClass());\r\n" + + "var z:TestClass = this.tc || (this.tc = new TestClass());\r\n" + + "this.ti = this.ti && (this.ti = new TestClass());\r\n" + + "var a:* = this.ti && (this.ti = new TestClass());\r\n" + + "var b:int = 1 + (this.i || this.j);\r\n" + + "this.test(this.ti && (this.ti = new TestClass()));\r\n" + + "return this.ti && (this.ti = new TestClass());\r\n", + false); + } + @Test public void testArguments() { decompileMethod("classic", "testArguments", "return arguments[0];\r\n", diff --git a/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.air.swf b/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.air.swf index 4e176eb7f..bd64502d2 100644 Binary files a/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.air.swf and b/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.air.swf differ diff --git a/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.flex.swf b/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.flex.swf index 072d8bae4..d28f3e2c7 100644 Binary files a/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.flex.swf and b/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.flex.swf differ diff --git a/libsrc/ffdec_lib/testdata/as3_new/src/Main.as b/libsrc/ffdec_lib/testdata/as3_new/src/Main.as index a06a37b1d..186ef4ab7 100644 --- a/libsrc/ffdec_lib/testdata/as3_new/src/Main.as +++ b/libsrc/ffdec_lib/testdata/as3_new/src/Main.as @@ -17,6 +17,7 @@ package public class Main extends Sprite { TestActivationArguments; + TestAndOrCoercion; TestArguments; TestBitwiseOperands; TestCallCall; diff --git a/libsrc/ffdec_lib/testdata/as3_new/src/tests/TestAndOrCoercion.as b/libsrc/ffdec_lib/testdata/as3_new/src/tests/TestAndOrCoercion.as new file mode 100644 index 000000000..175ab6f33 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/as3_new/src/tests/TestAndOrCoercion.as @@ -0,0 +1,41 @@ +package tests +{ + import tests_classes.mypackage1.TestClass; + import tests_classes.mypackage1.TestInterface; + + /* + * This is more like a direct editation test than decompilation. + * The compiler should add coercion around both sides of AND and OR operators, + * if there is specific type required + */ + public class TestAndOrCoercion + { + private var ti:TestInterface; + private var tc:TestClass; + private var i:int = 5; + private var j:int = 6; + private var tx:TestInterface = ti || tc; + + public function run():TestInterface + { + var x:TestInterface = ti || ((ti = new TestClass()) && (ti = new TestClass())); + var y:TestInterface = ti && (ti = new TestClass()); + var z:TestClass = tc || (tc = new TestClass()); + + this.ti = ti && (ti = new TestClass()); + + var a = ti && (ti = new TestClass()); + + var b:int = 1 + (i || j); //no coercion + + test(ti && (ti = new TestClass())); + + return ti && (ti = new TestClass()); + } + + public function test(p:TestInterface) + { + + } + } +}