| Index: src/js/json.js
|
| diff --git a/src/js/json.js b/src/js/json.js
|
| index a7b692000870b1722be6d914383559675eed23f1..eee56afae05c20d8e88e20e23f8e59234520f6e9 100644
|
| --- a/src/js/json.js
|
| +++ b/src/js/json.js
|
| @@ -15,7 +15,26 @@
|
| var GlobalJSON = global.JSON;
|
| var GlobalSet = global.Set;
|
| var InternalArray = utils.InternalArray;
|
| +var MakeTypeError;
|
| +var MaxSimple;
|
| +var MinSimple;
|
| +var ObjectHasOwnProperty;
|
| +var Stack;
|
| +var StackHas;
|
| +var StackPop;
|
| +var StackPush;
|
| var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
|
| +
|
| +utils.Import(function(from) {
|
| + MakeTypeError = from.MakeTypeError;
|
| + MaxSimple = from.MaxSimple;
|
| + MinSimple = from.MinSimple;
|
| + ObjectHasOwnProperty = from.ObjectHasOwnProperty;
|
| + Stack = from.Stack;
|
| + StackHas = from.StackHas;
|
| + StackPop = from.StackPop;
|
| + StackPush = from.StackPush;
|
| +});
|
|
|
| // -------------------------------------------------------------------
|
|
|
| @@ -65,6 +84,137 @@
|
| }
|
| }
|
|
|
| +
|
| +function SerializeArray(value, replacer, stack, indent, gap) {
|
| + if (StackHas(stack, value)) throw MakeTypeError(kCircularStructure);
|
| + StackPush(stack, value);
|
| + var stepback = indent;
|
| + indent += gap;
|
| + var partial = new InternalArray();
|
| + var len = TO_LENGTH(value.length);
|
| + for (var i = 0; i < len; i++) {
|
| + var strP = JSONSerialize(%_NumberToString(i), value, replacer, stack,
|
| + indent, gap);
|
| + if (IS_UNDEFINED(strP)) {
|
| + strP = "null";
|
| + }
|
| + partial.push(strP);
|
| + }
|
| + var final;
|
| + if (gap == "") {
|
| + final = "[" + partial.join(",") + "]";
|
| + } else if (partial.length > 0) {
|
| + var separator = ",\n" + indent;
|
| + final = "[\n" + indent + partial.join(separator) + "\n" +
|
| + stepback + "]";
|
| + } else {
|
| + final = "[]";
|
| + }
|
| + StackPop(stack);
|
| + return final;
|
| +}
|
| +
|
| +
|
| +function SerializeObject(value, replacer, stack, indent, gap) {
|
| + if (StackHas(stack, value)) throw MakeTypeError(kCircularStructure);
|
| + StackPush(stack, value);
|
| + var stepback = indent;
|
| + indent += gap;
|
| + var partial = new InternalArray();
|
| + var keys = %object_keys(value);
|
| + for (var i = 0; i < keys.length; i++) {
|
| + var p = keys[i];
|
| + var strP = JSONSerialize(p, value, replacer, stack, indent, gap);
|
| + if (!IS_UNDEFINED(strP)) {
|
| + var member = %QuoteJSONString(p) + ":";
|
| + if (gap != "") member += " ";
|
| + member += strP;
|
| + partial.push(member);
|
| + }
|
| + }
|
| + var final;
|
| + if (gap == "") {
|
| + final = "{" + partial.join(",") + "}";
|
| + } else if (partial.length > 0) {
|
| + var separator = ",\n" + indent;
|
| + final = "{\n" + indent + partial.join(separator) + "\n" +
|
| + stepback + "}";
|
| + } else {
|
| + final = "{}";
|
| + }
|
| + StackPop(stack);
|
| + return final;
|
| +}
|
| +
|
| +
|
| +function JSONSerialize(key, holder, replacer, stack, indent, gap) {
|
| + var value = holder[key];
|
| + if (IS_RECEIVER(value)) {
|
| + var toJSON = value.toJSON;
|
| + if (IS_CALLABLE(toJSON)) {
|
| + value = %_Call(toJSON, value, key);
|
| + }
|
| + }
|
| + if (IS_CALLABLE(replacer)) {
|
| + value = %_Call(replacer, holder, key, value);
|
| + }
|
| + if (IS_STRING(value)) {
|
| + return %QuoteJSONString(value);
|
| + } else if (IS_NUMBER(value)) {
|
| + return JSON_NUMBER_TO_STRING(value);
|
| + } else if (IS_BOOLEAN(value)) {
|
| + return value ? "true" : "false";
|
| + } else if (IS_NULL(value)) {
|
| + return "null";
|
| + } else if (IS_RECEIVER(value) && !IS_CALLABLE(value)) {
|
| + // Non-callable object. If it's a primitive wrapper, it must be unwrapped.
|
| + if (%is_arraylike(value)) {
|
| + return SerializeArray(value, replacer, stack, indent, gap);
|
| + } else if (IS_NUMBER_WRAPPER(value)) {
|
| + value = TO_NUMBER(value);
|
| + return JSON_NUMBER_TO_STRING(value);
|
| + } else if (IS_STRING_WRAPPER(value)) {
|
| + return %QuoteJSONString(TO_STRING(value));
|
| + } else if (IS_BOOLEAN_WRAPPER(value)) {
|
| + return %_ValueOf(value) ? "true" : "false";
|
| + } else {
|
| + return SerializeObject(value, replacer, stack, indent, gap);
|
| + }
|
| + }
|
| + // Undefined or a callable object.
|
| + return UNDEFINED;
|
| +}
|
| +
|
| +
|
| +function JSONStringify(value, replacer, space) {
|
| + if (arguments.length === 1) return %BasicJSONStringify(value, UNDEFINED, "");
|
| + if (!IS_CALLABLE(replacer)) {
|
| + return %BasicJSONStringify(value, replacer, space);
|
| + }
|
| + if (IS_OBJECT(space)) {
|
| + // Unwrap 'space' if it is wrapped
|
| + if (IS_NUMBER_WRAPPER(space)) {
|
| + space = TO_NUMBER(space);
|
| + } else if (IS_STRING_WRAPPER(space)) {
|
| + space = TO_STRING(space);
|
| + }
|
| + }
|
| + var gap;
|
| + if (IS_NUMBER(space)) {
|
| + space = MaxSimple(0, MinSimple(TO_INTEGER(space), 10));
|
| + gap = %_SubString(" ", 0, space);
|
| + } else if (IS_STRING(space)) {
|
| + if (space.length > 10) {
|
| + gap = %_SubString(space, 0, 10);
|
| + } else {
|
| + gap = space;
|
| + }
|
| + } else {
|
| + gap = "";
|
| + }
|
| + return JSONSerialize('', {'': value}, replacer, new Stack(), "", gap);
|
| +}
|
| +
|
| // -------------------------------------------------------------------
|
|
|
| %AddNamedProperty(GlobalJSON, toStringTagSymbol, "JSON", READ_ONLY | DONT_ENUM);
|
| @@ -72,6 +222,7 @@
|
| // Set up non-enumerable properties of the JSON object.
|
| utils.InstallFunctions(GlobalJSON, DONT_ENUM, [
|
| "parse", JSONParse,
|
| + "stringify", JSONStringify
|
| ]);
|
|
|
| // -------------------------------------------------------------------
|
|
|