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 } 86 var charCode = c.charCodeAt(0); 87 var result; 88 if (charCode < 16) result = '\\u000'; 89 else if (charCode < 256) result = '\\u00'; 90 else if (charCode < 4096) result = '\\u0'; 91 else result = '\\u'; 92 result += charCode.toString(16); 93 characterQuoteCache[c] = result; 94 return result; 95} 96 97function QuoteJSONString(str) { 98 var quotable = /[\\\"\x00-\x1f\x80-\uffff]/g; 99 return '"' + str.replace(quotable, QuoteSingleJSONCharacter) + '"'; 100} 101 102function StackContains(stack, val) { 103 var length = stack.length; 104 for (var i = 0; i < length; i++) { 105 if (stack[i] === val) { 106 return true; 107 } 108 } 109 return false; 110} 111 112function SerializeArray(value, replacer, stack, indent, gap) { 113 if (StackContains(stack, value)) { 114 throw MakeTypeError('circular_structure', []); 115 } 116 stack.push(value); 117 var stepback = indent; 118 indent += gap; 119 var partial = []; 120 var len = value.length; 121 for (var i = 0; i < len; i++) { 122 var strP = JSONSerialize($String(i), value, replacer, stack, 123 indent, gap); 124 if (IS_UNDEFINED(strP)) { 125 strP = "null"; 126 } 127 partial.push(strP); 128 } 129 var final; 130 if (gap == "") { 131 final = "[" + partial.join(",") + "]"; 132 } else if (partial.length > 0) { 133 var separator = ",\n" + indent; 134 final = "[\n" + indent + partial.join(separator) + "\n" + 135 stepback + "]"; 136 } else { 137 final = "[]"; 138 } 139 stack.pop(); 140 return final; 141} 142 143function SerializeObject(value, replacer, stack, indent, gap) { 144 if (StackContains(stack, value)) { 145 throw MakeTypeError('circular_structure', []); 146 } 147 stack.push(value); 148 var stepback = indent; 149 indent += gap; 150 var partial = []; 151 if (IS_ARRAY(replacer)) { 152 var length = replacer.length; 153 for (var i = 0; i < length; i++) { 154 if (ObjectHasOwnProperty.call(replacer, i)) { 155 var p = replacer[i]; 156 var strP = JSONSerialize(p, value, replacer, stack, indent, gap); 157 if (!IS_UNDEFINED(strP)) { 158 var member = QuoteJSONString(p) + ":"; 159 if (gap != "") member += " "; 160 member += strP; 161 partial.push(member); 162 } 163 } 164 } 165 } else { 166 for (var p in value) { 167 if (ObjectHasOwnProperty.call(value, p)) { 168 var strP = JSONSerialize(p, value, replacer, stack, indent, gap); 169 if (!IS_UNDEFINED(strP)) { 170 var member = QuoteJSONString(p) + ":"; 171 if (gap != "") member += " "; 172 member += strP; 173 partial.push(member); 174 } 175 } 176 } 177 } 178 var final; 179 if (gap == "") { 180 final = "{" + partial.join(",") + "}"; 181 } else if (partial.length > 0) { 182 var separator = ",\n" + indent; 183 final = "{\n" + indent + partial.join(separator) + "\n" + 184 stepback + "}"; 185 } else { 186 final = "{}"; 187 } 188 stack.pop(); 189 return final; 190} 191 192function JSONSerialize(key, holder, replacer, stack, indent, gap) { 193 var value = holder[key]; 194 if (IS_OBJECT(value) && value) { 195 var toJSON = value.toJSON; 196 if (IS_FUNCTION(toJSON)) { 197 value = toJSON.call(value, key); 198 } 199 } 200 if (IS_FUNCTION(replacer)) { 201 value = replacer.call(holder, key, value); 202 } 203 // Unwrap value if necessary 204 if (IS_OBJECT(value)) { 205 if (IS_NUMBER_WRAPPER(value)) { 206 value = $Number(value); 207 } else if (IS_STRING_WRAPPER(value)) { 208 value = $String(value); 209 } else if (IS_BOOLEAN_WRAPPER(value)) { 210 value = $Boolean(value); 211 } 212 } 213 switch (typeof value) { 214 case "string": 215 return QuoteJSONString(value); 216 case "object": 217 if (!value) { 218 return "null"; 219 } else if (IS_ARRAY(value)) { 220 return SerializeArray(value, replacer, stack, indent, gap); 221 } else { 222 return SerializeObject(value, replacer, stack, indent, gap); 223 } 224 case "number": 225 return $isFinite(value) ? $String(value) : "null"; 226 case "boolean": 227 return value ? "true" : "false"; 228 } 229} 230 231function JSONStringify(value, replacer, space) { 232 var stack = []; 233 var indent = ""; 234 if (IS_OBJECT(space)) { 235 // Unwrap 'space' if it is wrapped 236 if (IS_NUMBER_WRAPPER(space)) { 237 space = $Number(space); 238 } else if (IS_STRING_WRAPPER(space)) { 239 space = $String(space); 240 } 241 } 242 var gap; 243 if (IS_NUMBER(space)) { 244 space = $Math.min(space, 10); 245 gap = ""; 246 for (var i = 0; i < space; i++) { 247 gap += " "; 248 } 249 } else if (IS_STRING(space)) { 250 if (space.length > 10) { 251 gap = space.substring(0, 10); 252 } else { 253 gap = space; 254 } 255 } else { 256 gap = ""; 257 } 258 return JSONSerialize('', {'': value}, replacer, stack, indent, gap); 259} 260 261function SetupJSON() { 262 InstallFunctions($JSON, DONT_ENUM, $Array( 263 "parse", JSONParse, 264 "stringify", JSONStringify 265 )); 266} 267 268SetupJSON(); 269