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;
21 var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
23 utils.Import(function(from) {
24 MathMax = from.MathMax;
25 MathMin = from.MathMin;
26 ObjectHasOwnProperty = from.ObjectHasOwnProperty;
27 ToNumber = from.ToNumber;
28 ToString = from.ToString;
31 // -------------------------------------------------------------------
33 function Revive(holder, name, reviver) {
34 var val = holder[name];
37 var length = val.length;
38 for (var i = 0; i < length; i++) {
39 var newElement = Revive(val, %_NumberToString(i), reviver);
44 if (HAS_OWN_PROPERTY(val, p)) {
45 var newElement = Revive(val, p, reviver);
46 if (IS_UNDEFINED(newElement)) {
55 return %_Call(reviver, holder, name, val);
59 function JSONParse(text, reviver) {
60 var unfiltered = %ParseJson(TO_STRING_INLINE(text));
61 if (IS_CALLABLE(reviver)) {
62 return Revive({'': unfiltered}, '', reviver);
69 function SerializeArray(value, replacer, stack, indent, gap) {
70 if (!%PushIfAbsent(stack, value)) throw MakeTypeError(kCircularStructure);
71 var stepback = indent;
73 var partial = new InternalArray();
74 var len = value.length;
75 for (var i = 0; i < len; i++) {
76 var strP = JSONSerialize(%_NumberToString(i), value, replacer, stack,
78 if (IS_UNDEFINED(strP)) {
85 final = "[" + partial.join(",") + "]";
86 } else if (partial.length > 0) {
87 var separator = ",\n" + indent;
88 final = "[\n" + indent + partial.join(separator) + "\n" +
98 function SerializeObject(value, replacer, stack, indent, gap) {
99 if (!%PushIfAbsent(stack, value)) throw MakeTypeError(kCircularStructure);
100 var stepback = indent;
102 var partial = new InternalArray();
103 if (IS_ARRAY(replacer)) {
104 var length = replacer.length;
105 for (var i = 0; i < length; i++) {
106 if (HAS_OWN_PROPERTY(replacer, i)) {
108 var strP = JSONSerialize(p, value, replacer, stack, indent, gap);
109 if (!IS_UNDEFINED(strP)) {
110 var member = %QuoteJSONString(p) + ":";
111 if (gap != "") member += " ";
113 partial.push(member);
118 for (var p in value) {
119 if (HAS_OWN_PROPERTY(value, p)) {
120 var strP = JSONSerialize(p, value, replacer, stack, indent, gap);
121 if (!IS_UNDEFINED(strP)) {
122 var member = %QuoteJSONString(p) + ":";
123 if (gap != "") member += " ";
125 partial.push(member);
132 final = "{" + partial.join(",") + "}";
133 } else if (partial.length > 0) {
134 var separator = ",\n" + indent;
135 final = "{\n" + indent + partial.join(separator) + "\n" +
145 function JSONSerialize(key, holder, replacer, stack, indent, gap) {
146 var value = holder[key];
147 if (IS_SPEC_OBJECT(value)) {
148 var toJSON = value.toJSON;
149 if (IS_CALLABLE(toJSON)) {
150 value = %_Call(toJSON, value, key);
153 if (IS_CALLABLE(replacer)) {
154 value = %_Call(replacer, holder, key, value);
156 if (IS_STRING(value)) {
157 return %QuoteJSONString(value);
158 } else if (IS_NUMBER(value)) {
159 return JSON_NUMBER_TO_STRING(value);
160 } else if (IS_BOOLEAN(value)) {
161 return value ? "true" : "false";
162 } else if (IS_NULL(value)) {
164 } else if (IS_SPEC_OBJECT(value) && !(typeof value == "function")) {
165 // Non-callable object. If it's a primitive wrapper, it must be unwrapped.
166 if (IS_ARRAY(value)) {
167 return SerializeArray(value, replacer, stack, indent, gap);
168 } else if (IS_NUMBER_WRAPPER(value)) {
169 value = ToNumber(value);
170 return JSON_NUMBER_TO_STRING(value);
171 } else if (IS_STRING_WRAPPER(value)) {
172 return %QuoteJSONString(ToString(value));
173 } else if (IS_BOOLEAN_WRAPPER(value)) {
174 return %_ValueOf(value) ? "true" : "false";
176 return SerializeObject(value, replacer, stack, indent, gap);
179 // Undefined or a callable object.
184 function JSONStringify(value, replacer, space) {
185 if (%_ArgumentsLength() == 1) {
186 return %BasicJSONStringify(value);
188 if (IS_ARRAY(replacer)) {
189 // Deduplicate replacer array items.
190 var property_list = new InternalArray();
191 var seen_properties = { __proto__: null };
192 var length = replacer.length;
193 for (var i = 0; i < length; i++) {
198 } else if (IS_NUMBER(v)) {
199 item = %_NumberToString(v);
200 } else if (IS_STRING_WRAPPER(v) || IS_NUMBER_WRAPPER(v)) {
205 if (!seen_properties[item]) {
206 property_list.push(item);
207 seen_properties[item] = true;
210 replacer = property_list;
212 if (IS_OBJECT(space)) {
213 // Unwrap 'space' if it is wrapped
214 if (IS_NUMBER_WRAPPER(space)) {
215 space = ToNumber(space);
216 } else if (IS_STRING_WRAPPER(space)) {
217 space = ToString(space);
221 if (IS_NUMBER(space)) {
222 space = MathMax(0, MathMin($toInteger(space), 10));
223 gap = %_SubString(" ", 0, space);
224 } else if (IS_STRING(space)) {
225 if (space.length > 10) {
226 gap = %_SubString(space, 0, 10);
233 return JSONSerialize('', {'': value}, replacer, new InternalArray(), "", gap);
236 // -------------------------------------------------------------------
238 %AddNamedProperty(GlobalJSON, toStringTagSymbol, "JSON", READ_ONLY | DONT_ENUM);
240 // Set up non-enumerable properties of the JSON object.
241 utils.InstallFunctions(GlobalJSON, DONT_ENUM, [
243 "stringify", JSONStringify
246 // -------------------------------------------------------------------
249 function JsonSerializeAdapter(key, object) {
251 holder[key] = object;
252 // No need to pass the actual holder since there is no replacer function.
253 return JSONSerialize(key, holder, UNDEFINED, new InternalArray(), "", "");
256 %InstallToContext(["json_serialize_adapter", JsonSerializeAdapter]);