UNPKG

@awayfl/avm2

Version:

Virtual machine for executing AS3 code

470 lines (469 loc) 19.9 kB
import { __extends } from "tslib"; import { defineNonEnumerableProperty, isNumeric, isNumber, isString, release, Debug } from '@awayfl/swf-loader'; import { ASObject } from './ASObject'; import { addPrototypeFunctionAlias } from './addPrototypeFunctionAlias'; import { makeMultiname } from './makeMultiname'; import { axCoerceName } from '../run/axCoerceName'; import { ensureBoxedReceiver } from '../run/ensureBoxedReceiver'; import { rn } from './rn'; import { Namespace } from '../abc/lazy/Namespace'; import { Multiname } from '../abc/lazy/Multiname'; import { coerceArray } from './coerceArray'; import { createArrayValueFromArgs } from './createArrayValueFromArgs'; import { assert } from '@awayjs/graphics'; import { checkValue } from '../run/checkValue'; import { axDefaultCompareFunction } from '../run/axDefaultCompareFunction'; import { axCompare } from '../run/axCompare'; import { axCompareFields } from '../run/axCompareFields'; import { Errors } from '../errors'; var ASArray = /** @class */ (function (_super) { __extends(ASArray, _super); function ASArray() { var _this = _super.call(this) || this; _this.value = createArrayValueFromArgs(_this.sec, arguments); return _this; } ASArray.classInitializer = function () { var proto = this.dPrototype; var asProto = ASArray.prototype; // option flags for sort and sortOn defineNonEnumerableProperty(this, '$BgCASEINSENSITIVE', 1); defineNonEnumerableProperty(this, '$BgDESCENDING', 2); defineNonEnumerableProperty(this, '$BgUNIQUESORT', 4); defineNonEnumerableProperty(this, '$BgRETURNINDEXEDARRAY', 8); defineNonEnumerableProperty(this, '$BgNUMERIC', 16); addPrototypeFunctionAlias(proto, '$Bgpush', asProto.generic_push); addPrototypeFunctionAlias(proto, '$Bgpop', asProto.generic_pop); addPrototypeFunctionAlias(proto, '$Bgshift', asProto.generic_shift); addPrototypeFunctionAlias(proto, '$Bgunshift', asProto.generic_unshift); addPrototypeFunctionAlias(proto, '$Bgreverse', asProto.generic_reverse); addPrototypeFunctionAlias(proto, '$Bgconcat', asProto.generic_concat); addPrototypeFunctionAlias(proto, '$Bgslice', asProto.generic_slice); addPrototypeFunctionAlias(proto, '$Bgsplice', asProto.generic_splice); addPrototypeFunctionAlias(proto, '$Bgjoin', asProto.generic_join); addPrototypeFunctionAlias(proto, '$BgtoString', asProto.generic_toString); addPrototypeFunctionAlias(proto, '$BgindexOf', asProto.generic_indexOf); addPrototypeFunctionAlias(proto, '$BglastIndexOf', asProto.generic_lastIndexOf); addPrototypeFunctionAlias(proto, '$Bgevery', asProto.generic_every); addPrototypeFunctionAlias(proto, '$Bgsome', asProto.generic_some); addPrototypeFunctionAlias(proto, '$BgforEach', asProto.generic_forEach); addPrototypeFunctionAlias(proto, '$Bgmap', asProto.generic_map); addPrototypeFunctionAlias(proto, '$Bgfilter', asProto.generic_filter); addPrototypeFunctionAlias(proto, '$Bgsort', asProto.generic_sort); addPrototypeFunctionAlias(proto, '$BgsortOn', asProto.generic_sortOn); addPrototypeFunctionAlias(proto, '$BghasOwnProperty', asProto.native_hasOwnProperty); addPrototypeFunctionAlias(proto, '$BgpropertyIsEnumerable', asProto.native_propertyIsEnumerable); addPrototypeFunctionAlias(proto, '$BgtoLocaleString', asProto.generic_toString); }; ASArray.prototype.native_hasOwnProperty = function (nm) { return this.axHasOwnProperty(makeMultiname(nm)); }; ASArray.prototype.native_propertyIsEnumerable = function (nm) { if (typeof nm === 'number' || isNumeric(nm = axCoerceName(nm))) { var descriptor = Object.getOwnPropertyDescriptor(this.value, nm); return !!descriptor && descriptor.enumerable; } _super.prototype.native_propertyIsEnumerable.call(this, nm); }; ASArray.axApply = function (self, args) { return this.sec.createArrayUnsafe(createArrayValueFromArgs(this.sec, args)); }; ASArray.axConstruct = function (args) { return this.sec.createArrayUnsafe(createArrayValueFromArgs(this.sec, args)); }; ASArray.prototype.push = function () { // Amazingly, AS3 doesn't throw an error if `push` would make the argument too large. // Instead, it just replaces the last element. if (this.value.length + arguments.length > 0xffffffff) { var limit = 0xffffffff - this.value.length; for (var i = 0; i < limit; i++) { this.value.push(arguments[i]); } return 0xffffffff; } return this.value.push.apply(this.value, arguments); }; ASArray.prototype.generic_push = function () { if (this && this.value instanceof Array) { return this.push.apply(this, arguments); } var n = this.axGetPublicProperty('length') >>> 0; for (var i = 0; i < arguments.length; i++) { this.axSetNumericProperty(n++, arguments[i]); } this.axSetPublicProperty('length', n); return n; }; ASArray.prototype.pop = function () { return this.value.pop(); }; ASArray.prototype.generic_pop = function () { if (this && this.value instanceof Array) { return this.value.pop(); } var len = this.axGetPublicProperty('length') >>> 0; if (!len) { this.axSetPublicProperty('length', 0); return; } var retVal = this.axGetNumericProperty(len - 1); rn.name = len - 1; rn.namespaces = [Namespace.PUBLIC]; this.axDeleteProperty(rn); this.axSetPublicProperty('length', len - 1); return retVal; }; ASArray.prototype.shift = function () { return this.value.shift(); }; ASArray.prototype.generic_shift = function () { return coerceArray(this).shift(); }; ASArray.prototype.unshift = function () { return this.value.unshift.apply(this.value, arguments); }; ASArray.prototype.generic_unshift = function () { var self = coerceArray(this); return self.value.unshift.apply(self.value, arguments); }; ASArray.prototype.reverse = function () { this.value.reverse(); return this; }; ASArray.prototype.generic_reverse = function () { return coerceArray(this).reverse(); }; ASArray.prototype.concat = function () { var value = this.value.slice(); for (var i = 0; i < arguments.length; i++) { var a = arguments[i]; // Treat all objects with a `sec` property and a value that's an Array as // concat-spreadable. // TODO: verify that this is correct. if (typeof a === 'object' && a && a.sec && Array.isArray(a.value)) { value.push.apply(value, a.value); } else { value.push(a); } } return this.sec.createArrayUnsafe(value); }; ASArray.prototype.generic_concat = function () { return coerceArray(this).concat.apply(this, arguments); }; ASArray.prototype.slice = function (startIndex, endIndex) { return this.sec.createArray(this.value.slice(startIndex, endIndex)); }; ASArray.prototype.generic_slice = function (startIndex, endIndex) { return coerceArray(this).slice(startIndex, endIndex); }; ASArray.prototype.splice = function () { var o = this.value; if (arguments.length === 0) { return undefined; } return this.sec.createArray(o.splice.apply(o, arguments)); }; ASArray.prototype.generic_splice = function () { return coerceArray(this).splice.apply(this, arguments); }; ASArray.prototype.join = function (sep) { return this.value.join(sep); }; ASArray.prototype.generic_join = function (sep) { return coerceArray(this).join(sep); }; ASArray.prototype.toString = function () { return this.value.join(','); }; ASArray.prototype.valueOf = function () { // same return this.value.join(','); }; ASArray.prototype.generic_toString = function () { return coerceArray(this).join(','); }; ASArray.prototype.indexOf = function (value, fromIndex) { return this.value.indexOf(value, fromIndex | 0); }; ASArray.prototype.generic_indexOf = function (value, fromIndex) { return coerceArray(this).indexOf(value, fromIndex | 0); }; ASArray.prototype.lastIndexOf = function (value, fromIndex) { return this.value.lastIndexOf(value, arguments.length > 1 ? fromIndex : 0x7fffffff); }; ASArray.prototype.generic_lastIndexOf = function (value, fromIndex) { return coerceArray(this).lastIndexOf(value, arguments.length > 1 ? fromIndex : 0x7fffffff); }; ASArray.prototype.every = function (callbackfn, thisArg) { if (!callbackfn || !callbackfn.value || typeof callbackfn.value !== 'function') { return true; } thisArg = ensureBoxedReceiver(this.sec, thisArg, callbackfn); var o = this.value; for (var i = 0; i < o.length; i++) { if (callbackfn.value.call(thisArg, o[i], i, this) !== true) { return false; } } return true; }; ASArray.prototype.generic_every = function (callbackfn, thisArg) { return coerceArray(this).every(callbackfn, thisArg); }; ASArray.prototype.some = function (callbackfn, thisArg) { if (!callbackfn || !callbackfn.value || typeof callbackfn.value !== 'function') { return false; } thisArg = ensureBoxedReceiver(this.sec, thisArg, callbackfn); var self = this; return this.value.some(function (currentValue, index, array) { return callbackfn.value.call(thisArg, currentValue, index, self); }); }; ASArray.prototype.generic_some = function (callbackfn, thisArg) { return coerceArray(this).some(callbackfn, thisArg); }; ASArray.prototype.forEach = function (callbackfn, thisArg) { if (!callbackfn || !callbackfn.value || typeof callbackfn.value !== 'function') { return; } thisArg = ensureBoxedReceiver(this.sec, thisArg, callbackfn); var self = this; this.value.forEach(function (currentValue, index) { callbackfn.value.call(thisArg, currentValue, index, self); }); }; ASArray.prototype.generic_forEach = function (callbackfn, thisArg) { return coerceArray(this).forEach(callbackfn, thisArg); }; ASArray.prototype.map = function (callbackfn, thisArg) { if (!callbackfn || !callbackfn.value || typeof callbackfn.value !== 'function') { return this.sec.createArrayUnsafe([]); } thisArg = ensureBoxedReceiver(this.sec, thisArg, callbackfn); var self = this; return this.sec.createArrayUnsafe(this.value.map(function (currentValue, index) { return callbackfn.value.call(thisArg, currentValue, index, self); })); }; ASArray.prototype.generic_map = function (callbackfn, thisArg) { return coerceArray(this).map(callbackfn, thisArg); }; ASArray.prototype.filter = function (callbackfn, thisArg) { if (!callbackfn || !callbackfn.value || typeof callbackfn.value !== 'function') { return this.sec.createArrayUnsafe([]); } thisArg = ensureBoxedReceiver(this.sec, thisArg, callbackfn); var result = []; var o = this.value; for (var i = 0; i < o.length; i++) { if (callbackfn.value.call(thisArg, o[i], i, this) === true) { result.push(o[i]); } } return this.sec.createArrayUnsafe(result); }; ASArray.prototype.generic_filter = function (callbackfn, thisArg) { return coerceArray(this).filter(callbackfn, thisArg); }; ASArray.prototype.toLocaleString = function () { var value = this.sec.AXArray.axCoerce(this).value; var out = ''; for (var i = 0, n = value.length; i < n; i++) { var val = value[i]; if (val !== null && val !== undefined) { out += val.toLocaleString(); } if (i + 1 < n) { out += ','; } } return out; }; ASArray.prototype.sort = function (func, options) { var value = this.value; if (func == void 0) { value.sort(); return this; } var compareFunction; var context; if (this.sec.AXFunction.axIsInstanceOf(func)) { compareFunction = func.value; context = func.receiver; } else if (isNumber(func)) { options = func; } if (options != void 0 && !isNumber(options)) { options = void 0; } if (!options) { // Just passing compareFunction is ok because `undefined` is treated as not passed in JS. if (context) { // we must pass context for sort function because it can be bounded onto closure value.sort(compareFunction.bind(context)); return this; } value.sort(compareFunction); return this; } if (!compareFunction) { compareFunction = axDefaultCompareFunction; } var sortOrder = options & 2 /* SORT.DESCENDING */ ? -1 : 1; value.sort(function (a, b) { return axCompare(a, b, options, sortOrder, compareFunction); }); return this; }; ASArray.prototype.generic_sort = function () { return coerceArray(this).sort.apply(this, arguments); }; ASArray.prototype.sortOn = function (names, options) { if (arguments.length === 0) { this.sec.throwError('ArgumentError', Errors.WrongArgumentCountError, 'Array/http://adobe.com/AS3/2006/builtin::sortOn()', '1', '0'); } // The following oddities in how the arguments are used are gleaned from Tamarin, so hush. var o = this.value; // The options we'll end up using. var optionsList = []; if (isString(names)) { names = [Multiname.getPublicMangledName(names)]; // If the name is a string, coerce `options` to int. optionsList = [options | 0]; } else if (names && Array.isArray(names.value)) { names = names.value; for (var i = 0; i < names.length; i++) { names[i] = Multiname.getPublicMangledName(names[i]); } if (options && Array.isArray(options.value)) { options = options.value; // Use the options Array only if it's the same length as names. if (options.length === names.length) { for (var i = 0; i < options.length; i++) { optionsList[i] = options[i] | 0; } // Otherwise, use 0 for all options. } else { for (var i = 0; i < names.length; i++) { optionsList[i] = 0; } } } else { var optionsVal_1 = options | 0; for (var i = 0; i < names.length; i++) { optionsList[i] = optionsVal_1; } } } else { // Not supplying either a String or an Array means nothing is sorted on. return this; } release || assert(optionsList.length === names.length); // For use with uniqueSort and returnIndexedArray once we support them. var optionsVal = optionsList[0]; release || Debug.assertNotImplemented(!(optionsVal & 4 /* SORT.UNIQUESORT */), 'UNIQUESORT'); var ret = o; if (optionsVal & 8 /* SORT.RETURNINDEXEDARRAY */) { var cp = o.concat(); // Make a copy of the array cp.sort(function (a, b) { return axCompareFields(a, b, names, optionsList); }); ret = cp.map(function (e) { return o.indexOf(e); }); // TODO: What happens if there are duplicate elements in the array? } else { o.sort(function (a, b) { return axCompareFields(a, b, names, optionsList); }); } return ret; }; ASArray.prototype.generic_sortOn = function () { return coerceArray(this).sortOn.apply(this, arguments); }; Object.defineProperty(ASArray.prototype, "length", { get: function () { return this.value.length; }, set: function (newLength) { this.value.length = newLength >>> 0; }, enumerable: false, configurable: true }); ASArray.prototype.axGetEnumerableKeys = function () { // Get the numeric Array keys first ... var keys = Object.keys(this.value); // ... then the keys that live on the array object. return keys.concat(_super.prototype.axGetEnumerableKeys.call(this)); }; ASArray.prototype.axHasPropertyInternal = function (mn) { var name = mn.name; if (typeof name === 'number' || isNumeric(name = axCoerceName(name))) { release || assert(mn.isRuntimeName()); return name in this.value; } if (this.traits.getTrait(mn.namespaces, name)) { return true; } return '$Bg' + name in this; }; ASArray.prototype.axHasOwnProperty = function (mn) { var name = mn.name; if (typeof name === 'number' || isNumeric(name = axCoerceName(name))) { release || assert(mn.isRuntimeName()); return this.value.hasOwnProperty(name); } return !!this.traits.getTrait(mn.namespaces, name) || this.hasOwnProperty('$Bg' + name); }; ASArray.prototype.axGetProperty = function (mn) { var _a; var name = mn.name; if (typeof name === 'number' || isNumeric(name = axCoerceName(name))) { return (_a = this.value[name]) !== null && _a !== void 0 ? _a : this[name]; // quick fix } return _super.prototype.axGetProperty.call(this, mn); }; ASArray.prototype.axSetProperty = function (mn, value, bc) { release || checkValue(value); var name = mn.name; if (typeof name === 'number' || isNumeric(name = axCoerceName(name))) { this.value[name] = value; return; } _super.prototype.axSetProperty.call(this, mn, value, bc); }; ASArray.prototype.axDeleteProperty = function (mn) { var name = mn.name; if (typeof name === 'number' || isNumeric(name = axCoerceName(name))) { return delete this.value[name]; } // Cannot delete array traits. if (this.traits.getTrait(mn.namespaces, name)) { return false; } return delete this['$Bg' + name]; }; ASArray.prototype.axGetNumericProperty = function (nm) { return this.value[nm]; }; ASArray.prototype.axGetPublicProperty = function (nm) { if (typeof nm === 'number' || isNumeric(nm = axCoerceName(nm))) { return this.value[nm]; } return this['$Bg' + nm]; }; ASArray.prototype.axSetPublicProperty = function (nm, value) { release || checkValue(value); if (typeof nm === 'number' || isNumeric(nm = axCoerceName(nm))) { this.value[nm] = value; return; } this['$Bg' + nm] = value; }; return ASArray; }(ASObject)); export { ASArray };