Fixed: #2586 AS3 direct editation + decompilation - XML escape sequences and other XML problems

This commit is contained in:
Jindra Petřík
2026-01-03 23:58:04 +01:00
parent 41b9d1874b
commit b9e00fd673
11 changed files with 255 additions and 14 deletions

View File

@@ -16,6 +16,8 @@
*/
package com.jpexs.decompiler.flash.abc.avm2.model;
import com.jpexs.decompiler.flash.abc.avm2.parser.script.AbcIndexing;
import com.jpexs.decompiler.flash.abc.avm2.parser.script.ActionScript3Parser;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.decompiler.graph.GraphSourceItem;
@@ -23,8 +25,17 @@ import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.GraphTargetVisitorInterface;
import com.jpexs.decompiler.graph.TypeItem;
import com.jpexs.decompiler.graph.model.LocalData;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Reference;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* XML.
@@ -40,6 +51,7 @@ public class XMLAVM2Item extends AVM2Item {
/**
* Constructor.
*
* @param instruction Instruction
* @param lineStartIns Line start instruction
* @param parts Parts
@@ -54,14 +66,104 @@ public class XMLAVM2Item extends AVM2Item {
visitor.visitAll(parts);
}
private String handleSingleXml(String s, Reference<Boolean> inAttributeRef, Reference<Boolean> inOpeningTagRef) {
String identRegexp = "[:A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD][\\-\\.0-9:A-Z_a-z\\u00B7\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0300-\\u036F\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u203F-\\u2040\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD]*";
StringBuilder writer = new StringBuilder();
for (int j = 0; j < s.length(); j++) {
char c = s.charAt(j);
switch (c) {
case '"':
if (inOpeningTagRef.getVal()) {
inAttributeRef.setVal(!inAttributeRef.getVal());
}
writer.append(c);
break;
case '<':
Pattern p = Pattern.compile("^(" + identRegexp + ").*", Pattern.MULTILINE | Pattern.DOTALL);
String sub = s.substring(j + 1);
Matcher m = p.matcher(sub);
writer.append(c);
if (m.matches()) {
inOpeningTagRef.setVal(true);
String tag = m.group(1);
writer.append(tag);
j += tag.length();
}
break;
case '>':
if (inOpeningTagRef.getVal()) {
inOpeningTagRef.setVal(false);
}
writer.append(c);
break;
default:
if (inAttributeRef.getVal()) {
switch (c) {
case '\r':
writer.append("&#13;");
break;
case '\n':
writer.append("&#10;");
break;
case '\t':
writer.append("&#9;");
break;
default:
writer.append(c);
}
} else {
writer.append(c);
}
}
}
return writer.toString();
}
@Override
public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException {
Reference<Boolean> inAttributeRef = new Reference<>(false);
Reference<Boolean> inOpeningTagRef = new Reference<>(false);
if (parts.size() == 1 && parts.get(0) instanceof StringAVM2Item) {
String s = ((StringAVM2Item) parts.get(0)).getValue();
s = handleSingleXml(s, inAttributeRef, inOpeningTagRef);
boolean validXml = true;
try {
ActionScript3Parser par = new ActionScript3Parser(new AbcIndexing());
if (!par.checkBasicXmlOnly(s)) {
validXml = false;
}
} catch (Throwable ex) {
validXml = false;
}
if (!validXml) {
writer.append("new XML");
writer.spaceBeforeCallParenthesis(1);
writer.append("(\"").append(Helper.escapeActionScriptString(s)).append("\")");
return writer;
}
writer.spaceBeforeCallParenthesis(precedence);
writer.append(s);
return writer;
}
for (int i = 0; i < parts.size(); i++) {
GraphTargetItem part = parts.get(i);
GraphTargetItem partBefore = i > 0 ? parts.get(i - 1) : null;
GraphTargetItem partAfter = i < parts.size() - 1 ? parts.get(i + 1) : null;
/*
Older versions of Flex allow inserting escape sequences like \r, \n, \t
into attributes. Air does not allow this. We handle this by converting it into XML entities.
*/
if (part instanceof StringAVM2Item) {
String s = ((StringAVM2Item) part).getValue();
if (partAfter instanceof EscapeXAttrAVM2Item) {
if (s.endsWith("\"")) {
s = s.substring(0, s.length() - 1);
@@ -72,7 +174,8 @@ public class XMLAVM2Item extends AVM2Item {
s = s.substring(1);
}
}
writer.append(s);
writer.append(handleSingleXml(s, inAttributeRef, inOpeningTagRef));
} else if ((part instanceof EscapeXElemAVM2Item) || (part instanceof EscapeXAttrAVM2Item)) {
part.toString(writer, localData);
} else {

View File

@@ -1572,6 +1572,21 @@ public class ActionScript3Parser {
//TODO: Order of additions as in official compiler
return ret;
}
public boolean checkBasicXmlOnly(String txt) {
lexer = new ActionScriptLexer(txt);
try {
xml(new ArrayList<>(), null, null, new Reference<>(false), new ArrayList<>(), new ArrayList<>(), new HashMap<>(), false, false, new ArrayList<>(), null);
ParsedSymbol s = lexer.lex();
if (s.type != SymbolType.EOF) {
return false;
}
} catch (Exception ex) {
return false;
}
return true;
}
private GraphTargetItem command(List<List<NamespaceItem>> allOpenedNamespaces, TypeItem thisType, NamespaceItem pkg, Reference<Boolean> needsActivation, List<DottedChain> importedClasses, List<NamespaceItem> openedNamespaces, Stack<Loop> loops, Map<Loop, String> loopLabels, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, int forinlevel, boolean mustBeCommand, List<AssignableAVM2Item> variables, ABC abc) throws IOException, AVM2ParseException, InterruptedException {
LexBufferer buf = new LexBufferer();