@awayfl/avm1
Version:
Virtual machine for executing AS1 and AS2 code
1,836 lines (1,660 loc) • 54.5 kB
text/typescript
/*
* 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.
*/
// 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, AVM1DefaultValueHint, AVM1PropertyFlags,
IAVM1Context
} 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';
class AVM1ObjectPrototype extends AVM1Object {
public constructor(context: IAVM1Context) {
super(context);
// Initialization must be perfromed later after the Function creation.
// See the _initializePrototype and createBuiltins below.
}
_initializePrototype() {
const 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
}
});
}
public _valueOf() {
return this;
}
public _toString() {
if (alIsFunction(this)) {
// Really weird case of functions.
return '[type ' + alGetObjectClass(this) + ']';
}
return '[object ' + alGetObjectClass(this) + ']';
}
public addProperty(name, getter, setter) {
if (typeof name !== 'string' || name === '') {
return false;
}
if (!alIsFunction(getter)) {
return false;
}
if (!alIsFunction(setter) && setter !== null) {
return false;
}
const desc = this.alGetOwnProperty(name);
if (desc && !!(desc.flags & AVM1PropertyFlags.DONT_DELETE)) {
return false; // protected property
}
this.alSetOwnProperty(name, new AVM1PropertyDescriptor(AVM1PropertyFlags.ACCESSOR, null,
getter, setter || undefined));
return true;
}
public hasOwnProperty(name): boolean {
return this.alHasOwnProperty(name);
}
public isPropertyEnumerable(name): boolean {
const desc = this.alGetProperty(name);
return !(desc.flags & AVM1PropertyFlags.DONT_ENUM);
}
public isPrototypeOf(theClass: AVM1Function): boolean {
return alInstanceOf(this.context, this, theClass);
}
public unwatch(name: string): boolean {
name = alCoerceString(this.context, name);
return this.alRemotePropertyWatcher(name);
}
public watch(name: string, callback: AVM1Function, userData?: any): boolean {
name = alCoerceString(this.context, name);
if (!alIsFunction(callback)) {
return false;
}
return this.alAddPropertyWatcher(name, callback, userData);
}
}
export class AVM1ObjectFunction extends AVM1Function {
public constructor(context: IAVM1Context) {
super(context);
this.alPrototype = context.builtins.Function.alGetPrototypeProperty();
const proto = context.builtins.Object.alGetPrototypeProperty();
alDefineObjectProperties(this, {
prototype: {
value: proto
},
registerClass: {
value: this.registerClass
}
});
}
public registerClass(name, theClass) {
this.context.registerClass(name, theClass);
}
public alConstruct(args?: any[]): AVM1Object {
if (args) {
const 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);
}
public alCall(thisArg: any, args?: any[]): any {
if (!args || args[0] === null || args[0] === undefined) {
return alNewObject(this.context);
}
return alToObject(this.context, args[0]);
}
}
// Function natives
class AVM1FunctionPrototype extends AVM1Object {
public constructor(context: IAVM1Context) {
super(context);
}
_initializePrototype() {
const context = this.context;
this.alPrototype = context.builtins.Object.alGetPrototypeProperty();
alDefineObjectProperties(this, {
constructor: {
value: context.builtins.Function,
writable: true
},
call: this.call,
apply: this.apply
});
}
public call(thisArg: any, ...args: any[]): any {
const fn = alEnsureType<AVM1Function>(this, AVM1Function);
return fn.alCall(thisArg, args);
}
public apply(thisArg: any, args?: AVM1ArrayNative): any {
const fn = alEnsureType<AVM1Function>(this, AVM1Function);
const nativeArgs = !args ? undefined :
alEnsureType<AVM1ArrayNative>(args, AVM1ArrayNative).value;
return fn.alCall(thisArg, nativeArgs);
}
}
export class AVM1FunctionFunction extends AVM1Function {
public constructor(context: IAVM1Context) {
super(context);
this.alPrototype = context.builtins.Function.alGetPrototypeProperty();
const proto = context.builtins.Function.alGetPrototypeProperty();
alDefineObjectProperties(this, {
prototype: {
value: proto
}
});
}
public alConstruct(args?: any[]): AVM1Object {
// ActionScript just returns the first argument.
return args ? args[0] : undefined;
}
public alCall(thisArg: any, args?: any[]): any {
// ActionScript just returns the first argument.
return args ? args[0] : undefined;
}
}
// Boolean natives
export class AVM1BooleanNative extends AVM1Object {
public value: boolean;
public constructor(context: IAVM1Context, value: boolean) {
super(context);
this.alPrototype = context.builtins.Boolean.alGetPrototypeProperty();
this.alSetOwnConstructorProperty(context.builtins.Boolean);
this.value = value;
}
public valueOf(): any {
return this.value;
}
}
export class AVM1BooleanPrototype extends AVM1Object {
public constructor(context: IAVM1Context) {
super(context);
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
}
});
}
public _valueOf() {
const native = alEnsureType<AVM1BooleanNative>(this, AVM1BooleanNative);
return native.value;
}
public _toString() {
const native = alEnsureType<AVM1BooleanNative>(this, AVM1BooleanNative);
return native.value ? 'true' : 'false';
}
}
export class AVM1BooleanFunction extends AVM1Function {
public constructor(context: IAVM1Context) {
super(context);
this.alPrototype = context.builtins.Function.alGetPrototypeProperty();
const proto = new AVM1BooleanPrototype(context);
alDefineObjectProperties(this, {
prototype: {
value: proto
}
});
}
public alConstruct(args?: any[]): AVM1Object {
const value = args ? alToBoolean(this.context, args[0]) : false;
return new AVM1BooleanNative(this.context, value);
}
public alCall(thisArg: any, args?: any[]): any {
// TODO returns boolean value?
const value = args ? alToBoolean(this.context, args[0]) : false;
return value;
}
}
// Number natives
export class AVM1NumberNative extends AVM1Object {
public value: number;
public constructor(context: IAVM1Context, value: number) {
super(context);
this.alPrototype = context.builtins.Number.alGetPrototypeProperty();
this.alSetOwnConstructorProperty(context.builtins.Number);
this.value = value;
}
public valueOf(): any {
return this.value;
}
}
export class AVM1NumberPrototype extends AVM1Object {
public constructor(context: IAVM1Context) {
super(context);
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
}
});
}
public _valueOf() {
const native = alEnsureType<AVM1NumberNative>(this, AVM1NumberNative);
return native.value;
}
public _toString(radix) {
const native = alEnsureType<AVM1NumberNative>(this, AVM1NumberNative);
return native.value.toString(radix || 10);
}
}
export class AVM1NumberFunction extends AVM1Function {
public constructor(context: IAVM1Context) {
super(context);
this.alPrototype = context.builtins.Function.alGetPrototypeProperty();
const 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
});
}
public alConstruct(args?: any[]): AVM1Object {
const value = args ? alToNumber(this.context, args[0]) : 0;
return new AVM1NumberNative(this.context, value);
}
public alCall(thisArg: any, args?: any[]): any {
// TODO returns number value?
const value = args ? alToNumber(this.context, args[0]) : 0;
return value;
}
}
// String natives
export class AVM1StringNative extends AVM1Object {
public value: string;
public constructor(context: IAVM1Context, value: string) {
super(context);
this.alPrototype = context.builtins.String.alGetPrototypeProperty();
this.alSetOwnConstructorProperty(context.builtins.String);
this.value = value;
}
public toString(): string {
return this.value;
}
}
// Most of the methods of String prototype are generic and accept any object.
export class AVM1StringPrototype extends AVM1Object {
public constructor(context: IAVM1Context) {
super(context);
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
}
});
}
public _valueOf() {
const native = alEnsureType<AVM1StringNative>(this, AVM1StringNative);
return native.value;
}
public _toString() {
const native = alEnsureType<AVM1StringNative>(this, AVM1StringNative);
return native.value;
}
public getLength(): number {
const native = alEnsureType<AVM1StringNative>(this, AVM1StringNative);
return native.value.length;
}
public charAt(index: number): string {
const value = alToString(this.context, this);
return value.charAt(alToInteger(this.context, index));
}
public charCodeAt(index: number): number {
const value = alToString(this.context, this);
return value.charCodeAt(alToInteger(this.context, index));
}
public concat(...items: AVM1Object[]): string {
const stringItems: string[] = [alToString(this.context, this)];
for (let i = 0; i < items.length; ++i) {
stringItems.push(alToString(this.context, items[i]));
}
return stringItems.join('');
}
public indexOf(searchString: string, position?: number): number {
const value = alToString(this.context, this);
searchString = alToString(this.context, searchString);
position = alToInteger(this.context, position);
return value.indexOf(searchString, position);
}
public lastIndexOf(searchString: string, position?: number): number {
const 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);
}
public slice(start: number, end?: number): string {
if (arguments.length === 0) {
// Different from JS
return undefined;
}
const value = alToString(this.context, this);
start = alToInteger(this.context, start);
end = end === undefined ? undefined : alToInteger(this.context, end);
return value.slice(start, end);
}
public split(separator: any, limit?: number): AVM1ArrayNative {
const 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]);
}
public substr(start: number, length?: number): string {
// Different from JS
const value = alToString(this.context, this);
const 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);
}
public substring(start: number, end?: number): string {
const 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);
}
public toLowerCase(): string {
const value = alToString(this.context, this);
return value.toLowerCase();
}
public toUpperCase(): string {
const value = alToString(this.context, this);
return value.toUpperCase();
}
}
export class AVM1StringFunction extends AVM1Function {
public constructor(context: IAVM1Context) {
super(context);
this.alPrototype = context.builtins.Function.alGetPrototypeProperty();
const proto = new AVM1StringPrototype(context);
alDefineObjectProperties(this, {
prototype: {
value: proto
},
fromCharCode: {
value: this.fromCharCode
}
});
}
public alConstruct(args?: any[]): AVM1Object {
const value = args ? alToString(this.context, args[0]) : '';
return new AVM1StringNative(this.context, value);
}
public alCall(thisArg: any, args?: any[]): any {
const value = args ? alToString(this.context, args[0]) : '';
return value;
}
public fromCharCode(...codes: number[]): string {
codes = codes.map((code) => alToInt32(this.context, code) & 0xFFFF);
return String.fromCharCode.apply(String, codes);
}
}
// Array natives
const cachedArrayPropertyDescriptor = new AVM1PropertyDescriptor(AVM1PropertyFlags.DATA,
undefined);
export class AVM1ArrayNative extends AVM1Object {
public value: any[];
public constructor(context: IAVM1Context, value: any[]) {
super(context);
this.alPrototype = context.builtins.Array.alGetPrototypeProperty();
this.alSetOwnConstructorProperty(context.builtins.Array);
this.value = value;
}
public alGetOwnProperty(p): AVM1PropertyDescriptor {
if (alIsIndex(this.context, p)) {
const index = alToInt32(this.context, p);
if (Object.getOwnPropertyDescriptor(this.value, <any>index)) {
cachedArrayPropertyDescriptor.value = this.value[index];
return cachedArrayPropertyDescriptor;
}
}
return super.alGetOwnProperty(p);
}
public alSetOwnProperty(p, v: AVM1PropertyDescriptor) {
if (alIsIndex(this.context, p)) {
const index = alToInt32(this.context, p);
if (!(v.flags & AVM1PropertyFlags.DATA) ||
!!(v.flags & AVM1PropertyFlags.DONT_ENUM) ||
!!(v.flags & AVM1PropertyFlags.DONT_DELETE)) {
throw new Error('Special property is non-supported for array');
}
this.value[index] = v.value;
return;
}
super.alSetOwnProperty(p, v);
}
public alDeleteOwnProperty(p) {
if (alIsIndex(this.context, p)) {
const index = alToInt32(this.context, p);
delete this.value[index];
return;
}
super.alDeleteOwnProperty(p);
}
public alGetOwnPropertiesKeys(): string[] {
const keys = super.alGetOwnPropertiesKeys();
const itemIndices = [];
for (const 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.
*/
public static mapToJSArray(arr: AVM1Object, fn: (item: any, index?: number) => any, thisArg?): any[] {
if (arr instanceof AVM1ArrayNative) {
return (<AVM1ArrayNative>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.
}
const result = [];
alIterateArray(arr.context, arr, (item: any, index: number) => {
result.push(fn.call(thisArg, item, index));
});
return result;
}
}
enum AVM1ArraySortOnOptions {
CASEINSENSITIVE = 1,
DESCENDING = 2,
UNIQUESORT = 4,
RETURNINDEXEDARRAY = 8,
NUMERIC = 16
}
const sortArray = function(arr_in: any, comparefn?: AVM1Function): AVM1Object {
const arr = alEnsureType<AVM1ArrayNative>(arr_in, AVM1ArrayNative).value;
if (!alIsFunction(comparefn)) {
const doNumeric = typeof comparefn === 'number'
&& (comparefn & AVM1ArraySortOnOptions.NUMERIC || comparefn == -1);
const doDescending = typeof comparefn === 'number' && (comparefn & AVM1ArraySortOnOptions.DESCENDING);
if (doNumeric) {
arr.sort(function(a,b) {
while (a instanceof AVM1ArrayNative) {
a = alEnsureType<AVM1ArrayNative>(a, AVM1ArrayNative).value;
if (a && a.length > 0)
a = a[0];
}
while (b instanceof AVM1ArrayNative) {
b = alEnsureType<AVM1ArrayNative>(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
let 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<AVM1ArrayNative>(a, AVM1ArrayNative).value;
if (a && a.length > 0)
a = a[0];
}
while (b instanceof AVM1ArrayNative) {
b = alEnsureType<AVM1ArrayNative>(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 {
const args = [undefined, undefined];
arr.sort(function (a, b) {
args[0] = a;
args[1] = b;
return comparefn.alCall(null, args);
});
}
return arr_in;
};
// TODO implement all the Array class and its prototype natives
export class AVM1ArrayPrototype extends AVM1Object {
public constructor(context: IAVM1Context) {
super(context);
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
},
});
}
public _toString() {
const len: number = this.alGet('length');
let _i: number = 0;
let outputStr: string = '';
let 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;
}
public getLength(): number {
const arr = alEnsureType<AVM1ArrayNative>(this, AVM1ArrayNative).value;
return arr.length;
}
public setLength(length: number) {
if (length === null) {
length = 0;
}
if (!isIndex(length)) {
return; // no action on invalid length
}
length = alToInt32(this.context, length) >>> 0;
const arr = alEnsureType<AVM1ArrayNative>(this, AVM1ArrayNative).value;
arr.length = length;
}
public concat(...items: any[]): AVM1Object {
if (this instanceof AVM1ArrayNative) {
// Faster case for native array implementation
const arr = alEnsureType<AVM1ArrayNative>(this, AVM1ArrayNative).value;
for (let i = 0; i < items.length; i++) {
if (items[i] instanceof AVM1ArrayNative) {
items[i] = alEnsureType<AVM1ArrayNative>(items[i], AVM1ArrayNative).value;
}
}
//this.value=arr.concat(items);
return new AVM1ArrayNative(this.context, Array.prototype.concat.apply(arr, items));
//return this;
}
// Generic behavior
const a = [];
let e: any = this;
let isArrayObject = alIsArrayLike(this.context, this);
let i = 0;
while (true) {
if (isArrayObject) {
alIterateArray(this.context, e, (value) => 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);
}
public join(separator?: string): string {
separator = separator === undefined ? ',' : alCoerceString(this.context, separator);
if (this instanceof AVM1ArrayNative) {
// Faster case for native array implementation
const arr = alEnsureType<AVM1ArrayNative>(this, AVM1ArrayNative).value;
if (arr.length === 0) {
return '';
}
if (arr.every(function (i) { return !(i instanceof AVM1Object); })) {
return arr.join(separator);
}
}
const context = this.context;
const length = alToInt32(context, this.alGet('length')) >>> 0;
if (length === 0) {
return '';
}
const result = [];
for (let i = 0; i < length; i++) {
const item = this.alGet(i);
result[i] = item === null || item === undefined ? '' : alCoerceString(context, item);
}
return result.join(separator);
}
public pop(): any {
if (this instanceof AVM1ArrayNative) {
// Faster case for native array implementation
const arr = alEnsureType<AVM1ArrayNative>(this, AVM1ArrayNative).value;
return arr.pop();
}
const length = alToInt32(this.context, this.alGet('length')) >>> 0;
if (length === 0) {
return undefined;
}
const i = length - 1;
const result = this.alGet(i);
this.alDeleteProperty(i);
this.alPut('length', i);
return result;
}
public push(...items: any[]): number {
if (this instanceof AVM1ArrayNative) {
// Faster case for native array implementation
const arr = alEnsureType<AVM1ArrayNative>(this, AVM1ArrayNative).value;
return Array.prototype.push.apply(arr, items);
}
let length = alToInt32(this.context, this.alGet('length')) >>> 0;
for (let i = 0; i < items.length; i++) {
this.alPut(length, items[i]);
length++; // TODO check overflow
}
this.alPut('length', length);
return length;
}
public reverse() {
const items: any[] = [];
const len: number = this.alGet('length');
let _i: number = 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 (<any> this).value;
}
public shift(): any {
if (this instanceof AVM1ArrayNative) {
// Faster case for native array implementation
const arr = alEnsureType<AVM1ArrayNative>(this, AVM1ArrayNative).value;
return arr.shift();
}
const length = alToInt32(this.context, this.alGet('length')) >>> 0;
if (length === 0) {
return undefined;
}
const result = this.alGet(0);
for (let 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;
}
public slice(start: number, end?: number): AVM1Object {
start = alToInteger(this.context, start);
end = end !== undefined ? alToInteger(this.context, end) : undefined;
if (this instanceof AVM1ArrayNative) {
// Faster case for native array implementation
const arr = alEnsureType<AVM1ArrayNative>(this, AVM1ArrayNative).value;
return new AVM1ArrayNative(this.context, arr.slice(start, end));
}
const a = [];
const 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 (let i = start, j = 0; i < end; i++, j++) {
if (this.alHasProperty(i)) {
a[j] = this.alGet(i);
}
}
return new AVM1ArrayNative(this.context, a);
}
public splice(start: number, deleteCount: number, ...items: any[]): AVM1Object {
start = alToInteger(this.context, start);
const 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
const arr = alEnsureType<AVM1ArrayNative>(this, AVM1ArrayNative).value;
return new AVM1ArrayNative(this.context,
Array.prototype.splice.apply(arr, [start, deleteCount].concat(items)));
}
const a = [];
for (let i = 0; i < deleteCount; i++) {
if (this.alHasProperty(start + i)) {
a[i] = this.alGet(start + i);
}
}
const delta = items.length - deleteCount;
if (delta < 0) {
for (let i = start - delta; i < length; i++) {
if (this.alHasProperty(i)) {
this.alPut(i + delta, this.alGet(i));
} else {
this.alDeleteProperty(i + delta);
}
}
for (let i = delta; i < 0; i++) {
this.alDeleteProperty(length + i);
}
} else if (delta > 0) {
// TODO check overflow
for (let i = length - 1; i >= start + delta; i--) {
if (this.alHasProperty(i)) {
this.alPut(i + delta, this.alGet(i));
} else {
this.alDeleteProperty(i + delta);
}
}
}
for (let i = 0; i < items.length; i++) {
this.alPut(start + i, items[i]);
}
this.alPut('length', length + delta);
return new AVM1ArrayNative(this.context, a);
}
public sort(comparefn?: AVM1Function): AVM1Object {
return sortArray(this, comparefn);
}
public sortOn(fieldNames: AVM1Object, options: any): AVM1Object {
const context = this.context;
// The field names and options we'll end up using.
let fieldNamesList: string[] = [];
let optionsList: number[] = [];
if (alIsString(context, fieldNames)) {
fieldNamesList = [alToString(context, fieldNames)];
optionsList = [alToInt32(context, options)];
} else if (alIsArray(context, fieldNames)) {
fieldNamesList = [];
optionsList = [];
let optionsArray: AVM1Object = alIsArray(context, options) ? options : null;
const length = alToInteger(context, fieldNames.alGet('length'));
if (optionsArray) {
// checking in length of fieldNames == options
const optionsLength = alToInteger(context, optionsArray.alGet('length'));
if (length !== optionsLength) {
optionsArray = null;
}
}
for (let i = 0; i < length; 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
const optionsVal: number = optionsList[0];
release || Debug.assertNotImplemented(!(optionsVal & AVM1ArraySortOnOptions.UNIQUESORT), 'UNIQUESORT');
release || Debug.assertNotImplemented(
!(optionsVal & AVM1ArraySortOnOptions.RETURNINDEXEDARRAY), 'RETURNINDEXEDARRAY');
const comparer = (a, b) => {
while (a instanceof AVM1ArrayNative) {
a = alEnsureType<AVM1ArrayNative>(a, AVM1ArrayNative).value;
if (a && a.length > 0)
a = a[0];
}
while (b instanceof AVM1ArrayNative) {
b = alEnsureType<AVM1ArrayNative>(b, AVM1ArrayNative).value;
if (b && b.length > 0)
b = b[0];
}
const aObj = alToObject(context, a);
const bObj = alToObject(context, b);
for (let i = 0; i < fieldNamesList.length; i++) {
const aField = aObj.alGet(fieldNamesList[i]);
const bField = bObj.alGet(fieldNamesList[i]);
let result;
if (optionsList[i] & AVM1ArraySortOnOptions.NUMERIC) {
const aNum = alToNumber(context, aField);
const bNum = alToNumber(context, bField);
result = aNum < bNum ? -1 : aNum > bNum ? +1 : 0;
} else {
let aStr = alToString(context, aField);
let 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;
};
const arr = alEnsureType<AVM1ArrayNative>(this, AVM1ArrayNative).value;
arr.sort(comparer);
// Strange, the documentation said to do not return anything.
return this;
}
public unshift(...items: any[]): number {
if (this instanceof AVM1ArrayNative) {
// Faster case for native array implementation
const arr = alEnsureType<AVM1ArrayNative>(this, AVM1ArrayNative).value;
return Array.prototype.unshift.apply(arr, items);
}
let length = alToInt32(this.context, this.alGet('length')) >>> 0;
const insertCount = items.length;
// TODO check overflow
for (let i = length - 1; i >= 0; i--) {
if (this.alHasProperty(i)) {
this.alPut(i + insertCount, this.alGet(i));
} else {
this.alDeleteProperty(i + insertCount);
}
}
for (let i = 0; i < items.length; i++) {
this.alPut(i, items[i]);
}
length += insertCount;
this.alPut('length', length); // ActionScript does not do that?
return length;
}
}
export class AVM1ArrayFunction extends AVM1Function {
public constructor(context: IAVM1Context) {
super(context);
this.alPrototype = context.builtins.Function.alGetPrototypeProperty();
const proto = new AVM1ArrayPrototype(context);
alDefineObjectProperties(this, {
prototype: {
value: proto
}
});
alDefineObjectProperties(this, {
NUMERIC: {
value: AVM1ArraySortOnOptions.NUMERIC
}
});
alDefineObjectProperties(this, {
DESCENDING: {
value: AVM1ArraySortOnOptions.DESCENDING
}
});
}
public alConstruct(args?: any[]): AVM1Object {
if (!args) {
return new AVM1ArrayNative(this.context, []);
}
if (args.length === 1 && typeof args[0] === 'number') {
const 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);
}
public alCall(thisArg: any, args?: any[]): any {
return this.alConstruct.apply(this, args);
}
}
// Math natives
class AVM1MathObject extends AVM1Object {
public constructor(context: IAVM1Context) {
super(context);
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
});
}
public abs(x: number): number {
return Math.abs(alToNumber(this.context, x));
}
public acos(x: number): number {
return Math.acos(alToNumber(this.context, x));
}
public asin(x: number): number {
return Math.asin(alToNumber(this.context, x));
}
public atan(x: number): number {
return Math.atan(alToNumber(this.context, x));
}
public atan2(y: number, x: number): number {
return Math.atan2(alToNumber(this.context, y), alToNumber(this.context, x));
}
public ceil(x: number): number {
return Math.ceil(alToNumber(this.context, x));
}
public cos(x: number): number {
return Math.cos(alToNumber(this.context, x));
}
public exp(x: number): number {
return Math.exp(alToNumber(this.context, x));
}
public floor(x: number): number {
return Math.floor(alToNumber(this.context, x));
}
public log(x: number): number {
return Math.log(alToNumber(this.context, x));
}
public max(...values: number[]): number {
values = values.map((x) => alToNumber(this.context, x));
return Math.max.apply(null, values);
}
public min(...values: number[]): number {
values = values.map((x) => alToNumber(this.context, x));
return Math.min.apply(null, values);
}
public pow(x: number, y: number): number {
return Math.pow(alToNumber(this.context, x), alToNumber(this.context, y));
}
public random(): number {
if (AVM1Globals.randomProvider) {
return AVM1Globals.randomProvider.random();
}
return Math.random();
}
public round(x: number): number {
return Math.round(alToNumber(this.context, x));
}
public sin(x: number): number {
return Math.sin(alToNumber(this.context, x));
}
public sqrt(x: number): number {
return Math.sqrt(alToNumber(this.context, x));
}
public tan(x: number): number {
return Math.tan(alToNumber(this.context, x));
}
}
// Date natives
class AVM1DateNative extends AVM1Object {
public value: Date;
public constructor(context: IAVM1Context, value: Date) {
super(context);
this.alPrototype = context.builtins.Date.alGetPrototypeProperty();
this.alSetOwnConstructorProperty(context.builtins.Date);
this.value = value;
}
public alDefaultValue(hint?: AVM1DefaultValueHint): any {
if (hint !== undefined) {
return super.alDefaultValue(hint);
}
if (this.context.swfVersion >= 6) {
return super.alDefaultValue(AVM1DefaultValueHint.STRING);
} else {
return super.alDefaultValue(AVM1DefaultValueHint.NUMBER);
}
}
}
// TODO implement all the Date class and its prototype natives
class AVM1DatePrototype extends AVM1Object {
public constructor(context: IAVM1Context) {
super(context);
this.alPrototype = context.builtins.Object.alGetPrototypeProperty();
alDefineObjectProperties(this, {
constructor: {
value: context.builtins.Date,
writable: true
},
valueOf: {
value: this._valueOf,
writable: true
},
toString: {
value: this._toString,
writable: true
},
toLocaleString: {
value: this._toLocaleString,
writable: true
},
toDateString: {
value: this.toDateString,
writable: true
},
toTimeString: {
value: this.toTimeString,
writable: true
},
toLocaleDateString: {
value: this.toLocaleDateString,
writable: true
},
toLocaleTimeString: {
value: this.toLocaleTimeString,
writable: true
},
getTime: {
value: this.getTime,
writable: true
},
getFullYear: {
value: this.getFullYear,
writable: true
},
getUTCFullYear: {
value: this.getUTCFullYear,
writable: true
},
getMonth: {
value: this.getMonth,
writable: true
},
getUTCMonth: {
value: this.getUTCMonth,
writable: true
},
getDate: {
value: this.getDate,
writable: true
},
getUTCDate: {
value: this.getUTCDate,
writable: true
},
getDay: {
value: this.getDay,
writable: true
},
getUTCDay: {
value: this.getUTCDay,
writable: true
},
getHours: {
value: this.getHours,
writable: true
},
getUTCHours: {
value: this.getUTCHours,
writable: true
},
getMinutes: {
value: this.getMinutes,
writable: true
},
getUTCMinutes: {
value: this.getUTCMinutes,
writable: true
},
getSeconds: {
value: this.getSeconds,
writable: true
},
getUTCSeconds: {
value: this.getUTCSeconds,
writable: true
},
getMilliseconds: {
value: this.getMilliseconds,
writable: true
},
getUTCMilliseconds: {
value: this.getUTCMilliseconds,
writable: true
},
getTimezoneOffset: {
value: this.getTimezoneOffset,
writable: true
},
setTime: {
value: this.setTime,
writable: true
},
setMilliseconds: {
value: this.setMilliseconds,
writable: true
},
setUTCMilliseconds: {
value: this.setUTCMilliseconds,
writable: true
},
setSeconds: {
value: this.setSeconds,
writable: true
},
setUTCSeconds: {
value: this.setUTCSeconds,
writable: true
},
setMinutes: {
value: this.setMinutes,
writable: true
},
setUTCMinutes: {
value: this.setUTCMinutes,
writable: true
},
setHours: {
value: this.setHours,
writable: true
},
setUTCHours: {
value: this.setUTCHours,
writable: true
},
setDate: {
value: this.setDate,
writable: true
},
setUTCDate: {
value: this.setUTCDate,
writable: true
},
setMonth: {
value: this.setMonth,
writable: true
},
setUTCMonth: {
value: this.setUTCMonth,
writable: true
},
setFullYear: {
value: this.setFullYear,
writable: true
},
setUTCFullYear: {
value: this.setUTCFullYear,
writable: true
},
toUTCString: {
value: this.toUTCString,
writable: true
}
});
}
public _valueOf() {
const native = alEnsureType<AVM1DateNative>(this, AVM1DateNative);
return native.value.valueOf();
}
public _toString() {
const native = alEnsureType<AVM1DateNative>(this, AVM1DateNative);
return native.value.toString();
}
public _toLocaleString(): string {
const native = alEnsureType<AVM1DateNative>(this, AVM1DateNative);
return native.value.toLocaleString();
}
public toDateString(): string {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.toDateString();
}
public toTimeString(): string {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.toTimeString();
}
public toLocaleDateString(): string {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.toLocaleDateString();
}
public toLocaleTimeString(): string {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.toLocaleTimeString();
}
public getTime(): number {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.getTime();
}
public getFullYear(): number {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.getFullYear();
}
public getUTCFullYear(): number {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.getUTCFullYear();
}
public getMonth(): number {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.getMonth();
}
public getUTCMonth(): number {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.getUTCMonth();
}
public getDate(): number {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.getDate();
}
public getUTCDate(): number {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.getUTCDate();
}
public getDay(): number {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.getDay();
}
public getUTCDay(): number {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.getUTCDay();
}
public getHours(): number {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.getHours();
}
public getUTCHours(): number {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.getUTCHours();
}
public getMinutes(): number {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.getMinutes();
}
public getUTCMinutes(): number {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.getUTCMinutes();
}
public getSeconds(): number {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.getSeconds();
}
public getUTCSeconds(): number {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.getUTCSeconds();
}
public getMilliseconds(): number {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.getMilliseconds();
}
public getUTCMilliseconds(): number {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.getUTCMilliseconds();
}
public getTimezoneOffset(): number {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.getTimezoneOffset();
}
public setTime(time: number): number {
time = alToNumber(this.context, time);
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setTime(time);
}
public setMilliseconds(ms: number): number {
ms = alToNumber(this.context, ms);
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setMilliseconds(ms);
}
public setUTCMilliseconds(ms: number): number {
ms = alToNumber(this.context, ms);
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setUTCMilliseconds(ms) ;
}
public setSeconds(sec: number, ms?: number): number {
sec = alToNumber(this.context, sec);
if (arguments.length <= 1) {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setSeconds(sec);
} else {
ms = alToNumber(this.context, ms);
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setSeconds(sec, ms);
}
}
public setUTCSeconds(sec: number, ms?: number): number {
sec = alToNumber(this.context, sec);
if (arguments.length <= 1) {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setUTCSeconds(sec);
} else {
ms = alToNumber(this.context, ms);
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setUTCSeconds(sec, ms);
}
}
public setMinutes(min: number, sec?: number, ms?: number): number {
min = alToNumber(this.context, min);
if (arguments.length <= 1) {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setMinutes(min);
} else {
sec = alToNumber(this.context, sec);
if (arguments.length <= 2) {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setMinutes(min, sec);
} else {
ms = alToNumber(this.context, ms);
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setMinutes(min, sec, ms);
}
}
}
public setUTCMinutes(min: number, sec?: number, ms?: number): number {
min = alToNumber(this.context, min);
if (arguments.length <= 1) {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setUTCMinutes(min);
} else {
sec = alToNumber(this.context, sec);
if (arguments.length <= 2) {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setUTCMinutes(min, sec);
} else {
ms = alToNumber(this.context, ms);
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setUTCMinutes(min, sec, ms);
}
}
}
public setHours(hour: number, min?: number, sec?: number, ms?: number): number {
hour = alToNumber(this.context, hour);
if (arguments.length <= 1) {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setHours(hour);
} else {
min = alToNumber(this.context, min);
if (arguments.length <= 2) {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setHours(hour, min);
} else {
sec = alToNumber(this.context, sec);
if (arguments.length <= 3) {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setHours(hour, min, sec);
} else {
ms = alToNumber(this.context, ms);
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setHours(hour, min, sec, ms);
}
}
}
}
public setUTCHours(hour: number, min?: number, sec?: number, ms?: number): number {
hour = alToNumber(this.context, hour);
if (arguments.length <= 1) {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setUTCHours(hour);
} else {
min = alToNumber(this.context, min);
if (arguments.length <= 2) {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setUTCHours(hour, min);
} else {
sec = alToNumber(this.context, sec);
if (arguments.length <= 3) {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setUTCHours(hour, min, sec);
} else {
ms = alToNumber(this.context, ms);
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setUTCHours(hour, min, sec, ms);
}
}
}
}
public setDate(date: number): number {
date = alToNumber(this.context, date);
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setDate(date) ;
}
public setUTCDate(date: number): number {
date = alToNumber(this.context, date);
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setUTCDate(date) ;
}
public setMonth(month: number, date?: number): number {
month = alToNumber(this.context, month);
if (arguments.length <= 1) {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setMonth(month);
} else {
date = alToNumber(this.context, date);
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setMonth(month, date);
}
}
public setUTCMonth(month: number, date?: number): number {
month = alToNumber(this.context, month);
if (arguments.length <= 1) {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setUTCMonth(month);
} else {
date = alToNumber(this.context, date);
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setUTCMonth(month, date);
}
}
public setFullYear(year: number, month?: number, date?: number): number {
year = alToNumber(this.context, year);
if (arguments.length <= 1) {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setFullYear(year);
} else {
month = alToNumber(this.context, month);
if (arguments.length <= 2) {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setFullYear(year, month);
} else {
date = alToNumber(this.context, date);
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setFullYear(year, month, date);
}
}
}
public setUTCFullYear(year: number, month?: number, date?: number): number {
year = alToNumber(this.context, year);
if (arguments.length <= 1) {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setUTCFullYear(year);
} else {
month = alToNumber(this.context, month);
if (arguments.length <= 2) {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setUTCFullYear(year, month);
} else {
date = alToNumber(this.context, date);
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.setUTCFullYear(year, month, date);
}
}
}
public toUTCString(): string {
return alEnsureType<AVM1DateNative>(this, AVM1DateNative).value.toUTCString();
}
}
class AVM1DateFunction extends AVM1Function {
public constructor(context: IAVM1Context) {
super(context);
this.alPrototype = context.builtins.Function.alGetPrototypeProperty();
const proto = new AVM1DatePrototype(context);
alDefineObjectProperties(this, {
prototype: {
value: proto
},
UTC: {
value: this._UTC,
writable: true
}
});
}
public alConstruct(args?: any[]): AVM1Object {
const context = this.context;
let value: Date;
switch (args.len