package com.intellij.flex.uiDesigner.abc;
import com.intellij.util.ArrayUtil;
import gnu.trove.TIntHashSet;
import org.jetbrains.annotations.Nullable;
import static com.intellij.flex.uiDesigner.abc.ActionBlockConstants.*;
public final class Decoder {
public final ConstantPool constantPool;
public final MethodInfo methodInfo;
public final MetaDataInfo metadataInfo;
public final ClassInfo classInfo;
public final ScriptInfo scriptInfo;
public final MethodBodies methodBodies;
final DataBuffer in;
public final char[] name;
@Nullable
public final AbcModifier abcModifier;
public Decoder(DataBuffer in) {
this(in, null, null);
}
public Decoder(DataBuffer in, AbcModifier abcModifier) {
this(in, null, abcModifier);
}
public Decoder(DataBuffer in, @Nullable char[] name) {
this(in, name, null);
}
public Decoder(DataBuffer in, @Nullable char[] name, @Nullable AbcModifier abcModifier) {
this.abcModifier = abcModifier;
assert in.position() == 0;
in.skip(4);
this.name = name;
constantPool = new ConstantPool(in);
methodInfo = new MethodInfo(in);
metadataInfo = new MetaDataInfo(in);
classInfo = new ClassInfo(in);
scriptInfo = new ScriptInfo(in);
methodBodies = new MethodBodies(in);
this.in = in;
}
public int position() {
return in.position();
}
private abstract static class Info {
final int estimatedSize;
protected final int[] positions;
Info(DataBuffer in) throws DecoderException {
int pos = in.position();
positions = scan(in);
estimatedSize = in.position() - pos;
}
abstract protected int[] scan(DataBuffer in) throws DecoderException;
public int size() {
return positions.length;
}
}
static final class MethodInfo extends Info {
MethodInfo(DataBuffer in) throws DecoderException {
super(in);
}
@Override
protected int[] scan(DataBuffer in) {
return Scanner.scanMethods(in);
}
public void decodeAll(Encoder visitor, DataBuffer in) throws DecoderException {
for (int position : positions) {
in.seek(position);
visitor.methodInfo(in);
}
}
}
static final class MetaDataInfo extends Info {
MetaDataInfo(DataBuffer in) throws DecoderException {
super(in);
}
@Override
protected int[] scan(DataBuffer in) {
return Scanner.scanMetadata(in);
}
public void decodeAll(Encoder visitor, DataBuffer in) throws DecoderException {
for (int index = 0, n = positions.length; index < n; index++) {
in.seek(positions[index]);
visitor.metadataInfo(index, in.readU32(), in.readU32(), in);
}
}
}
public static final class ClassInfo {
final int instanceEstimatedSize;
final int classEstimatedSize;
// first half - instance, second half - class
private final int[] positions;
ClassInfo(final DataBuffer in) throws DecoderException {
int position = in.position();
final int size = in.readU32();
if (size == 0) {
positions = ArrayUtil.EMPTY_INT_ARRAY;
instanceEstimatedSize = in.position() - position;
classEstimatedSize = 0;
}
else {
positions = new int[size * 2];
scanInstances(in, size, positions);
instanceEstimatedSize = in.position() - position;
position = in.position();
scanClasses(in, size, positions);
classEstimatedSize = in.position() - position;
}
}
private static void scanInstances(DataBuffer in, int size, int[] positions) throws DecoderException {
for (int i = 0; i < size; i++) {
positions[i] = in.position();
in.skipEntries(2); //name & super index
int flags = in.readU8();
if ((flags & CLASS_FLAG_protected) != 0) {
in.readU32();//protected namespace
}
in.skipEntries(in.readU32());
in.readU32(); //init index
Scanner.scanTraits(in);
}
}
private static void scanClasses(DataBuffer in, int size, int[] positions) throws DecoderException {
for (int i = 0; i < size; i++) {
positions[i + size] = in.position();
in.readU32();
Scanner.scanTraits(in);
}
}
public int size() {
return positions.length / 2;
}
public void decodeInstances(Encoder visitor, DataBuffer in) throws DecoderException {
for (int i = 0, n = size(); i < n; i++) {
in.seek(positions[i]);
visitor.startInstance(in);
decodeTraits(visitor, in);
visitor.endInstance();
}
}
public void decodeClasses(Encoder visitor, DataBuffer in) throws DecoderException {
for (int i = size(), n = positions.length; i < n; i++) {
in.seek(positions[i]);
visitor.startClass(in.readU32());
decodeTraits(visitor, in);
visitor.endClass();
}
}
}
public static final class ScriptInfo extends Info {
ScriptInfo(DataBuffer in) throws DecoderException {
super(in);
}
@Override
protected int[] scan(DataBuffer in) throws DecoderException {
return Scanner.scanScripts(in);
}
public void decodeAll(Encoder visitor, DataBuffer in) throws DecoderException {
for (int position : positions) {
in.seek(position);
visitor.startScript(in.readU32());
decodeTraits(visitor, in);
visitor.endScript();
}
}
}
public static final class MethodBodies extends Info {
MethodBodies(DataBuffer in) throws DecoderException {
super(in);
}
@Override
protected int[] scan(DataBuffer in) throws DecoderException {
return Scanner.scanMethodBodies(in);
}
public void decodeAll(Encoder visitor, DataBuffer in) throws DecoderException {
final Opcodes opcodes = visitor.opcodeDecoder;
for (int position : positions) {
in.seek(position);
int methodInfo = in.readU32();
int maxStack = in.readU32();
int maxRegs = in.readU32();
int scopeDepth = in.readU32();
int maxScope = in.readU32();
int codeLength = in.readU32();
int codeStart = in.position();
in.skip(codeLength);
final int behaviour = visitor.startMethodBody(methodInfo, maxStack, maxRegs, scopeDepth, maxScope);
if (behaviour == MethodCodeDecoding.STOP) {
skipExceptions(in.readU32(), in);
int traitCount = in.readU32();
assert traitCount == 0;
continue;
}
int exPos = in.position();
for (int i = 0; i < 2; i++) {
opcodes.reset();
in.seek(exPos);
int exCount = in.readU32();
visitor.startExceptions(exCount);
decodeExceptions(in, codeStart, visitor, exCount);
opcodes.decode(codeStart, codeLength, visitor, behaviour == MethodCodeDecoding.STOP_AFTER_CONSTRUCT_SUPER, in);
visitor.endOpcodes();
visitor.endExceptions();
}
decodeTraits(visitor, in);
visitor.endMethodBody();
}
}
private static void skipExceptions(int exCount, DataBuffer in) {
for (int i = 0; i < exCount; i++) {
in.readU32();
in.readU32();
in.readU32();
in.readU32();
in.readU32();
}
}
private static void decodeExceptions(DataBuffer in, int codeStart, Encoder visitor, int exCount) {
final Opcodes opcodes = visitor.opcodeDecoder;
for (int i = 0; i < exCount; i++) {
int start = codeStart + in.readU32();
int end = codeStart + in.readU32();
int target = codeStart + in.readU32();
int type = in.readU32(); // multiname
int nameIndex = in.readU32();
opcodes.addTarget(start);
opcodes.addTarget(end);
opcodes.addTarget(target);
visitor.exception(start, end, target, type, nameIndex);
}
}
}
private static void decodeTraits(Encoder visitor, DataBuffer in) throws DecoderException {
int count = in.readU32();
visitor.traitCount(count);
for (int i = 0; i < count; i++) {
int name = in.readU32();
int kind = in.readU8();
int valueId;
switch (kind & 0x0f) {
case TRAIT_Var:
case TRAIT_Const:
visitor.slotTrait(kind, name, in.readU32(), in.readU32(), (valueId = in.readU32()), valueId != 0 ? in.readU8() : 0,
decodeTraitsMetadata(kind, in), in);
break;
case TRAIT_Method:
case TRAIT_Getter:
case TRAIT_Setter:
visitor.methodTrait(kind, name, in.readU32(), in.readU32(), decodeTraitsMetadata(kind, in), in);
break;
case TRAIT_Class:
visitor.classTrait(kind, name, in.readU32(), in.readU32(), decodeTraitsMetadata(kind, in));
break;
case TRAIT_Function:
visitor.functionTrait(kind, name, in.readU32(), in.readU32(), decodeTraitsMetadata(kind, in));
break;
default:
throw new DecoderException("Unknown trait kind " + kind);
}
}
}
@Nullable
private static int[] decodeTraitsMetadata(int kind, DataBuffer in) {
int[] md = null;
if (((kind >> 4) & TRAIT_FLAG_metadata) != 0) {
int length = in.readU32();
if (length > 0) {
md = new int[length];
for (int i = 0; i < length; i++) {
md[i] = in.readU32();
}
}
}
return md;
}
@SuppressWarnings({"deprecation"})
static class Opcodes {
private TIntHashSet targetSet;
private boolean targetSetExists;
public void addTarget(int pos) {
if (targetSet == null) {
targetSet = new TIntHashSet();
}
targetSetExists = true;
targetSet.add(pos);
}
public void reset() {
if (targetSet != null) {
targetSet.clear();
targetSetExists = false;
}
}
@SuppressWarnings("ConstantConditions")
public void decode(int start, int length, Encoder v, boolean stopAfterConstructSuper, DataBuffer in) throws DecoderException {
int originalPos = in.position();
in.seek(start);
int end = start + length;
w:
while (in.position() < end) {
int pos = in.position();
int opcode = in.readU8();
if (opcode == OP_label) {
addTarget(pos);
}
if (targetSetExists && targetSet.contains(pos)) {
v.target(pos);
}
switch (opcode) {
case OP_ifnlt: {
int offset = in.readS24();
addTarget(offset + in.position());
v.OP_ifnlt(offset, in.position());
continue;
}
case OP_ifnle: {
int offset = in.readS24();
addTarget(offset + in.position());
v.OP_ifnle(offset, in.position());
continue;
}
case OP_ifngt: {
int offset = in.readS24();
addTarget(offset + in.position());
v.OP_ifngt(offset, in.position());
continue;
}
case OP_ifnge: {
int offset = in.readS24();
addTarget(offset + in.position());
v.OP_ifnge(offset, in.position());
continue;
}
case OP_newcatch: {
v.OP_newcatch(in.readU32());
continue;
}
case OP_setlocal1:
v.OP_setlocal1();
continue;
case OP_setlocal2:
v.OP_setlocal2();
continue;
case OP_setlocal3:
v.OP_setlocal3();
continue;
case OP_returnvoid:
if (v.opcodePass == 1) {
v.beginop(OP_returnvoid);
}
continue;
case OP_returnvalue:
v.OP_returnvalue();
continue;
case OP_nop:
if (v.opcodePass == 1) {
v.beginop(OP_nop);
}
continue;
case OP_bkpt:
if (v.opcodePass == 1) {
v.beginop(OP_bkpt);
}
continue;
case OP_timestamp:
if (v.opcodePass == 1) {
v.beginop(OP_timestamp);
}
continue;
case OP_debugline:
v.OP_debugline(in.readU32());
continue;
case OP_bkptline:
in.readU32();
if (v.opcodePass == 1) {
v.beginop(OP_bkptline);
}
continue;
case OP_debug: {
v.OP_debug(in.readU8(), in.readU32(), in.readU8(), in.readU32());
continue;
}
case OP_debugfile:
v.OP_debugfile(in);
continue;
case OP_jump: {
int jump = in.readS24(); // readjust jump...
addTarget(jump + in.position());
v.OP_jump(jump, in.position());
continue;
}
case OP_pushstring:
v.OP_pushstring(in.readU32());
continue;
case OP_pushnamespace:
v.OP_pushnamespace(in.readU32());
continue;
case OP_pushint:
v.OP_pushint(in.readU32());
continue;
case OP_pushuint:
v.OP_pushuint(in.readU32());
continue;
case OP_pushdouble:
v.OP_pushdouble(in.readU32());
continue;
case OP_getlocal:
v.OP_getlocal(in.readU32());
continue;
case OP_pop: {
v.OP_pop();
continue;
}
case OP_convert_s: {
v.OP_convert_s();
continue;
}
case OP_convert_b: {
v.OP_convert_b();
continue;
}
case OP_pushnull:
case OP_pushundefined:
case OP_pushtrue:
case OP_pushfalse:
case OP_pushnan:
case OP_pushdnan:
case OP_dup:
case OP_swap:
case OP_checkfilter:
case OP_convert_d:
case OP_newactivation:
case OP_deldescendants:
case OP_getglobalscope:
case OP_getlocal0:
case OP_getlocal1:
case OP_getlocal2:
case OP_getlocal3:
case OP_setlocal0:
case OP_pushscope:
case OP_popscope:
case OP_coerce_b:
case OP_esc_xelem:
case OP_esc_xattr:
case OP_negate:
case OP_convert_o:
case OP_convert_m:
case OP_nextname:
case OP_nextvalue:
case OP_astypelate:
case OP_coerce_o:
case OP_hasnext:
case OP_increment:
case OP_increment_i:
case OP_typeof:
case OP_not:
case OP_bitnot:
case OP_lshift:
case OP_rshift:
case OP_urshift:
case OP_bitand:
case OP_bitor:
case OP_bitxor:
case OP_equals:
case OP_strictequals:
case OP_negate_i:
case OP_decrement_i:
case OP_add_i:
case OP_multiply_i:
case OP_divide:
case OP_multiply:
case OP_lessthan:
case OP_lessequals:
case OP_greaterthan:
case OP_greaterequals:
case OP_pushwith:
case OP_instanceof:
case OP_in:
case OP_dxnslate:
case OP_li8:
case OP_li16:
case OP_li32:
case OP_lf32:
case OP_lf64:
case OP_si8:
case OP_si16:
case OP_si32:
case OP_sf32:
case OP_sf64:
case OP_sxi1:
case OP_sxi8:
case OP_sxi16:
case OP_convert_u:
case OP_throw:
if (v.opcodePass == 1) {
v.beginop(opcode);
}
continue;
case OP_convert_m_p: {
v.OP_convert_m_p(in.readU32());
continue;
}
case OP_negate_p: {
v.OP_negate_p(in.readU32());
continue;
}
case OP_increment_p: {
v.OP_increment_p(in.readU32());
continue;
}
case OP_inclocal: {
v.OP_inclocal(in.readU32());
continue;
}
case OP_inclocal_p: {
v.OP_inclocal_p(in.readU32(), in.readU32());
continue;
}
case OP_kill: {
v.OP_kill(in.readU32());
continue;
}
case OP_label: {
v.OP_label();
continue;
}
case OP_inclocal_i: {
v.OP_inclocal_i(in.readU32());
continue;
}
case OP_decrement: {
v.OP_decrement();
continue;
}
case OP_decrement_p: {
int param = in.readU32();
v.OP_decrement_p(param);
continue;
}
case OP_declocal: {
int index = in.readU32();
v.OP_declocal(index);
continue;
}
case OP_declocal_p: {
v.OP_declocal_p(in.readU32(), in.readU32());
continue;
}
case OP_declocal_i: {
v.OP_declocal_i(in.readU32());
continue;
}
case OP_setlocal: {
int index = in.readU32();
v.OP_setlocal(index);
continue;
}
case OP_add: {
v.OP_add();
continue;
}
case OP_add_p: {
int param = in.readU32();
v.OP_add_p(param);
continue;
}
case OP_subtract: {
v.OP_subtract();
continue;
}
case OP_subtract_p: {
v.OP_subtract_p(in.readU32());
continue;
}
case OP_subtract_i: {
v.OP_subtract_i();
continue;
}
case OP_multiply_p: {
v.OP_multiply_p(in.readU32());
continue;
}
case OP_divide_p: {
v.OP_divide_p(in.readU32());
continue;
}
case OP_modulo: {
v.OP_modulo();
continue;
}
case OP_modulo_p: {
v.OP_modulo_p(in.readU32());
continue;
}
case OP_lookupswitch: {
int opPos = in.position() - 1; // OP_lookupswtich position...
int defaultPos = in.readS24();
addTarget(defaultPos + opPos);
int size_1 = in.readU32(); // size - 1
int[] casePos = new int[size_1 + 1];
int caseTablePos = in.position(); // case position
for (int i = 0, size = casePos.length; i < size; i++) {
casePos[i] = in.readS24();
addTarget(casePos[i] + opPos);
}
v.OP_lookupswitch(defaultPos, casePos, opPos, caseTablePos);
continue;
}
case OP_iftrue: {
int offset = in.readS24();
addTarget(offset + in.position());
v.OP_iftrue(offset, in.position());
continue;
}
case OP_iffalse: {
int offset = in.readS24();
addTarget(offset + in.position());
v.OP_iffalse(offset, in.position());
continue;
}
case OP_ifeq: {
int offset = in.readS24();
addTarget(offset + in.position());
v.OP_ifeq(offset, in.position());
continue;
}
case OP_ifne: {
int offset = in.readS24();
addTarget(offset + in.position());
v.OP_ifne(offset, in.position());
continue;
}
case OP_ifstricteq: {
int offset = in.readS24();
addTarget(offset + in.position());
v.OP_ifstricteq(offset, in.position());
continue;
}
case OP_ifstrictne: {
int offset = in.readS24();
addTarget(offset + in.position());
v.OP_ifstrictne(offset, in.position());
continue;
}
case OP_iflt: {
int offset = in.readS24();
addTarget(offset + in.position());
v.OP_iflt(offset, in.position());
continue;
}
case OP_ifle: {
int offset = in.readS24();
addTarget(offset + in.position());
v.OP_ifle(offset, in.position());
continue;
}
case OP_ifgt: {
int offset = in.readS24();
addTarget(offset + in.position());
v.OP_ifgt(offset, in.position());
continue;
}
case OP_ifge: {
int offset = in.readS24();
addTarget(offset + in.position());
v.OP_ifge(offset, in.position());
continue;
}
case OP_newobject: {
v.OP_newobject(in.readU32());
continue;
}
case OP_newarray: {
v.OP_newarray(in.readU32());
continue;
}
// get a property using a multiname ref
case OP_getproperty: {
v.OP_getproperty(in.readU32());
continue;
}
// set a property using a multiname ref
case OP_setproperty: {
v.OP_setproperty(in.readU32());
continue;
}
// set a property using a multiname ref
case OP_initproperty: {
v.OP_initproperty(in.readU32());
continue;
}
case OP_getlex:
case OP_getdescendants:
case OP_finddef:
case OP_findproperty:
case OP_findpropstrict:
case OP_deleteproperty:
case OP_getsuper:
case OP_setsuper:
v.op32(in.readU32(), opcode);
continue;
case OP_hasnext2: {
v.OP_hasnext2(in.readU32(), in.readU32());
continue;
}
case OP_setslot: {
v.OP_setslot(in.readU32());
continue;
}
case OP_getslot: {
v.OP_getslot(in.readU32());
continue;
}
case OP_setglobalslot: {
v.OP_setglobalslot(in.readU32());
continue;
}
case OP_getglobalslot: {
v.OP_getglobalslot(in.readU32());
continue;
}
case OP_call: {
v.OP_call(in.readU32());
continue;
}
case OP_construct: {
v.OP_construct(in.readU32());
continue;
}
case OP_applytype: {
v.OP_applytype(in.readU32());
continue;
}
case OP_newfunction: {
v.OP_newfunction(in.readU32());
continue;
}
case OP_newclass: {
v.OP_newclass(in.readU32());
continue;
}
case OP_callstatic: {
v.OP_callstatic(in.readU32(), in.readU32());
continue;
}
case OP_callmethod: {
v.OP_callmethod(in.readU32(), in.readU32());
continue;
}
case OP_callproperty: {
v.OP_callproperty(in.readU32(), in.readU32());
continue;
}
case OP_callproplex: {
v.OP_callproplex(in.readU32(), in.readU32());
continue;
}
case OP_constructprop: {
v.OP_constructprop(in.readU32(), in.readU32());
continue;
}
case OP_callsuper: {
v.OP_callsuper(in.readU32(), in.readU32());
continue;
}
case OP_constructsuper:
v.OP_constructsuper(in.readU32());
if (stopAfterConstructSuper) {
if (v.opcodePass == 1) {
v.beginop(OP_returnvoid);
}
break w;
}
continue;
case OP_pushshort: {
// fixme this just pushes an integer since we dont have short atoms yet
int n = in.readU32();
v.OP_pushshort(n);
continue;
}
case OP_astype: {
v.OP_astype(in.readU32());
continue;
}
case OP_coerce: {
v.OP_coerce(in.readU32());
continue;
}
case OP_coerce_a: {
v.OP_coerce_a();
continue;
}
case OP_coerce_i: {
v.OP_coerce_i();
continue;
}
case OP_coerce_u: {
v.OP_coerce_u();
continue;
}
case OP_coerce_d: {
v.OP_coerce_d();
continue;
}
case OP_coerce_s: {
v.OP_coerce_s();
continue;
}
case OP_istype: {
v.OP_istype(in.readU32());
continue;
}
case OP_istypelate: {
v.OP_istypelate();
continue;
}
case OP_pushbyte: {
v.OP_pushbyte(in.readU8());
continue;
}
case OP_getscopeobject: {
v.OP_getscopeobject(in.readU8());
continue;
}
case OP_convert_i: {
v.OP_convert_i();
continue;
}
case OP_dxns: {
v.OP_dxns(in.readU32());
continue;
}
case OP_pushuninitialized: {
v.OP_pushconstant(in.readU32());
continue;
}
case OP_callsupervoid: {
v.OP_callsupervoid(in.readU32(), in.readU32());
continue;
}
case OP_callpropvoid: {
v.OP_callpropvoid(in.readU32(), in.readU32());
continue;
}
default: {
throw new DecoderException("unknown opcode:" + opcode);
}
}
}
in.seek(originalPos);
}
}
interface MethodCodeDecoding {
int CONTINUE = 0;
int STOP = 1;
int STOP_AFTER_CONSTRUCT_SUPER = 2;
}
}