1 // Copyright 2009 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 (function(global, utils) {
9 %CheckIsBootstrapping();
11 // -------------------------------------------------------------------
14 var GlobalJSON = global.JSON;
15 var InternalArray = utils.InternalArray;
18 var ObjectHasOwnProperty;
20 var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
22 utils.Import(function(from) {
23 MathMax = from.MathMax;
24 MathMin = from.MathMin;
25 ObjectHasOwnProperty = from.ObjectHasOwnProperty;
26 ToNumber = from.ToNumber;
29 // -------------------------------------------------------------------
31 function Revive(holder, name, reviver) {
32 var val = holder[name];
35 var length = val.length;
36 for (var i = 0; i < length; i++) {
37 var newElement = Revive(val, %_NumberToString(i), reviver);
42 if (HAS_OWN_PROPERTY(val, p)) {
43 var newElement = Revive(val, p, reviver);
44 if (IS_UNDEFINED(newElement)) {
53 return %_Call(reviver, holder, name, val);
57 function JSONParse(text, reviver) {
58 var unfiltered = %ParseJson(text);
59 if (IS_CALLABLE(reviver)) {
60 return Revive({'': unfiltered}, '', reviver);
67 function SerializeArray(value, replacer, stack, indent, gap) {
68 if (!%PushIfAbsent(stack, value)) throw MakeTypeError(kCircularStructure);
69 var stepback = indent;
71 var partial = new InternalArray();
72 var len = value.length;
73 for (var i = 0; i < len; i++) {
74 var strP = JSONSerialize(%_NumberToString(i), value, replacer, stack,
76 if (IS_UNDEFINED(strP)) {
83 final = "[" + partial.join(",") + "]";
84 } else if (partial.length > 0) {
85 var separator = ",\n" + indent;
86 final = "[\n" + indent + partial.join(separator) + "\n" +
96 function SerializeObject(value, replacer, stack, indent, gap) {
97 if (!%PushIfAbsent(stack, value)) throw MakeTypeError(kCircularStructure);
98 var stepback = indent;
100 var partial = new InternalArray();
101 if (IS_ARRAY(replacer)) {
102 var length = replacer.length;
103 for (var i = 0; i < length; i++) {
104 if (HAS_OWN_PROPERTY(replacer, i)) {
106 var strP = JSONSerialize(p, value, replacer, stack, indent, gap);
107 if (!IS_UNDEFINED(strP)) {
108 var member = %QuoteJSONString(p) + ":";
109 if (gap != "") member += " ";
111 partial.push(member);
116 for (var p in value) {
117 if (HAS_OWN_PROPERTY(value, p)) {
118 var strP = JSONSerialize(p, value, replacer, stack, indent, gap);
119 if (!IS_UNDEFINED(strP)) {
120 var member = %QuoteJSONString(p) + ":";
121 if (gap != "") member += " ";
123 partial.push(member);
130 final = "{" + partial.join(",") + "}";
131 } else if (partial.length > 0) {
132 var separator = ",\n" + indent;
133 final = "{\n" + indent + partial.join(separator) + "\n" +
143 function JSONSerialize(key, holder, replacer, stack, indent, gap) {
144 var value = holder[key];
145 if (IS_SPEC_OBJECT(value)) {
146 var toJSON = value.toJSON;
147 if (IS_CALLABLE(toJSON)) {
148 value = %_Call(toJSON, value, key);
151 if (IS_CALLABLE(replacer)) {
152 value = %_Call(replacer, holder, key, value);
154 if (IS_STRING(value)) {
155 return %QuoteJSONString(value);
156 } else if (IS_NUMBER(value)) {
157 return JSON_NUMBER_TO_STRING(value);
158 } else if (IS_BOOLEAN(value)) {
159 return value ? "true" : "false";
160 } else if (IS_NULL(value)) {
162 } else if (IS_SPEC_OBJECT(value) && !IS_CALLABLE(value)) {
163 // Non-callable object. If it's a primitive wrapper, it must be unwrapped.
164 if (IS_ARRAY(value)) {
165 return SerializeArray(value, replacer, stack, indent, gap);
166 } else if (IS_NUMBER_WRAPPER(value)) {
167 value = ToNumber(value);
168 return JSON_NUMBER_TO_STRING(value);
169 } else if (IS_STRING_WRAPPER(value)) {
170 return %QuoteJSONString(TO_STRING(value));
171 } else if (IS_BOOLEAN_WRAPPER(value)) {
172 return %_ValueOf(value) ? "true" : "false";
174 return SerializeObject(value, replacer, stack, indent, gap);
177 // Undefined or a callable object.
182 function JSONStringify(value, replacer, space) {
183 if (%_ArgumentsLength() == 1) {
184 return %BasicJSONStringify(value);
186 if (IS_ARRAY(replacer)) {
187 // Deduplicate replacer array items.
188 var property_list = new InternalArray();
189 var seen_properties = { __proto__: null };
190 var length = replacer.length;
191 for (var i = 0; i < length; i++) {
196 } else if (IS_NUMBER(v)) {
197 item = %_NumberToString(v);
198 } else if (IS_STRING_WRAPPER(v) || IS_NUMBER_WRAPPER(v)) {
203 if (!seen_properties[item]) {
204 property_list.push(item);
205 seen_properties[item] = true;
208 replacer = property_list;
210 if (IS_OBJECT(space)) {
211 // Unwrap 'space' if it is wrapped
212 if (IS_NUMBER_WRAPPER(space)) {
213 space = ToNumber(space);
214 } else if (IS_STRING_WRAPPER(space)) {
215 space = TO_STRING(space);
219 if (IS_NUMBER(space)) {
220 space = MathMax(0, MathMin($toInteger(space), 10));
221 gap = %_SubString(" ", 0, space);
222 } else if (IS_STRING(space)) {
223 if (space.length > 10) {
224 gap = %_SubString(space, 0, 10);
231 return JSONSerialize('', {'': value}, replacer, new InternalArray(), "", gap);
234 // -------------------------------------------------------------------
236 %AddNamedProperty(GlobalJSON, toStringTagSymbol, "JSON", READ_ONLY | DONT_ENUM);
238 // Set up non-enumerable properties of the JSON object.
239 utils.InstallFunctions(GlobalJSON, DONT_ENUM, [
241 "stringify", JSONStringify
244 // -------------------------------------------------------------------
247 function JsonSerializeAdapter(key, object) {
249 holder[key] = object;
250 // No need to pass the actual holder since there is no replacer function.
251 return JSONSerialize(key, holder, UNDEFINED, new InternalArray(), "", "");
254 %InstallToContext(["json_serialize_adapter", JsonSerializeAdapter]);