AS3 Highlighter - local properties, class traits

This commit is contained in:
Jindra Petřík
2025-06-08 13:23:38 +02:00
parent 2a8cddc7f9
commit 279801a54f
4 changed files with 332 additions and 89 deletions
@@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.abc.ABC;
import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException;
import com.jpexs.decompiler.flash.simpleparser.CatchScope;
import com.jpexs.decompiler.flash.simpleparser.ClassScope;
import com.jpexs.decompiler.flash.simpleparser.ClassTrait;
import com.jpexs.decompiler.flash.simpleparser.FunctionScope;
import com.jpexs.decompiler.flash.simpleparser.Import;
import com.jpexs.decompiler.flash.simpleparser.MethodScope;
@@ -74,24 +75,30 @@ public class ActionScript3SimpleParser implements SimpleParser {
}
}
private boolean type(List<SimpleParseException> errors, TypeItem thisType, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, List<NamespaceItem> openedNamespaces, List<VariableOrScope> variables, ABC abc) throws IOException, AVM2ParseException, SimpleParseException, InterruptedException {
private String type(List<SimpleParseException> errors, TypeItem thisType, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, List<NamespaceItem> openedNamespaces, List<VariableOrScope> variables, ABC abc) throws IOException, AVM2ParseException, SimpleParseException, InterruptedException {
ParsedSymbol s = lex();
if (s.type == SymbolType.MULTIPLY) {
variables.add(new Type(false, "*", s.position));
return true;
return "*";
} else if (s.type == SymbolType.VOID) {
variables.add(new Type(false, "void", s.position));
return true;
return "void";
} else {
lexer.pushback(s);
}
boolean t = name(errors, thisType, needsActivation, openedNamespaces, null, false, false, true, variables, importedClasses, abc);
int varLenBefore = variables.size();
boolean t = true;
name(errors, thisType, needsActivation, openedNamespaces, null, false, false, true, variables, importedClasses, abc);
applyType(errors, thisType, needsActivation, importedClasses, openedNamespaces, t, new HashMap<>(), false, false, true, variables, abc);
return true;
if (variables.size() > varLenBefore && variables.get(variables.size() - 1) instanceof Variable) {
return ((Variable) variables.get(variables.size() - 1)).name;
}
return "*";
}
private boolean memberOrCall(List<SimpleParseException> errors, TypeItem thisType, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, List<NamespaceItem> openedNamespaces, boolean newcmds, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, boolean isStatic, List<VariableOrScope> variables, ABC abc) throws IOException, AVM2ParseException, SimpleParseException, InterruptedException {
private String memberOrCall(String lastVarName, List<SimpleParseException> errors, TypeItem thisType, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, List<NamespaceItem> openedNamespaces, boolean newcmds, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, boolean isStatic, List<VariableOrScope> variables, ABC abc) throws IOException, AVM2ParseException, SimpleParseException, InterruptedException {
if (debugMode) {
System.out.println("memberOrCall:");
}
@@ -104,7 +111,8 @@ public class ActionScript3SimpleParser implements SimpleParser {
case NULL_DOT:
case TYPENAME:
lexer.pushback(s);
ret = member(errors, thisType, needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, isStatic, variables, abc);
lastVarName = member(lastVarName, errors, thisType, needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, isStatic, variables, abc);
ret = true;
break;
case FILTER:
needsActivation.setVal(true);
@@ -114,6 +122,7 @@ public class ActionScript3SimpleParser implements SimpleParser {
break;
case PARENT_OPEN:
call(errors, thisType, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, isStatic, variables, abc);
lastVarName += "()";
ret = true;
break;
case DESCENDANTS:
@@ -147,7 +156,7 @@ public class ActionScript3SimpleParser implements SimpleParser {
if (debugMode) {
System.out.println("/memberOrCall");
}
return ret;
return lastVarName;
}
private boolean applyType(List<SimpleParseException> errors, TypeItem thisType, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, List<NamespaceItem> openedNamespaces, boolean obj, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, boolean isStatic, List<VariableOrScope> variables, ABC abc) throws IOException, AVM2ParseException, SimpleParseException, InterruptedException {
@@ -182,7 +191,7 @@ public class ActionScript3SimpleParser implements SimpleParser {
return ret;
}
private boolean member(List<SimpleParseException> errors, TypeItem thisType, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, List<NamespaceItem> openedNamespaces, boolean obj, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, boolean isStatic, List<VariableOrScope> variables, ABC abc) throws IOException, AVM2ParseException, SimpleParseException, InterruptedException {
private String member(String lastVarName, List<SimpleParseException> errors, TypeItem thisType, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, List<NamespaceItem> openedNamespaces, boolean obj, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, boolean isStatic, List<VariableOrScope> variables, ABC abc) throws IOException, AVM2ParseException, SimpleParseException, InterruptedException {
if (debugMode) {
System.out.println("member:");
}
@@ -207,11 +216,13 @@ public class ActionScript3SimpleParser implements SimpleParser {
ret = true;
s = lex();
} else if (s.type == SymbolType.BRACKET_OPEN) {
lastVarName += "[]";
expression(errors, thisType, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, isStatic, true, variables, false, abc);
expectedType(errors, SymbolType.BRACKET_CLOSE);
ret = true;
s = lex();
} else {
lastVarName += ".";
s = lex();
if (!expected(errors, s, lexer.yyline(), SymbolGroup.IDENTIFIER, SymbolType.MULTIPLY)) {
break;
@@ -228,7 +239,11 @@ public class ActionScript3SimpleParser implements SimpleParser {
} else {
expected(errors, s, lexer.yyline(), SymbolGroup.IDENTIFIER);
}
lastVarName += propName + "::" + s.value.toString();
variables.add(new Variable(false, lastVarName, s.position, null));
} else {
lastVarName += propName;
variables.add(new Variable(false, lastVarName, propPosition, null));
if (s.type == SymbolType.NAMESPACESUFFIX) {
//nsSuffix = "#" + s.value;
} else {
@@ -244,10 +259,10 @@ public class ActionScript3SimpleParser implements SimpleParser {
if (debugMode) {
System.out.println("/member");
}
return ret;
return lastVarName;
}
private boolean name(List<SimpleParseException> errors, TypeItem thisType, Reference<Boolean> needsActivation, List<NamespaceItem> openedNamespaces, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, boolean isStatic, List<VariableOrScope> variables, List<DottedChain> importedClasses, ABC abc) throws IOException, AVM2ParseException, SimpleParseException, InterruptedException {
private String name(List<SimpleParseException> errors, TypeItem thisType, Reference<Boolean> needsActivation, List<NamespaceItem> openedNamespaces, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, boolean isStatic, List<VariableOrScope> variables, List<DottedChain> importedClasses, ABC abc) throws IOException, AVM2ParseException, SimpleParseException, InterruptedException {
ParsedSymbol s = lex();
String lastName = "";
@@ -256,7 +271,7 @@ public class ActionScript3SimpleParser implements SimpleParser {
s = lex();
}
if (!expected(errors, s, lexer.yyline(), SymbolGroup.IDENTIFIER, SymbolType.THIS, SymbolType.SUPER, SymbolType.STRING_OP)) {
return true;
return "";
}
lastName += s.value.toString();
int identPos = s.position;
@@ -291,7 +306,7 @@ public class ActionScript3SimpleParser implements SimpleParser {
}
} else {
if (!expected(errors, s, lexer.yyline(), SymbolGroup.IDENTIFIER, SymbolType.NAMESPACE, SymbolType.MULTIPLY)) {
return true;
return "";
}
lastName = s.value.toString();
identPos = s.position;
@@ -318,14 +333,14 @@ public class ActionScript3SimpleParser implements SimpleParser {
variables.add(new Variable(false, fullName, identPos));
}
boolean ret = true;
String ret = fullName;
if (s.type == SymbolType.BRACKET_OPEN) {
lexer.pushback(s);
if (attrBracket) {
lexer.pushback(new ParsedSymbol(s.position - 1, SymbolGroup.OPERATOR, SymbolType.ATTRIBUTE, "@"));
lexer.pushback(new ParsedSymbol(s.position - 2, SymbolGroup.OPERATOR, SymbolType.DOT, "."));
}
ret = member(errors, thisType, needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, isStatic, variables, abc);
ret = member(fullName, errors, thisType, needsActivation, importedClasses, openedNamespaces, true, registerVars, inFunction, inMethod, isStatic, variables, abc);
} else {
lexer.pushback(s);
}
@@ -389,17 +404,18 @@ public class ActionScript3SimpleParser implements SimpleParser {
}
}
private void method(List<SimpleParseException> errors, boolean outsidePackage, boolean isPrivate, boolean isInterface, boolean isNative, String customAccess, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, boolean override, boolean isFinal, TypeItem thisType, List<NamespaceItem> openedNamespaces, boolean isStatic, String functionName, boolean isMethod, List<VariableOrScope> variables, ABC abc, int methodNamePos) throws IOException, AVM2ParseException, SimpleParseException, InterruptedException {
function(errors, isInterface, isNative, needsActivation, importedClasses, thisType, openedNamespaces, functionName, isMethod, variables, abc, methodNamePos, isStatic);
private void method(List<SimpleParseException> errors, boolean outsidePackage, boolean isPrivate, boolean isInterface, boolean isNative, String customAccess, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, boolean override, boolean isFinal, TypeItem thisType, List<NamespaceItem> openedNamespaces, boolean isStatic, String functionName, boolean isMethod, List<VariableOrScope> variables, ABC abc, int methodNamePos, Reference<String> returnTypeRef) throws IOException, AVM2ParseException, SimpleParseException, InterruptedException {
function(errors, isInterface, isNative, needsActivation, importedClasses, thisType, openedNamespaces, functionName, isMethod, variables, abc, methodNamePos, isStatic, returnTypeRef);
}
private void function(List<SimpleParseException> errors, boolean isInterface, boolean isNative, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, TypeItem thisType, List<NamespaceItem> openedNamespaces, String functionName, boolean isMethod, List<VariableOrScope> variables, ABC abc, int functionNamePos, boolean isStatic) throws IOException, AVM2ParseException, SimpleParseException, InterruptedException {
private void function(List<SimpleParseException> errors, boolean isInterface, boolean isNative, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, TypeItem thisType, List<NamespaceItem> openedNamespaces, String functionName, boolean isMethod, List<VariableOrScope> variables, ABC abc, int functionNamePos, boolean isStatic, Reference<String> returnTypeRef) throws IOException, AVM2ParseException, SimpleParseException, InterruptedException {
ParsedSymbol s;
expectedType(errors, SymbolType.PARENT_OPEN);
s = lex();
List<String> paramNames = new ArrayList<>();
List<Integer> paramPositions = new ArrayList<>();
List<String> paramTypes = new ArrayList<>();
boolean hasRest = false;
while (s.type != SymbolType.PARENT_CLOSE) {
if (s.type != SymbolType.COMMA) {
@@ -419,10 +435,10 @@ public class ActionScript3SimpleParser implements SimpleParser {
s = lex();
if (!hasRest) {
if (s.type == SymbolType.COLON) {
type(errors, thisType, needsActivation, importedClasses, openedNamespaces, variables, abc);
paramTypes.add(type(errors, thisType, needsActivation, importedClasses, openedNamespaces, variables, abc));
s = lex();
} else {
//*
paramTypes.add("*");
}
if (s.type == SymbolType.ASSIGN) {
expression(errors, thisType, new Reference<>(false), importedClasses, openedNamespaces, null, isMethod, isMethod, isMethod, isStatic, variables, false, abc);
@@ -446,8 +462,9 @@ public class ActionScript3SimpleParser implements SimpleParser {
}
s = lex();
if (s.type == SymbolType.COLON) {
type(errors, thisType, needsActivation, importedClasses, openedNamespaces, variables, abc);
returnTypeRef.setVal(type(errors, thisType, needsActivation, importedClasses, openedNamespaces, variables, abc));
} else {
returnTypeRef.setVal("*");
lexer.pushback(s);
}
List<VariableOrScope> subvariables = new ArrayList<>();
@@ -455,17 +472,16 @@ public class ActionScript3SimpleParser implements SimpleParser {
subvariables.add(new Variable(true, functionName, functionNamePos));
}
for (int i = 0; i < paramNames.size() - (hasRest ? 1 : 0); i++) {
subvariables.add(new Variable(true, paramNames.get(i), paramPositions.get(i)));
subvariables.add(new Variable(true, paramNames.get(i), paramPositions.get(i), null, paramTypes.get(i), null));
}
if (hasRest) {
subvariables.add(new Variable(true, paramNames.get(paramNames.size() - 1), paramPositions.get(paramNames.size() - 1)));
subvariables.add(new Variable(true, paramNames.get(paramNames.size() - 1), paramPositions.get(paramNames.size() - 1), null, "Array", null));
}
Reference<Boolean> needsActivation2 = new Reference<>(false);
if (!isInterface && !isNative) {
s = lex();
expected(errors, s, lexer.yyline(), SymbolType.CURLY_OPEN);
subvariables.add(new Variable(true, "arguments", -s.position - 1));
subvariables.add(new Variable(true, "arguments", -s.position - 1, null, "Array", null));
commands(errors, thisType, needsActivation2, importedClasses, openedNamespaces, new Stack<>(), new HashMap<>(), new HashMap<>(), true, isMethod, isStatic, 0, subvariables, abc);
expectedType(errors, SymbolType.CURLY_CLOSE);
} else {
@@ -632,10 +648,7 @@ public class ActionScript3SimpleParser implements SimpleParser {
s = lex();
}
String prefix = isStatic ? pkg.addWithSuffix(classNameStr).toPrintableString(true) + "." : "this.";
if (customNs != null) {
prefix += customNs + "::";
}
String fullClassName = pkg.addWithSuffix(classNameStr).toPrintableString(true);
switch (s.type) {
case FUNCTION:
s = lex();
@@ -668,9 +681,6 @@ public class ActionScript3SimpleParser implements SimpleParser {
} else {
fname = s.value.toString();
}
if (!fname.equals(classNameStr)) { //ignore constructor
traitVariables.add(new Variable(true, prefix + fname, fnamePos, isStatic));
}
if (fname.equals(classNameStr)) { //constructor
if (isStatic) {
errors.add(new SimpleParseException("Constructor cannot be static", lexer.yyline(), s.position));
@@ -684,7 +694,7 @@ public class ActionScript3SimpleParser implements SimpleParser {
if (isInterface) {
errors.add(new SimpleParseException("Interface cannot have constructor", lexer.yyline(), s.position));
}
method(errors, outsidePackage, isPrivate, false, false, customNs, iinitNeedsActivation, importedClasses, false, false, thisType, openedNamespaces, false, "", true, classVariables, abc, fnamePos);
method(errors, outsidePackage, isPrivate, false, false, customNs, iinitNeedsActivation, importedClasses, false, false, thisType, openedNamespaces, false, "", true, classVariables, abc, fnamePos, new Reference<>(null));
} else {
if (classNameStr == null) {
isStatic = true;
@@ -697,8 +707,11 @@ public class ActionScript3SimpleParser implements SimpleParser {
lexer.pushback(s);
}
method(errors, outsidePackage, isPrivate, isInterface, isNative, customNs, new Reference<>(false), importedClasses, isOverride, isFinal, thisType, openedNamespaces, isStatic, fname, true, classVariables, abc, fnamePos);
Reference<String> returnTypeRef = new Reference<>(null);
method(errors, outsidePackage, isPrivate, isInterface, isNative, customNs, new Reference<>(false), importedClasses, isOverride, isFinal, thisType, openedNamespaces, isStatic, fname, true, classVariables, abc, fnamePos, returnTypeRef);
traitVariables.add(new ClassTrait(fullClassName, fname, customNs, fnamePos, isStatic, "Function", returnTypeRef.getVal()));
/*
if (isGetter) {
if (!ft.paramTypes.isEmpty()) {
@@ -731,7 +744,7 @@ public class ActionScript3SimpleParser implements SimpleParser {
String nname = s.value.toString();
int npos = s.position;
traitVariables.add(new Variable(true, prefix + nname, npos, isStatic));
traitVariables.add(new ClassTrait(fullClassName, nname, customNs, npos, isStatic, "Namespace", null));
s = lex();
if (s.type == SymbolType.ASSIGN) {
@@ -762,7 +775,7 @@ public class ActionScript3SimpleParser implements SimpleParser {
if (!expected(errors, s, lexer.yyline(), SymbolGroup.IDENTIFIER)) {
break;
}
traitVariables.add(new Variable(true, prefix + s.value.toString(), s.position, isStatic));
ParsedSymbol nameSymbol = s;
s = lex();
if (s.type == SymbolType.NAMESPACESUFFIX) {
@@ -770,13 +783,16 @@ public class ActionScript3SimpleParser implements SimpleParser {
s = lex();
}
String traitType = "*";
if (s.type == SymbolType.COLON) {
type(errors, thisType, new Reference<>(false), importedClasses, openedNamespaces, classVariables, abc);
traitType = type(errors, thisType, new Reference<>(false), importedClasses, openedNamespaces, classVariables, abc);
s = lex();
} else {
//*
}
traitVariables.add(new ClassTrait(fullClassName, nameSymbol.value.toString(), customNs, nameSymbol.position, isStatic, traitType, "Function".equals(traitType) ? "*" : null));
if (s.type == SymbolType.ASSIGN) {
List<VariableOrScope> constVarVariables = new ArrayList<>();
expression(errors, thisType, new Reference<>(false), importedClasses, openedNamespaces, new HashMap<>(), false, false, isStatic, true, constVarVariables, false, abc);
@@ -818,7 +834,7 @@ public class ActionScript3SimpleParser implements SimpleParser {
Stack<Loop> sinitLoops = new Stack<>();
Map<Loop, String> sinitLoopLabels = new HashMap<>();
HashMap<String, Integer> sinitRegisterVars = new HashMap<>();
HashMap<String, Integer> sinitRegisterVars = new HashMap<>();
while (scriptTraitsBlock(
errors,
importedClasses,
@@ -830,7 +846,7 @@ public class ActionScript3SimpleParser implements SimpleParser {
sinitLoopLabels,
sinitRegisterVars,
sinitVariables,
externalTypes
externalTypes
)) {
//empty
}
@@ -996,7 +1012,7 @@ public class ActionScript3SimpleParser implements SimpleParser {
Reference<Boolean> cinitNeedsActivation = new Reference<>(false);
Reference<Boolean> iinitNeedsActivation = new Reference<>(false);
List<VariableOrScope> classVariables = new ArrayList<>();
classVariables.add(new Variable(true, "this", s.position, false));
classVariables.add(new Variable(true, "this", s.position, false, pkgName.add(subNameStr, null).toPrintableString(true), null));
classTraits(errors, !inPackage, cinitNeedsActivation, importedClasses, subOpenedNamespaces, pkgName, subNameStr, isInterface, iinitNeedsActivation, abc, classVariables);
@@ -1010,9 +1026,10 @@ public class ActionScript3SimpleParser implements SimpleParser {
expected(errors, s, lexer.yyline(), SymbolGroup.IDENTIFIER);
String fname = s.value.toString();
sinitTraitVariables.add(new Variable(true, fname, s.position, true));
method(errors, !inPackage, false, false, isNative, null, new Reference<>(false), importedClasses, false, isFinal, null, openedNamespaces, true, "", true, sinitVariables, abc, s.position);
Reference<String> returnTypeRef = new Reference<>(null);
method(errors, !inPackage, false, false, isNative, null, new Reference<>(false), importedClasses, false, isFinal, null, openedNamespaces, true, "", true, sinitVariables, abc, s.position, returnTypeRef);
sinitTraitVariables.add(new Variable(true, fname, s.position, true, "Function", returnTypeRef.getVal()));
break;
case CONST:
case VAR:
@@ -1026,14 +1043,15 @@ public class ActionScript3SimpleParser implements SimpleParser {
if (!expected(errors, s, lexer.yyline(), SymbolGroup.IDENTIFIER)) {
break;
}
sinitTraitVariables.add(new Variable(true, s.value.toString(), s.position));
ParsedSymbol traitSymb = s;
s = lex();
String traitType = "*";
if (s.type == SymbolType.COLON) {
type(errors, null, new Reference<>(false), importedClasses, openedNamespaces, sinitVariables, abc);
traitType = type(errors, null, new Reference<>(false), importedClasses, openedNamespaces, sinitVariables, abc);
s = lex();
} else {
//*
}
sinitTraitVariables.add(new Variable(true, traitSymb.value.toString(), traitSymb.position, null, traitType, traitType.equals("Function") ? "*" : null));
if (s.type == SymbolType.ASSIGN) {
expression(errors, null, new Reference<>(false), importedClasses, openedNamespaces, new HashMap<>(), false, false, true, true, sinitVariables, false, abc);
@@ -1052,7 +1070,7 @@ public class ActionScript3SimpleParser implements SimpleParser {
if (!expected(errors, s, lexer.yyline(), SymbolGroup.IDENTIFIER)) {
break;
}
sinitTraitVariables.add(new Variable(true, s.value.toString(), s.position));
sinitTraitVariables.add(new Variable(true, s.value.toString(), s.position, null, "Namespace", null));
s = lex();
@@ -1259,7 +1277,7 @@ public class ActionScript3SimpleParser implements SimpleParser {
s = lexer.lex();
expected(errors, s, lexer.yyline(), SymbolGroup.IDENTIFIER);
needsActivation.setVal(true);
function(errors, false, false, needsActivation, importedClasses, thisType, openedNamespaces, s.value.toString(), false, variables, abc, s.position, false);
function(errors, false, false, needsActivation, importedClasses, thisType, openedNamespaces, s.value.toString(), false, variables, abc, s.position, false, new Reference<>(null));
ret = true;
break;
case VAR:
@@ -1271,20 +1289,18 @@ public class ActionScript3SimpleParser implements SimpleParser {
String varIdentifier = s.value.toString();
int varPos = s.position;
s = lex();
String varType = "*";
if (s.type == SymbolType.COLON) {
//type =
type(errors, thisType, needsActivation, importedClasses, openedNamespaces, variables, abc);
varType = type(errors, thisType, needsActivation, importedClasses, openedNamespaces, variables, abc);
s = lex();
} else {
//*
}
if (s.type == SymbolType.ASSIGN) {
expression(errors, thisType, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, isStatic, true, variables, false, abc);
variables.add(new Variable(true, varIdentifier, varPos));
variables.add(new Variable(true, varIdentifier, varPos, null, varType, null));
ret = true;
} else {
variables.add(new Variable(true, varIdentifier, varPos));
variables.add(new Variable(true, varIdentifier, varPos, null, varType, null));
lexer.pushback(s);
ret = true;
}
@@ -1732,6 +1748,7 @@ public class ActionScript3SimpleParser implements SimpleParser {
}
boolean ret = false;
ParsedSymbol s = lex();
String lastVarName = "";
boolean allowMemberOrCall = false;
switch (s.type) {
case PREPROCESSOR:
@@ -1884,7 +1901,7 @@ public class ActionScript3SimpleParser implements SimpleParser {
lexer.pushback(s);
}
needsActivation.setVal(true);
function(errors, false, false, needsActivation, importedClasses, thisType, openedNamespaces, fname, false, variables, abc, fnamePos, false);
function(errors, false, false, needsActivation, importedClasses, thisType, openedNamespaces, fname, false, variables, abc, fnamePos, false, new Reference<>(null));
ret = true;
allowMemberOrCall = true;
break;
@@ -1920,7 +1937,8 @@ public class ActionScript3SimpleParser implements SimpleParser {
//parse again as method call
lexer.yypushbackstr(lexer.yytext().substring("float4".length()), ActionScriptLexer.YYINITIAL, "float4".length());
lexer.pushback(new ParsedSymbol(lexer.yychar() /*???*/, SymbolGroup.IDENTIFIER, SymbolType.IDENTIFIER, "float4"));
ret = name(errors, thisType, needsActivation, openedNamespaces, registerVars, inFunction, inMethod, isStatic, variables, importedClasses, abc);
name(errors, thisType, needsActivation, openedNamespaces, registerVars, inFunction, inMethod, isStatic, variables, importedClasses, abc);
ret = true;
} else {
ret = true; //new Float4ValueAVM2Item(null, null, (Float4) s.value);
}
@@ -1968,7 +1986,7 @@ public class ActionScript3SimpleParser implements SimpleParser {
lexer.pushback(s);
}
needsActivation.setVal(true);
function(errors, false, false, needsActivation, importedClasses, thisType, openedNamespaces, ffname, false, variables, abc, ffnamePos, false);
function(errors, false, false, needsActivation, importedClasses, thisType, openedNamespaces, ffname, false, variables, abc, ffnamePos, false, new Reference<>(null));
ret = true;
} else if (s.type == SymbolType.LOWER_THAN) {
type(errors, thisType, needsActivation, importedClasses, openedNamespaces, variables, abc);
@@ -1988,7 +2006,8 @@ public class ActionScript3SimpleParser implements SimpleParser {
} else {
lexer.pushback(s);
boolean newvar = name(errors, thisType, needsActivation, openedNamespaces, registerVars, inFunction, inMethod, isStatic, variables, importedClasses, abc);
boolean newvar = true;
name(errors, thisType, needsActivation, openedNamespaces, registerVars, inFunction, inMethod, isStatic, variables, importedClasses, abc);
applyType(errors, thisType, needsActivation, importedClasses, openedNamespaces, newvar, registerVars, inFunction, inMethod, isStatic, variables, abc);
expectedType(errors, SymbolType.PARENT_OPEN);
call(errors, thisType, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, isStatic, variables, abc);
@@ -2001,14 +2020,16 @@ public class ActionScript3SimpleParser implements SimpleParser {
case SUPER:
case ATTRIBUTE:
lexer.pushback(s);
ret = name(errors, thisType, needsActivation, openedNamespaces, registerVars, inFunction, inMethod, isStatic, variables, importedClasses, abc);
lastVarName = name(errors, thisType, needsActivation, openedNamespaces, registerVars, inFunction, inMethod, isStatic, variables, importedClasses, abc);
ret = true;
allowMemberOrCall = true;
break;
default:
lexer.pushback(s);
}
if (allowMemberOrCall && ret) {
ret = memberOrCall(errors, thisType, needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, isStatic, variables, abc);
memberOrCall(lastVarName, errors, thisType, needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, isStatic, variables, abc);
ret = true;
}
if (debugMode) {
System.out.println("/primary");
@@ -0,0 +1,53 @@
/*
* Copyright (C) 2010-2025 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.simpleparser;
/**
* @author JPEXS
*/
public class ClassTrait extends Variable {
public final String classFullName;
public final String customNs;
public final String simpleName;
/**
* Constructor
* @param classFullName Full class name including package
* @param name Name
* @param customNs Custom namespace
* @param position Position
* @param isStatic True = static
* @param type Type
* @param callType Call type
*/
public ClassTrait(String classFullName, String name, String customNs, int position, boolean isStatic, String type, String callType) {
super(true, isStatic ? classFullName + "." + name : "this." + (customNs != null ? customNs + "::" : "") + name, position, isStatic, type, callType);
this.classFullName = classFullName;
this.customNs = customNs;
this.simpleName = name;
}
@Override
public String toString() {
return (isStatic ? "static " : "") + "trait " + name + " of class " + classFullName + " at " + position;
}
public String getFullIdentifier() {
return classFullName + "/" + (customNs != null ? customNs + "::" : "") + simpleName;
}
}
@@ -66,7 +66,16 @@ public interface SimpleParser {
for (String type : externalTypes) {
externalSimpleTypes.add(type.contains(".") ? type.substring(type.lastIndexOf(".") + 1) : type);
}
parseVariablesList(privateVariables, sharedVariables, definitionPosToReferences, referenceToDefinition, new LinkedHashMap<>(), new LinkedHashMap<>(), new LinkedHashMap<>(), true, errors, null, innerFunctionCanUseTraits, externalSimpleTypes, referenceToExternalTypeIndex);
Map<Integer, String> definitionToType = new LinkedHashMap<>();
Map<Integer, String> definitionToCallType = new LinkedHashMap<>();
Map<String, Integer> traitFullNameToDefinition = new LinkedHashMap<>();
Map<Integer, Boolean> positionToStatic = new LinkedHashMap<>();
findClassTraits(privateVariables, traitFullNameToDefinition,definitionPosToReferences, positionToStatic, definitionToType, definitionToCallType);
findClassTraits(sharedVariables, traitFullNameToDefinition, definitionPosToReferences, positionToStatic, definitionToType, definitionToCallType);
parseVariablesList(privateVariables, sharedVariables, definitionPosToReferences, referenceToDefinition, new LinkedHashMap<>(), new LinkedHashMap<>(), positionToStatic, true, errors, null, innerFunctionCanUseTraits, externalSimpleTypes, referenceToExternalTypeIndex, definitionToType, definitionToCallType, traitFullNameToDefinition);
for (Map.Entry<Integer, Integer> entry : referenceToExternalTypeIndex.entrySet()) {
if (!externalTypeIndexToReference.containsKey(entry.getValue())) {
externalTypeIndexToReference.put(entry.getValue(), new ArrayList<>());
@@ -74,7 +83,88 @@ public interface SimpleParser {
externalTypeIndexToReference.get(entry.getValue()).add(entry.getKey());
}
}
public static void findClassTraits(
List<VariableOrScope> variables,
Map<String, Integer> traitFullNameToDefinition,
Map<Integer, List<Integer>> definitionPosToReferences,
Map<Integer, Boolean> positionToStatic,
Map<Integer, String> definitionToType,
Map<Integer, String> definitionToCallType
) {
for (VariableOrScope v : variables) {
if (v instanceof ClassTrait) {
ClassTrait ct = (ClassTrait) v;
definitionPosToReferences.put(ct.position, new ArrayList<>());
positionToStatic.put(ct.position, ct.isStatic);
if (ct.type != null) {
definitionToType.put(ct.position, ct.type);
}
if (ct.callType != null) {
definitionToCallType.put(ct.position, ct.callType);
}
traitFullNameToDefinition.put(ct.getFullIdentifier(), ((ClassTrait) v).position);
}
if (v instanceof Scope) {
Scope s = (Scope) v;
findClassTraits(s.getPrivateItems(), traitFullNameToDefinition, definitionPosToReferences, positionToStatic, definitionToType, definitionToCallType);
findClassTraits(s.getSharedItems(), traitFullNameToDefinition, definitionPosToReferences, positionToStatic, definitionToType, definitionToCallType);
}
}
}
public static boolean searchTrait(
Variable v,
Map<String, Integer> privateVarFullNameToDefinitionPosition,
Map<String, Integer> privateVarNameToDefinitionPosition,
Map<Integer, String> definitionToType,
Map<Integer, String> definitionToCallType,
Map<String, Integer> traitFullNameToDefinition,
Map<Integer, List<Integer>> definitionPosToReferences,
Map<Integer, Integer> referenceToDefinition
) {
boolean traitFound = false;
if (v.hasParent()) {
List<String> parts = v.getParts();
Integer definitionPos = null;
String firstName = parts.get(0);
if (privateVarFullNameToDefinitionPosition.containsKey(firstName)) {
definitionPos = privateVarFullNameToDefinitionPosition.get(firstName);
} else if (privateVarNameToDefinitionPosition.containsKey(firstName)) {
definitionPos = privateVarNameToDefinitionPosition.get(firstName);
}
if (definitionPos != null) {
if (definitionToType.containsKey(definitionPos)) {
String type = definitionToType.get(definitionPos);
traitFound = true;
for (int p = 1; p < parts.size(); p++) {
String part = parts.get(p);
if (part.equals("()")) {
type = definitionToCallType.get(definitionPos);
} else if (part.equals("[]")) {
type = "*";
} else {
String traitKey = type + "/" + part;
if (!traitFullNameToDefinition.containsKey(traitKey)) {
traitFound = false;
break;
}
definitionPos = traitFullNameToDefinition.get(traitKey);
type = definitionToType.get(definitionPos);
}
}
if (traitFound) {
definitionPosToReferences.get(definitionPos).add(v.position);
referenceToDefinition.put(v.position, definitionPos);
}
}
}
}
return traitFound;
}
public static void parseVariablesList(
List<VariableOrScope> privateVariables,
List<VariableOrScope> sharedVariables,
@@ -88,7 +178,10 @@ public interface SimpleParser {
Scope scope,
boolean innerFunctionCanUseTraits,
List<String> externalSimpleTypes,
Map<Integer, Integer> referenceToExternalTypeIndex
Map<Integer, Integer> referenceToExternalTypeIndex,
Map<Integer, String> definitionToType,
Map<Integer, String> definitionToCallType,
Map<String, Integer> traitFullNameToDefinition
) {
Map<String, Integer> privateVarNameToDefinitionPosition = new LinkedHashMap<>();
privateVarNameToDefinitionPosition.putAll(parentVarNameToDefinitionPosition);
@@ -102,8 +195,13 @@ public interface SimpleParser {
if (v.definition) {
privateVarFullNameToDefinitionPosition.put(v.name, v.position);
privateVarNameToDefinitionPosition.put(v.getLastName(), v.position);
definitionPosToReferences.put(v.position, new ArrayList<>());
if (!definitionPosToReferences.containsKey(v.position)) {
definitionPosToReferences.put(v.position, new ArrayList<>());
}
positionToStatic.put(v.position, v.isStatic != null ? v.isStatic : isStatic);
if (v.type != null) {
definitionToType.put(v.position, v.type);
}
} else {
if (!privateVarFullNameToDefinitionPosition.containsKey(v.name)
&& !privateVarNameToDefinitionPosition.containsKey(v.name)) {
@@ -111,13 +209,16 @@ public interface SimpleParser {
if (externalSimpleTypes.contains(v.name)) {
referenceToExternalTypeIndex.put(v.position, externalSimpleTypes.indexOf(v.name));
} else {
parentVarFullNameToDefinitionPosition.put(v.name, -v.position - 1);
parentVarNameToDefinitionPosition.put(v.getLastName(), -v.position - 1);
privateVarFullNameToDefinitionPosition.put(v.name, -v.position - 1);
privateVarNameToDefinitionPosition.put(v.getLastName(), -v.position - 1);
definitionPosToReferences.put(-v.position - 1, new ArrayList<>());
definitionPosToReferences.get(-v.position - 1).add(v.position);
referenceToDefinition.put(v.position, -v.position - 1);
boolean traitFound = searchTrait(v, privateVarFullNameToDefinitionPosition, privateVarNameToDefinitionPosition, definitionToType, definitionToCallType, traitFullNameToDefinition, definitionPosToReferences, referenceToDefinition);
if (!traitFound) {
parentVarFullNameToDefinitionPosition.put(v.name, -v.position - 1);
parentVarNameToDefinitionPosition.put(v.getLastName(), -v.position - 1);
privateVarFullNameToDefinitionPosition.put(v.name, -v.position - 1);
privateVarNameToDefinitionPosition.put(v.getLastName(), -v.position - 1);
definitionPosToReferences.put(-v.position - 1, new ArrayList<>());
definitionPosToReferences.get(-v.position - 1).add(v.position);
referenceToDefinition.put(v.position, -v.position - 1);
}
}
} else {
@@ -172,7 +273,7 @@ public interface SimpleParser {
}
}
parseVariablesList(vs.getPrivateItems(), vs.getSharedItems(), definitionPosToReferences, referenceToDefinition, subPrivateVarFullNameToDefinitionPosition, subPrivateVarNameToDefinitionPosition, positionToStatic, subStatic, errors, vs, innerFunctionCanUseTraits, externalSimpleTypes, referenceToExternalTypeIndex);
parseVariablesList(vs.getPrivateItems(), vs.getSharedItems(), definitionPosToReferences, referenceToDefinition, subPrivateVarFullNameToDefinitionPosition, subPrivateVarNameToDefinitionPosition, positionToStatic, subStatic, errors, vs, innerFunctionCanUseTraits, externalSimpleTypes, referenceToExternalTypeIndex, definitionToType, definitionToCallType, traitFullNameToDefinition);
}
}
for (VariableOrScope vt : sharedVariables) {
@@ -183,22 +284,30 @@ public interface SimpleParser {
parentVarNameToDefinitionPosition.put(v.getLastName(), v.position);
privateVarFullNameToDefinitionPosition.put(v.name, v.position);
privateVarNameToDefinitionPosition.put(v.getLastName(), v.position);
definitionPosToReferences.put(v.position, new ArrayList<>());
if (!definitionPosToReferences.containsKey(v.position)) {
definitionPosToReferences.put(v.position, new ArrayList<>());
}
positionToStatic.put(v.position, v.isStatic != null ? v.isStatic : isStatic);
if (v.type != null) {
definitionToType.put(v.position, v.type);
}
} else {
if (!privateVarFullNameToDefinitionPosition.containsKey(v.name)
&& !privateVarNameToDefinitionPosition.containsKey(v.name)) {
if (externalSimpleTypes.contains(v.name)) {
referenceToExternalTypeIndex.put(v.position, externalSimpleTypes.indexOf(v.name));
} else {
parentVarFullNameToDefinitionPosition.put(v.name, -v.position - 1);
parentVarNameToDefinitionPosition.put(v.getFirstName(), -v.position - 1);
privateVarFullNameToDefinitionPosition.put(v.name, -v.position - 1);
privateVarNameToDefinitionPosition.put(v.getFirstName(), -v.position - 1);
definitionPosToReferences.put(-v.position - 1, new ArrayList<>());
definitionPosToReferences.get(-v.position - 1).add(v.position);
referenceToDefinition.put(v.position, -v.position - 1);
} else {
boolean traitFound = searchTrait(v, privateVarFullNameToDefinitionPosition, privateVarNameToDefinitionPosition, definitionToType, definitionToCallType, traitFullNameToDefinition, definitionPosToReferences, referenceToDefinition);
if (!traitFound) {
parentVarFullNameToDefinitionPosition.put(v.name, -v.position - 1);
parentVarNameToDefinitionPosition.put(v.getFirstName(), -v.position - 1);
privateVarFullNameToDefinitionPosition.put(v.name, -v.position - 1);
privateVarNameToDefinitionPosition.put(v.getFirstName(), -v.position - 1);
definitionPosToReferences.put(-v.position - 1, new ArrayList<>());
definitionPosToReferences.get(-v.position - 1).add(v.position);
referenceToDefinition.put(v.position, -v.position - 1);
}
}
} else {
@@ -253,7 +362,7 @@ public interface SimpleParser {
}
}
parseVariablesList(vs.getPrivateItems(), vs.getSharedItems(), definitionPosToReferences, referenceToDefinition, privateVarFullNameToDefinitionPosition, privateVarNameToDefinitionPosition, positionToStatic, subStatic, errors, vs, innerFunctionCanUseTraits, externalSimpleTypes, referenceToExternalTypeIndex);
parseVariablesList(vs.getPrivateItems(), vs.getSharedItems(), definitionPosToReferences, referenceToDefinition, privateVarFullNameToDefinitionPosition, privateVarNameToDefinitionPosition, positionToStatic, subStatic, errors, vs, innerFunctionCanUseTraits, externalSimpleTypes, referenceToExternalTypeIndex, definitionToType, definitionToCallType, traitFullNameToDefinition);
}
}
}
@@ -16,6 +16,12 @@
*/
package com.jpexs.decompiler.flash.simpleparser;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
*
* @author JPEXS
@@ -26,34 +32,88 @@ public class Variable implements VariableOrScope {
public String name;
public int position;
public Boolean isStatic;
public String type;
public final String callType;
public Variable(boolean definition, String name, int position) {
this(definition, name, position, null);
}
public Variable(boolean definition, String name, int position, Boolean isStatic) {
this(definition, name, position, isStatic, null, null);
}
public Variable(boolean definition, String name, int position, Boolean isStatic, String type, String callType) {
this.definition = definition;
this.name = name;
this.position = position;
this.isStatic = isStatic;
this.type = type;
this.callType = callType;
}
@Override
public String toString() {
return (definition ? "definition of " : "") + (isStatic ? "static " : "") + name + " at " + position;
return (definition ? "definition of " : "") + (isStatic == Boolean.TRUE ? "static " : "") + name + " at " + position;
}
public String getLastName() {
if (name.contains(".")) {
return name.substring(name.lastIndexOf(".") + 1);
String ret = name;
if (ret.contains(".")) {
ret = ret.substring(ret.lastIndexOf(".") + 1);
}
return name;
if (ret.endsWith("()") || ret.endsWith("[]")) {
ret = ret.substring(0, ret.length() - 2);
}
return ret;
}
public String getFirstName() {
if (name.contains(".")) {
return name.substring(0, name.indexOf("."));
String ret = name;
if (ret.contains(".")) {
ret = ret.substring(0, ret.indexOf("."));
}
return name;
if (ret.endsWith("()") || ret.endsWith("[]")) {
ret = ret.substring(0, ret.length() - 2);
}
return ret;
}
public boolean firstNameIsCall() {
String ret = name;
if (ret.contains(".")) {
ret = ret.substring(0, ret.indexOf("."));
}
return ret.endsWith("()");
}
public String getParentName() {
if (name.contains(".")) {
return name.substring(0, name.lastIndexOf("."));
}
return null;
}
public List<String> getParts() {
List<String> ret = new ArrayList<>();
String[] dotSplit = name.split("\\.", -1);
Pattern pat = Pattern.compile("[^\\(\\)\\[\\]]+|\\(\\)|\\[\\]");
for (String s : dotSplit) {
Matcher m = pat.matcher(s);
while (m.find()) {
ret.add(m.group());
}
}
return ret;
}
public boolean hasParent() {
return name.contains(".");
}
public static void main(String[] args) {
String v = "v.getPoint().x";
}
}