• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(function(global, utils) {
6
7"use strict";
8
9%CheckIsBootstrapping();
10
11// -------------------------------------------------------------------
12// Imports
13
14var GlobalDate = global.Date;
15var GlobalJSON = global.JSON;
16var GlobalSet = global.Set;
17var InternalArray = utils.InternalArray;
18var MakeTypeError;
19var MaxSimple;
20var MinSimple;
21var ObjectHasOwnProperty;
22var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
23
24utils.Import(function(from) {
25  MakeTypeError = from.MakeTypeError;
26  MaxSimple = from.MaxSimple;
27  MinSimple = from.MinSimple;
28  ObjectHasOwnProperty = from.ObjectHasOwnProperty;
29});
30
31// -------------------------------------------------------------------
32
33function CreateDataProperty(o, p, v) {
34  var desc = {value: v, enumerable: true, writable: true, configurable: true};
35  return %reflect_define_property(o, p, desc);
36}
37
38
39function InternalizeJSONProperty(holder, name, reviver) {
40  var val = holder[name];
41  if (IS_RECEIVER(val)) {
42    if (%is_arraylike(val)) {
43      var length = TO_LENGTH(val.length);
44      for (var i = 0; i < length; i++) {
45        var newElement =
46            InternalizeJSONProperty(val, %_NumberToString(i), reviver);
47        if (IS_UNDEFINED(newElement)) {
48          %reflect_delete_property(val, i);
49        } else {
50          CreateDataProperty(val, i, newElement);
51        }
52      }
53    } else {
54      for (var p of %object_keys(val)) {
55        var newElement = InternalizeJSONProperty(val, p, reviver);
56        if (IS_UNDEFINED(newElement)) {
57          %reflect_delete_property(val, p);
58        } else {
59          CreateDataProperty(val, p, newElement);
60        }
61      }
62    }
63  }
64  return %_Call(reviver, holder, name, val);
65}
66
67
68function JSONParse(text, reviver) {
69  var unfiltered = %ParseJson(text);
70  if (IS_CALLABLE(reviver)) {
71    return InternalizeJSONProperty({'': unfiltered}, '', reviver);
72  } else {
73    return unfiltered;
74  }
75}
76
77
78function SerializeArray(value, replacer, stack, indent, gap) {
79  if (!%PushIfAbsent(stack, value)) throw MakeTypeError(kCircularStructure);
80  var stepback = indent;
81  indent += gap;
82  var partial = new InternalArray();
83  var len = TO_LENGTH(value.length);
84  for (var i = 0; i < len; i++) {
85    var strP = JSONSerialize(%_NumberToString(i), value, replacer, stack,
86                             indent, gap);
87    if (IS_UNDEFINED(strP)) {
88      strP = "null";
89    }
90    partial.push(strP);
91  }
92  var final;
93  if (gap == "") {
94    final = "[" + partial.join(",") + "]";
95  } else if (partial.length > 0) {
96    var separator = ",\n" + indent;
97    final = "[\n" + indent + partial.join(separator) + "\n" +
98        stepback + "]";
99  } else {
100    final = "[]";
101  }
102  stack.pop();
103  return final;
104}
105
106
107function SerializeObject(value, replacer, stack, indent, gap) {
108  if (!%PushIfAbsent(stack, value)) throw MakeTypeError(kCircularStructure);
109  var stepback = indent;
110  indent += gap;
111  var partial = new InternalArray();
112  if (IS_ARRAY(replacer)) {
113    var length = replacer.length;
114    for (var i = 0; i < length; i++) {
115      var p = replacer[i];
116      var strP = JSONSerialize(p, value, replacer, stack, indent, gap);
117      if (!IS_UNDEFINED(strP)) {
118        var member = %QuoteJSONString(p) + ":";
119        if (gap != "") member += " ";
120        member += strP;
121        partial.push(member);
122      }
123    }
124  } else {
125    for (var p of %object_keys(value)) {
126      var strP = JSONSerialize(p, value, replacer, stack, indent, gap);
127      if (!IS_UNDEFINED(strP)) {
128        var member = %QuoteJSONString(p) + ":";
129        if (gap != "") member += " ";
130        member += strP;
131        partial.push(member);
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
149
150function JSONSerialize(key, holder, replacer, stack, indent, gap) {
151  var value = holder[key];
152  if (IS_RECEIVER(value)) {
153    var toJSON = value.toJSON;
154    if (IS_CALLABLE(toJSON)) {
155      value = %_Call(toJSON, value, key);
156    }
157  }
158  if (IS_CALLABLE(replacer)) {
159    value = %_Call(replacer, holder, key, value);
160  }
161  if (IS_STRING(value)) {
162    return %QuoteJSONString(value);
163  } else if (IS_NUMBER(value)) {
164    return JSON_NUMBER_TO_STRING(value);
165  } else if (IS_BOOLEAN(value)) {
166    return value ? "true" : "false";
167  } else if (IS_NULL(value)) {
168    return "null";
169  } else if (IS_RECEIVER(value) && !IS_CALLABLE(value)) {
170    // Non-callable object. If it's a primitive wrapper, it must be unwrapped.
171    if (%is_arraylike(value)) {
172      return SerializeArray(value, replacer, stack, indent, gap);
173    } else if (IS_NUMBER_WRAPPER(value)) {
174      value = TO_NUMBER(value);
175      return JSON_NUMBER_TO_STRING(value);
176    } else if (IS_STRING_WRAPPER(value)) {
177      return %QuoteJSONString(TO_STRING(value));
178    } else if (IS_BOOLEAN_WRAPPER(value)) {
179      return %_ValueOf(value) ? "true" : "false";
180    } else {
181      return SerializeObject(value, replacer, stack, indent, gap);
182    }
183  }
184  // Undefined or a callable object.
185  return UNDEFINED;
186}
187
188
189function JSONStringify(value, replacer, space) {
190  if (%_ArgumentsLength() == 1 && !IS_PROXY(value)) {
191    return %BasicJSONStringify(value);
192  }
193  if (!IS_CALLABLE(replacer) && %is_arraylike(replacer)) {
194    var property_list = new InternalArray();
195    var seen_properties = new GlobalSet();
196    var length = TO_LENGTH(replacer.length);
197    for (var i = 0; i < length; i++) {
198      var v = replacer[i];
199      var item;
200      if (IS_STRING(v)) {
201        item = v;
202      } else if (IS_NUMBER(v)) {
203        item = %_NumberToString(v);
204      } else if (IS_STRING_WRAPPER(v) || IS_NUMBER_WRAPPER(v)) {
205        item = TO_STRING(v);
206      } else {
207        continue;
208      }
209      if (!seen_properties.has(item)) {
210        property_list.push(item);
211        seen_properties.add(item);
212      }
213    }
214    replacer = property_list;
215  }
216  if (IS_OBJECT(space)) {
217    // Unwrap 'space' if it is wrapped
218    if (IS_NUMBER_WRAPPER(space)) {
219      space = TO_NUMBER(space);
220    } else if (IS_STRING_WRAPPER(space)) {
221      space = TO_STRING(space);
222    }
223  }
224  var gap;
225  if (IS_NUMBER(space)) {
226    space = MaxSimple(0, MinSimple(TO_INTEGER(space), 10));
227    gap = %_SubString("          ", 0, space);
228  } else if (IS_STRING(space)) {
229    if (space.length > 10) {
230      gap = %_SubString(space, 0, 10);
231    } else {
232      gap = space;
233    }
234  } else {
235    gap = "";
236  }
237  return JSONSerialize('', {'': value}, replacer, new InternalArray(), "", gap);
238}
239
240// -------------------------------------------------------------------
241
242%AddNamedProperty(GlobalJSON, toStringTagSymbol, "JSON", READ_ONLY | DONT_ENUM);
243
244// Set up non-enumerable properties of the JSON object.
245utils.InstallFunctions(GlobalJSON, DONT_ENUM, [
246  "parse", JSONParse,
247  "stringify", JSONStringify
248]);
249
250// -------------------------------------------------------------------
251// Date.toJSON
252
253// 20.3.4.37 Date.prototype.toJSON ( key )
254function DateToJSON(key) {
255  var o = TO_OBJECT(this);
256  var tv = TO_PRIMITIVE_NUMBER(o);
257  if (IS_NUMBER(tv) && !NUMBER_IS_FINITE(tv)) {
258    return null;
259  }
260  return o.toISOString();
261}
262
263// Set up non-enumerable functions of the Date prototype object.
264utils.InstallFunctions(GlobalDate.prototype, DONT_ENUM, [
265  "toJSON", DateToJSON
266]);
267
268// -------------------------------------------------------------------
269// JSON Builtins
270
271function JsonSerializeAdapter(key, object) {
272  var holder = {};
273  holder[key] = object;
274  // No need to pass the actual holder since there is no replacer function.
275  return JSONSerialize(key, holder, UNDEFINED, new InternalArray(), "", "");
276}
277
278%InstallToContext(["json_serialize_adapter", JsonSerializeAdapter]);
279
280})
281