UNPKG

@awayfl/avm2

Version:

Virtual machine for executing AS3 code

106 lines (92 loc) 3.4 kB
import { ASObject } from './ASObject'; import { axCoerceString } from '../run/axCoerceString'; import { AXFunction } from '../run/AXFunction'; import { axIsCallable } from '../run/axIsCallable'; import { Errors } from '../errors'; import { transformJSValueToAS } from './transformJSValueToAS'; import { transformASValueToJS } from './transformASValueToJS'; import { walk } from './walk'; export class ASJSON extends ASObject { static parse(text: string, reviver: AXFunction = null): any { text = axCoerceString(text); if (reviver !== null && !axIsCallable(reviver)) { this.sec.throwError('TypeError', Errors.CheckTypeFailedError, reviver, 'Function'); } if (text === null) { this.sec.throwError('SyntaxError', Errors.JSONInvalidParseInput); } let unfiltered: Object; try { unfiltered = transformJSValueToAS(this.sec, JSON.parse(text), true); } catch (e) { this.sec.throwError('SyntaxError', Errors.JSONInvalidParseInput); } if (reviver === null) { return unfiltered; } return walk(this.sec, { '': unfiltered }, '', reviver.value); } static stringify(value: any, replacer = null, space = null): string { // We deliberately deviate from ECMA-262 and throw on // invalid replacer parameter. const sec = typeof replacer === 'object' ? replacer?.sec : null; if (replacer !== null) { if (!sec || !(sec.AXFunction.axIsType(replacer) || sec.AXArray.axIsType(replacer))) { this.sec.throwError('TypeError', Errors.JSONInvalidReplacer); } } let gap; if (typeof space === 'string') { gap = space.length > 10 ? space.substring(0, 10) : space; } else if (typeof space === 'number') { gap = ' '.substring(0, Math.min(10, space | 0)); } else { // We follow ECMA-262 and silently ignore invalid space parameter. gap = ''; } if (replacer === null) { return this.stringifySpecializedToString(value, null, null, gap); } else if (sec.AXArray.axIsType(replacer)) { return this.stringifySpecializedToString(value, this.computePropertyList(replacer.value), null, gap); } else { // replacer is Function return this.stringifySpecializedToString(value, null, replacer.value, gap); } } // ECMA-262 5th ed, section 15.12.3 stringify, step 4.b private static computePropertyList(r: any[]): string[] { const propertyList = []; const alreadyAdded = Object.create(null); for (let i = 0, length = r.length; i < length; i++) { if (!r.hasOwnProperty(<any>i)) { continue; } const v = r[i]; let item: string = null; if (typeof v === 'string') { item = v; } else if (typeof v === 'number') { item = axCoerceString(v); } if (item !== null && !alreadyAdded[item]) { alreadyAdded[item] = true; propertyList.push(item); } } return propertyList; } private static stringifySpecializedToString( value: Object, replacerArray: any [], replacerFunction: (key: string, value: any) => any, gap: string): string { // In AS3 |JSON.stringify(undefined)| returns "null", while JS returns |undefined|. // TODO: Is there anything to be done in case of a |replacerFunction| function? if (value === undefined) { return 'null'; } const jsValue = transformASValueToJS(this.sec, value, true, replacerFunction); try { return JSON.stringify(jsValue, null, gap); } catch (e) { this.sec.throwError('TypeError', Errors.JSONCyclicStructure); } } }