/*
* Copyright (C) 2010-2013 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package com.jpexs.decompiler.flash.abc.avm2;
import com.jpexs.decompiler.flash.abc.ABC;
import com.jpexs.decompiler.flash.abc.ABCInputStream;
import com.jpexs.decompiler.flash.abc.CopyOutputStream;
import com.jpexs.decompiler.flash.abc.avm2.graph.AVM2Graph;
import com.jpexs.decompiler.flash.abc.avm2.graph.AVM2GraphSource;
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction;
import com.jpexs.decompiler.flash.abc.avm2.instructions.IfTypeIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition;
import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.*;
import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.*;
import com.jpexs.decompiler.flash.abc.avm2.instructions.comparsion.*;
import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.*;
import com.jpexs.decompiler.flash.abc.avm2.instructions.debug.DebugFileIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.debug.DebugIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.debug.DebugLineIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.*;
import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.*;
import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.*;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.*;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.*;
import com.jpexs.decompiler.flash.abc.avm2.instructions.types.*;
import com.jpexs.decompiler.flash.abc.avm2.instructions.xml.*;
import com.jpexs.decompiler.flash.abc.avm2.parser.ASM3Parser;
import com.jpexs.decompiler.flash.abc.avm2.parser.ParseException;
import com.jpexs.decompiler.flash.abc.avm2.treemodel.*;
import com.jpexs.decompiler.flash.abc.avm2.treemodel.clauses.*;
import com.jpexs.decompiler.flash.abc.types.ABCException;
import com.jpexs.decompiler.flash.abc.types.MethodBody;
import com.jpexs.decompiler.flash.abc.types.MethodInfo;
import com.jpexs.decompiler.flash.abc.types.Multiname;
import com.jpexs.decompiler.flash.abc.types.traits.Trait;
import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst;
import com.jpexs.decompiler.flash.abc.types.traits.Traits;
import com.jpexs.decompiler.flash.graph.Graph;
import com.jpexs.decompiler.flash.graph.GraphPart;
import com.jpexs.decompiler.flash.graph.GraphSourceItem;
import com.jpexs.decompiler.flash.graph.GraphSourceItemPos;
import com.jpexs.decompiler.flash.graph.GraphTargetItem;
import com.jpexs.decompiler.flash.helpers.Helper;
import com.jpexs.decompiler.flash.helpers.Highlighting;
import java.io.*;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
public class AVM2Code implements Serializable {
private static final boolean DEBUG_MODE = false;
public static int toSourceLimit = -1;
public ArrayList code = new ArrayList<>();
public static boolean DEBUG_REWRITE = false;
public static final int OPT_U30 = 0x100;
public static final int OPT_U8 = 0x200;
public static final int OPT_S24 = 0x300;
public static final int OPT_CASE_OFFSETS = 0x400;
public static final int OPT_BYTE = 0x500;
public static final int DAT_MULTINAME_INDEX = OPT_U30 + 0x01;
public static final int DAT_ARG_COUNT = OPT_U30 + 0x02;
public static final int DAT_METHOD_INDEX = OPT_U30 + 0x03;
public static final int DAT_STRING_INDEX = OPT_U30 + 0x04;
public static final int DAT_DEBUG_TYPE = OPT_U8 + 0x05;
public static final int DAT_REGISTER_INDEX = OPT_U8 + 0x06;
public static final int DAT_LINENUM = OPT_U30 + 0x07;
public static final int DAT_LOCAL_REG_INDEX = OPT_U30 + 0x08;
public static final int DAT_SLOT_INDEX = OPT_U30 + 0x09;
public static final int DAT_SLOT_SCOPE_INDEX = OPT_U30 + 0x0A;
public static final int DAT_OFFSET = OPT_S24 + 0x0B;
public static final int DAT_EXCEPTION_INDEX = OPT_U30 + 0x0C;
public static final int DAT_CLASS_INDEX = OPT_U30 + 0x0D;
public static final int DAT_INT_INDEX = OPT_U30 + 0x0E;
public static final int DAT_UINT_INDEX = OPT_U30 + 0x0F;
public static final int DAT_DOUBLE_INDEX = OPT_U30 + 0x10;
public static final int DAT_DECIMAL_INDEX = OPT_U30 + 0x11;
public static final int DAT_CASE_BASEOFFSET = OPT_S24 + 0x12;
public static InstructionDefinition instructionSet[] = new InstructionDefinition[]{
new AddIns(),
new InstructionDefinition(0x9b, "add_d", new int[]{}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
return -2 + 1; //?
}
},
new AddIIns(),
new InstructionDefinition(0xb5, "add_p", new int[]{AVM2Code.OPT_U30}),
new ApplyTypeIns(),
new AsTypeIns(),
new AsTypeLateIns(),
new BitAndIns(),
new BitNotIns(),
new BitOrIns(),
new BitXorIns(),
new InstructionDefinition(0x01, "bkpt", new int[]{}),
new InstructionDefinition(0xf2, "bkptline", new int[]{AVM2Code.OPT_U30}),
new CallIns(),
new InstructionDefinition(0x4d, "callinterface", new int[]{AVM2Code.OPT_U30}),
new CallMethodIns(),
new CallPropertyIns(),
new CallPropLexIns(),
new CallPropVoidIns(),
new CallStaticIns(),
new CallSuperIns(),
new InstructionDefinition(0x4b, "callsuperid", new int[]{}),
new CallSuperVoidIns(),
new CheckFilterIns(),
new CoerceIns(),
new CoerceAIns(),
new InstructionDefinition(0x81, "coerce_b", new int[]{}), //stack:-1+1
new InstructionDefinition(0x84, "coerce_d", new int[]{}), //stack:-1+1
new InstructionDefinition(0x83, "coerce_i", new int[]{}), //stack:-1+1
new InstructionDefinition(0x89, "coerce_o", new int[]{}), //stack:-1+1
new CoerceSIns(),
new InstructionDefinition(0x88, "coerce_u", new int[]{}), //stack:-1+1
new InstructionDefinition(0x9a, "concat", new int[]{}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
return -2 + 1; //?
}
},
new ConstructIns(),
new ConstructPropIns(),
new ConstructSuperIns(),
new ConvertBIns(),
new ConvertIIns(),
new ConvertDIns(),
new ConvertOIns(),
new ConvertUIns(),
new ConvertSIns(),
new InstructionDefinition(0x79, "convert_m", new int[]{}), //-1 +1
new InstructionDefinition(0x7a, "convert_m_p", new int[]{AVM2Code.OPT_U30 /*param (?)*/}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
},
new DebugIns(),
new DebugFileIns(),
new DebugLineIns(),
new DecLocalIns(),
new DecLocalIIns(),
new DecrementIns(),
new DecrementIIns(),
new InstructionDefinition(0x5b, "deldescendants", new int[]{}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
},
new DeletePropertyIns(),
new InstructionDefinition(0x6b, "deletepropertylate", new int[]{}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
},
new DivideIns(),
new InstructionDefinition(0xb8, "divide_p", new int[]{AVM2Code.OPT_U30}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
return -2 + 1; //?
}
},
new DupIns(),
new DXNSIns(),
new DXNSLateIns(),
new EqualsIns(),
new EscXAttrIns(),
new EscXElemIns(),
new InstructionDefinition(0x5f, "finddef", new int[]{AVM2Code.DAT_MULTINAME_INDEX}),
/* //Duplicate OPCODE with deldescendants. Prefering deldescendants (found in FLEX compiler)
new InstructionDefinition(0x5b,"findpropglobalstrict",new int[]{AVM2Code.DAT_MULTINAME_INDEX}){
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
@Override
public int getScopeStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
},*/
new InstructionDefinition(0x5c, "findpropglobal", new int[]{AVM2Code.DAT_MULTINAME_INDEX}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
@Override
public int getScopeStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
},
new FindPropertyIns(),
new FindPropertyStrictIns(),
new GetDescendantsIns(),
new GetGlobalScopeIns(),
new GetGlobalSlotIns(),
new GetLexIns(),
new GetLocalIns(),
new GetLocal0Ins(),
new GetLocal1Ins(),
new GetLocal2Ins(),
new GetLocal3Ins(),
new InstructionDefinition(0x67, "getouterscope", new int[]{AVM2Code.DAT_MULTINAME_INDEX}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
@Override
public int getScopeStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
},
new GetPropertyIns(),
new GetScopeObjectIns(),
new GetSlotIns(),
new GetSuperIns(),
new GreaterEqualsIns(),
new GreaterThanIns(),
new HasNextIns(),
new HasNext2Ins(),
new IfEqIns(),
new IfFalseIns(),
new IfGeIns(),
new IfGtIns(),
new IfLeIns(),
new IfLtIns(),
new IfNGeIns(),
new IfNGtIns(),
new IfNLeIns(),
new IfNLtIns(),
new IfNeIns(),
new IfStrictEqIns(),
new IfStrictNeIns(),
new IfTrueIns(),
new InIns(),
new IncLocalIns(),
new IncLocalIIns(),
new IncrementIns(),
new IncrementIIns(),
new InstructionDefinition(0x9c, "increment_p", new int[]{AVM2Code.OPT_U30 /*param*/}),
new InstructionDefinition(0x9d, "inclocal_p", new int[]{AVM2Code.OPT_U30 /*param*/, AVM2Code.DAT_REGISTER_INDEX}),
new InstructionDefinition(0x9e, "decrement_p", new int[]{AVM2Code.OPT_U30 /*param*/}),
new InstructionDefinition(0x9f, "declocal_p", new int[]{AVM2Code.OPT_U30 /*param*/, AVM2Code.DAT_REGISTER_INDEX}),
new InitPropertyIns(),
new InstanceOfIns(),
new IsTypeIns(),
new IsTypeLateIns(),
new JumpIns(),
new KillIns(),
new LabelIns(),
new LessEqualsIns(),
new LessThanIns(),
new LookupSwitchIns(),
new LShiftIns(),
new ModuloIns(),
new InstructionDefinition(0xb9, "modulo_p", new int[]{AVM2Code.OPT_U30}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
return -2 + 1; //?
}
},
new MultiplyIns(),
new MultiplyIIns(),
new InstructionDefinition(0xb7, "multiply_p", new int[]{AVM2Code.OPT_U30}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
return -2 + 1; //?
}
},
new NegateIns(),
new NegateIIns(),
new InstructionDefinition(0x8f, "negate_p", new int[]{AVM2Code.OPT_U30 /* param */}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
@Override
public int getScopeStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
},
new NewActivationIns(),
new NewArrayIns(),
new NewCatchIns(),
new NewClassIns(),
new NewFunctionIns(),
new NewObjectIns(),
new NextNameIns(),
new NextValueIns(),
new NopIns(),
new NotIns(),
new PopIns(),
new PopScopeIns(),
new PushByteIns(),
new InstructionDefinition(0x22, "pushconstant", new int[]{AVM2Code.DAT_STRING_INDEX}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
return 1; //?
}
},
new InstructionDefinition(0x33, "pushdecimal", new int[]{AVM2Code.DAT_DECIMAL_INDEX}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
return 1; //?
}
},
new InstructionDefinition(0x34, "pushdnan", new int[]{}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
return 1; //?
}
},
new PushDoubleIns(),
new PushFalseIns(),
new PushIntIns(),
new PushNamespaceIns(),
new PushNanIns(),
new PushNullIns(),
new PushScopeIns(),
new PushShortIns(),
new PushStringIns(),
new PushTrueIns(),
new PushUIntIns(),
new PushUndefinedIns(),
new PushWithIns(),
new ReturnValueIns(),
new ReturnVoidIns(),
new RShiftIns(),
new SetLocalIns(),
new SetLocal0Ins(),
new SetLocal1Ins(),
new SetLocal2Ins(),
new SetLocal3Ins(),
new SetGlobalSlotIns(),
new SetPropertyIns(),
new InstructionDefinition(0x69, "setpropertylate", new int[]{}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
@Override
public int getScopeStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
},
new SetSlotIns(),
new SetSuperIns(),
new StrictEqualsIns(),
new SubtractIns(),
new SubtractIIns(),
new InstructionDefinition(0xb6, "subtract_p", new int[]{AVM2Code.OPT_U30}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
@Override
public int getScopeStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
},
new SwapIns(),
new ThrowIns(),
new InstructionDefinition(0xf3, "timestamp", new int[]{}),
new TypeOfIns(),
new URShiftIns(),
new InstructionDefinition(0x35, "li8", new int[]{}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
@Override
public int getScopeStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
},
new InstructionDefinition(0x36, "li16", new int[]{}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
@Override
public int getScopeStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
},
new InstructionDefinition(0x37, "li32", new int[]{}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
@Override
public int getScopeStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
},
new InstructionDefinition(0x38, "lf32", new int[]{}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
@Override
public int getScopeStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
},
new InstructionDefinition(0x39, "lf64", new int[]{}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
@Override
public int getScopeStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
},
new InstructionDefinition(0x3A, "si8", new int[]{}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
@Override
public int getScopeStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
},
new InstructionDefinition(0x3B, "si16", new int[]{}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
@Override
public int getScopeStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
},
new InstructionDefinition(0x3C, "si32", new int[]{}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
@Override
public int getScopeStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
},
new InstructionDefinition(0x3D, "sf32", new int[]{}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
@Override
public int getScopeStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
},
new InstructionDefinition(0x3E, "sf64", new int[]{}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
@Override
public int getScopeStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
},
new InstructionDefinition(0x50, "sxi1", new int[]{}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
@Override
public int getScopeStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
},
new InstructionDefinition(0x51, "sxi8", new int[]{}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
@Override
public int getScopeStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
},
new InstructionDefinition(0x52, "sxi16", new int[]{}) {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
@Override
public int getScopeStackDelta(AVM2Instruction ins, ABC abc) {
throw new UnsupportedOperationException();
}
}
};
//endoflist
public static InstructionDefinition instructionSetByCode[] = buildInstructionSetByCode();
public boolean hideTemporaryRegisters = true;
private static InstructionDefinition[] buildInstructionSetByCode() {
InstructionDefinition result[] = new InstructionDefinition[256];
for (InstructionDefinition id : instructionSet) {
if (result[id.instructionCode] != null) {
System.out.println("Warning: Duplicate OPCODE for instruction " + result[id.instructionCode] + " " + id);
}
result[id.instructionCode] = id;
}
return result;
}
public static final String IDENTOPEN = "/*IDENTOPEN*/";
public static final String IDENTCLOSE = "/*IDENTCLOSE*/";
public AVM2Code() {
}
public Object execute(HashMap arguments, ConstantPool constants) {
int pos = 0;
LocalDataArea lda = new LocalDataArea();
lda.localRegisters = arguments;
try {
while (true) {
AVM2Instruction ins = code.get(pos);
if (ins.definition instanceof JumpIns) {
pos = adr2pos((Long) ins.getParamsAsList(constants).get(0));
continue;
}
if (ins.definition instanceof IfFalseIns) {
Boolean b = (Boolean) lda.operandStack.pop();
if (b == false) {
pos = adr2pos((Long) ins.getParamsAsList(constants).get(0));
} else {
pos++;
}
continue;
}
if (ins.definition instanceof IfTrueIns) {
Boolean b = (Boolean) lda.operandStack.pop();
if (b == true) {
pos = adr2pos((Long) ins.getParamsAsList(constants).get(0));
} else {
pos++;
}
continue;
}
if (ins.definition instanceof ReturnValueIns) {
return lda.operandStack.pop();
}
if (ins.definition instanceof ReturnVoidIns) {
return null;
}
ins.definition.execute(lda, constants, ins.getParamsAsList(constants));
pos++;
}
} catch (ConvertException e) {
}
return null;
}
public AVM2Code(InputStream is) throws IOException {
ABCInputStream ais = new ABCInputStream(is);
while (ais.available() > 0) {
long startOffset = ais.getPosition();
ais.startBuffer();
int instructionCode = ais.read();
InstructionDefinition instr = instructionSetByCode[instructionCode];
if (instr != null) {
int actualOperands[];
if (instructionCode == 0x1b) { //switch
int firstOperand = ais.readS24();
int case_count = ais.readU30();
actualOperands = new int[case_count + 3];
actualOperands[0] = firstOperand;
actualOperands[1] = case_count;
for (int c = 0; c < case_count + 1; c++) {
actualOperands[2 + c] = ais.readS24();
}
} else {
actualOperands = new int[instr.operands.length];
for (int op = 0; op < instr.operands.length; op++) {
switch (instr.operands[op] & 0xff00) {
case OPT_U30:
actualOperands[op] = ais.readU30();
break;
case OPT_U8:
actualOperands[op] = ais.read();
break;
case OPT_BYTE:
actualOperands[op] = (byte) ais.read();
break;
case OPT_S24:
actualOperands[op] = ais.readS24();
break;
}
}
}
code.add(new AVM2Instruction(startOffset, instr, actualOperands, ais.stopBuffer()));
} else {
break; // Unknown instructions are ignored (Some of the obfuscators add unknown instructions)
//throw new UnknownInstructionCode(instructionCode);
}
}
}
public void compact() {
code.trimToSize();
}
public byte[] getBytes() {
return getBytes(null);
}
public byte[] getBytes(byte origBytes[]) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
OutputStream cos;
if ((origBytes != null) && (DEBUG_REWRITE)) {
ByteArrayInputStream origis = new ByteArrayInputStream(origBytes);
cos = new CopyOutputStream(bos, origis);
} else {
cos = bos;
}
try {
for (AVM2Instruction instruction : code) {
cos.write(instruction.getBytes());
}
} catch (IOException ex) {
}
return bos.toByteArray();
}
@Override
public String toString() {
String s = "";
for (AVM2Instruction instruction : code) {
s += instruction.toString() + "\r\n";
}
return s;
}
public String toString(ConstantPool constants) {
String s = "";
int i = 0;
for (AVM2Instruction instruction : code) {
s += Helper.formatAddress(i) + " " + instruction.toString(constants, new ArrayList()) + "\r\n";
i++;
}
return s;
}
private static String popStack(Stack stack) {
for (int i = stack.size() - 1; i >= 0; i--) {
String s = stack.get(i);
if (!s.startsWith("//")) {
stack.remove(i);
return s;
}
}
return null;
}
public String toASMSource(ConstantPool constants, MethodBody body, boolean hex) {
return toASMSource(constants, body, new ArrayList(), hex);
}
public String toASMSource(ConstantPool constants, MethodBody body, List outputMap, boolean hex) {
invalidateCache();
StringBuffer ret = new StringBuffer();
String t = "";
for (int e = 0; e < body.exceptions.length; e++) {
ret.append("exception " + e + " m[" + body.exceptions[e].name_index + "]\"" + Helper.escapeString(body.exceptions[e].getVarName(constants, new ArrayList())) + "\" "
+ "m[" + body.exceptions[e].type_index + "]\"" + Helper.escapeString(body.exceptions[e].getTypeName(constants, new ArrayList())) + "\"\n");
}
List offsets = new ArrayList<>();
for (AVM2Instruction ins : code) {
offsets.addAll(ins.getOffsets());
}
for (AVM2Instruction ins : code) {
if (ins.replaceWith != null) {
for (Object o : ins.replaceWith) {
if (o instanceof ControlFlowTag) {
ControlFlowTag cft = (ControlFlowTag) o;
if (cft.name.equals("appendjump")) {
offsets.add((long) pos2adr(cft.value));
}
}
}
}
}
long ofs = 0;
int ip = 0;
int largeLimit = 20000;
boolean markOffsets = code.size() <= largeLimit;
for (AVM2Instruction ins : code) {
if (hex) {
ret.append("");
ret.append(Helper.bytesToHexString(ins.getBytes()));
ret.append("\n");
}
if (ins.labelname != null) {
ret.append(ins.labelname + ":");
} else if (offsets.contains(ofs)) {
ret.append("ofs" + Helper.formatAddress(ofs) + ":");
}
for (int e = 0; e < body.exceptions.length; e++) {
if (body.exceptions[e].start == ofs) {
ret.append("exceptionstart " + e + ":");
}
if (body.exceptions[e].end == ofs) {
ret.append("exceptionend " + e + ":");
}
if (body.exceptions[e].target == ofs) {
ret.append("exceptiontarget " + e + ":");
}
}
if (ins.replaceWith != null) {
for (Object o : ins.replaceWith) {
if (o instanceof Integer) {
AVM2Instruction ins2 = code.get((Integer) o);
if (ins2.isIgnored()) {
continue;
}
t = Highlighting.hilighOffset("", ins2.mappedOffset > -1 ? ins2.mappedOffset : ofs) + ins2.toStringNoAddress(constants, new ArrayList()) + " ;copy from " + Helper.formatAddress(pos2adr((Integer) o)) + "\n";
ret.append(t);
outputMap.add((Integer) o);
} else if (o instanceof ControlFlowTag) {
ControlFlowTag cft = (ControlFlowTag) o;
if (cft.name.equals("appendjump")) {
t = "jump ofs" + Helper.formatAddress(pos2adr(cft.value)) + "\n";
ret.append(t);
outputMap.add(-1);
}
if (cft.name.equals("mark")) {
ret.append("ofs" + Helper.formatAddress(pos2adr(cft.value)) + ":");
}
}
}
} else {
if (!ins.isIgnored()) {
t = ins.toStringNoAddress(constants, new ArrayList());
if (ins.changeJumpTo > -1) {
t = ins.definition.instructionName + " ofs" + Helper.formatAddress(pos2adr(ins.changeJumpTo));
}
if (markOffsets) {
t = Highlighting.hilighOffset("", ins.mappedOffset > -1 ? ins.mappedOffset : ofs) + t + "\n";
} else {
t = t + "\n";
}
ret.append(t);
outputMap.add(ip);
}
}
ofs += ins.getBytes().length;
ip++;
}
String r = ret.toString();
return r;
}
private boolean cacheActual = false;
private List posCache;
private void buildCache() {
posCache = new ArrayList<>();
long a = 0;
for (int i = 0; i < code.size(); i++) {
posCache.add(a);
a += code.get(i).getBytes().length;
}
posCache.add(a);
cacheActual = true;
}
public int adr2pos(long address) throws ConvertException {
if (!cacheActual) {
buildCache();
}
int ret = posCache.indexOf(address);
if (ret == -1) {
throw new ConvertException("Bad jump try conver ofs" + Helper.formatAddress(address) + " ", -1);
}
return ret;
}
public int pos2adr(int pos) {
if (!cacheActual) {
buildCache();
}
return posCache.get(pos).intValue();
}
public void invalidateCache() {
cacheActual = false;
}
private static String innerStackToString(List