OLD | NEW |
| (Empty) |
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 toStringTagSymbol = utils.ImportNow("to_string_tag_symbol"); | |
20 | |
21 utils.Import(function(from) { | |
22 MathMax = from.MathMax; | |
23 MathMin = from.MathMin; | |
24 ObjectHasOwnProperty = from.ObjectHasOwnProperty; | |
25 }); | |
26 | |
27 // ------------------------------------------------------------------- | |
28 | |
29 function Revive(holder, name, reviver) { | |
30 var val = holder[name]; | |
31 if (IS_OBJECT(val)) { | |
32 if (IS_ARRAY(val)) { | |
33 var length = val.length; | |
34 for (var i = 0; i < length; i++) { | |
35 var newElement = Revive(val, %_NumberToString(i), reviver); | |
36 val[i] = newElement; | |
37 } | |
38 } else { | |
39 for (var p in val) { | |
40 if (HAS_OWN_PROPERTY(val, p)) { | |
41 var newElement = Revive(val, p, reviver); | |
42 if (IS_UNDEFINED(newElement)) { | |
43 delete val[p]; | |
44 } else { | |
45 val[p] = newElement; | |
46 } | |
47 } | |
48 } | |
49 } | |
50 } | |
51 return %_Call(reviver, holder, name, val); | |
52 } | |
53 | |
54 | |
55 function JSONParse(text, reviver) { | |
56 var unfiltered = %ParseJson(text); | |
57 if (IS_CALLABLE(reviver)) { | |
58 return Revive({'': unfiltered}, '', reviver); | |
59 } else { | |
60 return unfiltered; | |
61 } | |
62 } | |
63 | |
64 | |
65 function SerializeArray(value, replacer, stack, indent, gap) { | |
66 if (!%PushIfAbsent(stack, value)) throw MakeTypeError(kCircularStructure); | |
67 var stepback = indent; | |
68 indent += gap; | |
69 var partial = new InternalArray(); | |
70 var len = value.length; | |
71 for (var i = 0; i < len; i++) { | |
72 var strP = JSONSerialize(%_NumberToString(i), value, replacer, stack, | |
73 indent, gap); | |
74 if (IS_UNDEFINED(strP)) { | |
75 strP = "null"; | |
76 } | |
77 partial.push(strP); | |
78 } | |
79 var final; | |
80 if (gap == "") { | |
81 final = "[" + partial.join(",") + "]"; | |
82 } else if (partial.length > 0) { | |
83 var separator = ",\n" + indent; | |
84 final = "[\n" + indent + partial.join(separator) + "\n" + | |
85 stepback + "]"; | |
86 } else { | |
87 final = "[]"; | |
88 } | |
89 stack.pop(); | |
90 return final; | |
91 } | |
92 | |
93 | |
94 function SerializeObject(value, replacer, stack, indent, gap) { | |
95 if (!%PushIfAbsent(stack, value)) throw MakeTypeError(kCircularStructure); | |
96 var stepback = indent; | |
97 indent += gap; | |
98 var partial = new InternalArray(); | |
99 if (IS_ARRAY(replacer)) { | |
100 var length = replacer.length; | |
101 for (var i = 0; i < length; i++) { | |
102 if (HAS_OWN_PROPERTY(replacer, i)) { | |
103 var p = replacer[i]; | |
104 var strP = JSONSerialize(p, value, replacer, stack, indent, gap); | |
105 if (!IS_UNDEFINED(strP)) { | |
106 var member = %QuoteJSONString(p) + ":"; | |
107 if (gap != "") member += " "; | |
108 member += strP; | |
109 partial.push(member); | |
110 } | |
111 } | |
112 } | |
113 } else { | |
114 for (var p in value) { | |
115 if (HAS_OWN_PROPERTY(value, p)) { | |
116 var strP = JSONSerialize(p, value, replacer, stack, indent, gap); | |
117 if (!IS_UNDEFINED(strP)) { | |
118 var member = %QuoteJSONString(p) + ":"; | |
119 if (gap != "") member += " "; | |
120 member += strP; | |
121 partial.push(member); | |
122 } | |
123 } | |
124 } | |
125 } | |
126 var final; | |
127 if (gap == "") { | |
128 final = "{" + partial.join(",") + "}"; | |
129 } else if (partial.length > 0) { | |
130 var separator = ",\n" + indent; | |
131 final = "{\n" + indent + partial.join(separator) + "\n" + | |
132 stepback + "}"; | |
133 } else { | |
134 final = "{}"; | |
135 } | |
136 stack.pop(); | |
137 return final; | |
138 } | |
139 | |
140 | |
141 function JSONSerialize(key, holder, replacer, stack, indent, gap) { | |
142 var value = holder[key]; | |
143 if (IS_SPEC_OBJECT(value)) { | |
144 var toJSON = value.toJSON; | |
145 if (IS_CALLABLE(toJSON)) { | |
146 value = %_Call(toJSON, value, key); | |
147 } | |
148 } | |
149 if (IS_CALLABLE(replacer)) { | |
150 value = %_Call(replacer, holder, key, value); | |
151 } | |
152 if (IS_STRING(value)) { | |
153 return %QuoteJSONString(value); | |
154 } else if (IS_NUMBER(value)) { | |
155 return JSON_NUMBER_TO_STRING(value); | |
156 } else if (IS_BOOLEAN(value)) { | |
157 return value ? "true" : "false"; | |
158 } else if (IS_NULL(value)) { | |
159 return "null"; | |
160 } else if (IS_SPEC_OBJECT(value) && !IS_CALLABLE(value)) { | |
161 // Non-callable object. If it's a primitive wrapper, it must be unwrapped. | |
162 if (IS_ARRAY(value)) { | |
163 return SerializeArray(value, replacer, stack, indent, gap); | |
164 } else if (IS_NUMBER_WRAPPER(value)) { | |
165 value = TO_NUMBER(value); | |
166 return JSON_NUMBER_TO_STRING(value); | |
167 } else if (IS_STRING_WRAPPER(value)) { | |
168 return %QuoteJSONString(TO_STRING(value)); | |
169 } else if (IS_BOOLEAN_WRAPPER(value)) { | |
170 return %_ValueOf(value) ? "true" : "false"; | |
171 } else { | |
172 return SerializeObject(value, replacer, stack, indent, gap); | |
173 } | |
174 } | |
175 // Undefined or a callable object. | |
176 return UNDEFINED; | |
177 } | |
178 | |
179 | |
180 function JSONStringify(value, replacer, space) { | |
181 if (%_ArgumentsLength() == 1) { | |
182 return %BasicJSONStringify(value); | |
183 } | |
184 if (IS_ARRAY(replacer)) { | |
185 // Deduplicate replacer array items. | |
186 var property_list = new InternalArray(); | |
187 var seen_properties = { __proto__: null }; | |
188 var length = replacer.length; | |
189 for (var i = 0; i < length; i++) { | |
190 var v = replacer[i]; | |
191 var item; | |
192 if (IS_STRING(v)) { | |
193 item = v; | |
194 } else if (IS_NUMBER(v)) { | |
195 item = %_NumberToString(v); | |
196 } else if (IS_STRING_WRAPPER(v) || IS_NUMBER_WRAPPER(v)) { | |
197 item = TO_STRING(v); | |
198 } else { | |
199 continue; | |
200 } | |
201 if (!seen_properties[item]) { | |
202 property_list.push(item); | |
203 seen_properties[item] = true; | |
204 } | |
205 } | |
206 replacer = property_list; | |
207 } | |
208 if (IS_OBJECT(space)) { | |
209 // Unwrap 'space' if it is wrapped | |
210 if (IS_NUMBER_WRAPPER(space)) { | |
211 space = TO_NUMBER(space); | |
212 } else if (IS_STRING_WRAPPER(space)) { | |
213 space = TO_STRING(space); | |
214 } | |
215 } | |
216 var gap; | |
217 if (IS_NUMBER(space)) { | |
218 space = MathMax(0, MathMin(TO_INTEGER(space), 10)); | |
219 gap = %_SubString(" ", 0, space); | |
220 } else if (IS_STRING(space)) { | |
221 if (space.length > 10) { | |
222 gap = %_SubString(space, 0, 10); | |
223 } else { | |
224 gap = space; | |
225 } | |
226 } else { | |
227 gap = ""; | |
228 } | |
229 return JSONSerialize('', {'': value}, replacer, new InternalArray(), "", gap); | |
230 } | |
231 | |
232 // ------------------------------------------------------------------- | |
233 | |
234 %AddNamedProperty(GlobalJSON, toStringTagSymbol, "JSON", READ_ONLY | DONT_ENUM); | |
235 | |
236 // Set up non-enumerable properties of the JSON object. | |
237 utils.InstallFunctions(GlobalJSON, DONT_ENUM, [ | |
238 "parse", JSONParse, | |
239 "stringify", JSONStringify | |
240 ]); | |
241 | |
242 // ------------------------------------------------------------------- | |
243 // JSON Builtins | |
244 | |
245 function JsonSerializeAdapter(key, object) { | |
246 var holder = {}; | |
247 holder[key] = object; | |
248 // No need to pass the actual holder since there is no replacer function. | |
249 return JSONSerialize(key, holder, UNDEFINED, new InternalArray(), "", ""); | |
250 } | |
251 | |
252 %InstallToContext(["json_serialize_adapter", JsonSerializeAdapter]); | |
253 | |
254 }) | |
OLD | NEW |