a0a88a04c1abf86b8fcea1ec23958c9da43f79d2
[platform/upstream/v8.git] / src / json.js
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.
4
5 (function(global, utils) {
6
7 "use strict";
8
9 %CheckIsBootstrapping();
10
11 // -------------------------------------------------------------------
12 // Imports
13
14 var GlobalJSON = global.JSON;
15 var InternalArray = utils.InternalArray;
16 var MathMax;
17 var MathMin;
18 var ObjectHasOwnProperty;
19 var ToNumber;
20 var ToString;
21 var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
22
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;
29 });
30
31 // -------------------------------------------------------------------
32
33 function Revive(holder, name, reviver) {
34   var val = holder[name];
35   if (IS_OBJECT(val)) {
36     if (IS_ARRAY(val)) {
37       var length = val.length;
38       for (var i = 0; i < length; i++) {
39         var newElement = Revive(val, %_NumberToString(i), reviver);
40         val[i] = newElement;
41       }
42     } else {
43       for (var p in val) {
44         if (HAS_OWN_PROPERTY(val, p)) {
45           var newElement = Revive(val, p, reviver);
46           if (IS_UNDEFINED(newElement)) {
47             delete val[p];
48           } else {
49             val[p] = newElement;
50           }
51         }
52       }
53     }
54   }
55   return %_Call(reviver, holder, name, val);
56 }
57
58
59 function JSONParse(text, reviver) {
60   var unfiltered = %ParseJson(TO_STRING_INLINE(text));
61   if (IS_CALLABLE(reviver)) {
62     return Revive({'': unfiltered}, '', reviver);
63   } else {
64     return unfiltered;
65   }
66 }
67
68
69 function SerializeArray(value, replacer, stack, indent, gap) {
70   if (!%PushIfAbsent(stack, value)) throw MakeTypeError(kCircularStructure);
71   var stepback = indent;
72   indent += gap;
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,
77                              indent, gap);
78     if (IS_UNDEFINED(strP)) {
79       strP = "null";
80     }
81     partial.push(strP);
82   }
83   var final;
84   if (gap == "") {
85     final = "[" + partial.join(",") + "]";
86   } else if (partial.length > 0) {
87     var separator = ",\n" + indent;
88     final = "[\n" + indent + partial.join(separator) + "\n" +
89         stepback + "]";
90   } else {
91     final = "[]";
92   }
93   stack.pop();
94   return final;
95 }
96
97
98 function SerializeObject(value, replacer, stack, indent, gap) {
99   if (!%PushIfAbsent(stack, value)) throw MakeTypeError(kCircularStructure);
100   var stepback = indent;
101   indent += gap;
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)) {
107         var p = 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 += " ";
112           member += strP;
113           partial.push(member);
114         }
115       }
116     }
117   } else {
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 += " ";
124           member += strP;
125           partial.push(member);
126         }
127       }
128     }
129   }
130   var final;
131   if (gap == "") {
132     final = "{" + partial.join(",") + "}";
133   } else if (partial.length > 0) {
134     var separator = ",\n" + indent;
135     final = "{\n" + indent + partial.join(separator) + "\n" +
136         stepback + "}";
137   } else {
138     final = "{}";
139   }
140   stack.pop();
141   return final;
142 }
143
144
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);
151     }
152   }
153   if (IS_CALLABLE(replacer)) {
154     value = %_Call(replacer, holder, key, value);
155   }
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)) {
163     return "null";
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";
175     } else {
176       return SerializeObject(value, replacer, stack, indent, gap);
177     }
178   }
179   // Undefined or a callable object.
180   return UNDEFINED;
181 }
182
183
184 function JSONStringify(value, replacer, space) {
185   if (%_ArgumentsLength() == 1) {
186     return %BasicJSONStringify(value);
187   }
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++) {
194       var v = replacer[i];
195       var item;
196       if (IS_STRING(v)) {
197         item = v;
198       } else if (IS_NUMBER(v)) {
199         item = %_NumberToString(v);
200       } else if (IS_STRING_WRAPPER(v) || IS_NUMBER_WRAPPER(v)) {
201         item = ToString(v);
202       } else {
203         continue;
204       }
205       if (!seen_properties[item]) {
206         property_list.push(item);
207         seen_properties[item] = true;
208       }
209     }
210     replacer = property_list;
211   }
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);
218     }
219   }
220   var gap;
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);
227     } else {
228       gap = space;
229     }
230   } else {
231     gap = "";
232   }
233   return JSONSerialize('', {'': value}, replacer, new InternalArray(), "", gap);
234 }
235
236 // -------------------------------------------------------------------
237
238 %AddNamedProperty(GlobalJSON, toStringTagSymbol, "JSON", READ_ONLY | DONT_ENUM);
239
240 // Set up non-enumerable properties of the JSON object.
241 utils.InstallFunctions(GlobalJSON, DONT_ENUM, [
242   "parse", JSONParse,
243   "stringify", JSONStringify
244 ]);
245
246 // -------------------------------------------------------------------
247 // JSON Builtins
248
249 function JsonSerializeAdapter(key, object) {
250   var holder = {};
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(), "", "");
254 }
255
256 %InstallToContext(["json_serialize_adapter", JsonSerializeAdapter]);
257
258 })