[runtime] Replace %to_string_fun with %_ToString.
[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 toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
21
22 utils.Import(function(from) {
23   MathMax = from.MathMax;
24   MathMin = from.MathMin;
25   ObjectHasOwnProperty = from.ObjectHasOwnProperty;
26   ToNumber = from.ToNumber;
27 });
28
29 // -------------------------------------------------------------------
30
31 function Revive(holder, name, reviver) {
32   var val = holder[name];
33   if (IS_OBJECT(val)) {
34     if (IS_ARRAY(val)) {
35       var length = val.length;
36       for (var i = 0; i < length; i++) {
37         var newElement = Revive(val, %_NumberToString(i), reviver);
38         val[i] = newElement;
39       }
40     } else {
41       for (var p in val) {
42         if (HAS_OWN_PROPERTY(val, p)) {
43           var newElement = Revive(val, p, reviver);
44           if (IS_UNDEFINED(newElement)) {
45             delete val[p];
46           } else {
47             val[p] = newElement;
48           }
49         }
50       }
51     }
52   }
53   return %_Call(reviver, holder, name, val);
54 }
55
56
57 function JSONParse(text, reviver) {
58   var unfiltered = %ParseJson(text);
59   if (IS_CALLABLE(reviver)) {
60     return Revive({'': unfiltered}, '', reviver);
61   } else {
62     return unfiltered;
63   }
64 }
65
66
67 function SerializeArray(value, replacer, stack, indent, gap) {
68   if (!%PushIfAbsent(stack, value)) throw MakeTypeError(kCircularStructure);
69   var stepback = indent;
70   indent += gap;
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,
75                              indent, gap);
76     if (IS_UNDEFINED(strP)) {
77       strP = "null";
78     }
79     partial.push(strP);
80   }
81   var final;
82   if (gap == "") {
83     final = "[" + partial.join(",") + "]";
84   } else if (partial.length > 0) {
85     var separator = ",\n" + indent;
86     final = "[\n" + indent + partial.join(separator) + "\n" +
87         stepback + "]";
88   } else {
89     final = "[]";
90   }
91   stack.pop();
92   return final;
93 }
94
95
96 function SerializeObject(value, replacer, stack, indent, gap) {
97   if (!%PushIfAbsent(stack, value)) throw MakeTypeError(kCircularStructure);
98   var stepback = indent;
99   indent += gap;
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)) {
105         var p = 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 += " ";
110           member += strP;
111           partial.push(member);
112         }
113       }
114     }
115   } else {
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 += " ";
122           member += strP;
123           partial.push(member);
124         }
125       }
126     }
127   }
128   var final;
129   if (gap == "") {
130     final = "{" + partial.join(",") + "}";
131   } else if (partial.length > 0) {
132     var separator = ",\n" + indent;
133     final = "{\n" + indent + partial.join(separator) + "\n" +
134         stepback + "}";
135   } else {
136     final = "{}";
137   }
138   stack.pop();
139   return final;
140 }
141
142
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);
149     }
150   }
151   if (IS_CALLABLE(replacer)) {
152     value = %_Call(replacer, holder, key, value);
153   }
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)) {
161     return "null";
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";
173     } else {
174       return SerializeObject(value, replacer, stack, indent, gap);
175     }
176   }
177   // Undefined or a callable object.
178   return UNDEFINED;
179 }
180
181
182 function JSONStringify(value, replacer, space) {
183   if (%_ArgumentsLength() == 1) {
184     return %BasicJSONStringify(value);
185   }
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++) {
192       var v = replacer[i];
193       var item;
194       if (IS_STRING(v)) {
195         item = v;
196       } else if (IS_NUMBER(v)) {
197         item = %_NumberToString(v);
198       } else if (IS_STRING_WRAPPER(v) || IS_NUMBER_WRAPPER(v)) {
199         item = TO_STRING(v);
200       } else {
201         continue;
202       }
203       if (!seen_properties[item]) {
204         property_list.push(item);
205         seen_properties[item] = true;
206       }
207     }
208     replacer = property_list;
209   }
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);
216     }
217   }
218   var gap;
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);
225     } else {
226       gap = space;
227     }
228   } else {
229     gap = "";
230   }
231   return JSONSerialize('', {'': value}, replacer, new InternalArray(), "", gap);
232 }
233
234 // -------------------------------------------------------------------
235
236 %AddNamedProperty(GlobalJSON, toStringTagSymbol, "JSON", READ_ONLY | DONT_ENUM);
237
238 // Set up non-enumerable properties of the JSON object.
239 utils.InstallFunctions(GlobalJSON, DONT_ENUM, [
240   "parse", JSONParse,
241   "stringify", JSONStringify
242 ]);
243
244 // -------------------------------------------------------------------
245 // JSON Builtins
246
247 function JsonSerializeAdapter(key, object) {
248   var holder = {};
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(), "", "");
252 }
253
254 %InstallToContext(["json_serialize_adapter", JsonSerializeAdapter]);
255
256 })