@awayfl/avm2
Version:
Virtual machine for executing AS3 code
1,038 lines • 52.8 kB
JavaScript
import { assert } from '@awayjs/graphics';
import { Scope } from './run/Scope';
import { interpreterWriter, executionWriter, sliceArguments } from './run/writers';
import { HasNext2Info } from './run/HasNext2Info';
import { scopeStacks } from './run/scopeStacks';
import { axEquals } from './run/axEquals';
import { validateCall } from './run/validateCall';
import { validateConstruct } from './run/validateConstruct';
import { axGetDescendants } from './run/axGetDescendants';
import { axCoerceString } from './run/axCoerceString';
import { axCheckFilter } from './run/axCheckFilter';
import { axConvertString } from './run/axConvertString';
import { axAdd } from './run/axAdd';
import { axTypeOf } from './run/axTypeOf';
import { release, notImplemented, popManyInto, getPropertyDescriptor, isNumeric, AVMStage } from '@awayfl/swf-loader';
import { Multiname } from './abc/lazy/Multiname';
import { internNamespace } from './abc/lazy/internNamespace';
import { getBytecodeName } from './abc/ops';
import { escapeAttributeValue, escapeElementValue } from './natives/xml';
import { Errors } from './errors';
import { isValidASValue } from './run/initializeAXBasePrototype';
import { axCoerceName } from './run/axCoerceName';
import { compile, Context } from './jit';
import { Settings } from './Settings';
import { reconstructMetadata, nextScriptID } from './utils/reconstructMetadata';
var MAX_REPORTED_FAILINGS = 100;
var FAILINGS = 0;
/**
* Helps the interpreter allocate fewer Scope objects.
*/
var ScopeStack = /** @class */ (function () {
function ScopeStack(parent) {
this.parent = parent;
this.stack = [];
this.isWith = [];
}
ScopeStack.prototype.push = function (object, isWith) {
this.stack.push(object);
this.isWith.push(!!isWith);
};
ScopeStack.prototype.get = function (index) {
return this.stack[index];
};
ScopeStack.prototype.clear = function () {
this.stack.length = 0;
this.isWith.length = 0;
};
ScopeStack.prototype.pop = function () {
this.isWith.pop();
this.stack.pop();
if (this.scopes && this.scopes.length > this.stack.length) {
this.scopes.length--;
release || assert(this.scopes.length === this.stack.length);
}
};
ScopeStack.prototype.topScope = function () {
if (!this.scopes) {
if (this.stack.length === 0) {
return this.parent;
}
this.scopes = [];
}
var parent = this.parent;
for (var i = 0; i < this.stack.length; i++) {
var object = this.stack[i];
var isWith = this.isWith[i];
var scope = this.scopes[i];
if (!scope || scope.parent !== parent || scope.object !== object || scope.isWith !== isWith) {
scope = this.scopes[i] = new Scope(parent, object, isWith);
}
parent = scope;
}
return parent;
};
return ScopeStack;
}());
export { ScopeStack };
function popNameInto(stack, mn, rn) {
rn.resolved = {};
rn.globalInfo = null;
rn.numeric = false;
rn.id = mn.id;
rn.kind = mn.kind;
if (mn.isRuntimeName()) {
var name_1 = stack.pop();
// Unwrap content script-created AXQName instances.
if (name_1 && name_1.axClass && name_1.axClass === name_1.sec.AXQName) {
name_1 = name_1.name;
release || assert(name_1 instanceof Multiname);
rn.kind = mn.isAttribute() ? 18 /* CONSTANT.RTQNameLA */ : 17 /* CONSTANT.RTQNameL */;
rn.id = name_1.id;
rn.name = name_1.name;
rn.namespaces = name_1.namespaces;
return;
}
if (typeof name_1 === 'number' || isNumeric(axCoerceName(name_1))) {
rn.numeric = true;
rn.numericValue = +(axCoerceName(name_1));
}
rn.name = name_1;
rn.id = -1;
}
else {
rn.name = mn.name;
}
if (mn.isRuntimeNamespace()) {
var ns = stack.pop();
// Unwrap content script-created AXNamespace instances.
if (ns._ns) {
release || assert(ns.sec && ns.axClass === ns.sec.AXNamespace);
ns = ns._ns;
}
rn.namespaces = [ns];
rn.id = -1;
}
else {
rn.namespaces = mn.namespaces;
}
interpreterWriter && interpreterWriter.greenLn('Name: ' + rn.name);
}
export function interpret(methodInfo, savedScope, callee) {
try {
methodInfo.meta = methodInfo.meta || reconstructMetadata(methodInfo, nextScriptID());
}
catch (e) {
console.warn('[Interpret] Fail reconstruct method metadata:', e);
}
//no jit for global scope functions
if (savedScope.parent == null || AVMStage.forceINT && !Settings.NO_FALL_TO_INT) {
try {
var result = _interpret(methodInfo, savedScope, callee);
executionWriter && executionWriter.leave('< ' + methodInfo.trait);
return result;
}
catch (e) {
executionWriter && executionWriter.leave('< ' + methodInfo.trait + ', Exception: ' + e);
throw e;
}
}
// empty function for empty body
if (methodInfo.getBody() == null) {
return function () { };
}
// Try to compile
if (methodInfo.state === "pending" /* COMPILATION_STATE.PENDING */) {
var r = compile(methodInfo, { scope: savedScope });
if (r.error) {
methodInfo.error = r.error;
}
else {
methodInfo.compiled = r.compiled;
methodInfo.names = r.names;
}
}
if (methodInfo.state === "compiled" /* COMPILATION_STATE.COMPILLED */) {
methodInfo.useCount++;
return methodInfo.compiled(new Context(methodInfo, savedScope, methodInfo.names));
}
if (Settings.NO_FALL_TO_INT) {
console.warn('[Interpret] NO_FALL_TO_INT is used, interpret is disable. Fix JIT machin or disable NO_FALL_TO_INT');
throw methodInfo.error.message + '\n Method:' + methodInfo.meta.classPath;
}
methodInfo.useCount++;
return _interpret(methodInfo, savedScope, callee);
}
var InterpreterFrame = /** @class */ (function () {
function InterpreterFrame(receiver, methodInfo, parentScope, callArgs, callee) {
this.pc = 0;
this.stack = [];
this.hasNext2Infos = null;
var body = this.body = methodInfo.getBody();
this.code = body.code;
this.scopes = new ScopeStack(parentScope);
var locals = this.locals = [receiver];
var app = this.app = methodInfo.abc.applicationDomain;
var sec = this.sec = app.sec;
var argCount = callArgs.length;
var arg;
for (var i = 0, j = methodInfo.parameters.length; i < j; i++) {
var p = methodInfo.parameters[i];
if (i < argCount) {
arg = callArgs[i];
}
else if (p.hasOptionalValue()) {
arg = p.getOptionalValue();
}
else {
arg = undefined;
}
var rn = p.typeName;
if (rn && !rn.isAnyName()) {
var type = parentScope.getScopeProperty(rn, true, false);
if (!type) {
// During class initialization the class' constructor isn't in scope and can't be
// resolved as a scope property: trying to do so yields `null`.
// However, using it as a constructor parameter type *does* work, and correctly
// applies coercion to the constructor. It's unclear why and how this works in
// Tamarin, but since it does work, we check for this scenario here and work around it.
if ('axClass' in receiver && receiver.axClass.name.matches(rn)) {
type = receiver.axClass;
}
else {
continue;
}
}
if (!release && interpreterWriter) {
interpreterWriter.writeLn('Coercing argument to type ' +
(type.axClass ? type.axClass.name.toFQNString(false) : type));
}
arg = type.axCoerce(arg);
}
locals.push(arg);
}
if (methodInfo.needsRest()) {
locals.push(sec.createArrayUnsafe(sliceArguments(callArgs, methodInfo.parameters.length)));
}
else if (methodInfo.needsArguments()) {
var argsArray = sliceArguments(callArgs, 0);
var argumentsArray = Object.create(sec.argumentsPrototype);
argumentsArray.value = argsArray;
argumentsArray.callee = callee;
argumentsArray.receiver = receiver;
argumentsArray.methodInfo = methodInfo;
locals.push(argumentsArray);
}
}
InterpreterFrame.prototype.bc = function () {
return this.code[this.pc++];
};
InterpreterFrame.prototype.u30 = function () {
var code = this.code;
var pc = this.pc;
var result = code[pc++];
if (result & 0x80) {
result = result & 0x7f | code[pc++] << 7;
if (result & 0x4000) {
result = result & 0x3fff | code[pc++] << 14;
if (result & 0x200000) {
result = result & 0x1fffff | code[pc++] << 21;
if (result & 0x10000000) {
result = result & 0x0fffffff | code[pc++] << 28;
result = result & 0xffffffff;
}
}
}
}
this.pc = pc;
return result >>> 0;
};
InterpreterFrame.prototype.s24 = function () {
var code = this.code;
var pc = this.pc;
var u = code[pc] | (code[pc + 1] << 8) | (code[pc + 2] << 16);
this.pc = pc + 3;
return (u << 8) >> 8;
};
InterpreterFrame.prototype.getHasNext2Info = function () {
var pc = this.pc;
var hasNext2Infos = this.hasNext2Infos;
if (!hasNext2Infos) {
hasNext2Infos = this.hasNext2Infos = [];
}
if (!hasNext2Infos[pc]) {
hasNext2Infos[pc] = new HasNext2Info(null, 0);
}
return hasNext2Infos[pc];
};
return InterpreterFrame;
}());
function _interpret(methodInfo, savedScope, callee) {
if (methodInfo.error && methodInfo.useCount <= 1) {
FAILINGS++;
if (FAILINGS < MAX_REPORTED_FAILINGS) {
console.error('[INTERPRET] Compilation failed', methodInfo.error.reason, methodInfo.error.message);
}
else {
console.debug('[INTERPRET] To many compiler failings:', FAILINGS);
}
}
return function () {
var frame = new InterpreterFrame(this, methodInfo, savedScope, arguments, callee);
var stack = frame.stack;
var locals = frame.locals;
var scopes = frame.scopes;
var sec = frame.sec;
var abc = methodInfo.abc;
var rn = new Multiname(abc, 0, null, null, null, null, true);
var value, object, receiver, a, b, offset, index, result;
var args = [];
var argCount = 0;
var scopeStacksHeight = scopeStacks.length;
scopeStacks.push(frame.scopes);
interpretLabel:
// eslint-disable-next-line no-constant-condition
while (true) {
if (!release && interpreterWriter) {
interpreterWriter.greenLn('' + frame.pc + ': ' + getBytecodeName(frame.code[frame.pc]) + ' [' +
frame.stack.map(function (x) { return stringifyStackEntry(x); }).join(', ') + ']');
}
var bc = void 0;
try {
bc = frame.bc();
switch (bc) {
case 9 /* Bytecode.LABEL */:
continue;
case 3 /* Bytecode.THROW */:
throw stack.pop();
case 8 /* Bytecode.KILL */:
locals[frame.u30()] = undefined;
break;
case 12 /* Bytecode.IFNLT */:
case 24 /* Bytecode.IFGE */:
offset = frame.s24();
if (stack.pop() <= stack.pop()) {
frame.pc += offset;
}
continue;
case 13 /* Bytecode.IFNLE */:
case 23 /* Bytecode.IFGT */:
offset = frame.s24();
if (stack.pop() < stack.pop()) {
frame.pc += offset;
}
continue;
case 14 /* Bytecode.IFNGT */:
case 22 /* Bytecode.IFLE */:
offset = frame.s24();
if (stack.pop() >= stack.pop()) {
frame.pc += offset;
}
continue;
case 15 /* Bytecode.IFNGE */:
case 21 /* Bytecode.IFLT */:
offset = frame.s24();
if (stack.pop() > stack.pop()) {
frame.pc += offset;
}
continue;
case 16 /* Bytecode.JUMP */:
frame.pc = frame.s24() + frame.pc;
continue;
case 17 /* Bytecode.IFTRUE */:
offset = frame.s24();
if (stack.pop()) {
frame.pc += offset;
}
continue;
case 18 /* Bytecode.IFFALSE */:
offset = frame.s24();
if (!stack.pop()) {
frame.pc += offset;
}
continue;
case 19 /* Bytecode.IFEQ */:
offset = frame.s24();
if (axEquals(stack.pop(), stack.pop(), sec)) {
frame.pc += offset;
}
continue;
case 20 /* Bytecode.IFNE */:
offset = frame.s24();
if (!axEquals(stack.pop(), stack.pop(), sec)) {
frame.pc += offset;
}
continue;
case 25 /* Bytecode.IFSTRICTEQ */:
offset = frame.s24();
if (stack.pop() === stack.pop()) {
frame.pc += offset;
}
continue;
case 26 /* Bytecode.IFSTRICTNE */:
offset = frame.s24();
if (stack.pop() !== stack.pop()) {
frame.pc += offset;
}
continue;
case 27 /* Bytecode.LOOKUPSWITCH */: {
var basePC = frame.pc - 1;
offset = frame.s24();
var caseCount = frame.u30();
index = stack.pop();
if (index <= caseCount) {
frame.pc += 3 * index; // Jump to case offset.
offset = frame.s24();
}
frame.pc = basePC + offset;
continue;
}
case 29 /* Bytecode.POPSCOPE */:
scopes.pop();
break;
case 30 /* Bytecode.NEXTNAME */:
stack[stack.length - 2] = sec.box(stack[stack.length - 2]).axNextName(stack.pop());
break;
case 35 /* Bytecode.NEXTVALUE */:
stack[stack.length - 2] = sec.box(stack[stack.length - 2]).axNextValue(stack.pop());
break;
case 50 /* Bytecode.HASNEXT2 */: {
var hasNext2Info = frame.getHasNext2Info();
var objectIndex = frame.u30();
var indexIndex = frame.u30();
hasNext2Info.next(sec.box(locals[objectIndex]), locals[indexIndex]);
locals[objectIndex] = hasNext2Info.object;
locals[indexIndex] = hasNext2Info.index;
stack.push(!!hasNext2Info.index);
break;
}
case 32 /* Bytecode.PUSHNULL */:
stack.push(null);
break;
case 33 /* Bytecode.PUSHUNDEFINED */:
stack.push(undefined);
break;
case 36 /* Bytecode.PUSHBYTE */:
stack.push(frame.code[frame.pc++] << 24 >> 24);
break;
case 37 /* Bytecode.PUSHSHORT */:
stack.push(frame.u30() << 16 >> 16);
break;
case 44 /* Bytecode.PUSHSTRING */:
stack.push(abc.getString(frame.u30()));
break;
case 45 /* Bytecode.PUSHINT */:
stack.push(abc.ints[frame.u30()]);
break;
case 46 /* Bytecode.PUSHUINT */:
stack.push(abc.uints[frame.u30()]);
break;
case 47 /* Bytecode.PUSHDOUBLE */:
stack.push(abc.doubles[frame.u30()]);
break;
case 38 /* Bytecode.PUSHTRUE */:
stack.push(true);
break;
case 39 /* Bytecode.PUSHFALSE */:
stack.push(false);
break;
case 40 /* Bytecode.PUSHNAN */:
stack.push(NaN);
break;
case 41 /* Bytecode.POP */:
stack.pop();
break;
case 42 /* Bytecode.DUP */:
stack.push(stack[stack.length - 1]);
break;
case 43 /* Bytecode.SWAP */:
value = stack[stack.length - 1];
stack[stack.length - 1] = stack[stack.length - 2];
stack[stack.length - 2] = value;
break;
case 48 /* Bytecode.PUSHSCOPE */:
scopes.push(sec.box(stack.pop()), false);
break;
case 28 /* Bytecode.PUSHWITH */:
scopes.push(sec.box(stack.pop()), true);
break;
case 49 /* Bytecode.PUSHNAMESPACE */:
stack.push(sec.AXNamespace.FromNamespace(abc.getNamespace(frame.u30())));
break;
case 64 /* Bytecode.NEWFUNCTION */:
stack.push(sec.createFunction(abc.getMethodInfo(frame.u30()), scopes.topScope(), true));
break;
case 65 /* Bytecode.CALL */:
popManyInto(stack, frame.u30(), args);
object = stack.pop();
value = stack[stack.length - 1];
validateCall(sec, value, args.length);
stack[stack.length - 1] = value.axApply(object, args);
break;
case 66 /* Bytecode.CONSTRUCT */:
popManyInto(stack, frame.u30(), args);
receiver = sec.box(stack[stack.length - 1]);
validateConstruct(sec, receiver, args.length);
stack[stack.length - 1] = receiver.axConstruct(args);
break;
case 71 /* Bytecode.RETURNVOID */:
release || assert(scopeStacks.length === scopeStacksHeight + 1);
scopeStacks.length--;
return;
case 72 /* Bytecode.RETURNVALUE */:
value = stack.pop();
// TODO: ensure proper unwinding of the scope stack.
if (methodInfo.typeName) {
receiver = methodInfo.getType();
if (receiver) {
value = receiver.axCoerce(value);
}
}
release || assert(scopeStacks.length === scopeStacksHeight + 1);
scopeStacks.length--;
return value;
case 73 /* Bytecode.CONSTRUCTSUPER */:
popManyInto(stack, frame.u30(), args);
savedScope.object.superClass.tPrototype.axInitializer.apply(stack.pop(), args);
break;
case 74 /* Bytecode.CONSTRUCTPROP */:
index = frame.u30();
popManyInto(stack, frame.u30(), args);
popNameInto(stack, abc.getMultiname(index), rn);
stack[stack.length - 1] = sec.box(stack[stack.length - 1]).axConstructProperty(rn, args);
break;
case 76 /* Bytecode.CALLPROPLEX */:
case 70 /* Bytecode.CALLPROPERTY */:
case 79 /* Bytecode.CALLPROPVOID */:
index = frame.u30();
popManyInto(stack, frame.u30(), args);
popNameInto(stack, abc.getMultiname(index), rn);
result = sec.box(stack[stack.length - 1]).axCallProperty(rn, args, bc === 76 /* Bytecode.CALLPROPLEX */);
if (bc === 79 /* Bytecode.CALLPROPVOID */) {
stack.length--;
}
else {
stack[stack.length - 1] = result;
}
break;
case 69 /* Bytecode.CALLSUPER */:
case 78 /* Bytecode.CALLSUPERVOID */:
index = frame.u30();
popManyInto(stack, frame.u30(), args);
popNameInto(stack, abc.getMultiname(index), rn);
result = sec.box(stack[stack.length - 1]).axCallSuper(rn, savedScope, args);
if (bc === 78 /* Bytecode.CALLSUPERVOID */) {
stack.length--;
}
else {
stack[stack.length - 1] = result;
}
break;
//case Bytecode.CALLSTATIC:
// index = frame.u30();
// argCount = frame.u30();
// popManyInto(stack, argCount, args);
// value = abc.getMetadataInfo(index);
// var receiver = box(stack[stack.length - 1]);
// stack.push(value.axApply(receiver, args));
// break;
case 83 /* Bytecode.APPLYTYPE */:
popManyInto(stack, frame.u30(), args);
stack[stack.length - 1] = sec.applyType(stack[stack.length - 1], args);
break;
case 85 /* Bytecode.NEWOBJECT */:
object = Object.create(sec.AXObject.tPrototype);
argCount = frame.u30();
// For LIFO-order iteration to be correct, we have to add items highest on the stack
// first.
for (var i = stack.length - argCount * 2; i < stack.length; i += 2) {
value = stack[i + 1];
object.axSetPublicProperty(stack[i], value);
}
stack.length -= argCount * 2;
stack.push(object);
break;
case 86 /* Bytecode.NEWARRAY */:
object = [];
argCount = frame.u30();
for (var i = stack.length - argCount; i < stack.length; i++) {
object.push(stack[i]);
}
stack.length -= argCount;
stack.push(sec.AXArray.axBox(object));
break;
case 87 /* Bytecode.NEWACTIVATION */:
stack.push(sec.createActivation(methodInfo, scopes.topScope()));
break;
case 88 /* Bytecode.NEWCLASS */:
// Storing super class in `value` to make exception handling easier.
value = stack[stack.length - 1];
stack[stack.length - 1] = sec.createClass(abc.classes[frame.u30()], value, scopes.topScope());
break;
case 89 /* Bytecode.GETDESCENDANTS */:
popNameInto(stack, abc.getMultiname(frame.u30()), rn);
if (rn.name === undefined)
rn.name = '*';
stack[stack.length - 1] = axGetDescendants(stack[stack.length - 1], rn, sec);
break;
case 90 /* Bytecode.NEWCATCH */:
stack.push(sec.createCatch(frame.body.catchBlocks[frame.u30()], scopes.topScope()));
break;
case 94 /* Bytecode.FINDPROPERTY */:
case 93 /* Bytecode.FINDPROPSTRICT */:
popNameInto(stack, abc.getMultiname(frame.u30()), rn);
stack.push(scopes.topScope().findScopeProperty(rn, bc === 93 /* Bytecode.FINDPROPSTRICT */, false));
break;
case 96 /* Bytecode.GETLEX */:
popNameInto(stack, abc.getMultiname(frame.u30()), rn);
stack.push(scopes.topScope().findScopeProperty(rn, true, false).axGetProperty(rn));
break;
case 104 /* Bytecode.INITPROPERTY */:
case 97 /* Bytecode.SETPROPERTY */:
value = stack.pop();
popNameInto(stack, abc.getMultiname(frame.u30()), rn);
sec.box(stack.pop()).axSetProperty(rn, value, 104 /* Bytecode.INITPROPERTY */, methodInfo);
break;
case 102 /* Bytecode.GETPROPERTY */:
popNameInto(stack, abc.getMultiname(frame.u30()), rn);
stack[stack.length - 1] = sec.box(stack[stack.length - 1]).axGetProperty(rn);
break;
case 106 /* Bytecode.DELETEPROPERTY */:
popNameInto(stack, abc.getMultiname(frame.u30()), rn);
stack[stack.length - 1] = sec.box(stack[stack.length - 1]).axDeleteProperty(rn);
break;
case 4 /* Bytecode.GETSUPER */:
popNameInto(stack, abc.getMultiname(frame.u30()), rn);
stack[stack.length - 1] = sec.box(stack[stack.length - 1]).axGetSuper(rn, savedScope);
break;
case 5 /* Bytecode.SETSUPER */:
value = stack.pop();
popNameInto(stack, abc.getMultiname(frame.u30()), rn);
sec.box(stack.pop()).axSetSuper(rn, savedScope, value);
break;
case 98 /* Bytecode.GETLOCAL */:
stack.push(locals[frame.u30()]);
break;
case 99 /* Bytecode.SETLOCAL */:
locals[frame.u30()] = stack.pop();
break;
case 100 /* Bytecode.GETGLOBALSCOPE */:
stack.push(savedScope.global.object);
break;
case 101 /* Bytecode.GETSCOPEOBJECT */:
stack.push(scopes.get(frame.code[frame.pc++]));
break;
case 108 /* Bytecode.GETSLOT */:
stack[stack.length - 1] = sec.box(stack[stack.length - 1]).axGetSlot(frame.u30());
break;
case 109 /* Bytecode.SETSLOT */:
value = stack.pop();
sec.box(stack.pop()).axSetSlot(frame.u30(), value);
break;
case 110 /* Bytecode.GETGLOBALSLOT */:
stack[stack.length - 1] = savedScope.global.object.axGetSlot(frame.u30());
break;
case 111 /* Bytecode.SETGLOBALSLOT */:
value = stack.pop();
savedScope.global.object.axSetSlot(frame.u30(), value);
break;
case 114 /* Bytecode.ESC_XATTR */:
stack[stack.length - 1] = escapeAttributeValue(stack[stack.length - 1]);
break;
case 113 /* Bytecode.ESC_XELEM */:
stack[stack.length - 1] = escapeElementValue(sec, stack[stack.length - 1]);
break;
case 131 /* Bytecode.COERCE_I */:
case 115 /* Bytecode.CONVERT_I */:
stack[stack.length - 1] |= 0;
break;
case 136 /* Bytecode.COERCE_U */:
case 116 /* Bytecode.CONVERT_U */:
stack[stack.length - 1] >>>= 0;
break;
case 132 /* Bytecode.COERCE_D */:
case 117 /* Bytecode.CONVERT_D */:
stack[stack.length - 1] = +stack[stack.length - 1];
break;
case 129 /* Bytecode.COERCE_B */:
case 118 /* Bytecode.CONVERT_B */:
stack[stack.length - 1] = !!stack[stack.length - 1];
break;
case 133 /* Bytecode.COERCE_S */:
stack[stack.length - 1] = axCoerceString(stack[stack.length - 1]);
break;
case 112 /* Bytecode.CONVERT_S */:
stack[stack.length - 1] = axConvertString(stack[stack.length - 1]);
break;
case 120 /* Bytecode.CHECKFILTER */:
stack[stack.length - 1] = axCheckFilter(sec, stack[stack.length - 1]);
break;
case 128 /* Bytecode.COERCE */:
popNameInto(stack, abc.getMultiname(frame.u30()), rn);
stack[stack.length - 1] = scopes.topScope().getScopeProperty(rn, true, false)
.axCoerce(stack[stack.length - 1]);
break;
case 130 /* Bytecode.COERCE_A */: /* NOP */
break;
case 134 /* Bytecode.ASTYPE */:
popNameInto(stack, abc.getMultiname(frame.u30()), rn);
stack[stack.length - 1] = scopes.topScope().getScopeProperty(rn, true, false)
.axAsType(stack[stack.length - 1]);
break;
case 135 /* Bytecode.ASTYPELATE */:
stack[stack.length - 2] = stack.pop().axAsType(stack[stack.length - 1]);
break;
case 137 /* Bytecode.COERCE_O */:
object = stack[stack.length - 1];
stack[stack.length - 1] = object == undefined ? null : object;
break;
case 144 /* Bytecode.NEGATE */:
stack[stack.length - 1] = -stack[stack.length - 1];
break;
case 145 /* Bytecode.INCREMENT */:
++stack[stack.length - 1];
break;
case 146 /* Bytecode.INCLOCAL */:
++locals[frame.u30()];
break;
case 147 /* Bytecode.DECREMENT */:
--stack[stack.length - 1];
break;
case 148 /* Bytecode.DECLOCAL */:
--locals[frame.u30()];
break;
case 149 /* Bytecode.TYPEOF */:
stack[stack.length - 1] = axTypeOf(stack[stack.length - 1], sec);
break;
case 150 /* Bytecode.NOT */:
stack[stack.length - 1] = !stack[stack.length - 1];
break;
case 151 /* Bytecode.BITNOT */:
stack[stack.length - 1] = ~stack[stack.length - 1];
break;
case 160 /* Bytecode.ADD */:
b = stack.pop();
a = stack[stack.length - 1];
if (typeof a === 'number' && typeof b === 'number') {
stack[stack.length - 1] = a + b;
}
else {
stack[stack.length - 1] = axAdd(a, b, sec);
}
break;
case 161 /* Bytecode.SUBTRACT */:
stack[stack.length - 2] -= stack.pop();
break;
case 162 /* Bytecode.MULTIPLY */:
stack[stack.length - 2] *= stack.pop();
break;
case 163 /* Bytecode.DIVIDE */:
stack[stack.length - 2] /= stack.pop();
break;
case 164 /* Bytecode.MODULO */:
stack[stack.length - 2] %= stack.pop();
break;
case 165 /* Bytecode.LSHIFT */:
stack[stack.length - 2] <<= stack.pop();
break;
case 166 /* Bytecode.RSHIFT */:
stack[stack.length - 2] >>= stack.pop();
break;
case 167 /* Bytecode.URSHIFT */:
stack[stack.length - 2] >>>= stack.pop();
break;
case 168 /* Bytecode.BITAND */:
stack[stack.length - 2] &= stack.pop();
break;
case 169 /* Bytecode.BITOR */:
stack[stack.length - 2] |= stack.pop();
break;
case 170 /* Bytecode.BITXOR */:
stack[stack.length - 2] ^= stack.pop();
break;
case 171 /* Bytecode.EQUALS */:
stack[stack.length - 2] = axEquals(stack[stack.length - 2], stack.pop(), sec);
break;
case 172 /* Bytecode.STRICTEQUALS */:
stack[stack.length - 2] = stack[stack.length - 2] === stack.pop();
break;
case 173 /* Bytecode.LESSTHAN */:
stack[stack.length - 2] = stack[stack.length - 2] < stack.pop();
break;
case 174 /* Bytecode.LESSEQUALS */:
stack[stack.length - 2] = stack[stack.length - 2] <= stack.pop();
break;
case 175 /* Bytecode.GREATERTHAN */:
stack[stack.length - 2] = stack[stack.length - 2] > stack.pop();
break;
case 176 /* Bytecode.GREATEREQUALS */:
stack[stack.length - 2] = stack[stack.length - 2] >= stack.pop();
break;
case 177 /* Bytecode.INSTANCEOF */:
stack[stack.length - 2] = stack.pop().axIsInstanceOf(stack[stack.length - 1]);
break;
case 178 /* Bytecode.ISTYPE */:
popNameInto(stack, abc.getMultiname(frame.u30()), rn);
stack[stack.length - 1] = scopes.topScope().findScopeProperty(rn, true, false)
.axIsType(stack[stack.length - 1]);
break;
case 179 /* Bytecode.ISTYPELATE */:
stack[stack.length - 2] = stack.pop().axIsType(stack[stack.length - 1]);
break;
case 180 /* Bytecode.IN */: {
receiver = sec.box(stack.pop());
var name_2 = stack[stack.length - 1];
stack[stack.length - 1] = (name_2 && name_2.axClass === sec.AXQName)
? receiver.axHasProperty(name_2.name)
: receiver.axHasPublicProperty(name_2);
break;
}
case 192 /* Bytecode.INCREMENT_I */:
stack[stack.length - 1] = (stack[stack.length - 1] | 0) + 1;
break;
case 193 /* Bytecode.DECREMENT_I */:
stack[stack.length - 1] = (stack[stack.length - 1] | 0) - 1;
break;
case 194 /* Bytecode.INCLOCAL_I */:
index = frame.u30();
locals[index] = (locals[index] | 0) + 1;
break;
case 195 /* Bytecode.DECLOCAL_I */:
index = frame.u30();
locals[index] = (locals[index] | 0) - 1;
break;
case 196 /* Bytecode.NEGATE_I */:
stack[stack.length - 1] = -(stack[stack.length - 1] | 0);
break;
case 197 /* Bytecode.ADD_I */:
stack[stack.length - 2] = (stack[stack.length - 2] | 0) + (stack.pop() | 0) | 0;
break;
case 198 /* Bytecode.SUBTRACT_I */:
stack[stack.length - 2] = (stack[stack.length - 2] | 0) - (stack.pop() | 0) | 0;
break;
case 199 /* Bytecode.MULTIPLY_I */:
stack[stack.length - 2] = (stack[stack.length - 2] | 0) * (stack.pop() | 0) | 0;
break;
case 208 /* Bytecode.GETLOCAL0 */:
stack.push(locals[0]);
break;
case 209 /* Bytecode.GETLOCAL1 */:
stack.push(locals[1]);
break;
case 210 /* Bytecode.GETLOCAL2 */:
stack.push(locals[2]);
break;
case 211 /* Bytecode.GETLOCAL3 */:
stack.push(locals[3]);
break;
case 212 /* Bytecode.SETLOCAL0 */:
locals[0] = stack.pop();
break;
case 213 /* Bytecode.SETLOCAL1 */:
locals[1] = stack.pop();
break;
case 214 /* Bytecode.SETLOCAL2 */:
locals[2] = stack.pop();
break;
case 215 /* Bytecode.SETLOCAL3 */:
locals[3] = stack.pop();
break;
case 6 /* Bytecode.DXNS */:
scopes.topScope().defaultNamespace
= internNamespace(0 /* NamespaceType.Public */, abc.getString(frame.u30()));
break;
case 7 /* Bytecode.DXNSLATE */:
scopes.topScope().defaultNamespace
= internNamespace(0 /* NamespaceType.Public */, stack.pop());
break;
case 239 /* Bytecode.DEBUG */:
frame.pc++;
frame.u30();
frame.pc++;
frame.u30();
break;
case 240 /* Bytecode.DEBUGLINE */:
case 241 /* Bytecode.DEBUGFILE */:
frame.u30();
break;
case 2 /* Bytecode.NOP */:
case 1 /* Bytecode.BKPT */:
break;
default:
notImplemented(getBytecodeName(bc));
}
}
catch (e) {
// TODO: e = translateError(e);
var err = e;
// All script exceptions must be primitive or have a security domain, if they don't then
// this must be a VM exception.
if (!isValidASValue(err)) {
// We omit many checks in the interpreter loop above to keep the code small. These
// checks can be done after the fact here by turning the VM-internal exception into a
// proper error according to the current operation.
err = createValidException(sec, err, bc, value, receiver, a, b, rn, scopeStacksHeight + 1);
}
var catchBlocks = frame.body.catchBlocks;
for (var i = 0; i < catchBlocks.length; i++) {
var handler = catchBlocks[i];
if (frame.pc >= handler.start && frame.pc <= handler.end) {
var typeName = handler.type;
if (!typeName || frame.app.getClass(typeName).axIsType(err)) {
stack.length = 0;
stack.push(err);
scopes.clear();
frame.pc = handler.target;
continue interpretLabel;
}
}
}
release || assert(scopeStacks.length === scopeStacksHeight + 1);
scopeStacks.length--;
throw err;
}
}
};
}
function createValidException(sec, internalError, bc, value, receiver, a, b, mn, expectedScopeStacksHeight) {
var isProperErrorObject = internalError instanceof Error &&
typeof internalError.name === 'string' &&
typeof internalError.message === 'string';
if (isProperErrorObject) {
if (internalError instanceof RangeError || internalError.name === 'InternalError') {
var obj = Object.create(sec.AXError.tPrototype);
obj._errorID = 1023;
// Stack exhaustion errors are annoying to catch: Identifying them requires
// pattern-matching of error messages, and throwing them must be done very
// carefully to not cause the next one.
if (internalError.message === 'allocation size overflow') {
obj.$Bgmessage = 'allocation size overflow';
return obj;
}
if (internalError.message.indexOf('recursion') > -1 ||
internalError.message.indexOf('call stack size exceeded') > -1) {
obj.$Bgmessage = 'Stack overflow occurred';
scopeStacks.length = expectedScopeStacksHeight;
return obj;
}
}
else if (internalError instanceof TypeError) {
if (internalError.message.indexOf('convert') > -1 &&
(internalError.message.indexOf('to primitive') > -1 ||
internalError.message.indexOf('to string') > -1)) {
return sec.createError('TypeError', Errors.ConvertToPrimitiveError, 'value');
}
// Internal error thrown by generic Array methods.
if (internalError.message === 'Conversion to Array failed') {
return sec.createError('TypeError', Errors.CheckTypeFailedError, 'value', 'Array');
}
}
}
var message;
var isSuper = false;
switch (bc) {
case 65 /* Bytecode.CALL */:
if (!value || !value.axApply) {
return sec.createError('TypeError', Errors.CallOfNonFunctionError, 'value');
}
break;
case 66 /* Bytecode.CONSTRUCT */:
if (!receiver || !receiver.axConstruct) {
return sec.createError('TypeError', Errors.ConstructOfNonFunctionError);
}
break;
case 88 /* Bytecode.NEWCLASS */:
if (!value || !sec.AXClass.axIsType(value)) {
return sec.createError('VerifyError', Errors.InvalidBaseClassError);
}
break;
case 78 /* Bytecode.CALLSUPERVOID */:
case 73 /* Bytecode.CONSTRUCTSUPER */:
isSuper = true;
// Fallthrough.
case 70 /* Bytecode.CALLPROPERTY */:
case 79 /* Bytecode.CALLPROPVOID */:
case 76 /* Bytecode.CALLPROPLEX */:
case 74 /* Bytecode.CONSTRUCTPROP */:
case 69 /* Bytecode.CALLSUPER */:
if (receiver === null) {
return sec.createError('TypeError', Errors.ConvertNullToObjectError);
}
if (receiver === undefined) {
return sec.createError('TypeError', Errors.ConvertUndefinedToObjectError);
}
if (!(receiver.axResolveMultiname(mn) in receiver)) {
var axClass = isSuper ? receiver.axClass.superClass : receiver.axClass;
if (axClass.classInfo.instanceInfo.isSealed()) {
return sec.createError('ReferenceError', Errors.ReadSealedError, mn.name, axClass.name.toFQNString(false));
}
return sec.createError('TypeError', isSuper ?
Errors.ConstructOfNonFunctionError :
Errors.CallOfNonFunctionError, mn.name);
}
if (isProperErrorObject && internalError.name === 'RangeError' &&
(internalError.message.indexOf('arguments array passed') > -1 ||
internalError.message.indexOf('call stack size') > -1)) {
return sec.createError('RangeError', Errors.StackOverflowError);
}
break;
case 4 /* Bytecode.GETSUPER */:
isSuper = true;
// Fallthrough.
case 102 /* Bytecode.GETPROPERTY */:
if (receiver === null) {
return sec.createError('TypeError', Errors.ConvertNullToObjectError);
}
if (receiver === undefined) {
return sec.createError('TypeError', Errors.ConvertUndefinedToObjectError);
}
break;
case 104 /* Bytecode.INITPROPERTY */:
case 97 /* Bytecode.SETPROPERTY */: {
if (receiver === null) {
return sec.createError('TypeError', Errors.ConvertNullToObjectError);
}
if (receiver === undefined) {
return sec.createError('TypeError', Errors.ConvertUndefinedToObjectError);
}
var nm = receiver.axResolveMultiname(mn);
if (nm in receiver && getPropertyDescriptor(receiver, nm).writable === false) {
return sec.createError('ReferenceError', Errors.ConstWriteError, nm, receiver.axClass.name.name);
}
break;
}
case 177 /* Bytecode.INSTANCEOF */:
if (!receiver || !receiver.axIsInstanceOf) {
return sec.createError('TypeError', Errors.CantUseInstanceofOnNonObjectError);
}
break;
case 134 /* Bytecode.ASTYPE */:
case 135 /* Bytecode.ASTYPELATE */:
// ASTYPE(LATE) have almost the same error messages as ISTYPE(LATE), but not *quite*.
if (receiver && !receiver.axAsType) {
return sec.createError('TypeError', Errors.ConvertNullToObjectError);
}
// Fallthrough.
case 178 /* Bytecode.ISTYPE */:
case 179 /* Bytecode.ISTYPELATE */:
if (receiver === null) {
return sec.createError('TypeError', Errors.ConvertNullToObjectError);
}
if (receiver === undefined) {
return sec.createError('TypeError', Errors.ConvertUndefinedToObjectError);
}
if (!receiver.axIsType) {
return sec.createError('TypeError', Errors.IsTypeMustBeClassError);
}
break;
case 128 /* Bytecode.COERCE */:
if (!receiver) {
return sec.createError('ReferenceError', Errors.ClassNotFoundError, mn.toFQNString(false));
}
break;
case 180 /* Bytecode.IN */:
if (receiver === null) {
return sec.createError('TypeError', Errors.ConvertNullToObjectError);
}
if (receiver === undefined) {
return sec.createError('TypeError', Errors.