mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-07-17 22:38:36 +00:00
AS3 RegExp literal decompilation and direct editation
This commit is contained in:
@@ -30,6 +30,7 @@ import com.jpexs.decompiler.flash.abc.avm2.model.FindPropertyAVM2Item;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.model.FullMultinameAVM2Item;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.model.GetLexAVM2Item;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.model.GetPropertyAVM2Item;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.model.RegExpAvm2Item;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.model.StringAVM2Item;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.model.XMLAVM2Item;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.model.operations.AddAVM2Item;
|
||||
@@ -114,6 +115,33 @@ public class ConstructIns extends InstructionDefinition {
|
||||
}
|
||||
}
|
||||
|
||||
boolean isRegExp = false;
|
||||
if (obj instanceof GetPropertyAVM2Item) {
|
||||
GetPropertyAVM2Item gpt = (GetPropertyAVM2Item) obj;
|
||||
if (gpt.object instanceof FindPropertyAVM2Item) {
|
||||
FindPropertyAVM2Item fpt = (FindPropertyAVM2Item) gpt.object;
|
||||
FullMultinameAVM2Item fptRegExpMult = (FullMultinameAVM2Item) fpt.propertyName;
|
||||
FullMultinameAVM2Item gptRegExpMult = (FullMultinameAVM2Item) gpt.propertyName;
|
||||
|
||||
isRegExp = fptRegExpMult.isTopLevel("RegExp", localData.getConstants(), localData.localRegNames, localData.fullyQualifiedNames)
|
||||
&& gptRegExpMult.isTopLevel("RegExp", localData.getConstants(), localData.localRegNames, localData.fullyQualifiedNames);
|
||||
}
|
||||
}
|
||||
if (obj instanceof GetLexAVM2Item) {
|
||||
GetLexAVM2Item glt = (GetLexAVM2Item) obj;
|
||||
isRegExp = glt.propertyName.getName(localData.getConstants(), localData.fullyQualifiedNames, true).equals("RegExp");
|
||||
}
|
||||
|
||||
if (isRegExp && (args.size() >= 1) && (args.get(0) instanceof StringAVM2Item) && (args.size() == 1 || (args.size() == 2 && args.get(1) instanceof StringAVM2Item))) {
|
||||
String pattern = ((StringAVM2Item) args.get(0)).getValue();
|
||||
String modifiers = "";
|
||||
if (args.size() == 2) {
|
||||
modifiers = ((StringAVM2Item) args.get(1)).getValue();
|
||||
}
|
||||
stack.push(new RegExpAvm2Item(pattern, modifiers, ins, localData.lineStartInstruction));
|
||||
return;
|
||||
}
|
||||
|
||||
stack.push(new ConstructAVM2Item(ins, localData.lineStartInstruction, obj, args));
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.model.ConstructPropAVM2Item;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.model.FindPropertyAVM2Item;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.model.FullMultinameAVM2Item;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.model.RegExpAvm2Item;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.model.StringAVM2Item;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.model.XMLAVM2Item;
|
||||
import com.jpexs.decompiler.graph.GraphTargetItem;
|
||||
import com.jpexs.decompiler.graph.TranslateStack;
|
||||
@@ -81,6 +83,19 @@ public class ConstructPropIns extends InstructionDefinition {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}//
|
||||
boolean isRegExp = false;
|
||||
if (multiname.isTopLevel("RegExp", localData.getConstants(), localData.localRegNames, localData.fullyQualifiedNames)) {
|
||||
isRegExp = true;
|
||||
}
|
||||
if (isRegExp && (args.size() >= 1) && (args.get(0) instanceof StringAVM2Item) && (args.size() == 1 || (args.size() == 2 && args.get(1) instanceof StringAVM2Item))) {
|
||||
String pattern = ((StringAVM2Item) args.get(0)).getValue();
|
||||
String modifiers = "";
|
||||
if (args.size() == 2) {
|
||||
modifiers = ((StringAVM2Item) args.get(1)).getValue();
|
||||
}
|
||||
stack.push(new RegExpAvm2Item(pattern, modifiers, ins, localData.lineStartInstruction));
|
||||
return;
|
||||
}
|
||||
|
||||
stack.push(new ConstructPropAVM2Item(ins, localData.lineStartInstruction, obj, multiname, args));
|
||||
|
||||
@@ -70,7 +70,7 @@ public class FullMultinameAVM2Item extends AVM2Item {
|
||||
return (name != null) || (namespace != null);
|
||||
}
|
||||
|
||||
public boolean isXML(AVM2ConstantPool constants, HashMap<Integer, String> localRegNames, List<DottedChain> fullyQualifiedNames) throws InterruptedException {
|
||||
public boolean isTopLevel(String tname, AVM2ConstantPool constants, HashMap<Integer, String> localRegNames, List<DottedChain> fullyQualifiedNames) throws InterruptedException {
|
||||
String cname;
|
||||
if (name != null) {
|
||||
cname = name.toString(LocalData.create(constants, localRegNames, fullyQualifiedNames));
|
||||
@@ -86,7 +86,11 @@ public class FullMultinameAVM2Item extends AVM2Item {
|
||||
cns = ns.getName(constants).toPrintableString(true);
|
||||
}
|
||||
}
|
||||
return cname.equals("XML") && cns.isEmpty();
|
||||
return cname.equals(tname) && cns.isEmpty();
|
||||
}
|
||||
|
||||
public boolean isXML(AVM2ConstantPool constants, HashMap<Integer, String> localRegNames, List<DottedChain> fullyQualifiedNames) throws InterruptedException {
|
||||
return isTopLevel("XML", constants, localRegNames, fullyQualifiedNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
package com.jpexs.decompiler.flash.abc.avm2.model;
|
||||
|
||||
import com.jpexs.decompiler.flash.SourceGeneratorLocalData;
|
||||
import com.jpexs.decompiler.flash.abc.ABC;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instructions;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetLexIns;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.parser.script.AVM2SourceGenerator;
|
||||
import com.jpexs.decompiler.flash.abc.types.Namespace;
|
||||
import com.jpexs.decompiler.flash.configuration.Configuration;
|
||||
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
|
||||
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.TypeItem;
|
||||
import com.jpexs.decompiler.graph.model.LocalData;
|
||||
import com.jpexs.helpers.Helper;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class RegExpAvm2Item extends AVM2Item {
|
||||
|
||||
public String pattern;
|
||||
public String modifier;
|
||||
|
||||
public RegExpAvm2Item(String pattern, String modifier, GraphSourceItem instruction, GraphSourceItem lineStartIns) {
|
||||
super(instruction, lineStartIns, PRECEDENCE_PRIMARY);
|
||||
this.pattern = pattern;
|
||||
this.modifier = modifier;
|
||||
}
|
||||
|
||||
public static String escapeRegExpString(String s) {
|
||||
StringBuilder ret = new StringBuilder(s.length());
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
if (c == '\n') {
|
||||
ret.append("\\n");
|
||||
} else if (c == '\r') {
|
||||
ret.append("\\r");
|
||||
} else if (c == '\t') {
|
||||
ret.append("\\t");
|
||||
} else if (c == '\b') {
|
||||
ret.append("\\b");
|
||||
} else if (c == '\f') {
|
||||
ret.append("\\f");
|
||||
} else if (c == '/') {
|
||||
ret.append("\\/");
|
||||
} else if (c < 32) {
|
||||
ret.append("\\x").append(Helper.byteToHex((byte) c));
|
||||
} else {
|
||||
ret.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException {
|
||||
if (Configuration.useRegExprLiteral.get()) {
|
||||
writer.append("/");
|
||||
writer.append(escapeRegExpString(pattern));
|
||||
writer.append("/");
|
||||
writer.append(modifier);
|
||||
} else {
|
||||
writer.append("new RegExp(");
|
||||
writer.append("\"" + Helper.escapeActionScriptString(pattern) + "\"");
|
||||
if (!(modifier == null || modifier.isEmpty())) {
|
||||
writer.append(",");
|
||||
writer.append("\"" + modifier + "\"");
|
||||
}
|
||||
writer.append(")");
|
||||
}
|
||||
return writer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasReturnValue() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphTargetItem returnType() {
|
||||
return new TypeItem("RegExp");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GraphSourceItem> toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException {
|
||||
AVM2SourceGenerator g = (AVM2SourceGenerator) generator;
|
||||
ABC abc = g.abcIndex.getSelectedAbc();
|
||||
AVM2ConstantPool constants = abc.constants;
|
||||
boolean hasModifier = !(modifier == null || modifier.isEmpty());
|
||||
return toSourceMerge(localData, generator,
|
||||
ins(AVM2Instructions.GetLex, constants.getQnameId("RegExp", Namespace.KIND_PACKAGE, "", true)),
|
||||
new StringAVM2Item(null, null, pattern),
|
||||
hasModifier ? new StringAVM2Item(null, null, modifier) : null,
|
||||
ins(AVM2Instructions.Construct, hasModifier ? 2 : 1)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -41,6 +41,7 @@ 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.PostDecrementAVM2Item;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.model.PostIncrementAVM2Item;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.model.RegExpAvm2Item;
|
||||
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;
|
||||
@@ -504,10 +505,8 @@ public class ActionScript3Parser {
|
||||
if (s.type == SymbolType.ASSIGN) {
|
||||
paramValues.add(expression(allOpenedNamespaces, thisType, pkg, new Reference<>(false), importedClasses, openedNamespaces, null, isMethod, isMethod, isMethod, variables));
|
||||
s = lex();
|
||||
} else {
|
||||
if (!paramValues.isEmpty()) {
|
||||
throw new AVM2ParseException("Some of parameters do not have default values", lexer.yyline());
|
||||
}
|
||||
} else if (!paramValues.isEmpty()) {
|
||||
throw new AVM2ParseException("Some of parameters do not have default values", lexer.yyline());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -661,10 +660,8 @@ public class ActionScript3Parser {
|
||||
throw new AVM2ParseException("Cannot compile native code", lexer.yyline());
|
||||
} else if (s.group == SymbolGroup.IDENTIFIER) {
|
||||
customNs = s.value.toString();
|
||||
} else {
|
||||
if (namespace != null) {
|
||||
throw new AVM2ParseException("Only one access identifier allowed", lexer.yyline());
|
||||
}
|
||||
} else if (namespace != null) {
|
||||
throw new AVM2ParseException("Only one access identifier allowed", lexer.yyline());
|
||||
}
|
||||
switch (s.type) {
|
||||
case PUBLIC:
|
||||
@@ -1378,16 +1375,14 @@ public class ActionScript3Parser {
|
||||
ParsedSymbol sx = lex();
|
||||
if (sx.group != SymbolGroup.IDENTIFIER) {
|
||||
lexer.pushback(sx);
|
||||
} else if (!sx.value.equals("xml")) {
|
||||
lexer.pushback(sx);
|
||||
} else {
|
||||
if (!sx.value.equals("xml")) {
|
||||
lexer.pushback(sx);
|
||||
} else {
|
||||
expectedType(SymbolType.NAMESPACE);
|
||||
expectedType(SymbolType.ASSIGN);
|
||||
GraphTargetItem ns = expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables);
|
||||
ret = new DefaultXMLNamespace(null, null, ns);
|
||||
//TODO: use dxns for attribute namespaces instead of dxnslate
|
||||
}
|
||||
expectedType(SymbolType.NAMESPACE);
|
||||
expectedType(SymbolType.ASSIGN);
|
||||
GraphTargetItem ns = expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables);
|
||||
ret = new DefaultXMLNamespace(null, null, ns);
|
||||
//TODO: use dxns for attribute namespaces instead of dxnslate
|
||||
}
|
||||
}
|
||||
if (ret == null) {
|
||||
@@ -1557,10 +1552,8 @@ public class ActionScript3Parser {
|
||||
if (firstCommand instanceof InAVM2Item) {
|
||||
forin = true;
|
||||
inexpr = (InAVM2Item) firstCommand;
|
||||
} else {
|
||||
if (forin) {
|
||||
throw new AVM2ParseException("In expression required", lexer.yyline());
|
||||
}
|
||||
} else if (forin) {
|
||||
throw new AVM2ParseException("In expression required", lexer.yyline());
|
||||
}
|
||||
|
||||
Loop floop = new Loop(uniqId(), null, null);
|
||||
@@ -1910,10 +1903,8 @@ public class ActionScript3Parser {
|
||||
lexer.pushback(s);
|
||||
if (cmd == null) {
|
||||
expr.add(expression(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables));
|
||||
} else {
|
||||
if (!cmd.hasReturnValue()) {
|
||||
throw new AVM2ParseException("Expression expected", lexer.yyline());
|
||||
}
|
||||
} else if (!cmd.hasReturnValue()) {
|
||||
throw new AVM2ParseException("Expression expected", lexer.yyline());
|
||||
}
|
||||
return new CommaExpressionItem(null, null, expr);
|
||||
}
|
||||
@@ -1932,17 +1923,42 @@ public class ActionScript3Parser {
|
||||
*
|
||||
* @param symb
|
||||
*/
|
||||
private void xmlToGreaterFix(ParsedSymbol symb) {
|
||||
private void xmlToLowerThanFix(ParsedSymbol symb) {
|
||||
if (symb.isType(SymbolType.XML_STARTVARTAG_BEGIN, SymbolType.XML_STARTTAG_BEGIN)) {
|
||||
lexer.yypushbackstr(symb.value.toString().substring(1)); //parse again as GREATER_THAN
|
||||
symb.type = SymbolType.GREATER_THAN;
|
||||
lexer.yypushbackstr(symb.value.toString().substring(1)); //parse again as LOWER_THAN
|
||||
String pb = symb.value.toString().substring(1);
|
||||
symb.type = SymbolType.LOWER_THAN;
|
||||
symb.group = SymbolGroup.OPERATOR;
|
||||
symb.value = "<";
|
||||
if (pb.charAt(0) == '=') {
|
||||
symb.type = SymbolType.LOWER_EQUAL;
|
||||
symb.value = "<=";
|
||||
pb = pb.substring(1);
|
||||
}
|
||||
lexer.yypushbackstr(pb); //parse again as LOWER_THAN
|
||||
}
|
||||
}
|
||||
|
||||
private void regexpToDivideFix(ParsedSymbol symb) {
|
||||
if (symb.isType(SymbolType.REGEXP)) {
|
||||
String pb = symb.value.toString().substring(1);
|
||||
symb.type = SymbolType.DIVIDE;
|
||||
symb.group = SymbolGroup.OPERATOR;
|
||||
symb.value = "/";
|
||||
if (pb.charAt(0) == '=') {
|
||||
symb.type = SymbolType.ASSIGN_DIVIDE;
|
||||
symb.value = "/=";
|
||||
pb = pb.substring(1);
|
||||
}
|
||||
lexer.yypushbackstr(pb); //parse again as DIVIDE
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private ParsedSymbol peekExprToken() throws IOException, AVM2ParseException {
|
||||
ParsedSymbol lookahead = lex();
|
||||
xmlToGreaterFix(lookahead);
|
||||
xmlToLowerThanFix(lookahead);
|
||||
regexpToDivideFix(lookahead);
|
||||
|
||||
lexer.pushback(lookahead);
|
||||
return lookahead;
|
||||
@@ -2169,6 +2185,17 @@ public class ActionScript3Parser {
|
||||
ParsedSymbol s = lex();
|
||||
boolean allowMemberOrCall = false;
|
||||
switch (s.type) {
|
||||
case REGEXP:
|
||||
String p = (String) s.value;
|
||||
p = p.substring(1);
|
||||
int spos = p.lastIndexOf("/");
|
||||
String mod = p.substring(spos + 1);
|
||||
p = p.substring(0, spos);
|
||||
p = p.replace("\\/", "/");
|
||||
ret = new RegExpAvm2Item(p, mod, null, null);
|
||||
allowMemberOrCall = true;
|
||||
|
||||
break;
|
||||
case XML_STARTTAG_BEGIN:
|
||||
lexer.pushback(s);
|
||||
ret = xml(allOpenedNamespaces, thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables);
|
||||
@@ -2430,13 +2457,11 @@ public class ActionScript3Parser {
|
||||
|
||||
if (isStar) {
|
||||
openedNamespaces.add(new NamespaceItem(fullName, Namespace.KIND_PACKAGE));
|
||||
} else if (isUse) {
|
||||
//Note: in this case, fullName attribute will be changed to real NS insude NamespaceItem
|
||||
openedNamespaces.add(new NamespaceItem(fullName, Namespace.KIND_NAMESPACE));
|
||||
} else {
|
||||
if (isUse) {
|
||||
//Note: in this case, fullName attribute will be changed to real NS insude NamespaceItem
|
||||
openedNamespaces.add(new NamespaceItem(fullName, Namespace.KIND_NAMESPACE));
|
||||
} else {
|
||||
importedClasses.add(fullName);
|
||||
}
|
||||
importedClasses.add(fullName);
|
||||
}
|
||||
|
||||
expected(s, lexer.yyline(), SymbolType.SEMICOLON);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -34,5 +34,6 @@ public enum SymbolGroup {
|
||||
EOF,
|
||||
//GLOBALFUNC,
|
||||
GLOBALCONST,
|
||||
PREPROCESSOR
|
||||
PREPROCESSOR,
|
||||
REGEXP
|
||||
}
|
||||
|
||||
@@ -206,7 +206,8 @@ public enum SymbolType {
|
||||
FILTER(GraphTargetItem.PRECEDENCE_PRIMARY, false),
|
||||
DESCENDANTS(GraphTargetItem.PRECEDENCE_PRIMARY, false),
|
||||
NATIVE,
|
||||
PREPROCESSOR(GraphTargetItem.PRECEDENCE_PRIMARY, false);
|
||||
PREPROCESSOR(GraphTargetItem.PRECEDENCE_PRIMARY, false),
|
||||
REGEXP(GraphTargetItem.PRECEDENCE_PRIMARY, false);
|
||||
|
||||
private int precedence = GraphTargetItem.NOPRECEDENCE;
|
||||
|
||||
|
||||
@@ -553,6 +553,14 @@ public class Configuration {
|
||||
@ConfigurationName("internalFlashViewer.execute.as12")
|
||||
public static final ConfigurationItem<Boolean> internalFlashViewerExecuteAs12 = null;
|
||||
|
||||
@ConfigurationDefaultBoolean(false)
|
||||
@ConfigurationCategory("script")
|
||||
public static final ConfigurationItem<Boolean> displayDupInstructions = null;
|
||||
|
||||
@ConfigurationDefaultBoolean(true)
|
||||
@ConfigurationCategory("script")
|
||||
public static final ConfigurationItem<Boolean> useRegExprLiteral = null;
|
||||
|
||||
private enum OSId {
|
||||
|
||||
WINDOWS, OSX, UNIX
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.jpexs.decompiler.graph.model;
|
||||
|
||||
import com.jpexs.decompiler.flash.SourceGeneratorLocalData;
|
||||
import com.jpexs.decompiler.flash.configuration.Configuration;
|
||||
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
|
||||
import com.jpexs.decompiler.graph.CompilationException;
|
||||
import com.jpexs.decompiler.graph.GraphSourceItem;
|
||||
@@ -49,7 +50,7 @@ public class DuplicateItem extends GraphTargetItem implements SimpleValue {
|
||||
|
||||
@Override
|
||||
public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException {
|
||||
if ((value instanceof SimpleValue) && (((SimpleValue) value).isSimpleValue())) {
|
||||
if (((value instanceof SimpleValue) && (((SimpleValue) value).isSimpleValue())) || !Configuration.displayDupInstructions.get()) {
|
||||
return value.appendTo(writer, localData);
|
||||
}
|
||||
writer.append("§§dup(");
|
||||
|
||||
Reference in New Issue
Block a user