Home | History | Annotate | Download | only in src
      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 
     12 var $JSON = global.JSON;
     13 
     14 // -------------------------------------------------------------------
     15 
     16 function 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 
     41 function 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 
     50 function 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 
     80 function 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 
    128 function 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 
    167 function 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 
    220 function 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 
    230 SetUpJSON();
    231 
    232 
    233 // -------------------------------------------------------------------
    234 // JSON Builtins
    235 
    236 function 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