UNPKG

@awayfl/avm1

Version:

Virtual machine for executing AS1 and AS2 code

1,310 lines 73.2 kB
/* * Copyright 2015 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { __extends } from "tslib"; // Implementation of the built-in ActionScript classes. Here we will implement // functions and object prototypes that will be exposed to the AVM1 code. // Object natives import { alCallProperty, alCoerceString, alDefineObjectProperties, alGetObjectClass, alInstanceOf, alIsArray, alIsArrayLike, alIsFunction, alIsIndex, alIsString, alIterateArray, alNewObject, alToBoolean, alToInt32, alToInteger, alToNumber, alToObject, alToPrimitive, alToString } from './runtime'; import { Debug, release, isIndex } from '@awayfl/swf-loader'; import { AVM1Object } from './runtime/AVM1Object'; import { AVM1Globals } from './lib/AVM1Globals'; import { AVM1Function } from './runtime/AVM1Function'; import { AVM1PropertyDescriptor } from './runtime/AVM1PropertyDescriptor'; var AVM1ObjectPrototype = /** @class */ (function (_super) { __extends(AVM1ObjectPrototype, _super); function AVM1ObjectPrototype(context) { return _super.call(this, context) || this; // Initialization must be perfromed later after the Function creation. // See the _initializePrototype and createBuiltins below. } AVM1ObjectPrototype.prototype._initializePrototype = function () { var context = this.context; alDefineObjectProperties(this, { constructor: { value: context.builtins.Object, writable: true }, valueOf: { value: this._valueOf, writable: true }, toString: { value: this._toString, writable: true }, addProperty: { value: this.addProperty }, hasOwnProperty: { value: this.hasOwnProperty }, isPropertyEnumerable: { value: this.isPropertyEnumerable }, isPrototypeOf: { value: this.isPrototypeOf }, unwatch: { value: this.unwatch }, watch: { value: this.watch } }); }; AVM1ObjectPrototype.prototype._valueOf = function () { return this; }; AVM1ObjectPrototype.prototype._toString = function () { if (alIsFunction(this)) { // Really weird case of functions. return '[type ' + alGetObjectClass(this) + ']'; } return '[object ' + alGetObjectClass(this) + ']'; }; AVM1ObjectPrototype.prototype.addProperty = function (name, getter, setter) { if (typeof name !== 'string' || name === '') { return false; } if (!alIsFunction(getter)) { return false; } if (!alIsFunction(setter) && setter !== null) { return false; } var desc = this.alGetOwnProperty(name); if (desc && !!(desc.flags & 2 /* AVM1PropertyFlags.DONT_DELETE */)) { return false; // protected property } this.alSetOwnProperty(name, new AVM1PropertyDescriptor(128 /* AVM1PropertyFlags.ACCESSOR */, null, getter, setter || undefined)); return true; }; AVM1ObjectPrototype.prototype.hasOwnProperty = function (name) { return this.alHasOwnProperty(name); }; AVM1ObjectPrototype.prototype.isPropertyEnumerable = function (name) { var desc = this.alGetProperty(name); return !(desc.flags & 1 /* AVM1PropertyFlags.DONT_ENUM */); }; AVM1ObjectPrototype.prototype.isPrototypeOf = function (theClass) { return alInstanceOf(this.context, this, theClass); }; AVM1ObjectPrototype.prototype.unwatch = function (name) { name = alCoerceString(this.context, name); return this.alRemotePropertyWatcher(name); }; AVM1ObjectPrototype.prototype.watch = function (name, callback, userData) { name = alCoerceString(this.context, name); if (!alIsFunction(callback)) { return false; } return this.alAddPropertyWatcher(name, callback, userData); }; return AVM1ObjectPrototype; }(AVM1Object)); var AVM1ObjectFunction = /** @class */ (function (_super) { __extends(AVM1ObjectFunction, _super); function AVM1ObjectFunction(context) { var _this = _super.call(this, context) || this; _this.alPrototype = context.builtins.Function.alGetPrototypeProperty(); var proto = context.builtins.Object.alGetPrototypeProperty(); alDefineObjectProperties(_this, { prototype: { value: proto }, registerClass: { value: _this.registerClass } }); return _this; } AVM1ObjectFunction.prototype.registerClass = function (name, theClass) { this.context.registerClass(name, theClass); }; AVM1ObjectFunction.prototype.alConstruct = function (args) { if (args) { var value = args[0]; if (value instanceof AVM1Object) { return value; } switch (typeof value) { case 'string': case 'boolean': case 'number': return alToObject(this.context, value); } } // null or undefined return alNewObject(this.context); }; AVM1ObjectFunction.prototype.alCall = function (thisArg, args) { if (!args || args[0] === null || args[0] === undefined) { return alNewObject(this.context); } return alToObject(this.context, args[0]); }; return AVM1ObjectFunction; }(AVM1Function)); export { AVM1ObjectFunction }; // Function natives var AVM1FunctionPrototype = /** @class */ (function (_super) { __extends(AVM1FunctionPrototype, _super); function AVM1FunctionPrototype(context) { return _super.call(this, context) || this; } AVM1FunctionPrototype.prototype._initializePrototype = function () { var context = this.context; this.alPrototype = context.builtins.Object.alGetPrototypeProperty(); alDefineObjectProperties(this, { constructor: { value: context.builtins.Function, writable: true }, call: this.call, apply: this.apply }); }; AVM1FunctionPrototype.prototype.call = function (thisArg) { var args = []; for (var _a = 1; _a < arguments.length; _a++) { args[_a - 1] = arguments[_a]; } var fn = alEnsureType(this, AVM1Function); return fn.alCall(thisArg, args); }; AVM1FunctionPrototype.prototype.apply = function (thisArg, args) { var fn = alEnsureType(this, AVM1Function); var nativeArgs = !args ? undefined : alEnsureType(args, AVM1ArrayNative).value; return fn.alCall(thisArg, nativeArgs); }; return AVM1FunctionPrototype; }(AVM1Object)); var AVM1FunctionFunction = /** @class */ (function (_super) { __extends(AVM1FunctionFunction, _super); function AVM1FunctionFunction(context) { var _this = _super.call(this, context) || this; _this.alPrototype = context.builtins.Function.alGetPrototypeProperty(); var proto = context.builtins.Function.alGetPrototypeProperty(); alDefineObjectProperties(_this, { prototype: { value: proto } }); return _this; } AVM1FunctionFunction.prototype.alConstruct = function (args) { // ActionScript just returns the first argument. return args ? args[0] : undefined; }; AVM1FunctionFunction.prototype.alCall = function (thisArg, args) { // ActionScript just returns the first argument. return args ? args[0] : undefined; }; return AVM1FunctionFunction; }(AVM1Function)); export { AVM1FunctionFunction }; // Boolean natives var AVM1BooleanNative = /** @class */ (function (_super) { __extends(AVM1BooleanNative, _super); function AVM1BooleanNative(context, value) { var _this = _super.call(this, context) || this; _this.alPrototype = context.builtins.Boolean.alGetPrototypeProperty(); _this.alSetOwnConstructorProperty(context.builtins.Boolean); _this.value = value; return _this; } AVM1BooleanNative.prototype.valueOf = function () { return this.value; }; return AVM1BooleanNative; }(AVM1Object)); export { AVM1BooleanNative }; var AVM1BooleanPrototype = /** @class */ (function (_super) { __extends(AVM1BooleanPrototype, _super); function AVM1BooleanPrototype(context) { var _this = _super.call(this, context) || this; _this.alPrototype = context.builtins.Object.alGetPrototypeProperty(); alDefineObjectProperties(_this, { constructor: { value: context.builtins.Boolean, writable: true }, valueOf: { value: _this._valueOf, writable: true }, toString: { value: _this._toString, writable: true } }); return _this; } AVM1BooleanPrototype.prototype._valueOf = function () { var native = alEnsureType(this, AVM1BooleanNative); return native.value; }; AVM1BooleanPrototype.prototype._toString = function () { var native = alEnsureType(this, AVM1BooleanNative); return native.value ? 'true' : 'false'; }; return AVM1BooleanPrototype; }(AVM1Object)); export { AVM1BooleanPrototype }; var AVM1BooleanFunction = /** @class */ (function (_super) { __extends(AVM1BooleanFunction, _super); function AVM1BooleanFunction(context) { var _this = _super.call(this, context) || this; _this.alPrototype = context.builtins.Function.alGetPrototypeProperty(); var proto = new AVM1BooleanPrototype(context); alDefineObjectProperties(_this, { prototype: { value: proto } }); return _this; } AVM1BooleanFunction.prototype.alConstruct = function (args) { var value = args ? alToBoolean(this.context, args[0]) : false; return new AVM1BooleanNative(this.context, value); }; AVM1BooleanFunction.prototype.alCall = function (thisArg, args) { // TODO returns boolean value? var value = args ? alToBoolean(this.context, args[0]) : false; return value; }; return AVM1BooleanFunction; }(AVM1Function)); export { AVM1BooleanFunction }; // Number natives var AVM1NumberNative = /** @class */ (function (_super) { __extends(AVM1NumberNative, _super); function AVM1NumberNative(context, value) { var _this = _super.call(this, context) || this; _this.alPrototype = context.builtins.Number.alGetPrototypeProperty(); _this.alSetOwnConstructorProperty(context.builtins.Number); _this.value = value; return _this; } AVM1NumberNative.prototype.valueOf = function () { return this.value; }; return AVM1NumberNative; }(AVM1Object)); export { AVM1NumberNative }; var AVM1NumberPrototype = /** @class */ (function (_super) { __extends(AVM1NumberPrototype, _super); function AVM1NumberPrototype(context) { var _this = _super.call(this, context) || this; _this.alPrototype = context.builtins.Object.alGetPrototypeProperty(); alDefineObjectProperties(_this, { constructor: { value: context.builtins.Number, writable: true }, valueOf: { value: _this._valueOf, writable: true }, toString: { value: _this._toString, writable: true } }); return _this; } AVM1NumberPrototype.prototype._valueOf = function () { var native = alEnsureType(this, AVM1NumberNative); return native.value; }; AVM1NumberPrototype.prototype._toString = function (radix) { var native = alEnsureType(this, AVM1NumberNative); return native.value.toString(radix || 10); }; return AVM1NumberPrototype; }(AVM1Object)); export { AVM1NumberPrototype }; var AVM1NumberFunction = /** @class */ (function (_super) { __extends(AVM1NumberFunction, _super); function AVM1NumberFunction(context) { var _this = _super.call(this, context) || this; _this.alPrototype = context.builtins.Function.alGetPrototypeProperty(); var proto = new AVM1NumberPrototype(context); alDefineObjectProperties(_this, { prototype: { value: proto }, MAX_VALUE: Number.MAX_VALUE, MIN_VALUE: Number.MIN_VALUE, NaN: Number.NaN, NEGATIVE_INFINITY: Number.NEGATIVE_INFINITY, POSITIVE_INFINITY: Number.POSITIVE_INFINITY }); return _this; } AVM1NumberFunction.prototype.alConstruct = function (args) { var value = args ? alToNumber(this.context, args[0]) : 0; return new AVM1NumberNative(this.context, value); }; AVM1NumberFunction.prototype.alCall = function (thisArg, args) { // TODO returns number value? var value = args ? alToNumber(this.context, args[0]) : 0; return value; }; return AVM1NumberFunction; }(AVM1Function)); export { AVM1NumberFunction }; // String natives var AVM1StringNative = /** @class */ (function (_super) { __extends(AVM1StringNative, _super); function AVM1StringNative(context, value) { var _this = _super.call(this, context) || this; _this.alPrototype = context.builtins.String.alGetPrototypeProperty(); _this.alSetOwnConstructorProperty(context.builtins.String); _this.value = value; return _this; } AVM1StringNative.prototype.toString = function () { return this.value; }; return AVM1StringNative; }(AVM1Object)); export { AVM1StringNative }; // Most of the methods of String prototype are generic and accept any object. var AVM1StringPrototype = /** @class */ (function (_super) { __extends(AVM1StringPrototype, _super); function AVM1StringPrototype(context) { var _this = _super.call(this, context) || this; _this.alPrototype = context.builtins.Object.alGetPrototypeProperty(); alDefineObjectProperties(_this, { constructor: { value: context.builtins.String, writable: true }, valueOf: { value: _this._valueOf, writable: true }, toString: { value: _this._toString, writable: true }, length: { get: _this.getLength }, charAt: { value: _this.charAt, writable: true }, charCodeAt: { value: _this.charCodeAt, writable: true }, concat: { value: _this.concat, writable: true }, indexOf: { value: _this.indexOf, writable: true }, lastIndexOf: { value: _this.lastIndexOf, writable: true }, slice: { value: _this.slice, writable: true }, split: { value: _this.split, writable: true }, substr: { value: _this.substr, writable: true }, substring: { value: _this.substring, writable: true }, toLowerCase: { value: _this.toLowerCase, writable: true }, toUpperCase: { value: _this.toUpperCase, writable: true } }); return _this; } AVM1StringPrototype.prototype._valueOf = function () { var native = alEnsureType(this, AVM1StringNative); return native.value; }; AVM1StringPrototype.prototype._toString = function () { var native = alEnsureType(this, AVM1StringNative); return native.value; }; AVM1StringPrototype.prototype.getLength = function () { var native = alEnsureType(this, AVM1StringNative); return native.value.length; }; AVM1StringPrototype.prototype.charAt = function (index) { var value = alToString(this.context, this); return value.charAt(alToInteger(this.context, index)); }; AVM1StringPrototype.prototype.charCodeAt = function (index) { var value = alToString(this.context, this); return value.charCodeAt(alToInteger(this.context, index)); }; AVM1StringPrototype.prototype.concat = function () { var items = []; for (var _a = 0; _a < arguments.length; _a++) { items[_a] = arguments[_a]; } var stringItems = [alToString(this.context, this)]; for (var i = 0; i < items.length; ++i) { stringItems.push(alToString(this.context, items[i])); } return stringItems.join(''); }; AVM1StringPrototype.prototype.indexOf = function (searchString, position) { var value = alToString(this.context, this); searchString = alToString(this.context, searchString); position = alToInteger(this.context, position); return value.indexOf(searchString, position); }; AVM1StringPrototype.prototype.lastIndexOf = function (searchString, position) { var value = alToString(this.context, this); searchString = alToString(this.context, searchString); position = arguments.length < 2 ? NaN : alToNumber(this.context, position); // SWF6 alToNumber(undefined) === 0 if (position < 0) { // Different from JS return -1; } return value.lastIndexOf(searchString, isNaN(position) ? undefined : position); }; AVM1StringPrototype.prototype.slice = function (start, end) { if (arguments.length === 0) { // Different from JS return undefined; } var value = alToString(this.context, this); start = alToInteger(this.context, start); end = end === undefined ? undefined : alToInteger(this.context, end); return value.slice(start, end); }; AVM1StringPrototype.prototype.split = function (separator, limit) { var value = alToString(this.context, this); // TODO separator as regular expression? // for undefined seperator, flash does not do any split at all if (typeof separator !== 'undefined') { separator = alToString(this.context, separator); limit = (limit === undefined ? ~0 : alToInt32(this.context, limit)) >>> 0; return new AVM1ArrayNative(this.context, value.split(separator, limit)); } return new AVM1ArrayNative(this.context, [value]); }; AVM1StringPrototype.prototype.substr = function (start, length) { // Different from JS var value = alToString(this.context, this); var valueLength = value.length; start = alToInteger(this.context, start); length = length === undefined ? valueLength : alToInteger(this.context, length); if (start < 0) { start = Math.max(0, valueLength + start); } if (length < 0) { if (-length <= start) { // this one is weird -- don't ask return ''; } length = Math.max(0, valueLength + length); } return value.substr(start, length); }; AVM1StringPrototype.prototype.substring = function (start, end) { var value = alToString(this.context, this); start = alToInteger(this.context, start); if (start >= value.length) { // Different from JS return ''; } end = end === undefined ? undefined : alToInteger(this.context, end); return value.substring(start, end); }; AVM1StringPrototype.prototype.toLowerCase = function () { var value = alToString(this.context, this); return value.toLowerCase(); }; AVM1StringPrototype.prototype.toUpperCase = function () { var value = alToString(this.context, this); return value.toUpperCase(); }; return AVM1StringPrototype; }(AVM1Object)); export { AVM1StringPrototype }; var AVM1StringFunction = /** @class */ (function (_super) { __extends(AVM1StringFunction, _super); function AVM1StringFunction(context) { var _this = _super.call(this, context) || this; _this.alPrototype = context.builtins.Function.alGetPrototypeProperty(); var proto = new AVM1StringPrototype(context); alDefineObjectProperties(_this, { prototype: { value: proto }, fromCharCode: { value: _this.fromCharCode } }); return _this; } AVM1StringFunction.prototype.alConstruct = function (args) { var value = args ? alToString(this.context, args[0]) : ''; return new AVM1StringNative(this.context, value); }; AVM1StringFunction.prototype.alCall = function (thisArg, args) { var value = args ? alToString(this.context, args[0]) : ''; return value; }; AVM1StringFunction.prototype.fromCharCode = function () { var _this = this; var codes = []; for (var _a = 0; _a < arguments.length; _a++) { codes[_a] = arguments[_a]; } codes = codes.map(function (code) { return alToInt32(_this.context, code) & 0xFFFF; }); return String.fromCharCode.apply(String, codes); }; return AVM1StringFunction; }(AVM1Function)); export { AVM1StringFunction }; // Array natives var cachedArrayPropertyDescriptor = new AVM1PropertyDescriptor(64 /* AVM1PropertyFlags.DATA */, undefined); var AVM1ArrayNative = /** @class */ (function (_super) { __extends(AVM1ArrayNative, _super); function AVM1ArrayNative(context, value) { var _this = _super.call(this, context) || this; _this.alPrototype = context.builtins.Array.alGetPrototypeProperty(); _this.alSetOwnConstructorProperty(context.builtins.Array); _this.value = value; return _this; } AVM1ArrayNative.prototype.alGetOwnProperty = function (p) { if (alIsIndex(this.context, p)) { var index = alToInt32(this.context, p); if (Object.getOwnPropertyDescriptor(this.value, index)) { cachedArrayPropertyDescriptor.value = this.value[index]; return cachedArrayPropertyDescriptor; } } return _super.prototype.alGetOwnProperty.call(this, p); }; AVM1ArrayNative.prototype.alSetOwnProperty = function (p, v) { if (alIsIndex(this.context, p)) { var index = alToInt32(this.context, p); if (!(v.flags & 64 /* AVM1PropertyFlags.DATA */) || !!(v.flags & 1 /* AVM1PropertyFlags.DONT_ENUM */) || !!(v.flags & 2 /* AVM1PropertyFlags.DONT_DELETE */)) { throw new Error('Special property is non-supported for array'); } this.value[index] = v.value; return; } _super.prototype.alSetOwnProperty.call(this, p, v); }; AVM1ArrayNative.prototype.alDeleteOwnProperty = function (p) { if (alIsIndex(this.context, p)) { var index = alToInt32(this.context, p); delete this.value[index]; return; } _super.prototype.alDeleteOwnProperty.call(this, p); }; AVM1ArrayNative.prototype.alGetOwnPropertiesKeys = function () { var keys = _super.prototype.alGetOwnPropertiesKeys.call(this); var itemIndices = []; for (var i in this.value) { itemIndices.push(i); } return itemIndices.concat(keys); }; /** * Creates a JavaScript array from the AVM1 list object. * @param arr An array-like AVM1 object. * @param fn A function that converts AVM1 list object item to JavaScript object. * @param thisArg Optional. Value to use as this when executing fn. * @returns {any[]} A JavaScript array. */ AVM1ArrayNative.mapToJSArray = function (arr, fn, thisArg) { if (arr instanceof AVM1ArrayNative) { return arr.value.map(fn, thisArg); } // This method is generic, so array-like objects can use it. if (!alIsArrayLike(arr.context, arr)) { // TODO generate proper AVM1 exception. throw new Error('Invalid type'); // Interpreter will catch this. } var result = []; alIterateArray(arr.context, arr, function (item, index) { result.push(fn.call(thisArg, item, index)); }); return result; }; return AVM1ArrayNative; }(AVM1Object)); export { AVM1ArrayNative }; var AVM1ArraySortOnOptions; (function (AVM1ArraySortOnOptions) { AVM1ArraySortOnOptions[AVM1ArraySortOnOptions["CASEINSENSITIVE"] = 1] = "CASEINSENSITIVE"; AVM1ArraySortOnOptions[AVM1ArraySortOnOptions["DESCENDING"] = 2] = "DESCENDING"; AVM1ArraySortOnOptions[AVM1ArraySortOnOptions["UNIQUESORT"] = 4] = "UNIQUESORT"; AVM1ArraySortOnOptions[AVM1ArraySortOnOptions["RETURNINDEXEDARRAY"] = 8] = "RETURNINDEXEDARRAY"; AVM1ArraySortOnOptions[AVM1ArraySortOnOptions["NUMERIC"] = 16] = "NUMERIC"; })(AVM1ArraySortOnOptions || (AVM1ArraySortOnOptions = {})); var sortArray = function (arr_in, comparefn) { var arr = alEnsureType(arr_in, AVM1ArrayNative).value; if (!alIsFunction(comparefn)) { var doNumeric = typeof comparefn === 'number' && (comparefn & AVM1ArraySortOnOptions.NUMERIC || comparefn == -1); var doDescending = typeof comparefn === 'number' && (comparefn & AVM1ArraySortOnOptions.DESCENDING); if (doNumeric) { arr.sort(function (a, b) { while (a instanceof AVM1ArrayNative) { a = alEnsureType(a, AVM1ArrayNative).value; if (a && a.length > 0) a = a[0]; } while (b instanceof AVM1ArrayNative) { b = alEnsureType(b, AVM1ArrayNative).value; if (b && b.length > 0) b = b[0]; } return a - b; }); } else { // ugly hack for moving undefined values to the beginning of the array var i = arr.length; while (i > 0) { i--; if (typeof arr[i] === 'undefined' || arr[i] === null) { arr[i] = '00000000000AwayInternal'; } } arr.sort(function (a, b) { while (a instanceof AVM1ArrayNative) { a = alEnsureType(a, AVM1ArrayNative).value; if (a && a.length > 0) a = a[0]; } while (b instanceof AVM1ArrayNative) { b = alEnsureType(b, AVM1ArrayNative).value; if (b && b.length > 0) b = b[0]; } return (a < b) ? -1 : 1; }); i = arr.length; while (i > 0) { i--; if (arr[i] == '00000000000AwayInternal') { arr[i] = undefined; } } } if (doDescending) arr.reverse(); } else { var args_1 = [undefined, undefined]; arr.sort(function (a, b) { args_1[0] = a; args_1[1] = b; return comparefn.alCall(null, args_1); }); } return arr_in; }; // TODO implement all the Array class and its prototype natives var AVM1ArrayPrototype = /** @class */ (function (_super) { __extends(AVM1ArrayPrototype, _super); function AVM1ArrayPrototype(context) { var _this = _super.call(this, context) || this; _this.alPrototype = context.builtins.Object.alGetPrototypeProperty(); alDefineObjectProperties(_this, { constructor: { value: context.builtins.Array, writable: true }, join: { value: _this.join, writable: true }, length: { get: _this.getLength, set: _this.setLength }, concat: { value: _this.concat, writable: true }, pop: { value: _this.pop, writable: true }, push: { value: _this.push, writable: true }, reverse: { value: _this.reverse, writable: true }, shift: { value: _this.shift, writable: true }, slice: { value: _this.slice, writable: true }, splice: { value: _this.splice, writable: true }, sort: { value: _this.sort, writable: true }, sortOn: { value: _this.sortOn, writable: true }, toString: { value: _this._toString, writable: true }, unshift: { value: _this.unshift, writable: true }, }); return _this; } AVM1ArrayPrototype.prototype._toString = function () { var len = this.alGet('length'); var _i = 0; var outputStr = ''; var tmpitem; for (_i = 0; _i < len; _i++) { tmpitem = this.alGet(_i); if (typeof tmpitem !== 'undefined') { outputStr += alToString(this.context, tmpitem) + ((_i == len - 1) ? '' : ','); } else { outputStr += ((_i == len - 1) ? '' : ','); } } return outputStr; }; AVM1ArrayPrototype.prototype.getLength = function () { var arr = alEnsureType(this, AVM1ArrayNative).value; return arr.length; }; AVM1ArrayPrototype.prototype.setLength = function (length) { if (length === null) { length = 0; } if (!isIndex(length)) { return; // no action on invalid length } length = alToInt32(this.context, length) >>> 0; var arr = alEnsureType(this, AVM1ArrayNative).value; arr.length = length; }; AVM1ArrayPrototype.prototype.concat = function () { var items = []; for (var _a = 0; _a < arguments.length; _a++) { items[_a] = arguments[_a]; } if (this instanceof AVM1ArrayNative) { // Faster case for native array implementation var arr = alEnsureType(this, AVM1ArrayNative).value; for (var i_1 = 0; i_1 < items.length; i_1++) { if (items[i_1] instanceof AVM1ArrayNative) { items[i_1] = alEnsureType(items[i_1], AVM1ArrayNative).value; } } //this.value=arr.concat(items); return new AVM1ArrayNative(this.context, Array.prototype.concat.apply(arr, items)); //return this; } // Generic behavior var a = []; var e = this; var isArrayObject = alIsArrayLike(this.context, this); var i = 0; while (true) { if (isArrayObject) { alIterateArray(this.context, e, function (value) { return a.push(value); }); } else { a.push(alToString(this.context, e)); } if (i >= items.length) { break; } e = items[i++]; isArrayObject = alIsArray(this.context, e); // not-logical behavior } return new AVM1ArrayNative(this.context, a); }; AVM1ArrayPrototype.prototype.join = function (separator) { separator = separator === undefined ? ',' : alCoerceString(this.context, separator); if (this instanceof AVM1ArrayNative) { // Faster case for native array implementation var arr = alEnsureType(this, AVM1ArrayNative).value; if (arr.length === 0) { return ''; } if (arr.every(function (i) { return !(i instanceof AVM1Object); })) { return arr.join(separator); } } var context = this.context; var length = alToInt32(context, this.alGet('length')) >>> 0; if (length === 0) { return ''; } var result = []; for (var i = 0; i < length; i++) { var item = this.alGet(i); result[i] = item === null || item === undefined ? '' : alCoerceString(context, item); } return result.join(separator); }; AVM1ArrayPrototype.prototype.pop = function () { if (this instanceof AVM1ArrayNative) { // Faster case for native array implementation var arr = alEnsureType(this, AVM1ArrayNative).value; return arr.pop(); } var length = alToInt32(this.context, this.alGet('length')) >>> 0; if (length === 0) { return undefined; } var i = length - 1; var result = this.alGet(i); this.alDeleteProperty(i); this.alPut('length', i); return result; }; AVM1ArrayPrototype.prototype.push = function () { var items = []; for (var _a = 0; _a < arguments.length; _a++) { items[_a] = arguments[_a]; } if (this instanceof AVM1ArrayNative) { // Faster case for native array implementation var arr = alEnsureType(this, AVM1ArrayNative).value; return Array.prototype.push.apply(arr, items); } var length = alToInt32(this.context, this.alGet('length')) >>> 0; for (var i = 0; i < items.length; i++) { this.alPut(length, items[i]); length++; // TODO check overflow } this.alPut('length', length); return length; }; AVM1ArrayPrototype.prototype.reverse = function () { var items = []; var len = this.alGet('length'); var _i = 0; for (_i = 0; _i < len; _i++) { items[_i] = this.alGet(_i); } _i = len - 1; while (_i >= 0) { this.alPut((len - 1 - _i), items[_i]); _i--; } return this.value; }; AVM1ArrayPrototype.prototype.shift = function () { if (this instanceof AVM1ArrayNative) { // Faster case for native array implementation var arr = alEnsureType(this, AVM1ArrayNative).value; return arr.shift(); } var length = alToInt32(this.context, this.alGet('length')) >>> 0; if (length === 0) { return undefined; } var result = this.alGet(0); for (var i = 1; i < length; i++) { if (this.alHasProperty(i)) { this.alPut(i - 1, this.alGet(i)); } else { this.alDeleteProperty(i - 1); } } this.alDeleteProperty(length - 1); this.alPut('length', length - 1); return result; }; AVM1ArrayPrototype.prototype.slice = function (start, end) { start = alToInteger(this.context, start); end = end !== undefined ? alToInteger(this.context, end) : undefined; if (this instanceof AVM1ArrayNative) { // Faster case for native array implementation var arr = alEnsureType(this, AVM1ArrayNative).value; return new AVM1ArrayNative(this.context, arr.slice(start, end)); } var a = []; var length = alToInt32(this.context, this.alGet('length')) >>> 0; start = start < 0 ? Math.max(length + start, 0) : Math.min(length, start); end = end === undefined ? length : (end < 0 ? Math.max(length + end, 0) : Math.min(length, end)); for (var i = start, j = 0; i < end; i++, j++) { if (this.alHasProperty(i)) { a[j] = this.alGet(i); } } return new AVM1ArrayNative(this.context, a); }; AVM1ArrayPrototype.prototype.splice = function (start, deleteCount) { var items = []; for (var _a = 2; _a < arguments.length; _a++) { items[_a - 2] = arguments[_a]; } start = alToInteger(this.context, start); var length = alToInt32(this.context, this.alGet('length')) >>> 0; start = start < 0 ? Math.max(length + start, 0) : Math.min(length, start); //if(deleteCount || typeof deleteCount==="number") // deleteCount = alToInteger(this.context, deleteCount); if (deleteCount || typeof deleteCount === 'number') deleteCount = alToInteger(this.context, deleteCount); else deleteCount = length - start; if (this instanceof AVM1ArrayNative) { // Faster case for native array implementation var arr = alEnsureType(this, AVM1ArrayNative).value; return new AVM1ArrayNative(this.context, Array.prototype.splice.apply(arr, [start, deleteCount].concat(items))); } var a = []; for (var i = 0; i < deleteCount; i++) { if (this.alHasProperty(start + i)) { a[i] = this.alGet(start + i); } } var delta = items.length - deleteCount; if (delta < 0) { for (var i = start - delta; i < length; i++) { if (this.alHasProperty(i)) { this.alPut(i + delta, this.alGet(i)); } else { this.alDeleteProperty(i + delta); } } for (var i = delta; i < 0; i++) { this.alDeleteProperty(length + i); } } else if (delta > 0) { // TODO check overflow for (var i = length - 1; i >= start + delta; i--) { if (this.alHasProperty(i)) { this.alPut(i + delta, this.alGet(i)); } else { this.alDeleteProperty(i + delta); } } } for (var i = 0; i < items.length; i++) { this.alPut(start + i, items[i]); } this.alPut('length', length + delta); return new AVM1ArrayNative(this.context, a); }; AVM1ArrayPrototype.prototype.sort = function (comparefn) { return sortArray(this, comparefn); }; AVM1ArrayPrototype.prototype.sortOn = function (fieldNames, options) { var context = this.context; // The field names and options we'll end up using. var fieldNamesList = []; var optionsList = []; if (alIsString(context, fieldNames)) { fieldNamesList = [alToString(context, fieldNames)]; optionsList = [alToInt32(context, options)]; } else if (alIsArray(context, fieldNames)) { fieldNamesList = []; optionsList = []; var optionsArray = alIsArray(context, options) ? options : null; var length_1 = alToInteger(context, fieldNames.alGet('length')); if (optionsArray) { // checking in length of fieldNames == options var optionsLength = alToInteger(context, optionsArray.alGet('length')); if (length_1 !== optionsLength) { optionsArray = null; } } for (var i = 0; i < length_1; i++) { fieldNamesList.push(alToString(context, fieldNames.alGet(i))); optionsList.push(optionsArray ? alToInt32(context, optionsArray.alGet(i)) : 0); } } else { // Field parameters are incorrect. return sortArray(this, options); } // TODO revisit this code var optionsVal = optionsList[0]; release || Debug.assertNotImplemented(!(optionsVal & AVM1ArraySortOnOptions.UNIQUESORT), 'UNIQUESORT'); release || Debug.assertNotImplemented(!(optionsVal & AVM1ArraySortOnOptions.RETURNINDEXEDARRAY), 'RETURNINDEXEDARRAY'); var comparer = function (a, b) { while (a instanceof AVM1ArrayNative) { a = alEnsureType(a, AVM1ArrayNative).value; if (a && a.length > 0) a = a[0]; } while (b instanceof AVM1ArrayNative) { b = alEnsureType(b, AVM1ArrayNative).value; if (b && b.length > 0) b = b[0]; } var aObj = alToObject(context, a); var bObj = alToObject(context, b); for (var i = 0; i < fieldNamesList.length; i++) { var aField = aObj.alGet(fieldNamesList[i]); var bField = bObj.alGet(fieldNamesList[i]); var result = void 0; if (optionsList[i] & AVM1ArraySortOnOptions.NUMERIC) { var aNum = alToNumber(context, aField); var bNum = alToNumber(context, bField); result = aNum < bNum ? -1 : aNum > bNum ? +1 : 0; } else { var aStr = alToString(context, aField); var bStr = alToString(context, bField); if (optionsList[i] & AVM1ArraySortOnOptions.CASEINSENSITIVE) { aStr = aStr.toLowerCase(); bStr = bStr.toLowerCase(); } result = aStr < bStr ? -1 : aStr > bStr ? +1 : 0; } if (result !== 0) { return !(optionsList[i] & AVM1ArraySortOnOptions.DESCENDING) ? result : -result; } } return 0; }; var arr = alEnsureType(this, AVM1ArrayNative).value; arr.sort(comparer); // Strange, the documentation said to do not return anything. return this; }; AVM1ArrayPrototype.prototype.unshift = function () { var items = []; for (var _a = 0; _a < arguments.length; _a++) { items[_a] = arguments[_a]; } if (this instanceof AVM1ArrayNative) { // Faster case for native array implementation var arr = alEnsureType(this, AVM1ArrayNative).value; return Array.prototype.unshift.apply(arr, items); } var length = alToInt32(this.context, this.alGet('length')) >>> 0; var insertCount = items.length; // TODO check overflow for (var i = length - 1; i >= 0; i--) { if (this.alHasProperty(i)) { this.alPut(i + insertCount, this.alGet(i)); } else { this.alDeleteProperty(i + insertCount); } } for (var i = 0; i < items.length; i++) { this.alPut(i, items[i]); } length += insertCount; this.alPut('length', length); // ActionScript does not do that? return length; }; return AVM1ArrayPrototype; }(AVM1Object)); export { AVM1ArrayPrototype }; var AVM1ArrayFunction = /** @class */ (function (_super) { __extends(AVM1ArrayFunction, _super); function AVM1ArrayFunction(context) { var _this = _super.call(this, context) || this; _this.alPrototype = context.builtins.Function.alGetPrototypeProperty(); var proto = new AVM1ArrayPrototype(context); alDefineObjectProperties(_this, { prototype: { value: proto } }); alDefineObjectProperties(_this, { NUMERIC: { value: AVM1ArraySortOnOptions.NUMERIC } }); alDefineObjectProperties(_this, { DESCENDING: { value: AVM1ArraySortOnOptions.DESCENDING } }); return _this; } AVM1ArrayFunction.prototype.alConstruct = function (args) { if (!args) { return new AVM1ArrayNative(this.context, []); } if (args.length === 1 && typeof args[0] === 'number') { var len = args[0]; if (len >>> 0 !== len) { throw new Error('Range error'); // TODO avm1 native } return new AVM1ArrayNative(this.context, new Array(len)); } return new AVM1ArrayNative(this.context, args); }; AVM1ArrayFunction.prototype.alCall = function (thisArg, args) { return this.alConstruct.apply(this, args); }; return AVM1ArrayFunction; }(AVM1Function)); export { AVM1ArrayFunction }; // Math natives var AVM1MathObject = /** @class */ (function (_super) { __extends(AVM1MathObject, _super); function AVM1MathObject(context) { var _this = _super.call(this, context) || this; _this.alPrototype = context.builtins.Object.alGetPrototypeProperty(); alDefineObjectProperties(_this, { E: Math.E, LN10: Math.LN10, LN2: Math.LN2, LOG10E: Math.LOG10E, LOG2E: Math.LOG2E, PI: Math.PI, SQRT1_2: Math.SQRT1_2, SQRT2: Math.SQRT2, abs: _this.abs, acos: _this.acos, asin: _this.asin, atan: _this.atan, atan2: _this.atan2, ceil: _this.ceil, cos: _this.cos, exp: _this.exp, floor: _this.floor, log: _this.log, max: _this.max, min: _this.min, pow: _this.pow, random: _this.random, round: _this.round, sin: _this.sin, sqrt: _this.sqrt, tan: _this.tan }); return _this; } AVM1MathObject.prototype.abs = function (x) { return Math.abs(alToNumber(this.context, x)); }; AVM1MathObject.prototype.acos = function (x) { return Math.acos(alToNumber(this.context, x)); }; AVM1MathObject.prototype.asin = function (x) { return Math.asin(alToNumber(this.context, x)); }; AVM1MathObject.prototype.atan = function (x) { return Math.atan(alToNumber(this.context, x)); }; AVM1MathObject.prototype.atan2 = function (y, x) { return Math.atan2(alToNumber(this.context, y), alToNumber(this.context, x)); }; AVM1MathObject.prototype.ceil = function (x) { return Math.ceil(alToNumber(this.context, x)); }; AVM1MathObject.prototype.cos = function (x) { return Math.cos(alToNumber(this.context, x)); }; AVM1MathObject.prototype.exp = function (x) { return Math.exp(alToNumber(this.context, x)); }; AVM1MathObject.prototype.floor = function (x) { return Math.floor(alToNumber(this.context, x)); }; AVM1MathObject.prototype.log = function (x) { return Math.log(alToNumber(this.context, x)); }; AVM1MathObject.prototype.max = function () { var _this = this; var values = []; for (var _a = 0; _a < arguments.length; _a++) { values[_a] = arguments[_a]; } values = values.map(function (x) { return alToNumber(_this.context, x); }); return Math.max.apply(null, values); }; AVM1MathObject.prototype.min = function () { var _this = this; var values = []; for (var _a = 0; _a < arguments.length; _a++) { values[_a] = arguments[_a]; } values = values.map(function (x) { return alToNumber(_this.context, x); }); return Math.min.apply(null, values); }; AVM1MathObject.prototype.pow = function (x, y) { return Math.pow(alToNumber(this.context, x), alToNumber(this.context, y)); }; AVM1MathObject.prototype.random = function () { if (AVM1Globals.randomProvider) { return AVM1Globals.randomProvider.random(); } return Math.random(); }; AVM1MathObject.prototype.round = function (x) { return Math.round(alToNumber(this.context, x)); }; AVM1MathObject.prototype.sin = function (x) { return Math.sin(alToNumber(this.context, x)); }; AVM1MathObject.prototype.sqrt = function (x) { return Math.sqrt(alToNumber(this.context, x)); }; AVM1MathObject.prototype.tan = function (x) { return Math.tan(alToNumber(this.context, x)); }; return AVM1MathObject; }(AVM1Object)); // Date natives var AVM1DateNative = /** @class */ (function (_su