1// Copyright 2009 the V8 project authors. All rights reserved. 2// Redistribution and use in source and binary forms, with or without 3// modification, are permitted provided that the following conditions are 4// met: 5// 6// * Redistributions of source code must retain the above copyright 7// notice, this list of conditions and the following disclaimer. 8// * Redistributions in binary form must reproduce the above 9// copyright notice, this list of conditions and the following 10// disclaimer in the documentation and/or other materials provided 11// with the distribution. 12// * Neither the name of Google Inc. nor the names of its 13// contributors may be used to endorse or promote products derived 14// from this software without specific prior written permission. 15// 16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28// This file relies on the fact that the following declarations have been made 29// in runtime.js: 30// var $Array = global.Array; 31// var $String = global.String; 32 33var $JSON = global.JSON; 34 35// ------------------------------------------------------------------- 36 37function Revive(holder, name, reviver) { 38 var val = holder[name]; 39 if (IS_OBJECT(val)) { 40 if (IS_ARRAY(val)) { 41 var length = val.length; 42 for (var i = 0; i < length; i++) { 43 var newElement = Revive(val, $String(i), reviver); 44 val[i] = newElement; 45 } 46 } else { 47 for (var p in val) { 48 if (%_CallFunction(val, p, ObjectHasOwnProperty)) { 49 var newElement = Revive(val, p, reviver); 50 if (IS_UNDEFINED(newElement)) { 51 delete val[p]; 52 } else { 53 val[p] = newElement; 54 } 55 } 56 } 57 } 58 } 59 return %_CallFunction(holder, name, val, reviver); 60} 61 62function JSONParse(text, reviver) { 63 var unfiltered = %ParseJson(TO_STRING_INLINE(text)); 64 if (IS_SPEC_FUNCTION(reviver)) { 65 return Revive({'': unfiltered}, '', reviver); 66 } else { 67 return unfiltered; 68 } 69} 70 71function SerializeArray(value, replacer, stack, indent, gap) { 72 if (!%PushIfAbsent(stack, value)) { 73 throw MakeTypeError('circular_structure', $Array()); 74 } 75 var stepback = indent; 76 indent += gap; 77 var partial = new InternalArray(); 78 var len = value.length; 79 for (var i = 0; i < len; i++) { 80 var strP = JSONSerialize($String(i), value, replacer, stack, 81 indent, gap); 82 if (IS_UNDEFINED(strP)) { 83 strP = "null"; 84 } 85 partial.push(strP); 86 } 87 var final; 88 if (gap == "") { 89 final = "[" + partial.join(",") + "]"; 90 } else if (partial.length > 0) { 91 var separator = ",\n" + indent; 92 final = "[\n" + indent + partial.join(separator) + "\n" + 93 stepback + "]"; 94 } else { 95 final = "[]"; 96 } 97 stack.pop(); 98 return final; 99} 100 101function SerializeObject(value, replacer, stack, indent, gap) { 102 if (!%PushIfAbsent(stack, value)) { 103 throw MakeTypeError('circular_structure', $Array()); 104 } 105 var stepback = indent; 106 indent += gap; 107 var partial = new InternalArray(); 108 if (IS_ARRAY(replacer)) { 109 var length = replacer.length; 110 for (var i = 0; i < length; i++) { 111 if (%_CallFunction(replacer, i, ObjectHasOwnProperty)) { 112 var p = replacer[i]; 113 var strP = JSONSerialize(p, value, replacer, stack, indent, gap); 114 if (!IS_UNDEFINED(strP)) { 115 var member = %QuoteJSONString(p) + ":"; 116 if (gap != "") member += " "; 117 member += strP; 118 partial.push(member); 119 } 120 } 121 } 122 } else { 123 for (var p in value) { 124 if (%_CallFunction(value, p, ObjectHasOwnProperty)) { 125 var strP = JSONSerialize(p, value, replacer, stack, indent, gap); 126 if (!IS_UNDEFINED(strP)) { 127 var member = %QuoteJSONString(p) + ":"; 128 if (gap != "") member += " "; 129 member += strP; 130 partial.push(member); 131 } 132 } 133 } 134 } 135 var final; 136 if (gap == "") { 137 final = "{" + partial.join(",") + "}"; 138 } else if (partial.length > 0) { 139 var separator = ",\n" + indent; 140 final = "{\n" + indent + partial.join(separator) + "\n" + 141 stepback + "}"; 142 } else { 143 final = "{}"; 144 } 145 stack.pop(); 146 return final; 147} 148 149function JSONSerialize(key, holder, replacer, stack, indent, gap) { 150 var value = holder[key]; 151 if (IS_SPEC_OBJECT(value)) { 152 var toJSON = value.toJSON; 153 if (IS_SPEC_FUNCTION(toJSON)) { 154 value = %_CallFunction(value, key, toJSON); 155 } 156 } 157 if (IS_SPEC_FUNCTION(replacer)) { 158 value = %_CallFunction(holder, key, value, replacer); 159 } 160 if (IS_STRING(value)) { 161 return %QuoteJSONString(value); 162 } else if (IS_NUMBER(value)) { 163 return JSON_NUMBER_TO_STRING(value); 164 } else if (IS_BOOLEAN(value)) { 165 return value ? "true" : "false"; 166 } else if (IS_NULL(value)) { 167 return "null"; 168 } else if (IS_SPEC_OBJECT(value) && !(typeof value == "function")) { 169 // Non-callable object. If it's a primitive wrapper, it must be unwrapped. 170 if (IS_ARRAY(value)) { 171 return SerializeArray(value, replacer, stack, indent, gap); 172 } else if (IS_NUMBER_WRAPPER(value)) { 173 value = ToNumber(value); 174 return JSON_NUMBER_TO_STRING(value); 175 } else if (IS_STRING_WRAPPER(value)) { 176 return %QuoteJSONString(ToString(value)); 177 } else if (IS_BOOLEAN_WRAPPER(value)) { 178 return %_ValueOf(value) ? "true" : "false"; 179 } else { 180 return SerializeObject(value, replacer, stack, indent, gap); 181 } 182 } 183 // Undefined or a callable object. 184 return UNDEFINED; 185} 186 187 188function JSONStringify(value, replacer, space) { 189 if (%_ArgumentsLength() == 1) { 190 return %BasicJSONStringify(value); 191 } 192 if (IS_OBJECT(space)) { 193 // Unwrap 'space' if it is wrapped 194 if (IS_NUMBER_WRAPPER(space)) { 195 space = ToNumber(space); 196 } else if (IS_STRING_WRAPPER(space)) { 197 space = ToString(space); 198 } 199 } 200 var gap; 201 if (IS_NUMBER(space)) { 202 space = MathMax(0, MathMin(ToInteger(space), 10)); 203 gap = %_SubString(" ", 0, space); 204 } else if (IS_STRING(space)) { 205 if (space.length > 10) { 206 gap = %_SubString(space, 0, 10); 207 } else { 208 gap = space; 209 } 210 } else { 211 gap = ""; 212 } 213 return JSONSerialize('', {'': value}, replacer, new InternalArray(), "", gap); 214} 215 216 217// ------------------------------------------------------------------- 218 219function SetUpJSON() { 220 %CheckIsBootstrapping(); 221 222 // Set up non-enumerable properties of the JSON object. 223 InstallFunctions($JSON, DONT_ENUM, $Array( 224 "parse", JSONParse, 225 "stringify", JSONStringify 226 )); 227} 228 229SetUpJSON(); 230 231 232// ------------------------------------------------------------------- 233// JSON Builtins 234 235function JSONSerializeAdapter(key, object) { 236 var holder = {}; 237 holder[key] = object; 238 // No need to pass the actual holder since there is no replacer function. 239 return JSONSerialize(key, holder, UNDEFINED, new InternalArray(), "", ""); 240} 241