@awayfl/avm2
Version:
Virtual machine for executing AS3 code
493 lines (492 loc) • 20.8 kB
JavaScript
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';
import { axBoxPrimitive } from '../run/axBoxPrimitive';
var ASArray = /** @class */ (function (_super) {
__extends(ASArray, _super);
function ASArray() {
var _this = _super.call(this) || this;
_this.value = createArrayValueFromArgs(_this.sec, arguments);
return ASArray.wrapProxy(_this);
}
/**
* Proxy ASArray for allow use brackets notation [index], used in Box2D and other external modules
* @param original
*/
ASArray.wrapProxy = function (original) {
return new Proxy(original, {
set: function (target, p, value, receiver) {
if (typeof p === 'string' && !Number.isNaN(+p)) {
target.value[+p] = value;
return true;
}
target[p] = value;
return true;
},
get: function (target, p, receiver) {
if (typeof p === 'string' && !Number.isNaN(+p)) {
return target.value[+p];
}
return target[p];
}
});
};
ASArray.axBox = function (asValue) {
return ASArray.wrapProxy(axBoxPrimitive.call(this, asValue));
};
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.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 };