/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=99: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is SpiderMonkey JSON. * * The Initial Developer of the Original Code is * Mozilla Corporation. * Portions created by the Initial Developer are Copyright (C) 1998-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Robert Sayre <sayrer@gmail.com> * Dave Camp <dcamp@mozilla.com> * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */#include<string.h>#include"jsapi.h"#include"jsarray.h"#include"jsatom.h"#include"jsbool.h"#include"jscntxt.h"#include"jsfun.h"#include"jsinterp.h"#include"jsiter.h"#include"jsnum.h"#include"jsobj.h"#include"json.h"#include"jsonparser.h"#include"jsprf.h"#include"jsstr.h"#include"jstypes.h"#include"jsstdint.h"#include"jsutil.h"#include"jsxml.h"#include"frontend/TokenStream.h"#include"jsatominlines.h"#include"jsboolinlines.h"#include"jsinferinlines.h"#include"jsobjinlines.h"#include"jsstrinlines.h"#include"vm/Stack-inl.h"usingnamespacejs;usingnamespacejs::gc;usingnamespacejs::types;Classjs::JSONClass={js_JSON_str,JSCLASS_HAS_CACHED_PROTO(JSProto_JSON),JS_PropertyStub,/* addProperty */JS_PropertyStub,/* delProperty */JS_PropertyStub,/* getProperty */JS_StrictPropertyStub,/* setProperty */JS_EnumerateStub,JS_ResolveStub,JS_ConvertStub};/* ES5 15.12.2. */JSBooljs_json_parse(JSContext*cx,uintNargc,Value*vp){/* Step 1. */JSLinearString*linear;if(argc>=1){JSString*str=ToString(cx,vp[2]);if(!str)returnfalse;linear=str->ensureLinear(cx);if(!linear)returnfalse;}else{linear=cx->runtime->atomState.typeAtoms[JSTYPE_VOID];}JS::Anchor<JSString*>anchor(linear);Valuereviver=(argc>=2)?vp[3]:UndefinedValue();/* Steps 2-5. */returnParseJSONWithReviver(cx,linear->chars(),linear->length(),reviver,vp);}/* ES5 15.12.3. */JSBooljs_json_stringify(JSContext*cx,uintNargc,Value*vp){*vp=(argc>=1)?vp[2]:UndefinedValue();JSObject*replacer=(argc>=2&&vp[3].isObject())?&vp[3].toObject():NULL;Valuespace=(argc>=3)?vp[4]:UndefinedValue();StringBuffersb(cx);if(!js_Stringify(cx,vp,replacer,space,sb))returnfalse;// XXX This can never happen to nsJSON.cpp, but the JSON object// needs to support returning undefined. So this is a little awkward// for the API, because we want to support streaming writers.if(!sb.empty()){JSString*str=sb.finishString();if(!str)returnfalse;vp->setString(str);}else{vp->setUndefined();}returntrue;}staticinlineboolIsQuoteSpecialCharacter(jscharc){JS_STATIC_ASSERT('\b'<' ');JS_STATIC_ASSERT('\f'<' ');JS_STATIC_ASSERT('\n'<' ');JS_STATIC_ASSERT('\r'<' ');JS_STATIC_ASSERT('\t'<' ');returnc=='"'||c=='\\'||c<' ';}/* ES5 15.12.3 Quote. */staticboolQuote(JSContext*cx,StringBuffer&sb,JSString*str){JS::Anchor<JSString*>anchor(str);size_tlen=str->length();constjschar*buf=str->getChars(cx);if(!buf)returnfalse;/* Step 1. */if(!sb.append('"'))returnfalse;/* Step 2. */for(size_ti=0;i<len;++i){/* Batch-append maximal character sequences containing no escapes. */size_tmark=i;do{if(IsQuoteSpecialCharacter(buf[i]))break;}while(++i<len);if(i>mark){if(!sb.append(&buf[mark],i-mark))returnfalse;if(i==len)break;}jscharc=buf[i];if(c=='"'||c=='\\'){if(!sb.append('\\')||!sb.append(c))returnfalse;}elseif(c=='\b'||c=='\f'||c=='\n'||c=='\r'||c=='\t'){jscharabbrev=(c=='\b')?'b':(c=='\f')?'f':(c=='\n')?'n':(c=='\r')?'r':'t';if(!sb.append('\\')||!sb.append(abbrev))returnfalse;}else{JS_ASSERT(c<' ');if(!sb.append("\\u00"))returnfalse;JS_ASSERT((c>>4)<10);uint8_tx=c>>4,y=c%16;if(!sb.append('0'+x)||!sb.append(y<10?'0'+y:'a'+(y-10)))returnfalse;}}/* Steps 3-4. */returnsb.append('"');}classStringifyContext{public:StringifyContext(JSContext*cx,StringBuffer&sb,constStringBuffer&gap,JSObject*replacer,constAutoIdVector&propertyList):sb(sb),gap(gap),replacer(replacer),propertyList(propertyList),depth(0),objectStack(cx){}boolinit(){returnobjectStack.init(16);}#ifdef DEBUG~StringifyContext(){JS_ASSERT(objectStack.empty());}#endifStringBuffer&sb;constStringBuffer⪆JSObject*constreplacer;constAutoIdVector&propertyList;uint32_tdepth;HashSet<JSObject*>objectStack;};staticJSBoolStr(JSContext*cx,constValue&v,StringifyContext*scx);staticJSBoolWriteIndent(JSContext*cx,StringifyContext*scx,uint32_tlimit){if(!scx->gap.empty()){if(!scx->sb.append('\n'))returnJS_FALSE;for(uint32_ti=0;i<limit;i++){if(!scx->sb.append(scx->gap.begin(),scx->gap.end()))returnJS_FALSE;}}returnJS_TRUE;}classCycleDetector{public:CycleDetector(StringifyContext*scx,JSObject*obj):objectStack(scx->objectStack),obj(obj){}boolinit(JSContext*cx){HashSet<JSObject*>::AddPtrptr=objectStack.lookupForAdd(obj);if(ptr){JS_ReportErrorNumber(cx,js_GetErrorMessage,NULL,JSMSG_CYCLIC_VALUE,js_object_str);returnfalse;}returnobjectStack.add(ptr,obj);}~CycleDetector(){objectStack.remove(obj);}private:HashSet<JSObject*>&objectStack;JSObject*constobj;};template<typenameKeyType>classKeyStringifier{};template<>classKeyStringifier<uint32_t>{public:staticJSString*toString(JSContext*cx,uint32_tindex){returnIndexToString(cx,index);}};template<>classKeyStringifier<jsid>{public:staticJSString*toString(JSContext*cx,jsidid){returnIdToString(cx,id);}};/* * ES5 15.12.3 Str, steps 2-4, extracted to enable preprocessing of property * values when stringifying objects in JO. */template<typenameKeyType>staticboolPreprocessValue(JSContext*cx,JSObject*holder,KeyTypekey,Value*vp,StringifyContext*scx){JSString*keyStr=NULL;/* Step 2. */if(vp->isObject()){ValuetoJSON;jsidid=ATOM_TO_JSID(cx->runtime->atomState.toJSONAtom);if(!js_GetMethod(cx,&vp->toObject(),id,JSGET_NO_METHOD_BARRIER,&toJSON))returnfalse;if(js_IsCallable(toJSON)){keyStr=KeyStringifier<KeyType>::toString(cx,key);if(!keyStr)returnfalse;InvokeArgsGuardargs;if(!cx->stack.pushInvokeArgs(cx,1,&args))returnfalse;args.calleev()=toJSON;args.thisv()=*vp;args[0]=StringValue(keyStr);if(!Invoke(cx,args))returnfalse;*vp=args.rval();}}/* Step 3. */if(scx->replacer&&scx->replacer->isCallable()){if(!keyStr){keyStr=KeyStringifier<KeyType>::toString(cx,key);if(!keyStr)returnfalse;}InvokeArgsGuardargs;if(!cx->stack.pushInvokeArgs(cx,2,&args))returnfalse;args.calleev()=ObjectValue(*scx->replacer);args.thisv()=ObjectValue(*holder);args[0]=StringValue(keyStr);args[1]=*vp;if(!Invoke(cx,args))returnfalse;*vp=args.rval();}/* Step 4. */if(vp->isObject()){JSObject&obj=vp->toObject();if(ObjectClassIs(obj,ESClass_Number,cx)){doubled;if(!ToNumber(cx,*vp,&d))returnfalse;vp->setNumber(d);}elseif(ObjectClassIs(obj,ESClass_String,cx)){JSString*str=ToStringSlow(cx,*vp);if(!str)returnfalse;vp->setString(str);}elseif(ObjectClassIs(obj,ESClass_Boolean,cx)){if(!BooleanGetPrimitiveValue(cx,obj,vp))returnfalse;JS_ASSERT(vp->isBoolean());}}returntrue;}/* * Determines whether a value which has passed by ES5 150.2.3 Str steps 1-4's * gauntlet will result in Str returning |undefined|. This function is used to * properly omit properties resulting in such values when stringifying objects, * while properly stringifying such properties as null when they're encountered * in arrays. */staticinlineboolIsFilteredValue(constValue&v){returnv.isUndefined()||js_IsCallable(v)||(v.isObject()&&v.toObject().isXML());}/* ES5 15.12.3 JO. */staticJSBoolJO(JSContext*cx,JSObject*obj,StringifyContext*scx){/* * This method implements the JO algorithm in ES5 15.12.3, but: * * * The algorithm is somewhat reformulated to allow the final string to * be streamed into a single buffer, rather than be created and copied * into place incrementally as the ES5 algorithm specifies it. This * requires moving portions of the Str call in 8a into this algorithm * (and in JA as well). *//* Steps 1-2, 11. */CycleDetectordetect(scx,obj);if(!detect.init(cx))returnJS_FALSE;if(!scx->sb.append('{'))returnJS_FALSE;/* Steps 5-7. */Maybe<AutoIdVector>ids;constAutoIdVector*props;if(scx->replacer&&!scx->replacer->isCallable()){JS_ASSERT(JS_IsArrayObject(cx,scx->replacer));props=&scx->propertyList;}else{JS_ASSERT_IF(scx->replacer,scx->propertyList.length()==0);ids.construct(cx);if(!GetPropertyNames(cx,obj,JSITER_OWNONLY,ids.addr()))returnfalse;props=ids.addr();}/* My kingdom for not-quite-initialized-from-the-start references. */constAutoIdVector&propertyList=*props;/* Steps 8-10, 13. */boolwroteMember=false;for(size_ti=0,len=propertyList.length();i<len;i++){/* * Steps 8a-8b. Note that the call to Str is broken up into 1) getting * the property; 2) processing for toJSON, calling the replacer, and * handling boxed Number/String/Boolean objects; 3) filtering out * values which process to |undefined|, and 4) stringifying all values * which pass the filter. */constjsid&id=propertyList[i];ValueoutputValue;if(!obj->getGeneric(cx,id,&outputValue))returnfalse;if(!PreprocessValue(cx,obj,id,&outputValue,scx))returnfalse;if(IsFilteredValue(outputValue))continue;/* Output a comma unless this is the first member to write. */if(wroteMember&&!scx->sb.append(','))returnfalse;wroteMember=true;if(!WriteIndent(cx,scx,scx->depth))returnfalse;JSString*s=IdToString(cx,id);if(!s)returnfalse;if(!Quote(cx,scx->sb,s)||!scx->sb.append(':')||!(scx->gap.empty()||scx->sb.append(' '))||!Str(cx,outputValue,scx)){returnfalse;}}if(wroteMember&&!WriteIndent(cx,scx,scx->depth-1))returnfalse;returnscx->sb.append('}');}/* ES5 15.12.3 JA. */staticJSBoolJA(JSContext*cx,JSObject*obj,StringifyContext*scx){/* * This method implements the JA algorithm in ES5 15.12.3, but: * * * The algorithm is somewhat reformulated to allow the final string to * be streamed into a single buffer, rather than be created and copied * into place incrementally as the ES5 algorithm specifies it. This * requires moving portions of the Str call in 8a into this algorithm * (and in JO as well). *//* Steps 1-2, 11. */CycleDetectordetect(scx,obj);if(!detect.init(cx))returnJS_FALSE;if(!scx->sb.append('['))returnJS_FALSE;/* Step 6. */jsuintlength;if(!js_GetLengthProperty(cx,obj,&length))returnJS_FALSE;/* Steps 7-10. */if(length!=0){/* Steps 4, 10b(i). */if(!WriteIndent(cx,scx,scx->depth))returnJS_FALSE;/* Steps 7-10. */ValueoutputValue;for(uint32_ti=0;i<length;i++){/* * Steps 8a-8c. Again note how the call to the spec's Str method * is broken up into getting the property, running it past toJSON * and the replacer and maybe unboxing, and interpreting some * values as |null| in separate steps. */if(!obj->getElement(cx,i,&outputValue))returnJS_FALSE;if(!PreprocessValue(cx,obj,i,&outputValue,scx))returnJS_FALSE;if(IsFilteredValue(outputValue)){if(!scx->sb.append("null"))returnJS_FALSE;}else{if(!Str(cx,outputValue,scx))returnJS_FALSE;}/* Steps 3, 4, 10b(i). */if(i<length-1){if(!scx->sb.append(','))returnJS_FALSE;if(!WriteIndent(cx,scx,scx->depth))returnJS_FALSE;}}/* Step 10(b)(iii). */if(!WriteIndent(cx,scx,scx->depth-1))returnJS_FALSE;}returnscx->sb.append(']');}staticJSBoolStr(JSContext*cx,constValue&v,StringifyContext*scx){/* Step 11 must be handled by the caller. */JS_ASSERT(!IsFilteredValue(v));JS_CHECK_RECURSION(cx,returnfalse);/* * This method implements the Str algorithm in ES5 15.12.3, but: * * * We move property retrieval (step 1) into callers to stream the * stringification process and avoid constantly copying strings. * * We move the preprocessing in steps 2-4 into a helper function to * allow both JO and JA to use this method. While JA could use it * without this move, JO must omit any |undefined|-valued property per * so it can't stream out a value using the Str method exactly as * defined by ES5. * * We move step 11 into callers, again to ease streaming. *//* Step 8. */if(v.isString())returnQuote(cx,scx->sb,v.toString());/* Step 5. */if(v.isNull())returnscx->sb.append("null");/* Steps 6-7. */if(v.isBoolean())returnv.toBoolean()?scx->sb.append("true"):scx->sb.append("false");/* Step 9. */if(v.isNumber()){if(v.isDouble()){if(!JSDOUBLE_IS_FINITE(v.toDouble()))returnscx->sb.append("null");}StringBuffersb(cx);if(!NumberValueToStringBuffer(cx,v,sb))returnfalse;returnscx->sb.append(sb.begin(),sb.length());}/* Step 10. */JS_ASSERT(v.isObject());JSObject*obj=&v.toObject();scx->depth++;JSBoolok;if(ObjectClassIs(v.toObject(),ESClass_Array,cx))ok=JA(cx,obj,scx);elseok=JO(cx,obj,scx);scx->depth--;returnok;}/* ES5 15.12.3. */JSBooljs_Stringify(JSContext*cx,Value*vp,JSObject*replacer,Valuespace,StringBuffer&sb){/* Step 4. */AutoIdVectorpropertyList(cx);if(replacer){if(replacer->isCallable()){/* Step 4a(i): use replacer to transform values. */}elseif(ObjectClassIs(*replacer,ESClass_Array,cx)){/* * Step 4b: The spec algorithm is unhelpfully vague about the exact * steps taken when the replacer is an array, regarding the exact * sequence of [[Get]] calls for the array's elements, when its * overall length is calculated, whether own or own plus inherited * properties are considered, and so on. A rewrite was proposed in * <https://mail.mozilla.org/pipermail/es5-discuss/2011-April/003976.html>, * whose steps are copied below, and which are implemented here. * * i. Let PropertyList be an empty internal List. * ii. Let len be the result of calling the [[Get]] internal * method of replacer with the argument "length". * iii. Let i be 0. * iv. While i < len: * 1. Let item be undefined. * 2. Let v be the result of calling the [[Get]] internal * method of replacer with the argument ToString(i). * 3. If Type(v) is String then let item be v. * 4. Else if Type(v) is Number then let item be ToString(v). * 5. Else if Type(v) is Object then * a. If the [[Class]] internal property of v is "String" * or "Number" then let item be ToString(v). * 6. If item is not undefined and item is not currently an * element of PropertyList then, * a. Append item to the end of PropertyList. * 7. Let i be i + 1. *//* Step 4b(ii). */jsuintlen;JS_ALWAYS_TRUE(js_GetLengthProperty(cx,replacer,&len));if(replacer->isDenseArray())len=JS_MIN(len,replacer->getDenseArrayCapacity());HashSet<jsid>idSet(cx);if(!idSet.init(len))returnfalse;/* Step 4b(iii). */jsuinti=0;/* Step 4b(iv). */for(;i<len;i++){/* Step 4b(iv)(2). */Valuev;if(!replacer->getElement(cx,i,&v))returnfalse;jsidid;if(v.isNumber()){/* Step 4b(iv)(4). */int32_tn;if(v.isNumber()&&ValueFitsInInt32(v,&n)&&INT_FITS_IN_JSID(n)){id=INT_TO_JSID(n);}else{if(!js_ValueToStringId(cx,v,&id))returnfalse;id=js_CheckForStringIndex(id);}}elseif(v.isString()||(v.isObject()&&(ObjectClassIs(v.toObject(),ESClass_String,cx)||ObjectClassIs(v.toObject(),ESClass_Number,cx)))){/* Step 4b(iv)(3), 4b(iv)(5). */if(!js_ValueToStringId(cx,v,&id))returnfalse;id=js_CheckForStringIndex(id);}else{continue;}/* Step 4b(iv)(6). */HashSet<jsid>::AddPtrp=idSet.lookupForAdd(id);if(!p){/* Step 4b(iv)(6)(a). */if(!idSet.add(p,id)||!propertyList.append(id))returnfalse;}}}else{replacer=NULL;}}/* Step 5. */if(space.isObject()){JSObject&spaceObj=space.toObject();if(ObjectClassIs(spaceObj,ESClass_Number,cx)){jsdoubled;if(!ToNumber(cx,space,&d))returnfalse;space=NumberValue(d);}elseif(ObjectClassIs(spaceObj,ESClass_String,cx)){JSString*str=ToStringSlow(cx,space);if(!str)returnfalse;space=StringValue(str);}}StringBuffergap(cx);if(space.isNumber()){/* Step 6. */jsdoubled;JS_ALWAYS_TRUE(ToInteger(cx,space,&d));d=JS_MIN(10,d);if(d>=1&&!gap.appendN(' ',uint32_t(d)))returnfalse;}elseif(space.isString()){/* Step 7. */JSLinearString*str=space.toString()->ensureLinear(cx);if(!str)returnfalse;JS::Anchor<JSString*>anchor(str);size_tlen=JS_MIN(10,space.toString()->length());if(!gap.append(str->chars(),len))returnfalse;}else{/* Step 8. */JS_ASSERT(gap.empty());}/* Step 9. */JSObject*wrapper=NewBuiltinClassInstance(cx,&ObjectClass);if(!wrapper)returnfalse;/* Step 10. */jsidemptyId=ATOM_TO_JSID(cx->runtime->atomState.emptyAtom);if(!DefineNativeProperty(cx,wrapper,emptyId,*vp,JS_PropertyStub,JS_StrictPropertyStub,JSPROP_ENUMERATE,0,0)){returnfalse;}/* Step 11. */StringifyContextscx(cx,sb,gap,replacer,propertyList);if(!scx.init())returnfalse;if(!PreprocessValue(cx,wrapper,emptyId,vp,&scx))returnfalse;if(IsFilteredValue(*vp))returntrue;returnStr(cx,*vp,&scx);}/* ES5 15.12.2 Walk. */staticboolWalk(JSContext*cx,JSObject*holder,jsidname,constValue&reviver,Value*vp){JS_CHECK_RECURSION(cx,returnfalse);/* Step 1. */Valueval;if(!holder->getGeneric(cx,name,&val))returnfalse;/* Step 2. */if(val.isObject()){JSObject*obj=&val.toObject();/* 'val' must have been produced by the JSON parser, so not a proxy. */JS_ASSERT(!obj->isProxy());if(obj->isArray()){/* Step 2a(ii). */uint32_tlength=obj->getArrayLength();/* Step 2a(i), 2a(iii-iv). */for(uint32_ti=0;i<length;i++){jsidid;if(!IndexToId(cx,i,&id))returnfalse;/* Step 2a(iii)(1). */ValuenewElement;if(!Walk(cx,obj,id,reviver,&newElement))returnfalse;/* * Arrays which begin empty and whose properties are always * incrementally appended are always dense, no matter their * length, under current dense/slow array heuristics. * Also, deleting a property from a dense array which is not * currently being enumerated never makes it slow. This array * is never exposed until the reviver sees it below, so it must * be dense and isn't currently being enumerated. Therefore * property definition and deletion will always succeed, * and we need not check for failure. */if(newElement.isUndefined()){/* Step 2a(iii)(2). */JS_ALWAYS_TRUE(array_deleteElement(cx,obj,i,&newElement,false));}else{/* Step 2a(iii)(3). */JS_ALWAYS_TRUE(array_defineElement(cx,obj,i,&newElement,JS_PropertyStub,JS_StrictPropertyStub,JSPROP_ENUMERATE));}}}else{/* Step 2b(i). */AutoIdVectorkeys(cx);if(!GetPropertyNames(cx,obj,JSITER_OWNONLY,&keys))returnfalse;/* Step 2b(ii). */for(size_ti=0,len=keys.length();i<len;i++){/* Step 2b(ii)(1). */ValuenewElement;jsidid=keys[i];if(!Walk(cx,obj,id,reviver,&newElement))returnfalse;if(newElement.isUndefined()){/* Step 2b(ii)(2). */if(!obj->deleteByValue(cx,IdToValue(id),&newElement,false))returnfalse;}else{/* Step 2b(ii)(3). */JS_ASSERT(obj->isNative());if(!DefineNativeProperty(cx,obj,id,newElement,JS_PropertyStub,JS_StrictPropertyStub,JSPROP_ENUMERATE,0,0)){returnfalse;}}}}}/* Step 3. */JSString*key=IdToString(cx,name);if(!key)returnfalse;InvokeArgsGuardargs;if(!cx->stack.pushInvokeArgs(cx,2,&args))returnfalse;args.calleev()=reviver;args.thisv()=ObjectValue(*holder);args[0]=StringValue(key);args[1]=val;if(!Invoke(cx,args))returnfalse;*vp=args.rval();returntrue;}staticboolRevive(JSContext*cx,constValue&reviver,Value*vp){JSObject*obj=NewBuiltinClassInstance(cx,&ObjectClass);if(!obj)returnfalse;if(!obj->defineProperty(cx,cx->runtime->atomState.emptyAtom,*vp))returnfalse;returnWalk(cx,obj,ATOM_TO_JSID(cx->runtime->atomState.emptyAtom),reviver,vp);}namespacejs{JSBoolParseJSONWithReviver(JSContext*cx,constjschar*chars,size_tlength,constValue&reviver,Value*vp,DecodingModedecodingMode/* = STRICT */){/* 15.12.2 steps 2-3. */JSONParserparser(cx,chars,length,decodingMode==STRICT?JSONParser::StrictJSON:JSONParser::LegacyJSON);if(!parser.parse(vp))returnfalse;/* 15.12.2 steps 4-5. */if(js_IsCallable(reviver))returnRevive(cx,reviver,vp);returntrue;}}/* namespace js */#if JS_HAS_TOSOURCEstaticJSBooljson_toSource(JSContext*cx,uintNargc,Value*vp){vp->setString(CLASS_ATOM(cx,JSON));returnJS_TRUE;}#endifstaticJSFunctionSpecjson_static_methods[]={#if JS_HAS_TOSOURCEJS_FN(js_toSource_str,json_toSource,0,0),#endifJS_FN("parse",js_json_parse,2,0),JS_FN("stringify",js_json_stringify,3,0),JS_FS_END};JSObject*js_InitJSONClass(JSContext*cx,JSObject*obj){JSObject*JSON=NewObjectWithClassProto(cx,&JSONClass,NULL,obj);if(!JSON||!JSON->setSingletonType(cx))returnNULL;if(!JS_DefineProperty(cx,obj,js_JSON_str,OBJECT_TO_JSVAL(JSON),JS_PropertyStub,JS_StrictPropertyStub,0))returnNULL;if(!JS_DefineFunctions(cx,JSON,json_static_methods))returnNULL;MarkStandardClassInitializedNoProto(obj,&JSONClass);returnJSON;}