This is not a limited-enumerated attribute, so let's use reflectString instead.

/* -*- 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 * the Mozilla Foundation. * Portions created by the Initial Developer are Copyright (C) 2011 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Jeff Walden <jwalden+code@mit.edu> (original author) * * 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"jsarray.h"#include"jsnum.h"#include"jsonparser.h"#include"jsobjinlines.h"#include"jsstrinlines.h"usingnamespacejs;voidJSONParser::error(constchar*msg){if(errorHandling==RaiseError)JS_ReportErrorNumber(cx,js_GetErrorMessage,NULL,JSMSG_JSON_BAD_PARSE,msg);}boolJSONParser::errorReturn(){returnerrorHandling==NoError;}template<JSONParser::StringTypeST>JSONParser::TokenJSONParser::readString(){JS_ASSERT(current<end);JS_ASSERT(*current=='"');/* * JSONString: * /^"([^\u0000-\u001F"\\]|\\(["/\\bfnrt]|u[0-9a-fA-F]{4}))*"$/ */if(++current==end){error("unterminated string literal");returntoken(Error);}/* * Optimization: if the source contains no escaped characters, create the * string directly from the source text. */RangedPtr<constjschar>start=current;for(;current<end;current++){if(*current=='"'){size_tlength=current-start;current++;JSFlatString*str=(ST==JSONParser::PropertyName)?js_AtomizeChars(cx,start.get(),length):js_NewStringCopyN(cx,start.get(),length);if(!str)returntoken(OOM);returnstringToken(str);}if(*current=='\\')break;if(*current<=0x001F){error("bad control character in string literal");returntoken(Error);}}/* * Slow case: string contains escaped characters. Copy a maximal sequence * of unescaped characters into a temporary buffer, then an escaped * character, and repeat until the entire string is consumed. */StringBufferbuffer(cx);do{if(start<current&&!buffer.append(start.get(),current.get()))returntoken(OOM);if(current>=end)break;jscharc=*current++;if(c=='"'){JSFlatString*str=(ST==JSONParser::PropertyName)?buffer.finishAtom():buffer.finishString();if(!str)returntoken(OOM);returnstringToken(str);}if(c!='\\'){error("bad character in string literal");returntoken(Error);}if(current>=end)break;switch(*current++){case'"':c='"';break;case'/':c='/';break;case'\\':c='\\';break;case'b':c='\b';break;case'f':c='\f';break;case'n':c='\n';break;case'r':c='\r';break;case't':c='\t';break;case'u':if(end-current<4){error("bad Unicode escape");returntoken(Error);}if(JS7_ISHEX(current[0])&&JS7_ISHEX(current[1])&&JS7_ISHEX(current[2])&&JS7_ISHEX(current[3])){c=(JS7_UNHEX(current[0])<<12)|(JS7_UNHEX(current[1])<<8)|(JS7_UNHEX(current[2])<<4)|(JS7_UNHEX(current[3]));current+=4;break;}/* FALL THROUGH */default:error("bad escaped character");returntoken(Error);}if(!buffer.append(c))returntoken(OOM);start=current;for(;current<end;current++){if(*current=='"'||*current=='\\'||*current<=0x001F)break;}}while(current<end);error("unterminated string");returntoken(Error);}JSONParser::TokenJSONParser::readNumber(){JS_ASSERT(current<end);JS_ASSERT(JS7_ISDEC(*current)||*current=='-');/* * JSONNumber: * /^-?(0|[1-9][0-9]+)(\.[0-9]+)?([eE][\+\-]?[0-9]+)?$/ */boolnegative=*current=='-';/* -? */if(negative&&++current==end){error("no number after minus sign");returntoken(Error);}constRangedPtr<constjschar>digitStart=current;/* 0|[1-9][0-9]+ */if(!JS7_ISDEC(*current)){error("unexpected non-digit");returntoken(Error);}if(*current++!='0'){for(;current<end;current++){if(!JS7_ISDEC(*current))break;}}/* Fast path: no fractional or exponent part. */if(current==end||(*current!='.'&&*current!='e'&&*current!='E')){constjschar*dummy;jsdoubled;if(!GetPrefixInteger(cx,digitStart.get(),current.get(),10,&dummy,&d))returntoken(OOM);JS_ASSERT(current==dummy);returnnumberToken(negative?-d:d);}/* (\.[0-9]+)? */if(current<end&&*current=='.'){if(++current==end){error("missing digits after decimal point");returntoken(Error);}if(!JS7_ISDEC(*current)){error("unterminated fractional number");returntoken(Error);}while(++current<end){if(!JS7_ISDEC(*current))break;}}/* ([eE][\+\-]?[0-9]+)? */if(current<end&&(*current=='e'||*current=='E')){if(++current==end){error("missing digits after exponent indicator");returntoken(Error);}if(*current=='+'||*current=='-'){if(++current==end){error("missing digits after exponent sign");returntoken(Error);}}if(!JS7_ISDEC(*current)){error("exponent part is missing a number");returntoken(Error);}while(++current<end){if(!JS7_ISDEC(*current))break;}}jsdoubled;constjschar*finish;if(!js_strtod(cx,digitStart.get(),current.get(),&finish,&d))returntoken(OOM);JS_ASSERT(current==finish);returnnumberToken(negative?-d:d);}staticinlineboolIsJSONWhitespace(jscharc){returnc=='\t'||c=='\r'||c=='\n'||c==' ';}JSONParser::TokenJSONParser::advance(){while(current<end&&IsJSONWhitespace(*current))current++;if(current>=end){error("unexpected end of data");returntoken(Error);}switch(*current){case'"':returnreadString<LiteralValue>();case'-':case'0':case'1':case'2':case'3':case'4':case'5':case'6':case'7':case'8':case'9':returnreadNumber();case't':if(end-current<4||current[1]!='r'||current[2]!='u'||current[3]!='e'){error("unexpected keyword");returntoken(Error);}current+=4;returntoken(True);case'f':if(end-current<5||current[1]!='a'||current[2]!='l'||current[3]!='s'||current[4]!='e'){error("unexpected keyword");returntoken(Error);}current+=5;returntoken(False);case'n':if(end-current<4||current[1]!='u'||current[2]!='l'||current[3]!='l'){error("unexpected keyword");returntoken(Error);}current+=4;returntoken(Null);case'[':current++;returntoken(ArrayOpen);case']':current++;returntoken(ArrayClose);case'{':current++;returntoken(ObjectOpen);case'}':current++;returntoken(ObjectClose);case',':current++;returntoken(Comma);case':':current++;returntoken(Colon);default:error("unexpected character");returntoken(Error);}}JSONParser::TokenJSONParser::advanceAfterObjectOpen(){JS_ASSERT(current[-1]=='{');while(current<end&&IsJSONWhitespace(*current))current++;if(current>=end){error("end of data while reading object contents");returntoken(Error);}if(*current=='"')returnreadString<PropertyName>();if(*current=='}'){current++;returntoken(ObjectClose);}error("expected property name or '}'");returntoken(Error);}staticinlinevoidAssertPastValue(constRangedPtr<constjschar>current){/* * We're past an arbitrary JSON value, so the previous character is * *somewhat* constrained, even if this assertion is pretty broad. Don't * knock it till you tried it: this assertion *did* catch a bug once. */JS_ASSERT((current[-1]=='l'&&current[-2]=='l'&&current[-3]=='u'&&current[-4]=='n')||(current[-1]=='e'&&current[-2]=='u'&&current[-3]=='r'&&current[-4]=='t')||(current[-1]=='e'&&current[-2]=='s'&&current[-3]=='l'&&current[-4]=='a'&&current[-5]=='f')||current[-1]=='}'||current[-1]==']'||current[-1]=='"'||JS7_ISDEC(current[-1]));}JSONParser::TokenJSONParser::advanceAfterArrayElement(){AssertPastValue(current);while(current<end&&IsJSONWhitespace(*current))current++;if(current>=end){error("end of data when ',' or ']' was expected");returntoken(Error);}if(*current==','){current++;returntoken(Comma);}if(*current==']'){current++;returntoken(ArrayClose);}error("expected ',' or ']' after array element");returntoken(Error);}JSONParser::TokenJSONParser::advancePropertyName(){JS_ASSERT(current[-1]==',');while(current<end&&IsJSONWhitespace(*current))current++;if(current>=end){error("end of data when property name was expected");returntoken(Error);}if(*current=='"')returnreadString<PropertyName>();if(parsingMode==LegacyJSON&&*current=='}'){/* * Previous JSON parsing accepted trailing commas in non-empty object * syntax, and some users depend on this. (Specifically, Places data * serialization in versions of Firefox before 4.0. We can remove this * mode when profile upgrades from 3.6 become unsupported.) Permit * such trailing commas only when legacy parsing is specifically * requested. */current++;returntoken(ObjectClose);}error("expected double-quoted property name");returntoken(Error);}JSONParser::TokenJSONParser::advancePropertyColon(){JS_ASSERT(current[-1]=='"');while(current<end&&IsJSONWhitespace(*current))current++;if(current>=end){error("end of data after property name when ':' was expected");returntoken(Error);}if(*current==':'){current++;returntoken(Colon);}error("expected ':' after property name in object");returntoken(Error);}JSONParser::TokenJSONParser::advanceAfterProperty(){AssertPastValue(current);while(current<end&&IsJSONWhitespace(*current))current++;if(current>=end){error("end of data after property value in object");returntoken(Error);}if(*current==','){current++;returntoken(Comma);}if(*current=='}'){current++;returntoken(ObjectClose);}error("expected ',' or '}' after property value in object");returntoken(Error);}/* * This enum is local to JSONParser::parse, below, but ISO C++98 doesn't allow * templates to depend on local types. Boo-urns! */enumParserState{FinishArrayElement,FinishObjectMember,JSONValue};boolJSONParser::parse(Value*vp){Vector<ParserState>stateStack(cx);AutoValueVectorvalueStack(cx);*vp=UndefinedValue();Tokentoken;ParserStatestate=JSONValue;while(true){switch(state){caseFinishObjectMember:{Valuev=valueStack.popCopy();/* * NB: Relies on js_DefineNativeProperty performing * js_CheckForStringIndex. */jsidpropid=ATOM_TO_JSID(&valueStack.popCopy().toString()->asAtom());if(!DefineNativeProperty(cx,&valueStack.back().toObject(),propid,v,PropertyStub,StrictPropertyStub,JSPROP_ENUMERATE,0,0)){returnfalse;}token=advanceAfterProperty();if(token==ObjectClose)break;if(token!=Comma){if(token==OOM)returnfalse;if(token!=Error)error("expected ',' or '}' after property-value pair in object literal");returnerrorReturn();}token=advancePropertyName();/* FALL THROUGH */}JSONMember:if(token==String){if(!valueStack.append(atomValue()))returnfalse;token=advancePropertyColon();if(token!=Colon){JS_ASSERT(token==Error);returnerrorReturn();}if(!stateStack.append(FinishObjectMember))returnfalse;gotoJSONValue;}if(token==ObjectClose){JS_ASSERT(state==FinishObjectMember);JS_ASSERT(parsingMode==LegacyJSON);break;}if(token==OOM)returnfalse;if(token!=Error)error("property names must be double-quoted strings");returnerrorReturn();caseFinishArrayElement:{Valuev=valueStack.popCopy();if(!js_NewbornArrayPush(cx,&valueStack.back().toObject(),v))returnfalse;token=advanceAfterArrayElement();if(token==Comma){if(!stateStack.append(FinishArrayElement))returnfalse;gotoJSONValue;}if(token==ArrayClose)break;JS_ASSERT(token==Error);returnerrorReturn();}JSONValue:caseJSONValue:token=advance();JSONValueSwitch:switch(token){caseString:caseNumber:if(!valueStack.append(token==String?stringValue():numberValue()))returnfalse;break;caseTrue:if(!valueStack.append(BooleanValue(true)))returnfalse;break;caseFalse:if(!valueStack.append(BooleanValue(false)))returnfalse;break;caseNull:if(!valueStack.append(NullValue()))returnfalse;break;caseArrayOpen:{JSObject*obj=NewDenseEmptyArray(cx);if(!obj||!valueStack.append(ObjectValue(*obj)))returnfalse;token=advance();if(token==ArrayClose)break;if(!stateStack.append(FinishArrayElement))returnfalse;gotoJSONValueSwitch;}caseObjectOpen:{JSObject*obj=NewBuiltinClassInstance(cx,&js_ObjectClass);if(!obj||!valueStack.append(ObjectValue(*obj)))returnfalse;token=advanceAfterObjectOpen();if(token==ObjectClose)break;gotoJSONMember;}caseArrayClose:if(parsingMode==LegacyJSON&&!stateStack.empty()&&stateStack.back()==FinishArrayElement){/* * Previous JSON parsing accepted trailing commas in * non-empty array syntax, and some users depend on this. * (Specifically, Places data serialization in versions of * Firefox prior to 4.0. We can remove this mode when * profile upgrades from 3.6 become unsupported.) Permit * such trailing commas only when specifically * instructed to do so. */stateStack.popBack();break;}/* FALL THROUGH */caseObjectClose:caseColon:caseComma:error("unexpected character");returnerrorReturn();caseOOM:returnfalse;caseError:returnerrorReturn();}break;}if(stateStack.empty())break;state=stateStack.popCopy();}for(;current<end;current++){if(!IsJSONWhitespace(*current)){error("unexpected non-whitespace character after JSON data");returnerrorReturn();}}JS_ASSERT(end==current);JS_ASSERT(valueStack.length()==1);*vp=valueStack[0];returntrue;}