OLD | NEW |
1 // Copyright 2009 the V8 project authors. All rights reserved. | 1 // Copyright 2009 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
(...skipping 177 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
188 } else { | 188 } else { |
189 return SerializeObject(value, replacer, stack, indent, gap); | 189 return SerializeObject(value, replacer, stack, indent, gap); |
190 } | 190 } |
191 case "number": | 191 case "number": |
192 return $isFinite(value) ? $String(value) : "null"; | 192 return $isFinite(value) ? $String(value) : "null"; |
193 case "boolean": | 193 case "boolean": |
194 return value ? "true" : "false"; | 194 return value ? "true" : "false"; |
195 } | 195 } |
196 } | 196 } |
197 | 197 |
198 function BasicSerializeArray(value, stack) { | 198 |
| 199 function BasicSerializeArray(value, stack, builder) { |
199 if (StackContains(stack, value)) { | 200 if (StackContains(stack, value)) { |
200 throw MakeTypeError('circular_structure', []); | 201 throw MakeTypeError('circular_structure', []); |
201 } | 202 } |
202 stack.push(value); | 203 stack.push(value); |
203 var partial = []; | 204 builder.push("["); |
204 var len = value.length; | 205 var len = value.length; |
205 for (var i = 0; i < len; i++) { | 206 for (var i = 0; i < len; i++) { |
206 var strP = BasicJSONSerialize($String(i), value, stack); | 207 var before = builder.length; |
207 if (IS_UNDEFINED(strP)) strP = "null"; | 208 BasicJSONSerialize($String(i), value, stack, builder); |
208 partial.push(strP); | 209 if (before == builder.length) builder.push("null"); |
| 210 builder.push(","); |
209 } | 211 } |
210 stack.pop(); | 212 stack.pop(); |
211 return "[" + partial.join() + "]"; | 213 if (builder.pop() != ",") { |
| 214 builder.push("[]"); // Zero length array. Push "[" back on. |
| 215 } else { |
| 216 builder.push("]"); |
| 217 } |
| 218 |
212 } | 219 } |
213 | 220 |
214 function BasicSerializeObject(value, stack) { | 221 |
| 222 function BasicSerializeObject(value, stack, builder) { |
215 if (StackContains(stack, value)) { | 223 if (StackContains(stack, value)) { |
216 throw MakeTypeError('circular_structure', []); | 224 throw MakeTypeError('circular_structure', []); |
217 } | 225 } |
218 stack.push(value); | 226 stack.push(value); |
219 var partial = []; | 227 builder.push("{"); |
220 for (var p in value) { | 228 for (var p in value) { |
221 if (ObjectHasOwnProperty.call(value, p)) { | 229 if (ObjectHasOwnProperty.call(value, p)) { |
222 var strP = BasicJSONSerialize(p, value, stack); | 230 builder.push(%QuoteJSONString(p), ":"); |
223 if (!IS_UNDEFINED(strP)) partial.push(%QuoteJSONString(p) + ":" + strP); | 231 var before = builder.length; |
| 232 BasicJSONSerialize(p, value, stack, builder); |
| 233 if (before == builder.length) { |
| 234 builder.pop(); |
| 235 builder.pop(); |
| 236 } else { |
| 237 builder.push(","); |
| 238 } |
224 } | 239 } |
225 } | 240 } |
226 stack.pop(); | 241 stack.pop(); |
227 return "{" + partial.join() + "}"; | 242 if (builder.pop() != ",") { |
| 243 builder.push("{}"); // Object has no own properties. Push "{" back on. |
| 244 } else { |
| 245 builder.push("}"); |
| 246 } |
228 } | 247 } |
229 | 248 |
230 function BasicJSONSerialize(key, holder, stack) { | 249 |
| 250 function BasicJSONSerialize(key, holder, stack, builder) { |
231 var value = holder[key]; | 251 var value = holder[key]; |
232 if (IS_OBJECT(value) && value) { | 252 if (IS_OBJECT(value) && value) { |
233 var toJSON = value.toJSON; | 253 var toJSON = value.toJSON; |
234 if (IS_FUNCTION(toJSON)) value = toJSON.call(value, key); | 254 if (IS_FUNCTION(toJSON)) value = toJSON.call(value, key); |
235 } | 255 } |
236 // Unwrap value if necessary | 256 if (IS_STRING(value)) { |
237 if (IS_OBJECT(value)) { | 257 builder.push(%QuoteJSONString(value)); |
| 258 } else if (IS_NUMBER(value)) { |
| 259 builder.push(($isFinite(value) ? %_NumberToString(value) : "null")); |
| 260 } else if (IS_BOOLEAN(value)) { |
| 261 builder.push((value ? "true" : "false")); |
| 262 } else if (IS_OBJECT(value)) { |
| 263 // Unwrap value if necessary |
238 if (IS_NUMBER_WRAPPER(value)) { | 264 if (IS_NUMBER_WRAPPER(value)) { |
239 value = $Number(value); | 265 value = %_ValueOf(value); |
| 266 builder.push(($isFinite(value) ? %_NumberToString(value) : "null")); |
240 } else if (IS_STRING_WRAPPER(value)) { | 267 } else if (IS_STRING_WRAPPER(value)) { |
241 value = $String(value); | 268 builder.push(%QuoteJSONString(%_ValueOf(value))); |
242 } else if (IS_BOOLEAN_WRAPPER(value)) { | 269 } else if (IS_BOOLEAN_WRAPPER(value)) { |
243 value = %_ValueOf(value); | 270 builder.push((%_ValueOf(value) ? "true" : "false")); |
| 271 } else { |
| 272 // Regular non-wrapped object |
| 273 if (!value) { |
| 274 builder.push("null"); |
| 275 } else if (IS_ARRAY(value)) { |
| 276 BasicSerializeArray(value, stack, builder); |
| 277 } else { |
| 278 BasicSerializeObject(value, stack, builder); |
| 279 } |
244 } | 280 } |
245 } | 281 } |
246 switch (typeof value) { | |
247 case "string": | |
248 return %QuoteJSONString(value); | |
249 case "object": | |
250 if (!value) { | |
251 return "null"; | |
252 } else if (IS_ARRAY(value)) { | |
253 return BasicSerializeArray(value, stack); | |
254 } else { | |
255 return BasicSerializeObject(value, stack); | |
256 } | |
257 case "number": | |
258 return $isFinite(value) ? $String(value) : "null"; | |
259 case "boolean": | |
260 return value ? "true" : "false"; | |
261 } | |
262 } | 282 } |
263 | 283 |
264 function JSONStringify(value, replacer, space) { | 284 function JSONStringify(value, replacer, space) { |
265 if (IS_UNDEFINED(replacer) && IS_UNDEFINED(space)) { | 285 if (IS_UNDEFINED(replacer) && IS_UNDEFINED(space)) { |
266 return BasicJSONSerialize('', {'': value}, []); | 286 var builder = []; |
| 287 BasicJSONSerialize('', {'': value}, [], builder); |
| 288 if (builder.length == 0) return; |
| 289 var result = %_FastAsciiArrayJoin(builder, ""); |
| 290 if (!IS_UNDEFINED(result)) return result; |
| 291 return %StringBuilderConcat(builder, builder.length, ""); |
267 } | 292 } |
268 if (IS_OBJECT(space)) { | 293 if (IS_OBJECT(space)) { |
269 // Unwrap 'space' if it is wrapped | 294 // Unwrap 'space' if it is wrapped |
270 if (IS_NUMBER_WRAPPER(space)) { | 295 if (IS_NUMBER_WRAPPER(space)) { |
271 space = $Number(space); | 296 space = $Number(space); |
272 } else if (IS_STRING_WRAPPER(space)) { | 297 } else if (IS_STRING_WRAPPER(space)) { |
273 space = $String(space); | 298 space = $String(space); |
274 } | 299 } |
275 } | 300 } |
276 var gap; | 301 var gap; |
(...skipping 16 matching lines...) Expand all Loading... |
293 } | 318 } |
294 | 319 |
295 function SetupJSON() { | 320 function SetupJSON() { |
296 InstallFunctions($JSON, DONT_ENUM, $Array( | 321 InstallFunctions($JSON, DONT_ENUM, $Array( |
297 "parse", JSONParse, | 322 "parse", JSONParse, |
298 "stringify", JSONStringify | 323 "stringify", JSONStringify |
299 )); | 324 )); |
300 } | 325 } |
301 | 326 |
302 SetupJSON(); | 327 SetupJSON(); |
OLD | NEW |