UNPKG

@awayfl/avm2

Version:

Virtual machine for executing AS3 code

938 lines (937 loc) 102 kB
/* 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 = [];