Added: AS3.1 null-conditional operator ?. (air - swf version 50)

Added: AS3.1 nullish coalescing operator `??` (air - swf version 50)
This commit is contained in:
Jindra Petřík
2024-11-12 22:42:42 +01:00
parent 7d7f0666cc
commit 7fb2b6efb1
12 changed files with 119 additions and 25 deletions

View File

@@ -3229,9 +3229,6 @@ public class AVM2Graph extends Graph {
return ternar;
}
if (true) { //FIXME, WIP - implement compiler part
return ternar;
}
if (ternar.expression instanceof EqAVM2Item) {
EqAVM2Item eq = (EqAVM2Item) ternar.expression;
if (eq.rightSide instanceof NullAVM2Item) {

View File

@@ -16,8 +16,12 @@
*/
package com.jpexs.decompiler.flash.abc.avm2.model;
import com.jpexs.decompiler.flash.SourceGeneratorLocalData;
import com.jpexs.decompiler.flash.abc.avm2.parser.script.AVM2SourceGenerator;
import com.jpexs.decompiler.graph.CompilationException;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.SourceGenerator;
import com.jpexs.decompiler.graph.model.BinaryOpItem;
import java.util.ArrayList;
import java.util.List;
@@ -32,6 +36,12 @@ public class NullCoalesceAVM2Item extends BinaryOpItem {
super(instruction, lineStartItem, PRECEDENCE_NULLCOALESCE, leftSide, rightSide, "??", null, null);
}
@Override
public List<GraphSourceItem> toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException {
AVM2SourceGenerator a2generator = (AVM2SourceGenerator) generator;
return a2generator.generate(localData, this);
}
@Override
public GraphTargetItem returnType() {
return leftSide.returnType();

View File

@@ -43,6 +43,7 @@ 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.NullCoalesceAVM2Item;
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;
@@ -2671,12 +2672,33 @@ public class AVM2SourceGenerator implements SourceGenerator {
ret.addAll(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(xmlListReg)));
return ret;
}
@Override
public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, IfItem item) throws CompilationException {
return generateIf(localData, item.expression, item.onTrue, item.onFalse, false);
}
public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, NullCoalesceAVM2Item item) throws CompilationException {
List<GraphSourceItem> ret = new ArrayList<>();
ret.addAll(GraphTargetItem.toSourceMerge(localData, this, item.leftSide));
ret.add(ins(AVM2Instructions.PushNull));
AVM2Instruction ifEq = ins(AVM2Instructions.IfEq, 0);
ret.add(ifEq);
List<GraphSourceItem> onNotNull = GraphTargetItem.toSourceMerge(localData, this, item.leftSide);
AVM2Instruction jump = ins(AVM2Instructions.Jump, 0);
onNotNull.add(jump);
ifEq.operands[0] = insToBytes(toInsList(onNotNull)).length;
List<GraphSourceItem> onNull = GraphTargetItem.toSourceMerge(localData, this, item.rightSide);
jump.operands[0] = insToBytes(toInsList(onNull)).length;
ret.addAll(onNotNull);
ret.addAll(onNull);
return ret;
}
@Override
public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, TernarOpItem item) throws CompilationException {
List<GraphTargetItem> onTrue = new ArrayList<>();
@@ -2989,7 +3011,7 @@ public class AVM2SourceGenerator implements SourceGenerator {
ret.add(ins(AVM2Instructions.NewObject, 0));
ret.add(ins(AVM2Instructions.PushWith));
scope = localData.scopeStack.size();
localData.scopeStack.add(new PropertyAVM2Item(null, false, item.functionName, "" /*??*/, abcIndex, new ArrayList<>(), localData.callStack));
localData.scopeStack.add(new PropertyAVM2Item(null, false, item.functionName, "" /*??*/, abcIndex, new ArrayList<>(), localData.callStack, false));
}
AVM2ConstantPool constants = abcIndex.getSelectedAbc().constants;
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)));

View File

@@ -45,6 +45,7 @@ import com.jpexs.decompiler.flash.abc.avm2.model.NewArrayAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.NewObjectAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.NextNameAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.NullAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.NullCoalesceAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.PostDecrementAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.PostIncrementAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.RegExpAvm2Item;
@@ -196,10 +197,11 @@ public class ActionScript3Parser {
}
ParsedSymbol s = lex();
GraphTargetItem ret = newcmds;
while (s.isType(SymbolType.DOT, SymbolType.PARENT_OPEN, SymbolType.BRACKET_OPEN, SymbolType.TYPENAME, SymbolType.FILTER, SymbolType.DESCENDANTS)) {
while (s.isType(SymbolType.DOT, SymbolType.NULL_DOT, SymbolType.PARENT_OPEN, SymbolType.BRACKET_OPEN, SymbolType.TYPENAME, SymbolType.FILTER, SymbolType.DESCENDANTS)) {
switch (s.type) {
case BRACKET_OPEN:
case DOT:
case NULL_DOT:
case TYPENAME:
lexer.pushback(s);
ret = member(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables, abc);
@@ -282,10 +284,14 @@ public class ActionScript3Parser {
}
GraphTargetItem ret = obj;
ParsedSymbol s = lex();
while (s.isType(SymbolType.DOT, SymbolType.BRACKET_OPEN, SymbolType.TYPENAME)) {
while (s.isType(SymbolType.DOT, SymbolType.NULL_DOT, SymbolType.BRACKET_OPEN, SymbolType.TYPENAME)) {
ParsedSymbol s2 = lex();
boolean attr = false;
if (s.type == SymbolType.DOT) {
boolean nullDot = false;
if (s.type == SymbolType.NULL_DOT) {
nullDot = true;
lexer.pushback(s2);
} else if (s.type == SymbolType.DOT) {
if (s2.type == SymbolType.ATTRIBUTE) {
attr = true;
} else {
@@ -335,7 +341,7 @@ public class ActionScript3Parser {
if (ns != null) {
ret = new NamespacedAVM2Item(ns, propName, propItem, ret, attr, openedNamespaces, null);
} else {
ret = new PropertyAVM2Item(ret, attr, propName, nsSuffix, abcIndex, openedNamespaces, new ArrayList<>());
ret = new PropertyAVM2Item(ret, attr, propName, nsSuffix, abcIndex, openedNamespaces, new ArrayList<>(), nullDot);
}
s = lex();
}
@@ -2285,6 +2291,9 @@ public class ActionScript3Parser {
GraphTargetItem istype = rhs;
lhs = new IsTypeAVM2Item(null, null, lhs, istype);
break;
case NULL_COALESCE:
lhs = new NullCoalesceAVM2Item(null, null, lhs, rhs);
break;
case ASSIGN:
case ASSIGN_BITAND:
case ASSIGN_BITOR:

View File

@@ -138,7 +138,7 @@ public class CallAVM2Item extends AVM2Item {
nobj.setRegNumber(0);
obj = nobj;
}*/
PropertyAVM2Item p = new PropertyAVM2Item(obj, n.isAttribute(), n.getVariableName(), n.getNamespaceSuffix(), g.abcIndex, n.openedNamespaces, new ArrayList<>());
PropertyAVM2Item p = new PropertyAVM2Item(obj, n.isAttribute(), n.getVariableName(), n.getNamespaceSuffix(), g.abcIndex, n.openedNamespaces, new ArrayList<>(), false);
p.setAssignedValue(n.getAssignedValue());
callable = p;
}

View File

@@ -136,7 +136,7 @@ public class NamespaceItem {
}
if (kind == KIND_NAMESPACE_CUSTOM) {
String custom = name.toRawString();
PropertyAVM2Item prop = new PropertyAVM2Item(null, false, custom, "", abcIndex, openedNamespaces, new ArrayList<>());
PropertyAVM2Item prop = new PropertyAVM2Item(null, false, custom, "", abcIndex, openedNamespaces, new ArrayList<>(), false);
Reference<ValueKind> value = new Reference<>(null);
Reference<ABC> outAbc = new Reference<>(null);
Reference<Boolean> isType = new Reference<>(false);

View File

@@ -16,6 +16,7 @@
*/
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.AVM2ConstantPool;
@@ -41,6 +42,8 @@ import com.jpexs.decompiler.graph.SourceGenerator;
import com.jpexs.decompiler.graph.TypeItem;
import com.jpexs.decompiler.graph.model.LocalData;
import com.jpexs.helpers.Reference;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -95,10 +98,11 @@ public class PropertyAVM2Item extends AssignableAVM2Item {
* Scope stack
*/
public List<GraphTargetItem> scopeStack = new ArrayList<>();
private final boolean nullish;
@Override
public AssignableAVM2Item copy() {
PropertyAVM2Item p = new PropertyAVM2Item(object, attribute, propertyName, namespaceSuffix, abcIndex, openedNamespaces, callStack);
PropertyAVM2Item p = new PropertyAVM2Item(object, attribute, propertyName, namespaceSuffix, abcIndex, openedNamespaces, callStack, nullish);
return p;
}
@@ -112,8 +116,9 @@ public class PropertyAVM2Item extends AssignableAVM2Item {
* @param abcIndex ABC indexing
* @param openedNamespaces Opened namespaces
* @param callStack Call stack
* @param nullish Nullish
*/
public PropertyAVM2Item(GraphTargetItem object, boolean attribute, String propertyName, String namespaceSuffix, AbcIndexing abcIndex, List<NamespaceItem> openedNamespaces, List<MethodBody> callStack) {
public PropertyAVM2Item(GraphTargetItem object, boolean attribute, String propertyName, String namespaceSuffix, AbcIndexing abcIndex, List<NamespaceItem> openedNamespaces, List<MethodBody> callStack, boolean nullish) {
this.attribute = attribute;
this.propertyName = propertyName;
this.namespaceSuffix = namespaceSuffix;
@@ -121,6 +126,7 @@ public class PropertyAVM2Item extends AssignableAVM2Item {
this.abcIndex = abcIndex;
this.openedNamespaces = openedNamespaces;
this.callStack = callStack;
this.nullish = nullish;
}
@Override
@@ -468,6 +474,8 @@ public class PropertyAVM2Item extends AssignableAVM2Item {
*/
public List<GraphSourceItem> toSource(SourceGeneratorLocalData localData, SourceGenerator generator, boolean needsReturn) throws CompilationException {
AVM2SourceGenerator a2Generator = (AVM2SourceGenerator) generator;
Reference<GraphTargetItem> objType = new Reference<>(null);
Reference<GraphTargetItem> propType = new Reference<>(null);
Reference<Integer> propIndex = new Reference<>(0);
@@ -507,14 +515,46 @@ public class PropertyAVM2Item extends AssignableAVM2Item {
needsReturn ? null : ins(AVM2Instructions.Pop)
);
}
return toSourceMerge(localData, generator,
List<GraphSourceItem> onFalse = toSourceMerge(localData, generator,
isSuper ? ins(AVM2Instructions.FindPropertyStrict, propertyId) : obj,
ins(isSuper ? AVM2Instructions.GetSuper : AVM2Instructions.GetProperty, propertyId),
needsReturn ? null : ins(AVM2Instructions.Pop)
);
if (!nullish) {
return onFalse;
}
AVM2Instruction ifFalse = ins(AVM2Instructions.IfFalse, 0);
List<GraphSourceItem> result = toSourceMerge(localData, generator, obj,
ins(AVM2Instructions.PushNull),
ins(AVM2Instructions.Equals),
ifFalse
);
List<GraphSourceItem> onTrue = new ArrayList<>();
onTrue.add(ins(AVM2Instructions.PushNull));
AVM2Instruction jump = ins(AVM2Instructions.Jump, 0);
onTrue.add(jump);
ifFalse.operands[0] = getBytesLen(onTrue);
jump.operands[0] = getBytesLen(onFalse);
result.addAll(onTrue);
result.addAll(onFalse);
return result;
}
}
private int getBytesLen(List<GraphSourceItem> code) {
int len = 0;
for (GraphSourceItem instruction : code) {
AVM2Instruction ins = (AVM2Instruction) instruction;
len += ins.getBytes().length;
}
return len;
}
@Override
public List<GraphSourceItem> toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException {
return toSource(localData, generator, true);

View File

@@ -418,7 +418,7 @@ public class UnresolvedAVM2Item extends AssignableAVM2Item {
ret.setRegNumber(n.getRegNumber());
resolved = ret;
for (int i = 1; i < name.size(); i++) {
resolved = new PropertyAVM2Item(resolved, name.isAttribute(i), name.get(i), name.getNamespaceSuffix(i), abc, openedNamespaces, new ArrayList<>());
resolved = new PropertyAVM2Item(resolved, name.isAttribute(i), name.get(i), name.getNamespaceSuffix(i), abc, openedNamespaces, new ArrayList<>(), false);
if (i == name.size() - 1) {
((PropertyAVM2Item) resolved).assignedValue = assignedValue;
}
@@ -445,7 +445,7 @@ public class UnresolvedAVM2Item extends AssignableAVM2Item {
GraphTargetItem ret = new NameAVM2Item(t, line, name.isAttribute(0), name.get(0), name.getNamespaceSuffix(0), null, false, openedNamespaces, abcIndex, false);
resolved = ret;
for (int i = 1; i < name.size(); i++) {
resolved = new PropertyAVM2Item(resolved, name.isAttribute(i), name.get(i), name.getNamespaceSuffix(i), abc, openedNamespaces, new ArrayList<>());
resolved = new PropertyAVM2Item(resolved, name.isAttribute(i), name.get(i), name.getNamespaceSuffix(i), abc, openedNamespaces, new ArrayList<>(), false);
if (i == name.size() - 1) {
((PropertyAVM2Item) resolved).assignedValue = assignedValue;
}
@@ -458,7 +458,7 @@ public class UnresolvedAVM2Item extends AssignableAVM2Item {
boolean isProperty = false;
if (localData != null) { //resolve can be called without localData
PropertyAVM2Item resolvedx = new PropertyAVM2Item(null, name.isAttribute(0), name.get(0), name.getNamespaceSuffix(0), abc, openedNamespaces, callStack);
PropertyAVM2Item resolvedx = new PropertyAVM2Item(null, name.isAttribute(0), name.get(0), name.getNamespaceSuffix(0), abc, openedNamespaces, callStack, false);
((PropertyAVM2Item) resolvedx).scopeStack = scopeStack;
((PropertyAVM2Item) resolvedx).setAssignedValue(assignedValue);
Reference<GraphTargetItem> objectType = new Reference<>(null);
@@ -488,7 +488,7 @@ public class UnresolvedAVM2Item extends AssignableAVM2Item {
if (ci != null) {
resolved = ti;
for (int i = 1; i < name.size(); i++) {
resolved = new PropertyAVM2Item(resolved, name.isAttribute(i), name.get(i), name.getNamespaceSuffix(i), abc, openedNamespaces, new ArrayList<>());
resolved = new PropertyAVM2Item(resolved, name.isAttribute(i), name.get(i), name.getNamespaceSuffix(i), abc, openedNamespaces, new ArrayList<>(), false);
if (i == name.size() - 1) {
((PropertyAVM2Item) resolved).assignedValue = assignedValue;
}
@@ -515,7 +515,7 @@ public class UnresolvedAVM2Item extends AssignableAVM2Item {
}
resolved = ti;
for (int i = 1; i < name.size(); i++) {
resolved = new PropertyAVM2Item(resolved, name.isAttribute(i), name.get(i), name.getNamespaceSuffix(i), abc, openedNamespaces, new ArrayList<>());
resolved = new PropertyAVM2Item(resolved, name.isAttribute(i), name.get(i), name.getNamespaceSuffix(i), abc, openedNamespaces, new ArrayList<>(), false);
if (i == name.size() - 1) {
((PropertyAVM2Item) resolved).assignedValue = assignedValue;
}
@@ -533,7 +533,7 @@ public class UnresolvedAVM2Item extends AssignableAVM2Item {
TypeItem ret = new TypeItem(imp);
resolved = ret;
for (int i = 1; i < name.size(); i++) {
resolved = new PropertyAVM2Item(resolved, name.isAttribute(i), name.get(i), name.getNamespaceSuffix(i), abc, openedNamespaces, new ArrayList<>());
resolved = new PropertyAVM2Item(resolved, name.isAttribute(i), name.get(i), name.getNamespaceSuffix(i), abc, openedNamespaces, new ArrayList<>(), false);
if (i == name.size() - 1) {
((PropertyAVM2Item) resolved).assignedValue = assignedValue;
}
@@ -566,7 +566,7 @@ public class UnresolvedAVM2Item extends AssignableAVM2Item {
TypeItem ret = new TypeItem(fname);
resolved = ret;
for (int j = i + 1; j < name.size(); j++) {
resolved = new PropertyAVM2Item(resolved, name.isAttribute(j), name.get(j), name.getNamespaceSuffix(j), abc, openedNamespaces, new ArrayList<>());
resolved = new PropertyAVM2Item(resolved, name.isAttribute(j), name.get(j), name.getNamespaceSuffix(j), abc, openedNamespaces, new ArrayList<>(), false);
if (j == name.size() - 1) {
((PropertyAVM2Item) resolved).assignedValue = assignedValue;
}
@@ -595,7 +595,7 @@ public class UnresolvedAVM2Item extends AssignableAVM2Item {
TypeItem ret = ti;
resolved = ret;
for (int i = 1; i < name.size(); i++) {
resolved = new PropertyAVM2Item(resolved, name.isAttribute(i), name.get(i), name.getNamespaceSuffix(i), abc, openedNamespaces, new ArrayList<>());
resolved = new PropertyAVM2Item(resolved, name.isAttribute(i), name.get(i), name.getNamespaceSuffix(i), abc, openedNamespaces, new ArrayList<>(), false);
if (i == name.size() - 1) {
((PropertyAVM2Item) resolved).assignedValue = assignedValue;
}
@@ -632,7 +632,7 @@ public class UnresolvedAVM2Item extends AssignableAVM2Item {
NameAVM2Item ret = new NameAVM2Item(ntype, line, name.isAttribute(0), name.get(0), name.getNamespaceSuffix(0), null, false, openedNamespaces, abcIndex, false);
resolved = ret;
for (int i = 1; i < name.size(); i++) {
resolved = new PropertyAVM2Item(resolved, name.isAttribute(i), name.get(i), name.getNamespaceSuffix(i), abc, openedNamespaces, new ArrayList<>());
resolved = new PropertyAVM2Item(resolved, name.isAttribute(i), name.get(i), name.getNamespaceSuffix(i), abc, openedNamespaces, new ArrayList<>(), false);
if (i == name.size() - 1) {
((PropertyAVM2Item) resolved).assignedValue = assignedValue;
}
@@ -655,7 +655,7 @@ public class UnresolvedAVM2Item extends AssignableAVM2Item {
resolved = null;
GraphTargetItem ret = null;
for (int i = 0; i < name.size(); i++) {
resolved = new PropertyAVM2Item(resolved, name.isAttribute(i), name.get(i), name.getNamespaceSuffix(i), abc, openedNamespaces, callStack);
resolved = new PropertyAVM2Item(resolved, name.isAttribute(i), name.get(i), name.getNamespaceSuffix(i), abc, openedNamespaces, callStack, false);
if (ret == null) {
((PropertyAVM2Item) resolved).scopeStack = scopeStack;
ret = resolved;