@awayfl/avm2
Version:
Virtual machine for executing AS3 code
859 lines (858 loc) • 34.6 kB
JavaScript
import { Bytecode } from '../Bytecode';
import { Instruction } from './Instruction';
import { Settings } from '../Settings';
/**
* Propogade stack for calculation real stack size for every instruction
*/
export function propagateStack(position, stack, q) {
var v = stack;
var l = q.length;
var minStack = stack;
for (var i = 0; i < l; i++) {
if (q[i].position >= position) {
if (q[i].stack >= 0)
return minStack;
q[i].stack = v;
v += q[i].delta;
if (v < minStack)
minStack = v;
for (var j = 0; j < q[i].refs.length; j++) {
var s = propagateStack(q[i].refs[j], v, q);
if (s < minStack)
minStack = s;
}
if (q[i].terminal)
return minStack;
}
}
return minStack;
}
/**
* Like as propogadeStack, only for scope
*/
export function propagateScope(position, scope, q) {
var v = scope;
var l = q.length;
for (var i = 0; i < l; i++) {
if (q[i].position >= position) {
if (q[i].scope >= 0)
return;
q[i].scope = v;
v += q[i].deltaScope;
for (var j = 0; j < q[i].refs.length; j++) {
propagateScope(q[i].refs[j], v, q);
}
if (q[i].terminal)
return;
}
}
}
export function propogateTree(q, jumps) {
var branches = {};
var condNodes = [];
branches[0] = 0;
var l = q.length;
for (var i = 1; i < l; i++) {
var inst = q[i];
if (jumps.indexOf(inst.position) > -1) {
branches[inst.position] = i;
}
if (inst.name >= Bytecode.IFNLT
&& inst.name <= Bytecode.LOOKUPSWITCH) {
condNodes.push(inst);
}
}
for (var _i = 0, condNodes_1 = condNodes; _i < condNodes_1.length; _i++) {
var c = condNodes_1[_i];
if (c.name === Bytecode.LOOKUPSWITCH) {
var params = c.params;
c.childs = [];
for (var i = 0; i < params.length - 1; i++) {
c.childs.push(branches[i]);
}
continue;
}
if (c.name === Bytecode.JUMP) {
c.childs = [branches[c.params[0]]];
continue;
}
c.childs.push(branches[c.params[0]]);
}
}
function u30(state) {
var code = state.code;
var i = state.index;
var u = code[i++];
if (u & 0x80) {
u = u & 0x7f | code[i++] << 7;
if (u & 0x4000) {
u = u & 0x3fff | code[i++] << 14;
if (u & 0x200000) {
u = u & 0x1fffff | code[i++] << 21;
if (u & 0x10000000) {
u = u & 0x0fffffff | code[i++] << 28;
u = u & 0xffffffff;
}
}
}
}
state.index = i;
return u >>> 0;
}
function mn(state) {
var index = u30(state);
var name = state.abc.getMultiname(index);
var mnResult = state.currentMn;
mnResult[0] = index;
if (name.isRuntimeName() || name.isRuntimeNamespace()) {
mnResult[1] = 256;
mnResult[2] = name.isRuntimeName() && name.isRuntimeNamespace() ? -2 : -1;
return mnResult;
}
mnResult[1] = 0;
mnResult[2] = 0;
return mnResult;
}
function s24(state) {
var code = state.code;
var i = state.index;
var u = code[i++] | (code[i++] << 8) | (code[i++] << 16);
u = (u << 8) >> 8;
state.index = i;
return u;
}
function s8(state) {
return (state.code[state.index++] << 24) >> 24;
}
/**
* Analyzing instruction set from method info
* @param methodInfo
*/
export function analyze(methodInfo) {
var abc = methodInfo.abc;
var body = methodInfo.getBody();
var code = body.code;
var q = [];
var state = {
index: 0,
abc: abc,
code: code,
currentMn: [0, 0, 0]
};
var type = 0;
var lastType = 0;
var requireScope = false;
for (; state.index < code.length;) {
var oldi = state.index;
var z = code[state.index++];
var last = Settings.OPTIMISE_ON_IR ? q[q.length - 1] : null;
var ins = void 0;
switch (z) {
case Bytecode.NOP:
ins = (new Instruction(oldi, z));
break;
case Bytecode.LABEL:
ins = (new Instruction(oldi, z));
break;
case Bytecode.DXNSLATE:
ins = (new Instruction(oldi, z, 0, -1));
break;
case Bytecode.DEBUGFILE:
case Bytecode.DEBUGLINE:
ins = (new Instruction(oldi, z, u30(state)));
break;
case Bytecode.DEBUG:
ins = (new Instruction(oldi, z, [s8(state), u30(state), s8(state), u30(state)]));
break;
case Bytecode.THROW:
ins = (new Instruction(oldi, z, null, -1, 0, true));
break;
case Bytecode.PUSHSCOPE:
ins = (new Instruction(oldi, z, null, -1, 1));
ins.returnTypeId = lastType = ++type;
break;
case Bytecode.PUSHWITH:
ins = (new Instruction(oldi, z, null, -1, 1));
break;
case Bytecode.POPSCOPE:
ins = (new Instruction(oldi, z, null, 0, -1));
break;
case Bytecode.GETSCOPEOBJECT:
ins = (new Instruction(oldi, z, s8(state), 1, 0));
ins.returnTypeId = lastType = ++type;
requireScope = true;
break;
case Bytecode.GETGLOBALSCOPE:
ins = (new Instruction(oldi, z, null, 1));
ins.returnTypeId = lastType = ++type;
requireScope = true;
break;
case Bytecode.GETSLOT:
ins = (new Instruction(oldi, z, u30(state), 0));
ins.returnTypeId = lastType = ++type;
break;
case Bytecode.SETSLOT:
ins = (new Instruction(oldi, z, u30(state), -2));
break;
case Bytecode.NEXTNAME:
ins = (new Instruction(oldi, z, null, -1));
ins.returnTypeId = lastType = ++type;
break;
case Bytecode.NEXTVALUE:
ins = (new Instruction(oldi, z, null, -1));
ins.returnTypeId = lastType = ++type;
break;
case Bytecode.HASNEXT:
ins = (new Instruction(oldi, z, null, -1));
ins.returnTypeId = lastType = -2 /* PRIMITIVE_TYPE.BOOL */;
break;
case Bytecode.HASNEXT2:
ins = (new Instruction(oldi, z, [u30(state), u30(state)], 1));
ins.returnTypeId = lastType = -2 /* PRIMITIVE_TYPE.BOOL */;
break;
case Bytecode.IN:
ins = (new Instruction(oldi, z, null, -1));
ins.returnTypeId = lastType = -2 /* PRIMITIVE_TYPE.BOOL */;
break;
case Bytecode.DUP:
ins = (new Instruction(oldi, z, null, 1));
ins.returnTypeId = lastType = type;
if (last) {
switch (last.name) {
case Bytecode.PUSHTRUE:
case Bytecode.PUSHFALSE:
case Bytecode.PUSHNAN:
case Bytecode.PUSHINT:
case Bytecode.PUSHDOUBLE:
case Bytecode.PUSHBYTE:
case Bytecode.PUSHFLOAT:
case Bytecode.PUSHSTRING:
case Bytecode.PUSHNULL: {
ins.name = last.name;
ins.params = last.params;
ins.comment = 'IR: DUP changed to PUSH*, reason: prevent optimisation';
}
}
}
break;
case Bytecode.POP: {
ins = (new Instruction(oldi, z, null, -1));
ins.returnTypeId = lastType = type;
//
if (last && last.name === Bytecode.CALLPROPERTY) {
last.name = Bytecode.CALLPROPVOID;
last.comment = 'IR: Optimised from "CALLPROPERTY", reason: POP STACK';
}
break;
}
case Bytecode.SWAP:
ins = (new Instruction(oldi, z, null, 0));
break;
case Bytecode.PUSHTRUE:
ins = (new Instruction(oldi, z, null, 1));
ins.returnTypeId = lastType = -2 /* PRIMITIVE_TYPE.BOOL */;
break;
case Bytecode.PUSHFALSE:
ins = (new Instruction(oldi, z, null, 1));
ins.returnTypeId = lastType = -2 /* PRIMITIVE_TYPE.BOOL */;
break;
case Bytecode.PUSHBYTE:
ins = (new Instruction(oldi, z, s8(state), 1));
ins.returnTypeId = lastType = -3 /* PRIMITIVE_TYPE.NUMBER */;
break;
case Bytecode.PUSHSHORT:
ins = (new Instruction(oldi, z, u30(state) << 16 >> 16, 1));
ins.returnTypeId = lastType = -3 /* PRIMITIVE_TYPE.NUMBER */;
break;
case Bytecode.PUSHINT:
ins = (new Instruction(oldi, z, u30(state), 1));
ins.returnTypeId = lastType = -3 /* PRIMITIVE_TYPE.NUMBER */;
break;
case Bytecode.PUSHUINT:
ins = (new Instruction(oldi, z, u30(state), 1));
ins.returnTypeId = lastType = -3 /* PRIMITIVE_TYPE.NUMBER */;
break;
case Bytecode.PUSHDOUBLE:
ins = (new Instruction(oldi, z, u30(state), 1));
ins.returnTypeId = lastType = -3 /* PRIMITIVE_TYPE.NUMBER */;
break;
case Bytecode.PUSHNAN:
ins = (new Instruction(oldi, z, null, 1));
ins.returnTypeId = lastType = -3 /* PRIMITIVE_TYPE.NUMBER */;
break;
case Bytecode.PUSHNULL:
ins = (new Instruction(oldi, z, null, 1));
ins.returnTypeId = lastType = -6 /* PRIMITIVE_TYPE.NULL */;
break;
case Bytecode.PUSHUNDEFINED:
ins = (new Instruction(oldi, z, null, 1));
ins.returnTypeId = lastType = -5 /* PRIMITIVE_TYPE.UNDEF */;
break;
case Bytecode.PUSHSTRING:
ins = (new Instruction(oldi, z, u30(state), 1));
ins.returnTypeId = lastType = -4 /* PRIMITIVE_TYPE.STRING */;
break;
case Bytecode.IFEQ: {
var j = s24(state);
var i = state.index;
ins = (new Instruction(oldi, z, i + j, -2, 0, false, [i + j]));
break;
}
case Bytecode.IFNE: {
var j = s24(state);
var i = state.index;
ins = (new Instruction(oldi, z, i + j, -2, 0, false, [i + j]));
break;
}
case Bytecode.IFSTRICTEQ: {
var j = s24(state);
var i = state.index;
ins = (new Instruction(oldi, z, i + j, -2, 0, false, [i + j]));
break;
}
case Bytecode.IFSTRICTNE: {
var j = s24(state);
var i = state.index;
ins = (new Instruction(oldi, z, i + j, -2, 0, false, [i + j]));
break;
}
case Bytecode.IFGT:
case Bytecode.IFNLE: {
var j = s24(state);
var i = state.index;
ins = (new Instruction(oldi, z, i + j, -2, 0, false, [i + j]));
break;
}
case Bytecode.IFGE:
case Bytecode.IFNLT: {
var j = s24(state);
var i = state.index;
ins = (new Instruction(oldi, z, i + j, -2, 0, false, [i + j]));
break;
}
case Bytecode.IFLT:
case Bytecode.IFNGE: {
var j = s24(state);
var i = state.index;
ins = (new Instruction(oldi, z, i + j, -2, 0, false, [i + j]));
break;
}
case Bytecode.IFLE:
case Bytecode.IFNGT: {
var j = s24(state);
var i = state.index;
ins = (new Instruction(oldi, z, i + j, -2, 0, false, [i + j]));
break;
}
case Bytecode.IFTRUE: {
var j = s24(state);
var i = state.index;
ins = (new Instruction(oldi, z, i + j, -1, 0, false, [i + j]));
break;
}
case Bytecode.IFFALSE: {
var j = s24(state);
var i = state.index;
ins = (new Instruction(oldi, z, i + j, -1, 0, false, [i + j]));
break;
}
case Bytecode.LOOKUPSWITCH: {
var offset = oldi + s24(state);
var cases = u30(state);
var table = [offset];
for (var j = 0; j <= cases; j++)
table.push(oldi + s24(state));
ins = (new Instruction(oldi, z, table, -1, 0, true, table));
break;
}
case Bytecode.JUMP: {
var j = s24(state);
var i = state.index;
ins = (new Instruction(oldi, z, i + j, 0, 0, true, [i + j]));
break;
}
case Bytecode.RETURNVALUE:
ins = (new Instruction(oldi, z, null, -1, 0, true));
break;
case Bytecode.RETURNVOID:
ins = (new Instruction(oldi, z, null, 0, 0, true));
break;
case Bytecode.NOT:
ins = (new Instruction(oldi, z, null, 0));
ins.returnTypeId = lastType = -2 /* PRIMITIVE_TYPE.BOOL */;
break;
case Bytecode.BITNOT:
ins = (new Instruction(oldi, z, null, 0));
ins.returnTypeId = lastType = -3 /* PRIMITIVE_TYPE.NUMBER */;
break;
case Bytecode.NEGATE:
ins = (new Instruction(oldi, z, null, 0));
ins.returnTypeId = lastType = -3 /* PRIMITIVE_TYPE.NUMBER */;
break;
case Bytecode.INCREMENT:
ins = (new Instruction(oldi, z, null, 0));
ins.returnTypeId = lastType = -3 /* PRIMITIVE_TYPE.NUMBER */;
break;
case Bytecode.DECREMENT:
ins = (new Instruction(oldi, z, null, 0));
ins.returnTypeId = lastType = -3 /* PRIMITIVE_TYPE.NUMBER */;
break;
case Bytecode.INCLOCAL:
case Bytecode.DECLOCAL:
ins = (new Instruction(oldi, z, u30(state), 0));
ins.returnTypeId = lastType = -3 /* PRIMITIVE_TYPE.NUMBER */;
break;
case Bytecode.INCREMENT_I:
case Bytecode.DECREMENT_I:
ins = (new Instruction(oldi, z, null, 0));
ins.returnTypeId = lastType = -3 /* PRIMITIVE_TYPE.NUMBER */;
break;
case Bytecode.INCLOCAL_I:
case Bytecode.DECLOCAL_I:
ins = (new Instruction(oldi, z, u30(state), 0));
ins.returnTypeId = lastType = -3 /* PRIMITIVE_TYPE.NUMBER */;
break;
case Bytecode.NEGATE_I:
ins = (new Instruction(oldi, z, null, 0));
ins.returnTypeId = lastType = -3 /* PRIMITIVE_TYPE.NUMBER */;
break;
case Bytecode.ADD_I:
case Bytecode.SUBTRACT_I:
case Bytecode.MULTIPLY_I:
case Bytecode.ADD:
case Bytecode.SUBTRACT:
case Bytecode.MULTIPLY:
case Bytecode.DIVIDE:
case Bytecode.MODULO:
case Bytecode.LSHIFT:
case Bytecode.RSHIFT:
case Bytecode.URSHIFT:
case Bytecode.BITAND:
case Bytecode.BITOR:
case Bytecode.BITXOR:
ins = (new Instruction(oldi, z, null, -1));
ins.returnTypeId = lastType = -3 /* PRIMITIVE_TYPE.NUMBER */;
break;
case Bytecode.EQUALS:
case Bytecode.STRICTEQUALS:
case Bytecode.GREATERTHAN:
case Bytecode.GREATEREQUALS:
case Bytecode.LESSTHAN:
case Bytecode.LESSEQUALS:
ins = (new Instruction(oldi, z, null, -1));
ins.returnTypeId = lastType = -2 /* PRIMITIVE_TYPE.BOOL */;
break;
case Bytecode.TYPEOF:
ins = (new Instruction(oldi, z, null, 0));
ins.returnTypeId = lastType = ++type;
break;
case Bytecode.INSTANCEOF: {
ins = (new Instruction(oldi, z, null, -1));
ins.returnTypeId = lastType = -2 /* PRIMITIVE_TYPE.BOOL */;
break;
}
case Bytecode.ISTYPE: {
var _a = mn(state), index = _a[0], d = _a[2];
ins = (new Instruction(oldi, z, index, 0 + d));
ins.returnTypeId = lastType = -2 /* PRIMITIVE_TYPE.BOOL */;
requireScope = true;
break;
}
case Bytecode.ISTYPELATE:
ins = (new Instruction(oldi, z, null, -1));
ins.returnTypeId = lastType = -2 /* PRIMITIVE_TYPE.BOOL */;
requireScope = true;
break;
case Bytecode.ASTYPELATE:
ins = (new Instruction(oldi, z, null, -1));
break;
case Bytecode.ASTYPE: {
var _b = mn(state), index = _b[0], d = _b[2];
ins = (new Instruction(oldi, z, index, 0 + d));
requireScope = true;
break;
}
case Bytecode.CALL: {
var argnum = u30(state);
ins = (new Instruction(oldi, z, argnum, -argnum - 1));
requireScope = true;
break;
}
case Bytecode.CONSTRUCT: {
var argnum = u30(state);
ins = (new Instruction(oldi, z, argnum, -argnum));
ins.returnTypeId = lastType = ++type;
break;
}
case Bytecode.CALLPROPERTY: {
var _c = mn(state), index = _c[0], dyn = _c[1], d = _c[2];
var argnum = u30(state);
ins = (new Instruction(oldi, z + dyn, [argnum, index], -argnum + d));
ins.returnTypeId = lastType = ++type;
requireScope = requireScope || dyn !== 0;
break;
}
case Bytecode.CALLPROPLEX: {
var _d = mn(state), index = _d[0], dyn = _d[1], d = _d[2];
var argnum = u30(state);
ins = (new Instruction(oldi, z + dyn, [argnum, index], -argnum + d));
ins.returnTypeId = lastType = ++type;
requireScope = requireScope || dyn !== 0;
break;
}
case Bytecode.CALLPROPVOID: {
var _e = mn(state), index = _e[0], dyn = _e[1], d = _e[2];
var argnum = u30(state);
ins = (new Instruction(oldi, z + dyn, [argnum, index], -(argnum + 1) + d));
break;
}
case Bytecode.APPLYTYPE: {
var argnum = u30(state);
ins = (new Instruction(oldi, z, argnum, -argnum));
ins.returnTypeId = lastType = type;
break;
}
case Bytecode.FINDPROPSTRICT: {
var _f = mn(state), index = _f[0], dyn = _f[1], d = _f[2];
ins = (new Instruction(oldi, z + dyn, index, 1 + d));
ins.returnTypeId = lastType = ++type;
requireScope = true;
break;
}
case Bytecode.FINDPROPERTY: {
var _g = mn(state), index = _g[0], dyn = _g[1], d = _g[2];
ins = (new Instruction(oldi, z + dyn, index, 1 + d));
ins.returnTypeId = lastType = ++type;
requireScope = true;
break;
}
case Bytecode.NEWFUNCTION:
ins = (new Instruction(oldi, z, u30(state), 1));
ins.returnTypeId = lastType = ++type;
requireScope = true;
break;
case Bytecode.NEWCLASS:
ins = (new Instruction(oldi, z, u30(state), 0));
ins.returnTypeId = lastType = ++type;
requireScope = true;
break;
case Bytecode.GETDESCENDANTS:
ins = (new Instruction(oldi, z, u30(state), 0));
ins.returnTypeId = lastType = ++type;
break;
case Bytecode.NEWARRAY: {
var argnum = u30(state);
ins = (new Instruction(oldi, z, argnum, -argnum + 1));
ins.returnTypeId = lastType = ++type;
break;
}
case Bytecode.NEWOBJECT: {
var argnum = u30(state);
ins = (new Instruction(oldi, z, argnum, -2 * argnum + 1));
ins.returnTypeId = lastType = ++type;
break;
}
case Bytecode.NEWACTIVATION:
ins = (new Instruction(oldi, z, null, 1));
ins.returnTypeId = lastType = ++type;
requireScope = true;
break;
case Bytecode.NEWCATCH:
ins = (new Instruction(oldi, z, u30(state), 1));
requireScope = true;
break;
case Bytecode.CONSTRUCTSUPER: {
var argnum = u30(state);
ins = (new Instruction(oldi, z, argnum, -(argnum + 1)));
break;
}
case Bytecode.CALLSUPER: {
var _h = mn(state), index = _h[0], dyn = _h[1], d = _h[2];
var argnum = u30(state);
ins = (new Instruction(oldi, z + dyn, [argnum, index], -argnum + d));
break;
}
case Bytecode.CALLSUPERVOID: {
var _j = mn(state), index = _j[0], dyn = _j[1], d = _j[2];
var argnum = u30(state);
ins = (new Instruction(oldi, z + dyn, [argnum, index], -(argnum + 1) + d));
break;
}
case Bytecode.CONSTRUCTPROP: {
var _k = mn(state), index = _k[0], dyn = _k[1], d = _k[2];
var argnum = u30(state);
ins = (new Instruction(oldi, z + dyn, [argnum, index], -argnum + d));
ins.returnTypeId = lastType = ++type;
break;
}
case Bytecode.GETPROPERTY: {
var _l = mn(state), index = _l[0], dyn = _l[1], d = _l[2];
ins = (new Instruction(oldi, Bytecode.GETPROPERTY + dyn, index, 0 + d));
ins.returnTypeId = lastType = ++type;
break;
}
// we collapse 2 operation to one, but this is can prevent optimisations
case Bytecode.INITPROPERTY:
case Bytecode.SETPROPERTY: {
var _m = mn(state), index = _m[0], dyn = _m[1], d = _m[2];
ins = (new Instruction(oldi, Bytecode.SETPROPERTY + dyn, index, -2 + d));
break;
}
case Bytecode.DELETEPROPERTY: {
var _o = mn(state), index = _o[0], dyn = _o[1], d = _o[2];
ins = (new Instruction(oldi, z + dyn, index, 0 + d));
break;
}
case Bytecode.GETSUPER: {
var _p = mn(state), index = _p[0], dyn = _p[1], d = _p[2];
ins = (new Instruction(oldi, z + dyn, index, 0 + d));
ins.returnTypeId = lastType = ++type;
break;
}
case Bytecode.SETSUPER: {
var _q = mn(state), index = _q[0], dyn = _q[1], d = _q[2];
ins = (new Instruction(oldi, z + dyn, index, -2 + d));
break;
}
case Bytecode.COERCE: {
var _r = mn(state), index = _r[0], dyn = _r[1], d = _r[2];
ins = (new Instruction(oldi, z + dyn, index, 0 + d));
ins.returnTypeId = lastType;
// construct prop was called with same MN that used for coerce, redundant
if (last && last.name === Bytecode.CONSTRUCTPROP && last.params[1] === index) {
ins.returnTypeId = -1 /* PRIMITIVE_TYPE.VOID */;
ins.name = Bytecode.LABEL;
ins.comment = 'IR: Drop coerce, reason: redundant';
break;
}
requireScope = true;
break;
}
case Bytecode.COERCE_A:
ins = (new Instruction(oldi, z, null, 0));
ins.returnTypeId = lastType;
break;
case Bytecode.COERCE_S:
ins = (new Instruction(oldi, z, null, 0));
ins.returnTypeId = -4 /* PRIMITIVE_TYPE.STRING */;
break;
case Bytecode.CONVERT_D: {
ins = (new Instruction(oldi, z, null, 0));
if (last) {
switch (last.name) {
case Bytecode.PUSHINT:
case Bytecode.PUSHFLOAT:
case Bytecode.PUSHDOUBLE:
case Bytecode.ADD_I:
case Bytecode.INCREMENT:
case Bytecode.DECREMENT:
case Bytecode.DECREMENT_I:
case Bytecode.INCREMENT_I: {
ins.name = Bytecode.LABEL;
ins.comment = 'IR: CONVERT_D removed, reason: arguments strictly number';
break;
}
}
}
break;
}
case Bytecode.CONVERT_B: {
ins = (new Instruction(oldi, z, null, 0));
if (last) {
switch (last.name) {
case Bytecode.EQUALS:
case Bytecode.STRICTEQUALS:
case Bytecode.GREATERTHAN:
case Bytecode.GREATEREQUALS:
case Bytecode.LESSTHAN:
case Bytecode.LESSEQUALS:
case Bytecode.NOT:
{
ins.name = Bytecode.LABEL;
ins.comment = 'IR: CONVERT_B removed, reason: arguments strictly boolean';
break;
}
}
}
break;
}
case Bytecode.ESC_XATTR:
case Bytecode.ESC_XELEM:
case Bytecode.CONVERT_I:
case Bytecode.CONVERT_U:
case Bytecode.CONVERT_S:
case Bytecode.CONVERT_O:
ins = (new Instruction(oldi, z, null, 0));
break;
case Bytecode.CHECKFILTER:
ins = (new Instruction(oldi, z, null, 0));
break;
case Bytecode.GETLOCAL:
ins = (new Instruction(oldi, Bytecode.GETLOCAL, u30(state), 1));
ins.returnTypeId = lastType;
break;
case Bytecode.GETLOCAL0:
ins = (new Instruction(oldi, Bytecode.GETLOCAL, 0, 1));
ins.returnTypeId = lastType;
break;
case Bytecode.GETLOCAL1:
ins = (new Instruction(oldi, Bytecode.GETLOCAL, 1, 1));
ins.returnTypeId = lastType;
break;
case Bytecode.GETLOCAL2:
ins = (new Instruction(oldi, Bytecode.GETLOCAL, 2, 1));
ins.returnTypeId = lastType;
break;
case Bytecode.GETLOCAL3:
ins = (new Instruction(oldi, Bytecode.GETLOCAL, 3, 1));
ins.returnTypeId = lastType;
break;
case Bytecode.SETLOCAL:
ins = (new Instruction(oldi, Bytecode.SETLOCAL, u30(state), -1));
ins.returnTypeId = lastType;
break;
case Bytecode.SETLOCAL0:
ins = (new Instruction(oldi, Bytecode.SETLOCAL, 0, -1));
ins.returnTypeId = lastType;
break;
case Bytecode.SETLOCAL1:
ins = (new Instruction(oldi, Bytecode.SETLOCAL, 1, -1));
ins.returnTypeId = lastType;
break;
case Bytecode.SETLOCAL2:
ins = (new Instruction(oldi, Bytecode.SETLOCAL, 2, -1));
ins.returnTypeId = lastType;
break;
case Bytecode.SETLOCAL3:
ins = (new Instruction(oldi, Bytecode.SETLOCAL, 3, -1));
ins.returnTypeId = lastType;
break;
case Bytecode.KILL:
ins = (new Instruction(oldi, z, u30(state), 0));
ins.name = Bytecode.LABEL;
ins.comment = 'IR: KILL removed, reason: prevent optimisation';
break;
case Bytecode.GETLEX: {
var _s = mn(state), index = _s[0], dyn = _s[1], d = _s[2];
ins = (new Instruction(oldi, z + dyn, index, 1 + d));
ins.returnTypeId = lastType = ++type;
requireScope = true;
break;
}
//http://docs.redtamarin.com/0.4.1T124/avm2/intrinsics/memory/package.html#si32()
case Bytecode.SI8:
case Bytecode.SI16:
case Bytecode.SI32:
case Bytecode.SF32:
case Bytecode.SF64:
ins = (new Instruction(oldi, z, null, -2));
break;
//http://docs.redtamarin.com/0.4.1T124/avm2/intrinsics/memory/package.html#li32()
case Bytecode.LI8:
case Bytecode.LI16:
case Bytecode.LI32:
case Bytecode.LF32:
case Bytecode.LF64:
ins = (new Instruction(oldi, z, null, 0));
ins.returnTypeId = lastType = -3 /* PRIMITIVE_TYPE.NUMBER */;
break;
default: {
var c = code[state.index - 1];
return {
error: {
message: "UNKNOWN BYTECODE ".concat(c.toString(16), " ").concat(Bytecode[c], " at ").concat(oldi),
reason: "unknow_bytecode" /* COMPILATION_FAIL_REASON.UNKNOW_BYTECODE */,
}
};
}
}
q.push(ins);
}
var minStack = propagateStack(0, 0, q);
if (requireScope) {
propagateScope(0, 0, q);
}
else {
var scopeIndexes = [];
for (var i = 0; i < q.length; i++) {
if (q[i].name === Bytecode.PUSHSCOPE) {
scopeIndexes.push(i);
}
}
for (var _i = 0, scopeIndexes_1 = scopeIndexes; _i < scopeIndexes_1.length; _i++) {
var i = scopeIndexes_1[_i];
// we remove 3 commands, because push scope shift stack before
var comment = new Instruction(0, Bytecode.LABEL);
comment.comment = 'IR: PUSHSCOPE removed, reason: unused';
q.splice(i - 1, 2, comment);
}
}
var jumps = [0];
for (var i = 0; i < q.length; i++) {
for (var j = 0; j < q[i].refs.length; j++) {
jumps.push(q[i].refs[j]);
}
}
var catchStart;
var catchEnd;
if (body.catchBlocks.length) {
// collect try-catch blocks sorted by their start-position
catchStart = {};
// collect try-catch blocks sorted by their end-position
catchEnd = {};
for (var i = 0; i < body.catchBlocks.length; i++) {
var block = body.catchBlocks[i];
//let stack = 0;
var start = -1;
var end = -1;
var scope = 0;
for (var c = 0; c < q.length; c++) {
var pos = q[c].position;
if (pos >= block.start) {
start = pos;
break;
}
// propogade it
// stack = q[c].stack;
if (!Settings.NO_PROPAGATE_SCOPES_FOR_TRY)
scope = q[c].scope;
}
for (var c = q.length - 1; c >= 0; c--) {
var pos = q[c].position;
if (pos <= block.end) {
end = pos;
break;
}
}
if (!catchStart[start])
catchStart[start] = [];
catchStart[start].push(block);
if (!catchEnd[end])
catchEnd[end] = [];
catchEnd[end].push(block);
// make sure that the target-instruction for the catch is propagated and set as target
// using the stack value of the start-instruction does seem to do the trick
// and this broadcast bug, if breakpoints is exist
// IMPORTANT! Catch block push error on top of stack
// this is why stack should start from 1 instead of 0
var s = propagateStack(block.target, 1, q);
if (s < minStack)
minStack = s;
// IMPORTANT! SCOPE SHOULD BE PROPOGADED TOO
propagateScope(block.target, scope, q);
jumps.push(block.target);
}
}
propogateTree(q, jumps);
var error = minStack < 0 ? {
message: 'Stack underrun while preprocess, stack:' + minStack,
reason: "underrun" /* COMPILATION_FAIL_REASON.UNDERRUN */
} : null;
return {
set: q,
jumps: jumps,
catchStart: catchStart,
catchEnd: catchEnd,
error: error
};
}