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 28var $JSON = global.JSON; 29 30function ParseJSONUnfiltered(text) { 31 var s = $String(text); 32 var f = %CompileString("(" + text + ")", true); 33 return f(); 34} 35 36function Revive(holder, name, reviver) { 37 var val = holder[name]; 38 if (IS_OBJECT(val)) { 39 if (IS_ARRAY(val)) { 40 var length = val.length; 41 for (var i = 0; i < length; i++) { 42 var newElement = Revive(val, $String(i), reviver); 43 val[i] = newElement; 44 } 45 } else { 46 for (var p in val) { 47 if (ObjectHasOwnProperty.call(val, p)) { 48 var newElement = Revive(val, p, reviver); 49 if (IS_UNDEFINED(newElement)) { 50 delete val[p]; 51 } else { 52 val[p] = newElement; 53 } 54 } 55 } 56 } 57 } 58 return reviver.call(holder, name, val); 59} 60 61function JSONParse(text, reviver) { 62 var unfiltered = ParseJSONUnfiltered(text); 63 if (IS_FUNCTION(reviver)) { 64 return Revive({'': unfiltered}, '', reviver); 65 } else { 66 return unfiltered; 67 } 68} 69 70var characterQuoteCache = { 71 '\"': '\\"', 72 '\\': '\\\\', 73 '/': '\\/', 74 '\b': '\\b', 75 '\f': '\\f', 76 '\n': '\\n', 77 '\r': '\\r', 78 '\t': '\\t', 79 '\x0B': '\\u000b' 80}; 81 82function QuoteSingleJSONCharacter(c) { 83 if (c in characterQuoteCache) 84 return characterQuoteCache[c]; 85 var charCode = c.charCodeAt(0); 86 var result; 87 if (charCode < 16) result = '\\u000'; 88 else if (charCode < 256) result = '\\u00'; 89 else if (charCode < 4096) result = '\\u0'; 90 else result = '\\u'; 91 result += charCode.toString(16); 92 characterQuoteCache[c] = result; 93 return result; 94} 95 96function QuoteJSONString(str) { 97 var quotable = /[\\\"\x00-\x1f\x80-\uffff]/g; 98 return '"' + str.replace(quotable, QuoteSingleJSONCharacter) + '"'; 99} 100 101function StackContains(stack, val) { 102 var length = stack.length; 103 for (var i = 0; i < length; i++) { 104 if (stack[i] === val) 105 return true; 106 } 107 return false; 108} 109 110function SerializeArray(value, replacer, stack, indent, gap) { 111 if (StackContains(stack, value)) 112 throw MakeTypeError('circular_structure', []); 113 stack.push(value); 114 var stepback = indent; 115 indent += gap; 116 var partial = []; 117 var len = value.length; 118 for (var i = 0; i < len; i++) { 119 var strP = JSONSerialize($String(i), value, replacer, stack, 120 indent, gap); 121 if (IS_UNDEFINED(strP)) 122 strP = "null"; 123 partial.push(strP); 124 } 125 var final; 126 if (gap == "") { 127 final = "[" + partial.join(",") + "]"; 128 } else if (partial.length > 0) { 129 var separator = ",\n" + indent; 130 final = "[\n" + indent + partial.join(separator) + "\n" + 131 stepback + "]"; 132 } else { 133 final = "[]"; 134 } 135 stack.pop(); 136 return final; 137} 138 139function SerializeObject(value, replacer, stack, indent, gap) { 140 if (StackContains(stack, value)) 141 throw MakeTypeError('circular_structure', []); 142 stack.push(value); 143 var stepback = indent; 144 indent += gap; 145 var partial = []; 146 if (IS_ARRAY(replacer)) { 147 var length = replacer.length; 148 for (var i = 0; i < length; i++) { 149 if (ObjectHasOwnProperty.call(replacer, i)) { 150 var p = replacer[i]; 151 var strP = JSONSerialize(p, value, replacer, stack, indent, gap); 152 if (!IS_UNDEFINED(strP)) { 153 var member = QuoteJSONString(p) + ":"; 154 if (gap != "") member += " "; 155 member += strP; 156 partial.push(member); 157 } 158 } 159 } 160 } else { 161 for (var p in value) { 162 if (ObjectHasOwnProperty.call(value, p)) { 163 var strP = JSONSerialize(p, value, replacer, stack, indent, gap); 164 if (!IS_UNDEFINED(strP)) { 165 var member = QuoteJSONString(p) + ":"; 166 if (gap != "") member += " "; 167 member += strP; 168 partial.push(member); 169 } 170 } 171 } 172 } 173 var final; 174 if (gap == "") { 175 final = "{" + partial.join(",") + "}"; 176 } else if (partial.length > 0) { 177 var separator = ",\n" + indent; 178 final = "{\n" + indent + partial.join(separator) + "\n" + 179 stepback + "}"; 180 } else { 181 final = "{}"; 182 } 183 stack.pop(); 184 return final; 185} 186 187function JSONSerialize(key, holder, replacer, stack, indent, gap) { 188 var value = holder[key]; 189 if (IS_OBJECT(value) && value) { 190 var toJSON = value.toJSON; 191 if (IS_FUNCTION(toJSON)) 192 value = toJSON.call(value, key); 193 } 194 if (IS_FUNCTION(replacer)) 195 value = replacer.call(holder, key, value); 196 // Unwrap value if necessary 197 if (IS_OBJECT(value)) { 198 if (IS_NUMBER_WRAPPER(value)) { 199 value = $Number(value); 200 } else if (IS_STRING_WRAPPER(value)) { 201 value = $String(value); 202 } 203 } 204 switch (typeof value) { 205 case "string": 206 return QuoteJSONString(value); 207 case "object": 208 if (!value) { 209 return "null"; 210 } else if (IS_ARRAY(value)) { 211 return SerializeArray(value, replacer, stack, indent, gap); 212 } else { 213 return SerializeObject(value, replacer, stack, indent, gap); 214 } 215 case "number": 216 return $isFinite(value) ? $String(value) : "null"; 217 case "boolean": 218 return value ? "true" : "false"; 219 } 220} 221 222function JSONStringify(value, replacer, space) { 223 var stack = []; 224 var indent = ""; 225 if (IS_OBJECT(space)) { 226 // Unwrap 'space' if it is wrapped 227 if (IS_NUMBER_WRAPPER(space)) { 228 space = $Number(space); 229 } else if (IS_STRING_WRAPPER(space)) { 230 space = $String(space); 231 } 232 } 233 var gap; 234 if (IS_NUMBER(space)) { 235 space = $Math.min(space, 100); 236 gap = ""; 237 for (var i = 0; i < space; i++) 238 gap += " "; 239 } else if (IS_STRING(space)) { 240 gap = space; 241 } else { 242 gap = ""; 243 } 244 return JSONSerialize('', {'': value}, replacer, stack, indent, gap); 245} 246 247function SetupJSON() { 248 InstallFunctions($JSON, DONT_ENUM, $Array( 249 "parse", JSONParse, 250 "stringify", JSONStringify 251 )); 252} 253 254SetupJSON(); 255