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"use strict"; 6 7// This file relies on the fact that the following declarations have been made 8// in runtime.js: 9// var $Array = global.Array; 10// var $String = global.String; 11 12var $JSON = global.JSON; 13 14// ------------------------------------------------------------------- 15 16function Revive(holder, name, reviver) { 17 var val = holder[name]; 18 if (IS_OBJECT(val)) { 19 if (IS_ARRAY(val)) { 20 var length = val.length; 21 for (var i = 0; i < length; i++) { 22 var newElement = Revive(val, $String(i), reviver); 23 val[i] = newElement; 24 } 25 } else { 26 for (var p in val) { 27 if (%_CallFunction(val, p, ObjectHasOwnProperty)) { 28 var newElement = Revive(val, p, reviver); 29 if (IS_UNDEFINED(newElement)) { 30 delete val[p]; 31 } else { 32 val[p] = newElement; 33 } 34 } 35 } 36 } 37 } 38 return %_CallFunction(holder, name, val, reviver); 39} 40 41function JSONParse(text, reviver) { 42 var unfiltered = %ParseJson(TO_STRING_INLINE(text)); 43 if (IS_SPEC_FUNCTION(reviver)) { 44 return Revive({'': unfiltered}, '', reviver); 45 } else { 46 return unfiltered; 47 } 48} 49 50function SerializeArray(value, replacer, stack, indent, gap) { 51 if (!%PushIfAbsent(stack, value)) { 52 throw MakeTypeError('circular_structure', $Array()); 53 } 54 var stepback = indent; 55 indent += gap; 56 var partial = new InternalArray(); 57 var len = value.length; 58 for (var i = 0; i < len; i++) { 59 var strP = JSONSerialize($String(i), value, replacer, stack, 60 indent, gap); 61 if (IS_UNDEFINED(strP)) { 62 strP = "null"; 63 } 64 partial.push(strP); 65 } 66 var final; 67 if (gap == "") { 68 final = "[" + partial.join(",") + "]"; 69 } else if (partial.length > 0) { 70 var separator = ",\n" + indent; 71 final = "[\n" + indent + partial.join(separator) + "\n" + 72 stepback + "]"; 73 } else { 74 final = "[]"; 75 } 76 stack.pop(); 77 return final; 78} 79 80function SerializeObject(value, replacer, stack, indent, gap) { 81 if (!%PushIfAbsent(stack, value)) { 82 throw MakeTypeError('circular_structure', $Array()); 83 } 84 var stepback = indent; 85 indent += gap; 86 var partial = new InternalArray(); 87 if (IS_ARRAY(replacer)) { 88 var length = replacer.length; 89 for (var i = 0; i < length; i++) { 90 if (%_CallFunction(replacer, i, ObjectHasOwnProperty)) { 91 var p = replacer[i]; 92 var strP = JSONSerialize(p, value, replacer, stack, indent, gap); 93 if (!IS_UNDEFINED(strP)) { 94 var member = %QuoteJSONString(p) + ":"; 95 if (gap != "") member += " "; 96 member += strP; 97 partial.push(member); 98 } 99 } 100 } 101 } else { 102 for (var p in value) { 103 if (%_CallFunction(value, p, ObjectHasOwnProperty)) { 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 } 114 var final; 115 if (gap == "") { 116 final = "{" + partial.join(",") + "}"; 117 } else if (partial.length > 0) { 118 var separator = ",\n" + indent; 119 final = "{\n" + indent + partial.join(separator) + "\n" + 120 stepback + "}"; 121 } else { 122 final = "{}"; 123 } 124 stack.pop(); 125 return final; 126} 127 128function JSONSerialize(key, holder, replacer, stack, indent, gap) { 129 var value = holder[key]; 130 if (IS_SPEC_OBJECT(value)) { 131 var toJSON = value.toJSON; 132 if (IS_SPEC_FUNCTION(toJSON)) { 133 value = %_CallFunction(value, key, toJSON); 134 } 135 } 136 if (IS_SPEC_FUNCTION(replacer)) { 137 value = %_CallFunction(holder, key, value, replacer); 138 } 139 if (IS_STRING(value)) { 140 return %QuoteJSONString(value); 141 } else if (IS_NUMBER(value)) { 142 return JSON_NUMBER_TO_STRING(value); 143 } else if (IS_BOOLEAN(value)) { 144 return value ? "true" : "false"; 145 } else if (IS_NULL(value)) { 146 return "null"; 147 } else if (IS_SPEC_OBJECT(value) && !(typeof value == "function")) { 148 // Non-callable object. If it's a primitive wrapper, it must be unwrapped. 149 if (IS_ARRAY(value)) { 150 return SerializeArray(value, replacer, stack, indent, gap); 151 } else if (IS_NUMBER_WRAPPER(value)) { 152 value = ToNumber(value); 153 return JSON_NUMBER_TO_STRING(value); 154 } else if (IS_STRING_WRAPPER(value)) { 155 return %QuoteJSONString(ToString(value)); 156 } else if (IS_BOOLEAN_WRAPPER(value)) { 157 return %_ValueOf(value) ? "true" : "false"; 158 } else { 159 return SerializeObject(value, replacer, stack, indent, gap); 160 } 161 } 162 // Undefined or a callable object. 163 return UNDEFINED; 164} 165 166 167function JSONStringify(value, replacer, space) { 168 if (%_ArgumentsLength() == 1) { 169 return %BasicJSONStringify(value); 170 } 171 if (IS_OBJECT(space)) { 172 // Unwrap 'space' if it is wrapped 173 if (IS_NUMBER_WRAPPER(space)) { 174 space = ToNumber(space); 175 } else if (IS_STRING_WRAPPER(space)) { 176 space = ToString(space); 177 } 178 } 179 var gap; 180 if (IS_NUMBER(space)) { 181 space = MathMax(0, MathMin(ToInteger(space), 10)); 182 gap = %_SubString(" ", 0, space); 183 } else if (IS_STRING(space)) { 184 if (space.length > 10) { 185 gap = %_SubString(space, 0, 10); 186 } else { 187 gap = space; 188 } 189 } else { 190 gap = ""; 191 } 192 if (IS_ARRAY(replacer)) { 193 // Deduplicate replacer array items. 194 var property_list = new InternalArray(); 195 var seen_properties = { __proto__: null }; 196 var seen_sentinel = {}; 197 var length = replacer.length; 198 for (var i = 0; i < length; i++) { 199 var item = replacer[i]; 200 if (IS_STRING_WRAPPER(item)) { 201 item = ToString(item); 202 } else { 203 if (IS_NUMBER_WRAPPER(item)) item = ToNumber(item); 204 if (IS_NUMBER(item)) item = %_NumberToString(item); 205 } 206 if (IS_STRING(item) && seen_properties[item] != seen_sentinel) { 207 property_list.push(item); 208 // We cannot use true here because __proto__ needs to be an object. 209 seen_properties[item] = seen_sentinel; 210 } 211 } 212 replacer = property_list; 213 } 214 return JSONSerialize('', {'': value}, replacer, new InternalArray(), "", gap); 215} 216 217 218// ------------------------------------------------------------------- 219 220function SetUpJSON() { 221 %CheckIsBootstrapping(); 222 223 // Set up non-enumerable properties of the JSON object. 224 InstallFunctions($JSON, DONT_ENUM, $Array( 225 "parse", JSONParse, 226 "stringify", JSONStringify 227 )); 228} 229 230SetUpJSON(); 231 232 233// ------------------------------------------------------------------- 234// JSON Builtins 235 236function JSONSerializeAdapter(key, object) { 237 var holder = {}; 238 holder[key] = object; 239 // No need to pass the actual holder since there is no replacer function. 240 return JSONSerialize(key, holder, UNDEFINED, new InternalArray(), "", ""); 241} 242