@awayfl/avm2
Version:
Virtual machine for executing AS3 code
938 lines (937 loc) • 102 kB
JavaScript
/* eslint-disable no-fallthrough */
import { assert } from '@awayjs/graphics';
import { Scope } from './run/Scope';
import { HasNext2Info } from './run/HasNext2Info';
import { validateCall } from './run/validateCall';
import { validateConstruct } from './run/validateConstruct';
import { axCoerceString } from './run/axCoerceString';
import { axCheckFilter } from './run/axCheckFilter';
import { isNumeric, jsGlobal, release } from '@awayfl/swf-loader';
import { Multiname } from './abc/lazy/Multiname';
import { internNamespace } from './abc/lazy/internNamespace';
import { IS_AX_CLASS } from './run/AXClass';
import { axCoerceName } from './run/axCoerceName';
import { Bytecode } from './Bytecode';
import { ASObject } from './nat/ASObject';
import { escapeAttributeValue, escapeElementValue } from './natives/xml';
import { COMPILER_DEFAULT_OPT } from './flags';
// generators
import { analyze } from './gen/analyze';
import { TinyConstructor } from './gen/TinyConstructor';
import { Stat } from './gen/Stat';
import { ComplexGenerator, PhysicsLex, StaticHoistLex, TopLevelLex } from './gen/LexImportsGenerator';
import { emitAnnotation, emitAnnotationOld, emitCloseTryCatch, emitDomainMemOppcodes, emitInlineAccessor as emitAccess, emitInlineLocal, emitInlineMultiname, emitInlineStack, emitOpenTryCatch, emitPrimitiveCoerce, isPrimitiveType, UNDERRUN } from './gen/emiters';
import { emitIsAX, emitIsAXOrPrimitive, extClassConstructor, getExtClassField, IS_EXTERNAL_CLASS, needFastCheck } from './ext/external';
import { Settings } from './Settings';
import { CompilerState } from './gen/CompilerState';
import { TRAITNames } from './abc/lazy/TRAIT';
import { InstanceInfo } from './abc/lazy/InstanceInfo';
import { axConstructFast, isFastConstructSupport } from './run/axConstruct';
var METHOD_HOOKS = {};
export var BytecodeName = Bytecode;
/**
* Try resolve method and attach hook to it
*/
export function UNSAFE_attachMethodHook(path, place, hook) {
if (place === void 0) { place = 'begin'; }
if (!path || typeof hook !== 'function') {
throw 'Hook path should be exits and function should be a function';
}
METHOD_HOOKS[path + '__' + place] = {
path: path,
place: place,
hook: hook
};
}
function generateFunc(body, path) {
body += "\n//# sourceURL=".concat(Settings.HTTP_STRING, "jit/").concat(path, ".js");
try {
return new Function('context', body);
}
catch (e) {
throw new Error('Compiler error:\n\n' + body);
}
}
function findJumpTarget(instrs, jumps, initial, target) {
var startIndex = -1;
var endIndex = -1;
for (var i = initial; i < instrs.length; i++) {
var inst = instrs[i];
if (inst.position === target) {
startIndex = i;
}
if (startIndex > i && jumps.indexOf(inst.position) >= 0) {
endIndex = i;
break;
}
}
if (startIndex >= 0 && endIndex === -1)
endIndex = instrs.length;
return {
startIndex: startIndex,
endIndex: endIndex
};
}
function isFastReturnVoid(instrs, jumps, initial, target) {
if (Settings.MAX_INLINE_RETURN <= 0) {
return false;
}
var startIndex = findJumpTarget(instrs, jumps, initial, target).startIndex;
return startIndex >= 0 && instrs[startIndex].name === Bytecode.RETURNVOID;
}
//@ts-ignore
self.attach_hook = UNSAFE_attachMethodHook;
function resolveTrait(info, name, scope) {
var trait = info.traits.getTrait(name);
var superInstance = info;
while (Settings.CHEK_SUPER_TRAITS && !trait && superInstance && superInstance instanceof InstanceInfo) {
var superName = superInstance.superName;
if (!superName) {
return null;
}
var addDom = scope.global.object.applicationDomain;
var superClass = addDom.getClass(superName);
superInstance = superClass.classInfo.instanceInfo;
if (superInstance.runtimeTraits) {
trait = superInstance.runtimeTraits.getTrait(name.namespaces, name.name);
if (trait) {
return trait;
}
}
trait = superInstance.traits.getTrait(name);
if (trait) {
return trait;
}
}
return trait;
}
export function compile(methodInfo, options) {
var _a, _b;
if (options === void 0) { options = {}; }
var _c = options.optimise, optimise = _c === void 0 ? COMPILER_DEFAULT_OPT : _c, executionScope = options.scope;
var state = new CompilerState(methodInfo);
var staticHoistLex = new StaticHoistLex();
// lex generator
var lexGen = new ComplexGenerator([
new PhysicsLex(),
new TopLevelLex(), // generate alias for TopLevel props
//staticHoistLex // collided with fastCall yet, need fix it
]);
var tinyCtr = new TinyConstructor();
// todo
// fastCall not supported now, we change compile flow and it generate wrong results
// remove or refact it
var fastCall = null; //new FastCall(lexGen, executionScope);
Stat.begin('');
var USE_OPT = function (opt) {
return optimise & 16 /* COMPILER_OPT_FLAGS.ALLOW_CUSTOM_OPTIMISER */ && !!opt;
};
if (USE_OPT(tinyCtr)) {
var b = tinyCtr.getBody(methodInfo);
if (typeof b === 'function') {
Stat.drop();
return {
names: [],
compiled: b
};
}
}
var meta = methodInfo.meta;
var methodName = meta.name;
var _d = analyze(methodInfo), error = _d.error, jumps = _d.jumps, catchStart = _d.catchStart, catchEnd = _d.catchEnd, q = _d.set;
// if affilate a generate error, broadcast it
if (error) {
Stat.drop();
// if a error is not debuggable, drop compilation
if (error.reason !== "underrun" /* COMPILATION_FAIL_REASON.UNDERRUN */ || !(optimise & 32 /* COMPILER_OPT_FLAGS.DEBUG_UNDERRUN */)) {
return { error: error };
}
}
var instanceInfo = (methodInfo.instanceInfo || ((_a = methodInfo.trait) === null || _a === void 0 ? void 0 : _a.holder));
var abc = methodInfo.abc;
var body = methodInfo.getBody();
var maxstack = body.maxStack;
var maxlocal = body.localCount - 1;
var maxscope = body.maxScopeDepth - body.initScopeDepth;
var js0 = state.headerBlock;
var js = state.mainBlock;
var domMem = false;
for (var _i = 0, q_1 = q; _i < q_1.length; _i++) {
var q_i = q_1[_i];
var b = q_i.name;
domMem = domMem || (b >= Bytecode.LI8 && b <= Bytecode.SF64);
}
state.moveIndent(1);
var params = methodInfo.parameters;
var useESArguments = optimise & 1 /* COMPILER_OPT_FLAGS.USE_ES_PARAMS */;
var _e = useESArguments
? emitAnnotation(state)
: emitAnnotationOld(state), paramsShift = _e.paramsShift, annotation = _e.annotation;
// store indend after annotation
var namesIndent = state.indent;
js0.push(annotation);
var LOCALS_POS = js0.length;
// hack
js0.push('__PLACE__FOR__OPTIONAL__LOCALS__');
var optionalLocalVars = [];
for (var i = params.length + 1 + paramsShift; i <= maxlocal; i++) {
optionalLocalVars[i] = {
index: i,
isArgumentList: i === params.length + 1,
read: 0,
write: 0,
die: false,
};
}
for (var i = 0; i < maxstack; i++)
js0.push("".concat(namesIndent, "let stack").concat(i, " = undefined;"));
for (var i = 0; i < maxscope; i++)
js0.push("".concat(namesIndent, "let scope").concat(i, " = undefined;"));
js0.push("".concat(namesIndent, "let temp = undefined;"));
if (domMem)
js0.push("".concat(namesIndent, "let domainMemory; // domainMemory"));
var names = state.names;
var getname = function (n) { return emitInlineMultiname(state, state.getMultinameIndex(n)); };
js0.push("".concat(namesIndent, "let sec = context.sec;"));
var genBrancher = jumps.length > 1 || catchStart;
if (METHOD_HOOKS && METHOD_HOOKS[meta.classPath + '__begin']) {
state.emitMain('/* ATTACH METHOD HOOK */');
state.emitMain("context.executeHook(".concat(emitInlineLocal(state, 0), ", '").concat(meta.classPath + '__begin', "')"));
}
state.emitMain('');
var catches = catchStart
? Object.keys(catchStart).length
: 0;
var useLoopGuard = (Settings.ENABLE_LOOP_GUARD
&& genBrancher
// every cathch has 2 jumps, ignore it
&& (jumps.length - catches * 2) >= Settings.LOOP_GUARD_MIN_BRANCHES);
if (genBrancher) {
if (useLoopGuard) {
state.emitMain('let tick = 0;');
}
state.emitMain('let p = 0;');
state.emitBeginMain('while (true) {'); // add { automatically
if (useLoopGuard) {
var loops = Settings.LOOP_GUARD_MAX_LOOPS;
state.emitBeginMain("if (tick++ > ".concat(loops, ") {"));
state.emitMain(
// eslint-disable-next-line max-len
"throw 'To many loops (> ".concat(loops, ") in \"").concat(meta.classPath, "\" at' + p + ' ,method was dropped to avoid stucking';\n'"));
state.emitEndMain();
}
state.emitBeginMain('switch (p) {');
}
var currentCatchBlocks;
var lastZ;
var z;
// case + case int
genBrancher && state.moveIndent(2);
var stackF = function (n, alias) {
if (alias === void 0) { alias = true; }
return emitInlineStack(state, n, alias);
};
var local = function (n) { return emitInlineLocal(state, n); };
var param = function (n, oppcode) {
var p = oppcode || state.currentOpcode.params;
return typeof p === 'number' ? p : p[n];
};
for (var i = 0; i < q.length; i++) {
// store oppcode in state
state.currentOpcode = q[i];
z && (lastZ = z);
z = q[i];
USE_OPT(fastCall) && fastCall.killFar(i);
if (jumps.indexOf(z.position) >= 0) {
// drop aliases for stack, because branching, alias outside branch can be invalid
state.dropAllAliases();
// if we are in any try-catch-blocks, we must close them
//if (state.openTryCatchGroups)
state.openTryCatchGroups.forEach(function (e) { return emitCloseTryCatch(state, e); });
if (genBrancher) {
state.moveIndent(-1);
state.emitMain("case ".concat(z.position, ":"));
state.moveIndent(1);
}
// now we reopen all the try-catch again
state.openTryCatchGroups.forEach(function (e) { return emitOpenTryCatch(state, e); });
}
/* DIRTY */
currentCatchBlocks = catchStart ? catchStart[z.position] : null;
if (currentCatchBlocks) {
state.openTryCatchGroups.push(currentCatchBlocks);
state.emitBeginMain('try {');
state.moveIndent(1);
}
if (Settings.PRINT_BYTE_INSTRUCTION) {
var params_1 = typeof z.params === 'number' ? z.params : z.params.join(' / ');
state.emitMain("//".concat(BytecodeName[z.name], " ").concat(params_1, " -> ").concat(z.returnTypeId));
}
if (z.comment) {
state.emitMain("//".concat(z.comment));
}
var stack0 = stackF(0);
var stack1 = stackF(1);
var stack2 = stackF(2);
var stack3 = stackF(3);
var scope = z.scope > 0 ? "scope".concat((z.scope - 1)) : 'context.savedScope';
var scopeN = 'scope' + z.scope;
if (z.stack < 0) {
state.emitMain('// unreachable');
}
else {
var localIndex = 0;
switch (z.name) {
case Bytecode.LABEL:
break;
case Bytecode.DXNSLATE:
state.emitMain("".concat(scope, ".defaultNamespace = context.internNamespace(0, ").concat(stack0, ");"));
break;
case Bytecode.DEBUGFILE:
break;
case Bytecode.DEBUGLINE:
break;
case Bytecode.DEBUG:
break;
case Bytecode.THROW:
state.emitMain("throw ".concat(stack0, ";"));
break;
case Bytecode.GETLOCAL: {
localIndex = param(0);
optionalLocalVars[localIndex] && (optionalLocalVars[localIndex].read++);
// going onto state, we have a lot of test that allow inlining
state.emitGetLocal(-1, localIndex);
break;
}
case Bytecode.SETLOCAL:
localIndex = param(0);
if (optionalLocalVars[localIndex]) {
optionalLocalVars[localIndex].write++;
if (!optionalLocalVars[localIndex].read) {
optionalLocalVars[localIndex].die = true;
}
}
state.killConstAliasInstruction([stackF(0, false)]);
state.popAnyAlias(local(localIndex));
state.emitMain("".concat(local(localIndex), " = ").concat(stack0, ";"));
// this is unpossible, because AVM not store `this` in local another that 0
/*
if (state.isThisAlias(stack0)) {
state.pushThisAlias(local(localIndex));
}
*/
break;
case Bytecode.GETSLOT:
state.popAnyAlias(stack0);
// slots can be get/set only on AX objects
state.emitMain("".concat(stackF(0, false), " = ").concat(stack0, ".axGetSlot(").concat(param(0), ");"));
break;
case Bytecode.SETSLOT:
state.emitMain("".concat(stack1, ".axSetSlot(").concat(param(0), ", ").concat(stack0, ");"));
break;
case Bytecode.GETGLOBALSCOPE:
state.popAnyAlias(stackF(-1, false));
state.emitMain("".concat(stackF(-1, false), " = context.savedScope.global.object;"));
break;
case Bytecode.PUSHSCOPE:
staticHoistLex === null || staticHoistLex === void 0 ? void 0 : staticHoistLex.markScope(scopeN, js.length);
// extends can be used only on AXObject
state.emitMain("".concat(scopeN, " = ").concat(scope, ".extend(").concat(stack0, ");"));
break;
case Bytecode.PUSHWITH:
state.emitMain("".concat(scopeN, " = context.pushwith(").concat(scope, ", ").concat(stack0, ");"));
break;
case Bytecode.POPSCOPE:
state.emitMain("".concat(scope, " = undefined;"));
break;
case Bytecode.GETSCOPEOBJECT:
state.popAnyAlias(stackF(-1, false));
state.emitMain("".concat(stackF(-1), " = scope").concat(param(0), ".object;"));
break;
case Bytecode.NEXTNAME:
state.popAnyAlias(stackF(1, false));
state.emitMain("".concat(stackF(1), " = sec.box(").concat(stack1, ").axNextName(").concat(stack0, ");"));
break;
case Bytecode.NEXTVALUE:
state.popAnyAlias(stackF(1, false));
state.emitMain("".concat(stackF(1), " = sec.box(").concat(stack1, ").axNextValue(").concat(stack0, ");"));
break;
case Bytecode.HASNEXT:
state.popAnyAlias(stackF(1, false));
state.emitMain("".concat(stackF(1), " = sec.box(").concat(stack1, ").axNextNameIndex(").concat(stack0, ");"));
break;
case Bytecode.HASNEXT2:
state.popAnyAlias(stackF(-1, false));
state.emitMain("temp = context.hasnext2(".concat(local(param(0)), ", ").concat(local(param(1)), ");"));
state.emitMain("".concat(local(param(0)), " = temp[0];"));
state.emitMain("".concat(local(param(1)), " = temp[1];"));
state.emitMain("".concat(stackF(-1), " = ").concat(local(param(1)), " > 0;"));
break;
case Bytecode.IN:
state.popAnyAlias(stackF(1, false));
// eslint-disable-next-line max-len
state.emitMain("".concat(stackF(1), " = (").concat(stack1, " && ").concat(stack1, ".axClass === sec.AXQName) ? obj.axHasProperty(").concat(stack1, ".name) : ").concat(stack0, ".axHasPublicProperty(").concat(stack1, ");"));
break;
case Bytecode.DUP:
state.popAnyAlias(stackF(-1, false));
state.emitMain("".concat(stackF(-1), " = ").concat(stack0, ";"));
state.pushThisAlias(stackF(-1), stack0);
break;
case Bytecode.POP:
// it real pop stack0 ?
state.popAnyAlias(stackF(0, false));
//js.push(`${idnt};`)
break;
case Bytecode.SWAP: {
state.popAnyAlias(stackF(0, false));
state.popAnyAlias(stackF(1, false));
state.emitMain("temp = ".concat(stack0, ";"));
state.emitMain("".concat(stackF(0), " = ").concat(stack1, ";"));
state.emitMain("".concat(stackF(1), " = temp;"));
state.emitMain('temp = undefined;');
break;
}
case Bytecode.PUSHTRUE:
state.emitConst(-1, true);
break;
case Bytecode.PUSHFALSE:
state.emitConst(-1, false);
break;
case Bytecode.PUSHBYTE:
state.emitConst(-1, param(0));
break;
case Bytecode.PUSHSHORT:
state.emitConst(-1, param(0));
break;
case Bytecode.PUSHINT:
state.emitConst(-1, abc.ints[param(0)]);
break;
case Bytecode.PUSHUINT:
state.emitConst(-1, abc.uints[param(0)]);
break;
case Bytecode.PUSHDOUBLE:
state.emitConst(-1, abc.doubles[param(0)]);
break;
case Bytecode.PUSHSTRING:
state.emitConst(-1, abc.getString(param(0)));
break;
case Bytecode.PUSHNAN:
state.emitConst(-1, NaN);
break;
case Bytecode.PUSHNULL:
state.emitConst(-1, null);
break;
case Bytecode.PUSHUNDEFINED:
state.emitConst(-1, undefined);
break;
case Bytecode.IFEQ: {
if (isFastReturnVoid(q, jumps, i, param(0))) {
state.emitMain('//JIT: Emit inline return');
state.emitMain("if (".concat(stack0, " == ").concat(stack1, ") { return; }"));
break;
}
state.emitMain("if (".concat(stack0, " == ").concat(stack1, ") { p = ").concat(param(0), "; continue; };"));
break;
}
case Bytecode.IFNE: {
if (isFastReturnVoid(q, jumps, i, param(0))) {
state.emitMain('//JIT: Emit inline return');
state.emitMain("if (".concat(stack0, " != ").concat(stack1, ") { return; }"));
break;
}
state.emitMain("if (".concat(stack0, " != ").concat(stack1, ") { p = ").concat(param(0), "; continue; };"));
break;
}
case Bytecode.IFSTRICTEQ: {
if (isFastReturnVoid(q, jumps, i, param(0))) {
state.emitMain('//JIT: Emit inline return');
state.emitMain("if (".concat(stack0, " === ").concat(stack1, ") { return; }"));
break;
}
state.emitMain("if (".concat(stack0, " === ").concat(stack1, ") { p = ").concat(param(0), "; continue; };"));
break;
}
case Bytecode.IFSTRICTNE: {
if (isFastReturnVoid(q, jumps, i, param(0))) {
state.emitMain('//JIT: Emit inline return');
state.emitMain("if (".concat(stack0, " !== ").concat(stack1, ") { return; }"));
break;
}
state.emitMain("if (".concat(stack0, " !== ").concat(stack1, ") { p = ").concat(param(0), "; continue; };"));
break;
}
case Bytecode.IFNLE: {
if (isFastReturnVoid(q, jumps, i, param(0))) {
state.emitMain('//JIT: Emit inline return');
state.emitMain("if (!(".concat(stack0, " >= ").concat(stack1, ")) { return; }"));
break;
}
state.emitMain("if (!(".concat(stack0, " >= ").concat(stack1, ")) { p = ").concat(param(0), "; continue; };"));
break;
}
case Bytecode.IFGT: {
if (isFastReturnVoid(q, jumps, i, param(0))) {
state.emitMain('//JIT: Emit inline return');
state.emitMain("if (".concat(stack0, " < ").concat(stack1, ") { return; }"));
break;
}
state.emitMain("if (".concat(stack0, " < ").concat(stack1, ") { p = ").concat(param(0), "; continue; };"));
break;
}
case Bytecode.IFNLT: {
if (isFastReturnVoid(q, jumps, i, param(0))) {
state.emitMain('//JIT: Emit inline return');
state.emitMain("if (!(".concat(stack0, " > ").concat(stack1, ")) { return; }"));
break;
}
state.emitMain("if (!(".concat(stack0, " > ").concat(stack1, ")) { p = ").concat(param(0), "; continue; };"));
break;
}
case Bytecode.IFGE: {
if (isFastReturnVoid(q, jumps, i, param(0))) {
state.emitMain('//JIT: Emit inline return');
state.emitMain("if (".concat(stack0, " <= ").concat(stack1, ") { return; }"));
break;
}
state.emitMain("if (".concat(stack0, " <= ").concat(stack1, ") { p = ").concat(param(0), "; continue; };"));
break;
}
case Bytecode.IFNGE: {
if (isFastReturnVoid(q, jumps, i, param(0))) {
state.emitMain('//JIT: Emit inline return');
state.emitMain("if (!(".concat(stack0, " <= ").concat(stack1, ")) { return; }"));
break;
}
state.emitMain("if (!(".concat(stack0, " <= ").concat(stack1, ")) { p = ").concat(param(0), "; continue; };"));
break;
}
case Bytecode.IFLT: {
if (isFastReturnVoid(q, jumps, i, param(0))) {
state.emitMain('//JIT: Emit inline return');
state.emitMain("if (".concat(stack0, " > ").concat(stack1, ") { return; }"));
break;
}
state.emitMain("if (".concat(stack0, " > ").concat(stack1, ") { p = ").concat(param(0), "; continue; };"));
break;
}
case Bytecode.IFNGT: {
if (isFastReturnVoid(q, jumps, i, param(0))) {
state.emitMain('//JIT: Emit inline return');
state.emitMain("if (!(".concat(stack0, " < ").concat(stack1, ")) { return; }"));
break;
}
state.emitMain("if (!(".concat(stack0, " < ").concat(stack1, ")) { p = ").concat(param(0), "; continue; };"));
break;
}
case Bytecode.IFLE: {
if (isFastReturnVoid(q, jumps, i, param(0))) {
state.emitMain('//JIT: Emit inline return');
state.emitMain("if (".concat(stack0, " >= ").concat(stack1, ") { return; }"));
break;
}
state.emitMain("if (".concat(stack0, " >= ").concat(stack1, ") { p = ").concat(param(0), "; continue; };"));
break;
}
case Bytecode.IFFALSE: {
if (isFastReturnVoid(q, jumps, i, param(0))) {
state.emitMain('//JIT: Emit inline return');
state.emitMain("if (!".concat(stack0, ") { return; }"));
break;
}
state.emitMain("if (!".concat(stack0, ") { p = ").concat(param(0), "; continue; };"));
break;
}
case Bytecode.IFTRUE: {
if (isFastReturnVoid(q, jumps, i, param(0))) {
state.emitMain('//JIT: Emit inline return');
state.emitMain("if (".concat(stack0, ") { return; }"));
break;
}
state.emitMain("if (".concat(stack0, ") { p = ").concat(param(0), "; continue; };"));
break;
}
case Bytecode.LOOKUPSWITCH: {
var jj = z.params.concat();
var dj = jj.shift();
// eslint-disable-next-line max-len
state.emitMain("if (".concat(stack0, " >= 0 && ").concat(stack0, " < ").concat(jj.length, ") { p = [").concat(jj.join(', '), "][").concat(stack0, "]; continue; } else { p = ").concat(dj, "; continue; };"));
break;
}
case Bytecode.JUMP: {
if (isFastReturnVoid(q, jumps, i, param(0))) {
state.emitMain('//JIT: Emit inline return');
state.emitMain('return;');
break;
}
state.emitMain("{ p = ".concat(param(0), "; continue; };"));
break;
}
case Bytecode.INCREMENT:
state.popAnyAlias(stackF(0, false));
state.emitMain("".concat(stackF(0), "++;"));
break;
case Bytecode.DECREMENT:
state.popAnyAlias(stackF(0, false));
state.emitMain("".concat(stackF(0), "--;"));
break;
case Bytecode.INCLOCAL:
state.emitMain("".concat(local(param(0)), "++;"));
break;
case Bytecode.DECLOCAL:
state.emitMain("".concat(local(param(0)), "--;"));
break;
case Bytecode.INCREMENT_I:
state.popAnyAlias(stackF(0, false));
state.emitMain("".concat(stackF(0), " = (").concat(stackF(0), " | 0) + 1;"));
break;
case Bytecode.DECREMENT_I:
state.popAnyAlias(stackF(0, false));
state.emitMain("".concat(stackF(0), " = (").concat(stackF(0), " | 0) - 1;"));
break;
case Bytecode.INCLOCAL_I:
state.emitMain("".concat(local(param(0)), " = (").concat(local(param(0)), " | 0) + 1;"));
break;
case Bytecode.DECLOCAL_I:
state.emitMain("".concat(local(param(0)), " = (").concat(local(param(0)), " | 0) - 1;"));
break;
case Bytecode.NEGATE_I:
state.popAnyAlias(stackF(0, false));
state.emitMain("".concat(stackF(0), " = -(").concat(stack0, " | 0);"));
break;
case Bytecode.ADD_I:
state.killConstAliasInstruction([stackF(0, false)]);
state.popAnyAlias(stackF(1, false));
state.emitMain("".concat(stackF(1), " = (").concat(stack1, " | 0) + (").concat(stack0, " | 0);"));
break;
case Bytecode.SUBTRACT_I:
state.killConstAliasInstruction([stackF(0, false)]);
state.popAnyAlias(stackF(1, false));
state.emitMain("".concat(stackF(1), " = (").concat(stack1, " | 0) - (").concat(stack0, " | 0);"));
break;
case Bytecode.MULTIPLY_I:
state.killConstAliasInstruction([stackF(0, false)]);
state.popAnyAlias(stackF(1, false));
state.emitMain("".concat(stackF(1), " = (").concat(stack1, " | 0) * (").concat(stack0, " | 0);"));
break;
case Bytecode.ADD:
state.killConstAliasInstruction([stackF(0, false)]);
// LOL, this can be used when this used in string concation
state.popAnyAlias(stackF(1, false));
state.emitMain("".concat(stackF(1), " += ").concat(stack0, ";"));
break;
case Bytecode.SUBTRACT:
state.killConstAliasInstruction([stackF(0, false)]);
state.popAnyAlias(stackF(1, false));
state.emitMain("".concat(stackF(1), " -= ").concat(stack0, ";"));
break;
case Bytecode.MULTIPLY:
state.killConstAliasInstruction([stackF(0, false)]);
state.popAnyAlias(stackF(1, false));
state.emitMain("".concat(stackF(1), " *= ").concat(stack0, ";"));
break;
case Bytecode.DIVIDE:
state.killConstAliasInstruction([stackF(0, false)]);
state.popAnyAlias(stackF(1, false));
state.emitMain("".concat(stackF(1), " /= ").concat(stack0, ";"));
break;
case Bytecode.MODULO:
state.killConstAliasInstruction([stackF(0, false)]);
state.popAnyAlias(stackF(1, false));
state.emitMain("".concat(stackF(1), " %= ").concat(stack0, ";"));
break;
case Bytecode.LSHIFT:
state.popAnyAlias(stackF(1, false));
state.emitMain("".concat(stackF(1), " <<= ").concat(stack0, ";"));
break;
case Bytecode.RSHIFT:
state.popAnyAlias(stackF(1, false));
state.emitMain("".concat(stackF(1), " >>= ").concat(stack0, ";"));
break;
case Bytecode.URSHIFT:
state.popAnyAlias(stackF(1, false));
state.emitMain("".concat(stackF(1), " >>>= ").concat(stack0, ";"));
break;
case Bytecode.BITAND:
state.popAnyAlias(stackF(1, false));
state.emitMain("".concat(stackF(1), " &= ").concat(stack0, ";"));
break;
case Bytecode.BITOR:
state.popAnyAlias(stackF(1, false));
state.emitMain("".concat(stackF(1), " |= ").concat(stack0, ";"));
break;
case Bytecode.BITXOR:
state.popAnyAlias(stackF(1, false));
state.emitMain("".concat(stackF(1), " ^= ").concat(stack0, ";"));
break;
case Bytecode.EQUALS:
state.popAnyAlias(stackF(1, false));
state.emitMain("".concat(stackF(1), " = ").concat(stack1, " == ").concat(stack0, ";"));
break;
case Bytecode.STRICTEQUALS:
state.popAnyAlias(stackF(1, false));
state.emitMain("".concat(stackF(1), " = ").concat(stack1, " === ").concat(stack0, ";"));
break;
case Bytecode.GREATERTHAN:
state.popAnyAlias(stackF(1, false));
state.emitMain("".concat(stackF(1), " = ").concat(stack1, " > ").concat(stack0, ";"));
break;
case Bytecode.GREATEREQUALS:
state.popAnyAlias(stackF(1, false));
state.emitMain("".concat(stackF(1), " = ").concat(stack1, " >= ").concat(stack0, ";"));
break;
case Bytecode.LESSTHAN:
state.popAnyAlias(stackF(1, false));
state.emitMain("".concat(stackF(1), " = ").concat(stack1, " < ").concat(stack0, ";"));
break;
case Bytecode.LESSEQUALS:
state.popAnyAlias(stackF(1, false));
state.emitMain("".concat(stackF(1), " = ").concat(stack1, " <= ").concat(stack0, ";"));
break;
case Bytecode.NOT:
state.popAnyAlias(stackF(0, false));
state.emitMain("".concat(stackF(0), " = !").concat(stack0, ";"));
break;
case Bytecode.BITNOT:
state.popAnyAlias(stackF(0, false));
state.emitMain("".concat(stackF(0), " = ~").concat(stack0, ";"));
break;
case Bytecode.NEGATE:
state.popAnyAlias(stackF(0, false));
state.emitMain("".concat(stackF(0), " = -").concat(stack0, ";"));
break;
case Bytecode.TYPEOF:
state.popAnyAlias(stackF(0, false));
// eslint-disable-next-line max-len
state.emitMain("".concat(stackF(0), " = typeof ").concat(stack0, " === 'undefined' ? 'undefined' : context.typeof(").concat(stack0, ");"));
break;
case Bytecode.INSTANCEOF:
state.popAnyAlias(stackF(1, false));
state.emitMain("".concat(stackF(1), " = ").concat(stack0, ".axIsInstanceOf(").concat(stack1, ");"));
break;
case Bytecode.ISTYPE:
state.popAnyAlias(stackF(0, false));
// eslint-disable-next-line max-len
state.emitMain("".concat(stackF(0), " = ").concat(scope, ".getScopeProperty(").concat(getname(param(0)), ", true, false).axIsType(").concat(stack0, ");"));
break;
case Bytecode.ISTYPELATE:
state.popAnyAlias(stackF(1, false));
state.emitMain("".concat(stackF(1), " = ").concat(stack0, ".axIsType(").concat(stack1, ");"));
break;
case Bytecode.ASTYPE:
if ((optimise & 4 /* COMPILER_OPT_FLAGS.SKIP_NULL_COERCE */)
&& (lastZ.name === Bytecode.PUSHNULL
|| lastZ.name === Bytecode.PUSHUNDEFINED)) {
state.emitMain('// SKIP_NULL_COERCE');
break;
}
state.popAnyAlias(stackF(0, false));
// eslint-disable-next-line max-len
state.emitMain("".concat(stackF(0), " = ").concat(scope, ".getScopeProperty(").concat(getname(param(0)), ", true, false).axAsType(").concat(stack0, ");"));
break;
case Bytecode.ASTYPELATE:
state.popAnyAlias(stackF(1, false));
// eslint-disable-next-line max-len
state.emitMain("".concat(stackF(1), " = ").concat(emitIsAXOrPrimitive(stack1), " ? ").concat(stack0, ".axAsType(").concat(stack1, ") : ").concat(stack1, ";"));
break;
case Bytecode.CALL: {
state.popAnyAlias(stackF(param(0) + 1, false));
var pp = [];
var obj = stackF(param(0) + 1);
for (var j = 1; j <= param(0); j++) {
pp.push(stackF(param(0) - j));
state.killConstAliasInstruction([stackF(param(0) - j, false)]);
}
// eslint-disable-next-line max-len
state.emitMain("".concat(obj, " = context.call(").concat(stackF(param(0) + 1), ", ").concat(stackF(param(0)), ", [").concat(pp.join(', '), "], ").concat(scope, ");"));
break;
}
case Bytecode.CONSTRUCT: {
state.popAnyAlias(stackF(param(0), false));
var pp = [];
var obj = stackF(param(0));
for (var j = 1; j <= param(0); j++) {
pp.push(stackF(param(0) - j));
state.killConstAliasInstruction([stackF(param(0) - j, false)]);
}
var alias = state.getStackAlias(param(0));
if (alias && (alias.kind === "lookup" /* VAR_KIND.LOOKUP */ || alias.kind === "alias" /* VAR_KIND.ALIAS */) && alias.type) {
state.emitMain('//JIT: Possible source:' + alias.type);
if (alias.type.name === 'RegExp') {
state.emitMain("".concat(obj, " = context.getRegExp([").concat(pp.join(', '), "]);"));
break;
}
}
// eslint-disable-next-line max-len
state.emitMain("".concat(obj, " = context.construct(").concat(obj, ", [").concat(pp.join(', '), "]);"));
break;
}
case Bytecode.CALLPROPERTY: {
var mn = abc.getMultiname(param(1));
var pp = [];
var obj = stackF(param(0));
for (var j = 1; j <= param(0); j++) {
pp.push(stackF(param(0) - j));
state.killConstAliasInstruction([stackF(param(0) - j, false)]);
}
state.killConstAliasInstruction([stackF(param(0), false)]);
state.popAnyAlias(stackF(param(0), false));
var targetStack = stackF(param(0));
if (abc.getMultiname(param(1)).name == 'getDefinitionByName') {
// eslint-disable-next-line max-len
state.emitMain("".concat(targetStack, " = context.getdefinitionbyname(").concat(scope, ", ").concat(obj, ", [").concat(pp.join(', '), "]);"));
}
else {
var d = void 0;
if (USE_OPT(fastCall) && (d = fastCall.sureThatFast("".concat(obj), mn.getMangledName()))) {
var n = d.isMangled ? Multiname.getPublicMangledName(mn.name) : mn.name;
fastCall.kill("".concat(obj));
state.emitMain('/* We sure that this safe call */');
if (d.isFunc) {
state.emitMain("".concat(targetStack, " = ").concat(emitAccess(obj, n), "(").concat(pp.join(', '), ");"));
}
else {
// eslint-disable-next-line max-len
state.emitMain("".concat(targetStack, " = /*fast*/sec.box(").concat(obj, ").axCallProperty(").concat(getname(param(1)), ", [").concat(pp.join(', '), "], false);"));
}
break;
}
// we can check trite for `this` or any types that has trite
// @todo move this to fast-call
if (Settings.CHEK_TRAIT_GET_CALL && obj === 'this' && instanceInfo) {
var trait = resolveTrait(instanceInfo, mn, executionScope);
if (trait) {
// eslint-disable-next-line max-len
state.emitMain("/* We sure that this safe call, represented in TRAIT as ".concat(TRAITNames[trait.kind], " */ "));
if (trait.kind === 1 /* TRAIT.Method */) {
// eslint-disable-next-line max-len
state.emitMain("".concat(targetStack, " = ").concat(emitAccess(obj, mn.getMangledName()), "(").concat(pp.join(', '), ");"));
}
else {
// when is method, we should wrap caller, because JS miss `this`
// when method is used as outside object
// we can do with BIND, but this is can be unstable
// eslint-disable-next-line max-len
state.emitMain("".concat(targetStack, " = /*fast*/").concat(obj, ".axCallProperty(").concat(getname(param(1)), ", [").concat(pp.join(', '), "], false);"));
}
break;
}
}
var fast = needFastCheck() && (obj !== 'this' || !Settings.NO_CHECK_FASTCALL_FOR_THIS);
if (fast) {
state.emitMain("if (!".concat(emitIsAXOrPrimitive(obj), ") {"));
// fast instruction already binded
if (obj.split('__')[1] == mn.name)
state.emitMain(" ".concat(targetStack, " = ").concat(pp.join(', '), ";"));
else
state.emitMain(" ".concat(targetStack, " = ").concat(emitAccess(obj, mn.name), "(").concat(pp.join(', '), ");"));
state.emitBeginMain('} else {');
}
state.emitMain("// ".concat(mn));
state.emitBeginMain(); // {
var box = !Settings.NO_CHECK_BOXED_THIS || obj !== 'this';
if (box) {
state.emitMain("let t = ".concat(obj, ";"));
var accessor = emitAccess('t', '$Bg' + mn.name);
// eslint-disable-next-line max-len
state.emitMain("const m = ".concat(accessor, " || (t = sec.box(").concat(obj, "), ").concat(accessor, ");"));
}
else {
state.emitMain("const m = ".concat(emitAccess(obj, '$Bg' + mn.name), ";"));
}
state.emitMain('if( typeof m === "function" ) { ');
// eslint-disable-next-line max-len
state.emitMain(" ".concat(targetStack, " = ").concat(emitAccess(box ? 't' : obj, '$Bg' + mn.name), " (").concat(pp.join(', '), ");"));
state.emitMain('} else { ');
// eslint-disable-next-line max-len
state.emitMain(" ".concat(targetStack, " = ").concat(obj, ".axCallProperty(").concat(getname(param(1)), ", [").concat(pp.join(', '), "], false);"));
state.emitMain('}');
state.emitEndMain(); // }
if (fast) {
state.emitEndMain(); // }
}
}
break;
}
case Bytecode.CALLPROPLEX:
{
var mn = abc.getMultiname(param(1));
state.popAnyAlias(stackF(param(0), false));
var targetStack = stackF(param(0));
var pp = [];
for (var j = 1; j <= param(0); j++) {
pp.push(stackF(param(0) - j));
state.killConstAliasInstruction([stackF(param(0) - j, false)]);
}
state.emitMain("temp = sec.box(".concat(targetStack, ");"));
var accessor = emitAccess('temp', '$Bg' + mn.name);
// eslint-disable-next-line max-len
state.emitMain("".concat(targetStack, " = (typeof ").concat(accessor, " === 'function')? ").concat(accessor, "(").concat(pp.join(', '), ") : temp.axCallProperty(").concat(getname(param(1)), ", [").concat(pp.join(', '), "], true);"));
}
break;
case Bytecode.CALLPROPVOID:
{
var mn = abc.getMultiname(param(1));
var pp = [];
var obj = stackF(param(0));
for (var j = 1; j <= param(0); j++) {
pp.push(stackF(param(0) - j));
state.killConstAliasInstruction([stackF(param(0) - j, false)]);
}
state.killConstAliasInstruction([stackF(param(0), false)]);
{
if (USE_OPT(fastCall) && fastCall.sureThatFast(obj)) {
var n = fastCall.sureThatFast(obj).isMangled
? Multiname.getPublicMangledName(mn.name)
: mn.name;
state.emitMain('/* We sure that this safe call */ ');
state.emitMain("".concat(emitAccess(obj, n), "(").concat(pp.join(', '), ");"));
fastCall.kill("".concat(obj));
break;
}
}
// we can check trite for `this` or any types that has trite
// @todo move this to fast-call
if (Settings.CHEK_TRAIT_GET_CALL && obj === 'this' && instanceInfo) {
var trait = resolveTrait(instanceInfo, mn, executionScope);
if (trait) {
// eslint-disable-next-line max-len
state.emitMain("/* We sure that this safe call, represented in TRAIT as ".concat(TRAITNames[trait.kind], " */ "));
if (trait.kind === 1 /* TRAIT.Method */) {
// eslint-disable-next-line max-len
state.emitMain("".concat(emitAccess(obj, mn.getMangledName()), "(").concat(pp.join(', '), ");"));
}
else {
// when is method, we should wrap caller, because JS miss `this`
// when method is used as outside object
// we can do with BIND, but this is can be unstable
// eslint-disable-next-line max-len
state.emitMain("/*fast*/".concat(obj, ".axCallProperty(").concat(getname(param(1)), ", [").concat(pp.join(', '), "], false);"));
}
break;
}
}
var fast = needFastCheck() && (obj !== 'this' || !Settings.NO_CHECK_FASTCALL_FOR_THIS);
if (fast) {
state.emitMain("if (!".concat(emitIsAXOrPrimitive(obj), ") {"));
state.emitMain(" ".concat(emitAccess(obj, mn.name), "(").concat(pp.join(', '), ");"));
state.emitMain('} else {');
state.moveIndent(1);
}
state.emitMain("// ".concat(mn));
state.emitBeginMain(); // {
state.emitMain("let t = ".concat(obj, ";"));
var accessor = emitAccess('t', '$Bg' + mn.name);
state.emitMain("const m = ".concat(accessor, " || (t = sec.box(").concat(obj, "), ").concat(accessor, ");"));
state.emitMain('if( typeof m === "function" ) { ');
state.emitMain(" m.call(t".concat(pp.length ? ', ' : '').concat(pp.join(', '), ");"));
state.emitMain('} else { ');
state.emitMain(" ".concat(obj, ".axCallProperty(").concat(getname(param(1)), ", [").concat(pp.join(', '), "], false);"));
state.emitMain('}');
state.emitEndMain(); // }
if (fast) {
state.emitEndMain(); // }
}
}
break;
case Bytecode.APPLYTYPE: {
var pp = [];