AS3 p-code editing - metadata read/write support

This commit is contained in:
Jindra Petřík
2016-08-28 17:01:50 +02:00
parent c2e850ad49
commit da859ddb41
16 changed files with 3745 additions and 3563 deletions

View File

@@ -68,6 +68,7 @@ import com.jpexs.helpers.utf8.Utf8PrintWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -157,6 +158,29 @@ public class ABC {
}
}
/**
* Gets id of metadata/add metadata
*
* @param newMetadata
* @param add Add if not found?
* @return New index or -1 if not found (add=false)
*/
public int getMetadataId(MetadataInfo newMetadata, boolean add) {
for (int m = 0; m < metadata_info.size(); m++) {
MetadataInfo metadata = metadata_info.get(m);
if (metadata.name_index == newMetadata.name_index && Arrays.equals(metadata.keys, newMetadata.keys) && Arrays.equals(metadata.values, newMetadata.values)) {
return m;
}
}
if (add) {
int newIndex = metadata_info.size();
metadata_info.add(newMetadata);
((Tag) parentTag).setModified(true);
return newIndex;
}
return -1;
}
public TraitMethodGetterSetter addMethod(int classId, String name, boolean isStatic) {
Multiname multiname = new Multiname();
multiname.kind = Multiname.QNAME;

View File

@@ -16,6 +16,7 @@
*/
package com.jpexs.decompiler.flash.abc.avm2.parser.pcode;
import com.jpexs.decompiler.flash.abc.ABC;
import com.jpexs.decompiler.flash.abc.avm2.AVM2Code;
import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool;
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction;
@@ -26,6 +27,7 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushShortIns;
import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException;
import com.jpexs.decompiler.flash.abc.types.ABCException;
import com.jpexs.decompiler.flash.abc.types.Float4;
import com.jpexs.decompiler.flash.abc.types.MetadataInfo;
import com.jpexs.decompiler.flash.abc.types.MethodBody;
import com.jpexs.decompiler.flash.abc.types.MethodInfo;
import com.jpexs.decompiler.flash.abc.types.Multiname;
@@ -40,10 +42,13 @@ import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.helpers.Helper;
import java.io.IOException;
import java.io.Reader;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/**
*
@@ -85,8 +90,8 @@ public class ASM3Parser {
}
}
public static AVM2Code parse(Reader reader, AVM2ConstantPool constants, Trait trait, MethodBody body, MethodInfo info) throws IOException, AVM2ParseException, InterruptedException {
return parse(reader, constants, trait, null, body, info);
public static AVM2Code parse(ABC abc, Reader reader, Trait trait, MethodBody body, MethodInfo info) throws IOException, AVM2ParseException, InterruptedException {
return parse(abc, reader, trait, null, body, info);
}
private static int checkMultinameIndex(AVM2ConstantPool constants, int index, int line) throws AVM2ParseException {
@@ -109,60 +114,109 @@ public class ASM3Parser {
}
}
public static boolean parseSlotConst(Reader reader, AVM2ConstantPool constants, TraitSlotConst tsc) throws IOException, AVM2ParseException {
Flasm3Lexer lexer = new Flasm3Lexer(reader);
expected(ParsedSymbol.TYPE_KEYWORD_TRAIT, "trait", lexer);
int name_index = parseMultiName(constants, lexer);
private static void parseTraitParams(ABC abc, Flasm3Lexer lexer, Trait t) throws IOException, AVM2ParseException {
ParsedSymbol symb;// = lexer.lex();
ParsedSymbol symb = lexer.lex();
List<Map.Entry<String, Map<String, String>>> metadata = new ArrayList<>();
int flags = 0;
while (symb.type == ParsedSymbol.TYPE_KEYWORD_FLAG) {
while (true) {
symb = lexer.lex();
switch (symb.type) {
case ParsedSymbol.TYPE_KEYWORD_FINAL:
flags |= Trait.ATTR_Final;
break;
case ParsedSymbol.TYPE_KEYWORD_OVERRIDE:
flags |= Trait.ATTR_Override;
break;
case ParsedSymbol.TYPE_KEYWORD_METADATA:
flags |= Trait.ATTR_Metadata;
break;
default:
throw new AVM2ParseException("Invalid trait flag", lexer.yyline());
if (symb.type == ParsedSymbol.TYPE_KEYWORD_FLAG) {
symb = lexer.lex();
switch (symb.type) {
case ParsedSymbol.TYPE_KEYWORD_FINAL:
flags |= Trait.ATTR_Final;
break;
case ParsedSymbol.TYPE_KEYWORD_OVERRIDE:
flags |= Trait.ATTR_Override;
break;
case ParsedSymbol.TYPE_KEYWORD_METADATA:
flags |= Trait.ATTR_Metadata;
break;
default:
throw new AVM2ParseException("Invalid trait flag", lexer.yyline());
}
} else if (symb.type == ParsedSymbol.TYPE_KEYWORD_METADATA_BLOCK) {
symb = lexer.lex();
expected(symb, ParsedSymbol.TYPE_STRING, "string metadata");
String mkey = (String) symb.value;
symb = lexer.lex();
Map<String, String> items = new HashMap<>();
while (symb.type == ParsedSymbol.TYPE_KEYWORD_ITEM) {
symb = lexer.lex();
expected(symb, ParsedSymbol.TYPE_STRING, "string key");
String key = (String) symb.value;
symb = lexer.lex();
expected(symb, ParsedSymbol.TYPE_STRING, "string value");
String val = (String) symb.value;
items.put(key, val);
symb = lexer.lex();
}
expected(symb, ParsedSymbol.TYPE_KEYWORD_END, "end");
symb = lexer.lex();
if (symb.type != ParsedSymbol.TYPE_COMMENT) {
lexer.pushback(symb);
}
metadata.add(new AbstractMap.SimpleEntry<>(mkey, items));
} else {
lexer.pushback(symb);
break;
}
symb = lexer.lex();
}
switch (symb.type) {
case ParsedSymbol.TYPE_KEYWORD_SLOT:
case ParsedSymbol.TYPE_KEYWORD_CONST:
expected(ParsedSymbol.TYPE_KEYWORD_SLOTID, "slotid", lexer);
symb = lexer.lex();
expected(symb, ParsedSymbol.TYPE_INTEGER, "Integer");
int slotid = (int) (long) (Long) symb.value;
expected(ParsedSymbol.TYPE_KEYWORD_TYPE, "type", lexer);
int type = parseMultiName(constants, lexer);
expected(ParsedSymbol.TYPE_KEYWORD_VALUE, "value", lexer);
ValueKind val = parseValue(constants, lexer);
tsc.slot_id = slotid;
tsc.type_index = type;
tsc.value_kind = val.value_kind;
tsc.value_index = val.value_index;
tsc.kindFlags = flags;
break;
/*case ParsedSymbol.TYPE_KEYWORD_CLASS:
break;
case ParsedSymbol.TYPE_KEYWORD_FUNCTION:
break;
case ParsedSymbol.TYPE_KEYWORD_METHOD:
case ParsedSymbol.TYPE_KEYWORD_GETTER:
case ParsedSymbol.TYPE_KEYWORD_SETTER:
break;*/
default:
throw new AVM2ParseException("Unexpected trait type", lexer.yyline());
t.kindFlags = flags;
if ((flags & Trait.ATTR_Metadata) > 0) {
int metadataArray[] = new int[metadata.size()];
for (int i = 0; i < metadata.size(); i++) {
Map.Entry<String, Map<String, String>> entry = metadata.get(i);
int mkey = abc.constants.getStringId(entry.getKey(), true);
Map<String, String> items = entry.getValue();
int keys[] = new int[items.size()];
int vals[] = new int[items.size()];
int pos = 0;
for (String key : items.keySet()) {
int ikey = abc.constants.getStringId(key, true);
int ival = abc.constants.getStringId(items.get(key), true);
keys[pos] = ikey;
vals[pos] = ival;
pos++;
}
MetadataInfo mi = new MetadataInfo(mkey, keys, vals);
metadataArray[i] = abc.getMetadataId(mi, true);
}
t.metadata = metadataArray;
}
}
public static boolean parseSlotConst(ABC abc, Reader reader, AVM2ConstantPool constants, TraitSlotConst tsc) throws IOException, AVM2ParseException {
Flasm3Lexer lexer = new Flasm3Lexer(reader);
expected(ParsedSymbol.TYPE_KEYWORD_TRAIT, "trait", lexer);
ParsedSymbol symb = lexer.lex();
if (symb.type == ParsedSymbol.TYPE_KEYWORD_SLOT) {
tsc.kindType = Trait.TRAIT_SLOT;
} else if (symb.type == ParsedSymbol.TYPE_KEYWORD_CONST) {
tsc.kindType = Trait.TRAIT_CONST;
} else {
throw new AVM2ParseException("slot or const expected", lexer.yyline());
}
int name_index = parseMultiName(constants, lexer);
parseTraitParams(abc, lexer, tsc);
expected(ParsedSymbol.TYPE_KEYWORD_SLOTID, "slotid", lexer);
symb = lexer.lex();
expected(symb, ParsedSymbol.TYPE_INTEGER, "Integer");
int slotid = (int) (long) (Long) symb.value;
expected(ParsedSymbol.TYPE_KEYWORD_TYPE, "type", lexer);
int type = parseMultiName(constants, lexer);
expected(ParsedSymbol.TYPE_KEYWORD_VALUE, "value", lexer);
ValueKind val = parseValue(constants, lexer);
tsc.slot_id = slotid;
tsc.type_index = type;
tsc.value_kind = val.value_kind;
tsc.value_index = val.value_index;
tsc.name_index = name_index;
return true;
}
@@ -515,7 +569,8 @@ public class ASM3Parser {
return new ValueKind(value_index, value_kind);
}
public static AVM2Code parse(Reader reader, AVM2ConstantPool constants, Trait trait, MissingSymbolHandler missingHandler, MethodBody body, MethodInfo info) throws IOException, AVM2ParseException, InterruptedException {
public static AVM2Code parse(ABC abc, Reader reader, Trait trait, MissingSymbolHandler missingHandler, MethodBody body, MethodInfo info) throws IOException, AVM2ParseException, InterruptedException {
AVM2ConstantPool constants = abc.constants;
AVM2Code code = new AVM2Code();
List<OffsetItem> offsetItems = new ArrayList<>();
@@ -566,6 +621,7 @@ public class ASM3Parser {
break;
}
tm.name_index = parseMultiName(constants, lexer);
parseTraitParams(abc, lexer, trait);
expected(ParsedSymbol.TYPE_KEYWORD_DISPID, "dispid", lexer);
symb = lexer.lex();
expected(symb, ParsedSymbol.TYPE_INTEGER, "Integer");
@@ -576,6 +632,9 @@ public class ASM3Parser {
if (!(trait instanceof TraitFunction)) {
throw new AVM2ParseException("Unxpected trait type", lexer.yyline());
}
//NAME
parseTraitParams(abc, lexer, trait);
break;
}

View File

@@ -196,6 +196,10 @@ public class ParsedSymbol {
public static final int TYPE_KEYWORD_OVERRIDE = 85;
public static final int TYPE_KEYWORD_METADATA_BLOCK = 86;
public static final int TYPE_KEYWORD_ITEM = 87;
public static final int TYPE_KEYWORD_END = 88;
public ParsedSymbol(int type, Object value) {
this.type = type;
this.value = value;

View File

@@ -33,6 +33,7 @@ import com.jpexs.decompiler.flash.exporters.script.DependencyParser;
import com.jpexs.decompiler.flash.exporters.script.DependencyType;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.flash.helpers.NulWriter;
import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType;
import com.jpexs.decompiler.flash.tags.ABCContainerTag;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.helpers.Helper;
@@ -76,6 +77,8 @@ public abstract class Trait implements Cloneable, Serializable {
public static final int ATTR_Metadata = 0x4;
public static final int ATTR_0x8 = 0x8; //unknown
public static final int TRAIT_SLOT = 0;
public static final int TRAIT_METHOD = 1;
@@ -381,6 +384,62 @@ public abstract class Trait implements Cloneable, Serializable {
public void convert(Trait parent, ConvertData convertData, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List<DottedChain> fullyQualifiedNames, boolean parallel) throws InterruptedException {
}
public abstract GraphTextWriter convertTraitHeader(ABC abc, GraphTextWriter writer);
public GraphTextWriter convertCommonHeaderFlags(String traitType, ABC abc, GraphTextWriter writer) {
writer.appendNoHilight("trait ");
writer.hilightSpecial(traitType, HighlightSpecialType.TRAIT_TYPE);
writer.appendNoHilight(" ");
writer.hilightSpecial(abc.constants.multinameToString(name_index), HighlightSpecialType.TRAIT_NAME);
if ((kindFlags & ATTR_Final) > 0) {
writer.append(" flag ");
writer.hilightSpecial("FINAL", HighlightSpecialType.ATTR_FINAL);
}
if ((kindFlags & ATTR_Override) > 0) {
writer.append(" flag ");
writer.hilightSpecial("OVERRIDE", HighlightSpecialType.ATTR_OVERRIDE);
}
if ((kindFlags & ATTR_Metadata) > 0) {
writer.append(" flag ");
writer.hilightSpecial("METADATA", HighlightSpecialType.ATTR_METADATA);
}
if ((kindFlags & ATTR_0x8) > 0) {
writer.append(" flag ");
writer.hilightSpecial("0x8", HighlightSpecialType.ATTR_0x8);
}
if ((kindFlags & ATTR_Metadata) > 0) {
writer.newLine();
for (int m : metadata) {
writer.append("metadata");
writer.append("\"");
writer.append(Helper.escapeActionScriptString(abc.constants.getString(abc.metadata_info.get(m).name_index)));
writer.append("\"");
writer.newLine();
if (m >= 0 && m < abc.metadata_info.size()) {
for (int i = 0; i < abc.metadata_info.get(m).keys.length; i++) {
int key = abc.metadata_info.get(m).keys[i];
int val = abc.metadata_info.get(m).values[i];
writer.append("item ");
writer.append("\"");
writer.append(Helper.escapeActionScriptString(abc.constants.getString(key)));
writer.append("\"");
writer.append(" ");
writer.append("\"");
writer.append(Helper.escapeActionScriptString(abc.constants.getString(val)));
writer.append("\"");
writer.newLine();
}
}
writer.append("end ;metadata");
}
}
return writer;
}
public GraphTextWriter toStringPackaged(Trait parent, ConvertData convertData, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List<DottedChain> fullyQualifiedNames, boolean parallel) throws InterruptedException {
Namespace ns = abc.constants.getMultiname(name_index).getNamespace(abc.constants);
if ((ns.kind == Namespace.KIND_PACKAGE) || (ns.kind == Namespace.KIND_PACKAGE_INTERNAL)) {

View File

@@ -30,6 +30,7 @@ import com.jpexs.decompiler.flash.exporters.script.DependencyParser;
import com.jpexs.decompiler.flash.exporters.script.DependencyType;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.flash.helpers.NulWriter;
import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.decompiler.graph.ScopeStack;
import com.jpexs.decompiler.graph.TypeItem;
@@ -265,4 +266,13 @@ public class TraitClass extends Trait implements TraitWithSlot {
TraitClass ret = (TraitClass) super.clone();
return ret;
}
@Override
public GraphTextWriter convertTraitHeader(ABC abc, GraphTextWriter writer) {
convertCommonHeaderFlags("class", abc, writer);
writer.appendNoHilight(" slotid ");
writer.hilightSpecial(Integer.toString(slot_id), HighlightSpecialType.SLOT_ID);
writer.newLine();
return writer;
}
}

View File

@@ -135,4 +135,13 @@ public class TraitFunction extends Trait implements TraitWithSlot {
DependencyParser.parseDependenciesFromMethodInfo(customNs, abc, method_info, dependencies, uses, ignorePackage, fullyQualifiedNames, new ArrayList<>());
}
}
@Override
public GraphTextWriter convertTraitHeader(ABC abc, GraphTextWriter writer) {
convertCommonHeaderFlags("function", abc, writer);
writer.newLine();
writer.appendNoHilight("slotid ");
writer.hilightSpecial(Integer.toString(slot_id), HighlightSpecialType.SLOT_ID);
return writer;
}
}

View File

@@ -138,6 +138,9 @@ public class TraitMethodGetterSetter extends Trait {
} else {
writer.startBlock();
if (exportMode != ScriptExportMode.AS_METHOD_STUBS) {
if (exportMode != ScriptExportMode.AS) {
convertTraitHeader(abc, writer);
}
if (bodyIndex != -1) {
abc.bodies.get(bodyIndex).toString(path, exportMode, abc, this, writer, fullyQualifiedNames);
}
@@ -202,4 +205,24 @@ public class TraitMethodGetterSetter extends Trait {
return true;
}
@Override
public GraphTextWriter convertTraitHeader(ABC abc, GraphTextWriter writer) {
switch (kindType) {
case Trait.TRAIT_METHOD:
convertCommonHeaderFlags("method", abc, writer);
break;
case Trait.TRAIT_GETTER:
convertCommonHeaderFlags("getter", abc, writer);
break;
case Trait.TRAIT_SETTER:
convertCommonHeaderFlags("setter", abc, writer);
break;
}
writer.newLine();
writer.appendNoHilight("dispid ");
writer.hilightSpecial("" + disp_id, HighlightSpecialType.DISP_ID);
writer.newLine();
return writer;
}
}

View File

@@ -228,4 +228,19 @@ public class TraitSlotConst extends Trait implements TraitWithSlot {
}
return true;
}
@Override
public GraphTextWriter convertTraitHeader(ABC abc, GraphTextWriter writer) {
convertCommonHeaderFlags(isConst() ? "const" : "slot", abc, writer);
writer.newLine();
writer.appendNoHilight("slotid ");
writer.hilightSpecial(Integer.toString(slot_id), HighlightSpecialType.SLOT_ID);
writer.appendNoHilight(" type ");
writer.hilightSpecial(abc.constants.multinameToString(type_index), HighlightSpecialType.TRAIT_TYPE_NAME);
writer.appendNoHilight(" value ");
writer.hilightSpecial((new ValueKind(value_index, value_kind).toASMString(abc.constants)), HighlightSpecialType.TRAIT_VALUE);
writer.newLine();
return writer;
}
}

View File

@@ -29,5 +29,6 @@ public enum HighlightSpecialType {
FLAG_NEED_REST, FLAG_EXPLICIT, FLAG_HAS_OPTIONAL, FLAG_HAS_PARAM_NAMES,
FLAG_IGNORE_REST, FLAG_NEED_ACTIVATION, FLAG_NEED_ARGUMENTS, FLAG_SET_DXNS,
TRY_TYPE, TRY_NAME,
TEXT
TEXT,
ATTR_METADATA, ATTR_FINAL, ATTR_OVERRIDE, ATTR_0x8
}