mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge tm to m-c.
This commit is contained in:
commit
f4c8ebe128
@ -915,8 +915,10 @@ nsJSContext::DOMOperationCallback(JSContext *cx)
|
||||
// Check to see if we are running OOM
|
||||
nsCOMPtr<nsIMemory> mem;
|
||||
NS_GetMemoryManager(getter_AddRefs(mem));
|
||||
if (!mem)
|
||||
if (!mem) {
|
||||
JS_ClearPendingException(cx);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
PRBool lowMemory;
|
||||
mem->IsLowMemory(&lowMemory);
|
||||
@ -931,8 +933,10 @@ nsJSContext::DOMOperationCallback(JSContext *cx)
|
||||
mem->IsLowMemory(&lowMemory);
|
||||
if (lowMemory) {
|
||||
|
||||
if (nsContentUtils::GetBoolPref("dom.prevent_oom_dialog", PR_FALSE))
|
||||
if (nsContentUtils::GetBoolPref("dom.prevent_oom_dialog", PR_FALSE)) {
|
||||
JS_ClearPendingException(cx);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrompt> prompt = GetPromptFromContext(ctx);
|
||||
|
||||
@ -948,10 +952,12 @@ nsJSContext::DOMOperationCallback(JSContext *cx)
|
||||
//GetStringFromName can return NS_OK and still give NULL string
|
||||
if (NS_FAILED(rv) || !title || !msg) {
|
||||
NS_ERROR("Failed to get localized strings.");
|
||||
JS_ClearPendingException(cx);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
prompt->Alert(title, msg);
|
||||
JS_ClearPendingException(cx);
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
@ -1143,6 +1149,7 @@ nsJSContext::DOMOperationCallback(JSContext *cx)
|
||||
}
|
||||
}
|
||||
|
||||
JS_ClearPendingException(cx);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
|
@ -529,7 +529,7 @@ nsJSONListener::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
JSBool ok = JS_FinishJSONParse(mCx, mJSONParser);
|
||||
JSBool ok = JS_FinishJSONParse(mCx, mJSONParser, JSVAL_NULL);
|
||||
mJSONParser = nsnull;
|
||||
|
||||
if (!ok)
|
||||
@ -651,7 +651,7 @@ nsJSONListener::ConsumeConverted(const char* aBuffer, PRUint32 aByteLength)
|
||||
void nsJSONListener::Cleanup()
|
||||
{
|
||||
if (mJSONParser)
|
||||
JS_FinishJSONParse(mCx, mJSONParser);
|
||||
JS_FinishJSONParse(mCx, mJSONParser, JSVAL_NULL);
|
||||
mJSONParser = nsnull;
|
||||
}
|
||||
|
||||
|
@ -160,6 +160,7 @@
|
||||
|
||||
if (!this.crockfordJSON) {
|
||||
crockfordJSON = {};
|
||||
crockfordJSON.window = this;
|
||||
}
|
||||
(function () {
|
||||
|
||||
|
@ -16,4 +16,4 @@ if (!outputDir.exists()) {
|
||||
do_throw(outputName + " is not a directory?")
|
||||
}
|
||||
var crockfordJSON = null;
|
||||
do_import_script("dom/src/json/test/json2.js");
|
||||
do_import_script("dom/src/json/test/json2.js");
|
25
dom/src/json/test/unit/test_encoding_errors.js
Normal file
25
dom/src/json/test/unit/test_encoding_errors.js
Normal file
@ -0,0 +1,25 @@
|
||||
function tooDeep() {
|
||||
var arr = [];
|
||||
var root = [arr];
|
||||
var tail;
|
||||
for (var i = 0; i < 5000; i++) {
|
||||
tail = [];
|
||||
arr.push(tail);
|
||||
arr = tail;
|
||||
}
|
||||
JSON.stringify(root);
|
||||
}
|
||||
function run_test() {
|
||||
do_check_eq("undefined", JSON.stringify(undefined));
|
||||
do_check_eq("undefined", JSON.stringify(function(){}));
|
||||
do_check_eq("undefined", JSON.stringify(<x><y></y></x>));
|
||||
|
||||
var ok = false;
|
||||
try {
|
||||
tooDeep();
|
||||
} catch (e) {
|
||||
do_check_true(e instanceof Error);
|
||||
ok = true;
|
||||
}
|
||||
do_check_true(ok);
|
||||
}
|
21
dom/src/json/test/unit/test_reviver.js
Normal file
21
dom/src/json/test/unit/test_reviver.js
Normal file
@ -0,0 +1,21 @@
|
||||
function doubler(k, v) {
|
||||
do_check_true("string" == typeof k);
|
||||
|
||||
if ((typeof v) == "number")
|
||||
return 2 * v;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
var x = JSON.parse('{"a":5,"b":6}', doubler);
|
||||
do_check_true(x.hasOwnProperty('a'));
|
||||
do_check_true(x.hasOwnProperty('b'));
|
||||
do_check_eq(x.a, 10);
|
||||
do_check_eq(x.b, 12);
|
||||
|
||||
x = JSON.parse('[3, 4, 5]', doubler);
|
||||
do_check_eq(x[0], 6);
|
||||
do_check_eq(x[1], 8);
|
||||
do_check_eq(x[2], 10);
|
||||
}
|
28
dom/src/json/test/unit/test_syntax_errors.js
Normal file
28
dom/src/json/test/unit/test_syntax_errors.js
Normal file
@ -0,0 +1,28 @@
|
||||
function checkForSyntaxErrors(s) {
|
||||
var crockfordErrored = false;
|
||||
try {
|
||||
crockfordJSON.parse(s)
|
||||
} catch (e) {
|
||||
var crockfordErrored = true;
|
||||
do_check_true(e instanceof crockfordJSON.window.SyntaxError);
|
||||
}
|
||||
do_check_true(crockfordErrored);
|
||||
|
||||
var JSONErrored = false;
|
||||
try {
|
||||
JSON.parse(s)
|
||||
} catch (e) {
|
||||
var JSONErrored = true;
|
||||
do_check_true(e instanceof SyntaxError);
|
||||
}
|
||||
do_check_true(JSONErrored);
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
checkForSyntaxErrors("{}...");
|
||||
checkForSyntaxErrors('{"foo": truBBBB}');
|
||||
checkForSyntaxErrors('{foo: truBBBB}');
|
||||
checkForSyntaxErrors('{"foo": undefined}');
|
||||
checkForSyntaxErrors('{"foo": ]');
|
||||
checkForSyntaxErrors('{"foo');
|
||||
}
|
@ -407,7 +407,7 @@ DOMWorkerOperationCallback(JSContext* aCx)
|
||||
PRBool extraThreadAllowed = PR_FALSE;
|
||||
jsrefcount suspendDepth = 0;
|
||||
|
||||
do {
|
||||
for (;;) {
|
||||
// Kill execution if we're canceled.
|
||||
if (worker->IsCanceled()) {
|
||||
LOG(("Forcefully killing JS for worker [0x%p]",
|
||||
@ -420,7 +420,8 @@ DOMWorkerOperationCallback(JSContext* aCx)
|
||||
JS_ResumeRequest(aCx, suspendDepth);
|
||||
}
|
||||
|
||||
// Kill exectuion of the currently running JS.
|
||||
// Kill execution of the currently running JS.
|
||||
JS_ClearPendingException(aCx);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
@ -440,6 +441,7 @@ DOMWorkerOperationCallback(JSContext* aCx)
|
||||
// the worker was canceled since we checked above.
|
||||
if (worker->IsCanceled()) {
|
||||
NS_WARNING("Tried to suspend on a pool that has gone away");
|
||||
JS_ClearPendingException(aCx);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
@ -461,10 +463,7 @@ DOMWorkerOperationCallback(JSContext* aCx)
|
||||
|
||||
nsAutoMonitor mon(pool->Monitor());
|
||||
mon.Wait();
|
||||
} while (1);
|
||||
|
||||
NS_NOTREACHED("Shouldn't get here!");
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -307,7 +307,7 @@ nsDOMWorkerMessageEvent::GetData(nsAString& aData)
|
||||
(uint32)mData.Length());
|
||||
|
||||
// Note the '&& ok' after the call here!
|
||||
ok = JS_FinishJSONParse(cx, parser) && ok;
|
||||
ok = JS_FinishJSONParse(cx, parser, JSVAL_NULL) && ok;
|
||||
if (!ok) {
|
||||
mCachedJSVal = JSVAL_NULL;
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
@ -307,3 +307,8 @@ MSG_DEF(JSMSG_BAD_OBJECT_INIT, 224, 0, JSEXN_SYNTAXERR, "invalid object i
|
||||
MSG_DEF(JSMSG_CANT_SET_ARRAY_ATTRS, 225, 0, JSEXN_INTERNALERR, "can't set attributes on indexed array properties")
|
||||
MSG_DEF(JSMSG_EVAL_ARITY, 226, 0, JSEXN_TYPEERR, "eval accepts only one parameter")
|
||||
MSG_DEF(JSMSG_MISSING_FUN_ARG, 227, 2, JSEXN_TYPEERR, "missing argument {0} when calling function {1}")
|
||||
MSG_DEF(JSMSG_JSON_BAD_PARSE, 228, 0, JSEXN_SYNTAXERR, "JSON.parse")
|
||||
MSG_DEF(JSMSG_JSON_BAD_STRINGIFY, 229, 0, JSEXN_ERR, "JSON.stringify")
|
||||
|
||||
|
||||
|
||||
|
@ -4571,8 +4571,10 @@ js_generic_fast_native_method_dispatcher(JSContext *cx, uintN argc, jsval *vp)
|
||||
* it as if the static was called with one parameter, the explicit |this|
|
||||
* object.
|
||||
*/
|
||||
if (argc != 0)
|
||||
--argc;
|
||||
if (argc != 0) {
|
||||
/* Clear the last parameter in case too few arguments were passed. */
|
||||
vp[2 + --argc] = JSVAL_VOID;
|
||||
}
|
||||
|
||||
native =
|
||||
#ifdef JS_TRACER
|
||||
@ -4636,8 +4638,10 @@ js_generic_native_method_dispatcher(JSContext *cx, JSObject *obj,
|
||||
* it as if the static was called with one parameter, the explicit |this|
|
||||
* object.
|
||||
*/
|
||||
if (argc != 0)
|
||||
--argc;
|
||||
if (argc != 0) {
|
||||
/* Clear the last parameter in case too few arguments were passed. */
|
||||
argv[--argc] = JSVAL_VOID;
|
||||
}
|
||||
|
||||
return fs->call(cx, JSVAL_TO_OBJECT(argv[-1]), argc, argv, rval);
|
||||
}
|
||||
@ -5684,10 +5688,10 @@ JS_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_FinishJSONParse(JSContext *cx, JSONParser *jp)
|
||||
JS_FinishJSONParse(JSContext *cx, JSONParser *jp, jsval reviver)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
return js_FinishJSONParse(cx, jp);
|
||||
return js_FinishJSONParse(cx, jp, reviver);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2513,7 +2513,7 @@ JS_PUBLIC_API(JSBool)
|
||||
JS_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len);
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_FinishJSONParse(JSContext *cx, JSONParser *jp);
|
||||
JS_FinishJSONParse(JSContext *cx, JSONParser *jp, jsval reviver);
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
|
@ -401,7 +401,10 @@ JS_DECLARE_CALLINFO(js_NumberToString)
|
||||
/* Defined in jsstr.cpp */
|
||||
JS_DECLARE_CALLINFO(js_ConcatStrings)
|
||||
JS_DECLARE_CALLINFO(js_String_getelem)
|
||||
JS_DECLARE_CALLINFO(js_String_p_charCodeAt0)
|
||||
JS_DECLARE_CALLINFO(js_String_p_charCodeAt0_int)
|
||||
JS_DECLARE_CALLINFO(js_String_p_charCodeAt)
|
||||
JS_DECLARE_CALLINFO(js_String_p_charCodeAt_int)
|
||||
JS_DECLARE_CALLINFO(js_EqualStrings)
|
||||
JS_DECLARE_CALLINFO(js_CompareStrings)
|
||||
|
||||
|
@ -2235,17 +2235,14 @@ js_ValueToFunctionObject(JSContext *cx, jsval *vp, uintN flags)
|
||||
JSObject *
|
||||
js_ValueToCallableObject(JSContext *cx, jsval *vp, uintN flags)
|
||||
{
|
||||
JSObject *callable;
|
||||
JSObject *callable = JSVAL_IS_PRIMITIVE(*vp) ? NULL : JSVAL_TO_OBJECT(*vp);
|
||||
|
||||
callable = JSVAL_IS_PRIMITIVE(*vp) ? NULL : JSVAL_TO_OBJECT(*vp);
|
||||
if (callable &&
|
||||
((callable->map->ops == &js_ObjectOps)
|
||||
? OBJ_GET_CLASS(cx, callable)->call
|
||||
: callable->map->ops->call)) {
|
||||
if (js_IsCallable(cx, callable)) {
|
||||
*vp = OBJECT_TO_JSVAL(callable);
|
||||
} else {
|
||||
callable = js_ValueToFunctionObject(cx, vp, flags);
|
||||
}
|
||||
|
||||
return callable;
|
||||
}
|
||||
|
||||
|
@ -3205,6 +3205,7 @@ ProcessSetSlotRequest(JSContext *cx, JSSetSlotRequest *ssr)
|
||||
|
||||
/* Finally, do the deed. */
|
||||
STOBJ_SET_SLOT(obj, slot, OBJECT_TO_JSVAL(pobj));
|
||||
STOBJ_SET_DELEGATE(pobj);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -2876,7 +2876,6 @@ js_Interpret(JSContext *cx)
|
||||
ADD_EMPTY_CASE(JSOP_NOP)
|
||||
ADD_EMPTY_CASE(JSOP_CONDSWITCH)
|
||||
ADD_EMPTY_CASE(JSOP_TRY)
|
||||
ADD_EMPTY_CASE(JSOP_FINALLY)
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
ADD_EMPTY_CASE(JSOP_STARTXML)
|
||||
ADD_EMPTY_CASE(JSOP_STARTXMLEXPR)
|
||||
@ -2947,7 +2946,6 @@ js_Interpret(JSContext *cx)
|
||||
END_CASE(JSOP_LEAVEWITH)
|
||||
|
||||
BEGIN_CASE(JSOP_RETURN)
|
||||
CHECK_BRANCH();
|
||||
fp->rval = POP_OPND();
|
||||
/* FALL THROUGH */
|
||||
|
||||
@ -2958,6 +2956,7 @@ js_Interpret(JSContext *cx)
|
||||
* will be false after the inline_return label.
|
||||
*/
|
||||
ASSERT_NOT_THROWING(cx);
|
||||
CHECK_BRANCH();
|
||||
|
||||
if (fp->imacpc) {
|
||||
/*
|
||||
@ -6385,8 +6384,13 @@ js_Interpret(JSContext *cx)
|
||||
JS_ASSERT(cx->throwing);
|
||||
PUSH(cx->exception);
|
||||
cx->throwing = JS_FALSE;
|
||||
CHECK_BRANCH();
|
||||
END_CASE(JSOP_EXCEPTION)
|
||||
|
||||
BEGIN_CASE(JSOP_FINALLY)
|
||||
CHECK_BRANCH();
|
||||
END_CASE(JSOP_FINALLY)
|
||||
|
||||
BEGIN_CASE(JSOP_THROWING)
|
||||
JS_ASSERT(!cx->throwing);
|
||||
cx->throwing = JS_TRUE;
|
||||
@ -6395,6 +6399,7 @@ js_Interpret(JSContext *cx)
|
||||
|
||||
BEGIN_CASE(JSOP_THROW)
|
||||
JS_ASSERT(!cx->throwing);
|
||||
CHECK_BRANCH();
|
||||
cx->throwing = JS_TRUE;
|
||||
cx->exception = POP_OPND();
|
||||
/* let the code at error try to catch the exception. */
|
||||
|
@ -3334,6 +3334,13 @@ PurgeScopeChain(JSContext *cx, JSObject *obj, jsid id)
|
||||
if (!OBJ_IS_DELEGATE(cx, obj))
|
||||
return;
|
||||
|
||||
/*
|
||||
* All scope chains end in a global object, so this will change the global
|
||||
* shape. jstracer.cpp assumes that the global shape never changes on
|
||||
* trace, so we must deep-bail here.
|
||||
*/
|
||||
js_LeaveTrace(cx);
|
||||
|
||||
PurgeProtoChain(cx, OBJ_GET_PROTO(cx, obj), id);
|
||||
while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL) {
|
||||
if (PurgeProtoChain(cx, obj, id))
|
||||
|
@ -128,7 +128,30 @@ struct JSObjectMap {
|
||||
*/
|
||||
struct JSObject {
|
||||
JSObjectMap *map;
|
||||
|
||||
/*
|
||||
* Stores the JSClass* for this object, with the two lowest bits encoding
|
||||
* whether this object is a delegate or a system object.
|
||||
*
|
||||
* A delegate is an object linked on another object's prototype
|
||||
* (JSSLOT_PROTO) or scope (JSSLOT_PARENT) chain, which might be implicitly
|
||||
* asked to get or set a property on behalf of another object. Delegates
|
||||
* may be accessed directly too, as might any object, but only those
|
||||
* objects linked after the head of a prototype or scope chain are
|
||||
* delegates. This definition helps to optimize shape-based property cache
|
||||
* purging (see Purge{Scope,Proto}Chain in jsobj.cpp).
|
||||
*
|
||||
* The meaning of the system object bit is defined by the API client. It is
|
||||
* set in JS_NewSystemObject and is queried by JS_IsSystemObject, but it
|
||||
* has no intrinsic meaning to SpiderMonkey. Further, JSFILENAME_SYSTEM and
|
||||
* JS_FlagScriptFilenamePrefix are intended to be complementary to this
|
||||
* bit, but it is up to the API client to implement any such association.
|
||||
*
|
||||
* Both bits are initially zero and may be set or queried using the
|
||||
* STOBJ_(IS|SET)_(DELEGATE|SYSTEM) macros.
|
||||
*/
|
||||
jsuword classword;
|
||||
|
||||
jsval fslots[JS_INITIAL_NSLOTS];
|
||||
jsval *dslots; /* dynamically allocated slots */
|
||||
};
|
||||
@ -415,7 +438,9 @@ js_InitEval(JSContext *cx, JSObject *obj);
|
||||
extern JSObject *
|
||||
js_InitObjectClass(JSContext *cx, JSObject *obj);
|
||||
|
||||
/* Select Object.prototype method names shared between jsapi.c and jsobj.c. */
|
||||
/*
|
||||
* Select Object.prototype method names shared between jsapi.cpp and jsobj.cpp.
|
||||
*/
|
||||
extern const char js_watch_str[];
|
||||
extern const char js_unwatch_str[];
|
||||
extern const char js_hasOwnProperty_str[];
|
||||
@ -730,6 +755,14 @@ extern const char *
|
||||
js_ComputeFilename(JSContext *cx, JSStackFrame *caller,
|
||||
JSPrincipals *principals, uintN *linenop);
|
||||
|
||||
/* TODO: bug 481218. This returns false for functions */
|
||||
static JS_INLINE JSBool
|
||||
js_IsCallable(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
return (obj && ((obj->map->ops == &js_ObjectOps) ? OBJ_GET_CLASS(cx, obj)->call
|
||||
: obj->map->ops->call));
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
JS_FRIEND_API(void) js_DumpChars(const jschar *s, size_t n);
|
||||
JS_FRIEND_API(void) js_DumpString(JSString *str);
|
||||
|
713
js/src/json.cpp
713
js/src/json.cpp
@ -68,21 +68,19 @@ js_json_parse(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
JSString *s = NULL;
|
||||
jsval *argv = vp + 2;
|
||||
|
||||
if (!JS_ConvertArguments(cx, argc, argv, "S", &s)) {
|
||||
jsval reviver = JSVAL_VOID;
|
||||
JSAutoTempValueRooter(cx, 1, &reviver);
|
||||
|
||||
if (!JS_ConvertArguments(cx, argc, argv, "S / v", &s, &reviver))
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
JSONParser *jp = js_BeginJSONParse(cx, vp);
|
||||
JSBool ok = jp != NULL;
|
||||
if (ok) {
|
||||
ok = js_ConsumeJSONText(cx, jp, JS_GetStringChars(s), JS_GetStringLength(s));
|
||||
ok &= js_FinishJSONParse(cx, jp);
|
||||
ok &= js_FinishJSONParse(cx, jp, reviver);
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
JS_ReportError(cx, "Error parsing JSON");
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
@ -123,32 +121,23 @@ JSBool
|
||||
js_json_stringify(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
jsval *argv = vp + 2;
|
||||
JSBool ok = JS_TRUE;
|
||||
|
||||
// Must throw an Error if there isn't a first arg
|
||||
if (!JS_ConvertArguments(cx, argc, argv, "v", vp))
|
||||
return JS_FALSE;
|
||||
|
||||
ok = js_TryJSON(cx, vp);
|
||||
|
||||
JSType type;
|
||||
if (!ok || *vp == JSVAL_VOID ||
|
||||
((type = JS_TypeOfValue(cx, *vp)) == JSTYPE_FUNCTION || type == JSTYPE_XML)) {
|
||||
JS_ReportError(cx, "Invalid argument");
|
||||
if (!js_TryJSON(cx, vp))
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
JSString *s = JS_NewStringCopyN(cx, "", 0);
|
||||
if (!s)
|
||||
ok = JS_FALSE;
|
||||
return JS_FALSE;
|
||||
|
||||
if (ok) {
|
||||
jsval vec[2] = {STRING_TO_JSVAL(s), JSVAL_VOID};
|
||||
StringifyClosure sc(cx, 2, vec);
|
||||
JSAutoTempValueRooter resultTvr(cx, 1, sc.s);
|
||||
ok = js_Stringify(cx, vp, NULL, &WriteCallback, &sc, 0);
|
||||
*vp = *sc.s;
|
||||
}
|
||||
jsval vec[2] = {STRING_TO_JSVAL(s), JSVAL_VOID};
|
||||
StringifyClosure sc(cx, 2, vec);
|
||||
JSAutoTempValueRooter resultTvr(cx, 1, sc.s);
|
||||
JSBool ok = js_Stringify(cx, vp, NULL, &WriteCallback, &sc, 0);
|
||||
*vp = *sc.s;
|
||||
|
||||
return ok;
|
||||
}
|
||||
@ -158,12 +147,12 @@ js_TryJSON(JSContext *cx, jsval *vp)
|
||||
{
|
||||
// Checks whether the return value implements toJSON()
|
||||
JSBool ok = JS_TRUE;
|
||||
|
||||
|
||||
if (!JSVAL_IS_PRIMITIVE(*vp)) {
|
||||
JSObject *obj = JSVAL_TO_OBJECT(*vp);
|
||||
ok = js_TryMethod(cx, obj, cx->runtime->atomState.toJSONAtom, 0, NULL, vp);
|
||||
}
|
||||
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
@ -213,9 +202,9 @@ write_string(JSContext *cx, JSONWriteCallback callback, void *data, const jschar
|
||||
}
|
||||
|
||||
static JSBool
|
||||
stringify_primitive(JSContext *cx, jsval *vp,
|
||||
JSONWriteCallback callback, void *data, JSType type) {
|
||||
|
||||
stringify_leaf(JSContext *cx, jsval *vp,
|
||||
JSONWriteCallback callback, void *data, JSType type)
|
||||
{
|
||||
JSString *outputString;
|
||||
JSString *s = js_ValueToString(cx, *vp);
|
||||
|
||||
@ -239,9 +228,11 @@ stringify_primitive(JSContext *cx, jsval *vp,
|
||||
outputString = s;
|
||||
} else if (JSVAL_IS_NULL(*vp)) {
|
||||
outputString = JS_NewStringCopyN(cx, "null", 4);
|
||||
} else if (JSVAL_IS_VOID(*vp) || type == JSTYPE_FUNCTION || type == JSTYPE_XML) {
|
||||
outputString = JS_NewStringCopyN(cx, "undefined", 9);
|
||||
} else {
|
||||
JS_NOT_REACHED("A type we don't know about");
|
||||
return JS_FALSE; // encoding error
|
||||
outputString = JS_NewStringCopyN(cx, "undefined", 9);
|
||||
}
|
||||
|
||||
if (!outputString)
|
||||
@ -254,8 +245,10 @@ static JSBool
|
||||
stringify(JSContext *cx, jsval *vp, JSObject *replacer,
|
||||
JSONWriteCallback callback, void *data, uint32 depth)
|
||||
{
|
||||
if (depth > JSON_MAX_DEPTH)
|
||||
if (depth > JSON_MAX_DEPTH) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_STRINGIFY);
|
||||
return JS_FALSE; /* encoding error */
|
||||
}
|
||||
|
||||
JSBool ok = JS_TRUE;
|
||||
JSObject *obj = JSVAL_TO_OBJECT(*vp);
|
||||
@ -288,7 +281,12 @@ stringify(JSContext *cx, jsval *vp, JSObject *replacer,
|
||||
if (isArray) {
|
||||
if ((jsuint)i >= length)
|
||||
break;
|
||||
ok = OBJ_GET_PROPERTY(cx, obj, INT_TO_JSID(i), &outputValue);
|
||||
jsid index;
|
||||
if (!js_IndexToId(cx, i, &index))
|
||||
return JS_FALSE;
|
||||
ok = OBJ_GET_PROPERTY(cx, obj, index, &outputValue);
|
||||
if (!ok)
|
||||
break;
|
||||
i++;
|
||||
} else {
|
||||
ok = js_CallIteratorNext(cx, iterObj, &key);
|
||||
@ -316,16 +314,15 @@ stringify(JSContext *cx, jsval *vp, JSObject *replacer,
|
||||
!js_HasOwnProperty(cx, obj->map->ops->lookupProperty, obj, id, &v)) {
|
||||
ok = JS_FALSE;
|
||||
break;
|
||||
} else if (v == JSVAL_TRUE) {
|
||||
ok = JS_GetUCProperty(cx, obj, JS_GetStringChars(ks),
|
||||
JS_GetStringLength(ks), &outputValue);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
break;
|
||||
if (v != JSVAL_TRUE)
|
||||
continue;
|
||||
|
||||
ok = JS_GetPropertyById(cx, obj, id, &outputValue);
|
||||
if (!ok)
|
||||
break;
|
||||
}
|
||||
|
||||
// if this is an array, holes are transmitted as null
|
||||
if (isArray && outputValue == JSVAL_VOID) {
|
||||
@ -346,8 +343,8 @@ stringify(JSContext *cx, jsval *vp, JSObject *replacer,
|
||||
if (memberWritten) {
|
||||
output = jschar(',');
|
||||
ok = callback(&output, 1, data);
|
||||
if (!ok)
|
||||
break;
|
||||
if (!ok)
|
||||
break;
|
||||
}
|
||||
memberWritten = JS_TRUE;
|
||||
|
||||
@ -377,18 +374,18 @@ stringify(JSContext *cx, jsval *vp, JSObject *replacer,
|
||||
// recurse
|
||||
ok = stringify(cx, &outputValue, replacer, callback, data, depth + 1);
|
||||
} else {
|
||||
ok = stringify_primitive(cx, &outputValue, callback, data, type);
|
||||
ok = stringify_leaf(cx, &outputValue, callback, data, type);
|
||||
}
|
||||
} while (ok);
|
||||
|
||||
if (iterObj) {
|
||||
// Always close the iterator, but make sure not to stomp on OK
|
||||
ok &= js_CloseIterator(cx, *vp);
|
||||
// encoding error or propagate? FIXME: Bug 408838.
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
return JS_FALSE;
|
||||
|
||||
|
||||
output = jschar(isArray ? ']' : '}');
|
||||
ok = callback(&output, 1, data);
|
||||
@ -400,17 +397,15 @@ JSBool
|
||||
js_Stringify(JSContext *cx, jsval *vp, JSObject *replacer,
|
||||
JSONWriteCallback callback, void *data, uint32 depth)
|
||||
{
|
||||
JSBool ok = JS_TRUE;
|
||||
JSBool ok;
|
||||
JSType type = JS_TypeOfValue(cx, *vp);
|
||||
|
||||
if (JSVAL_IS_PRIMITIVE(*vp)) {
|
||||
ok = stringify_primitive(cx, vp, callback, data, JS_TypeOfValue(cx, *vp));
|
||||
if (JSVAL_IS_PRIMITIVE(*vp) || type == JSTYPE_FUNCTION || type == JSTYPE_XML) {
|
||||
ok = stringify_leaf(cx, vp, callback, data, type);
|
||||
} else {
|
||||
ok = stringify(cx, vp, replacer, callback, data, depth);
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
JS_ReportError(cx, "Error during JSON encoding");
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
@ -421,7 +416,108 @@ static JSBool IsNumChar(jschar c)
|
||||
}
|
||||
|
||||
static JSBool HandleData(JSContext *cx, JSONParser *jp, JSONDataType type);
|
||||
static JSBool PopState(JSONParser *jp);
|
||||
static JSBool PopState(JSContext *cx, JSONParser *jp);
|
||||
|
||||
static JSBool
|
||||
DestroyIdArrayOnError(JSContext *cx, JSIdArray *ida) {
|
||||
JS_DestroyIdArray(cx, ida);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
Walk(JSContext *cx, jsid id, JSObject *holder, jsval reviver, jsval *vp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return JS_FALSE);
|
||||
|
||||
if (!OBJ_GET_PROPERTY(cx, holder, id, vp))
|
||||
return JS_FALSE;
|
||||
|
||||
JSObject *obj;
|
||||
|
||||
if (!JSVAL_IS_PRIMITIVE(*vp) && !JS_ObjectIsFunction(cx, obj = JSVAL_TO_OBJECT(*vp)) &&
|
||||
!js_IsCallable(cx, obj)) {
|
||||
jsval propValue = JSVAL_VOID;
|
||||
JSAutoTempValueRooter tvr(cx, 1, &propValue);
|
||||
|
||||
if(OBJ_IS_ARRAY(cx, obj)) {
|
||||
jsuint length = 0;
|
||||
if (!js_GetLengthProperty(cx, obj, &length))
|
||||
return JS_FALSE;
|
||||
|
||||
for (jsuint i = 0; i < length; i++) {
|
||||
jsid index;
|
||||
if (!js_IndexToId(cx, i, &index))
|
||||
return JS_FALSE;
|
||||
|
||||
if (!Walk(cx, index, obj, reviver, &propValue))
|
||||
return JS_FALSE;
|
||||
|
||||
if (!OBJ_DEFINE_PROPERTY(cx, obj, index, propValue,
|
||||
NULL, NULL, JSPROP_ENUMERATE, NULL)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
JSIdArray *ida = JS_Enumerate(cx, obj);
|
||||
if (!ida)
|
||||
return JS_FALSE;
|
||||
|
||||
JSAutoTempValueRooter idaroot(cx, JS_ARRAY_LENGTH(ida), (jsval*)ida);
|
||||
|
||||
for(jsint i = 0; i < ida->length; i++) {
|
||||
jsid idName = ida->vector[i];
|
||||
if (!Walk(cx, idName, obj, reviver, &propValue))
|
||||
return DestroyIdArrayOnError(cx, ida);
|
||||
if (propValue == JSVAL_VOID) {
|
||||
if (!js_DeleteProperty(cx, obj, idName, &propValue))
|
||||
return DestroyIdArrayOnError(cx, ida);
|
||||
} else {
|
||||
if (!OBJ_DEFINE_PROPERTY(cx, obj, idName, propValue,
|
||||
NULL, NULL, JSPROP_ENUMERATE, NULL)) {
|
||||
return DestroyIdArrayOnError(cx, ida);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JS_DestroyIdArray(cx, ida);
|
||||
}
|
||||
}
|
||||
|
||||
// return reviver.call(holder, key, value);
|
||||
jsval value = *vp;
|
||||
JSString *key = js_ValueToString(cx, ID_TO_VALUE(id));
|
||||
if (!key)
|
||||
return JS_FALSE;
|
||||
|
||||
jsval vec[2] = {STRING_TO_JSVAL(key), value};
|
||||
jsval reviverResult;
|
||||
if (!JS_CallFunctionValue(cx, holder, reviver, 2, vec, &reviverResult))
|
||||
return JS_FALSE;
|
||||
|
||||
*vp = reviverResult;
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
Revive(JSContext *cx, jsval reviver, jsval *vp)
|
||||
{
|
||||
|
||||
JSObject *obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL, 0);
|
||||
if (!obj)
|
||||
return JS_FALSE;
|
||||
|
||||
jsval v = OBJECT_TO_JSVAL(obj);
|
||||
JSAutoTempValueRooter tvr(cx, 1, &v);
|
||||
if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.emptyAtom),
|
||||
*vp, NULL, NULL, JSPROP_ENUMERATE, NULL)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
return Walk(cx, ATOM_TO_JSID(cx->runtime->atomState.emptyAtom), obj, reviver, vp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
JSONParser *
|
||||
js_BeginJSONParse(JSContext *cx, jsval *rootVal)
|
||||
@ -466,7 +562,7 @@ bad:
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_FinishJSONParse(JSContext *cx, JSONParser *jp)
|
||||
js_FinishJSONParse(JSContext *cx, JSONParser *jp, jsval reviver)
|
||||
{
|
||||
if (!jp)
|
||||
return JS_TRUE;
|
||||
@ -476,11 +572,11 @@ js_FinishJSONParse(JSContext *cx, JSONParser *jp)
|
||||
if ((jp->statep - jp->stateStack) == 1) {
|
||||
if (*jp->statep == JSON_PARSE_STATE_KEYWORD) {
|
||||
if (HandleData(cx, jp, JSON_DATA_KEYWORD)) {
|
||||
PopState(jp);
|
||||
PopState(cx, jp);
|
||||
}
|
||||
} else if (*jp->statep == JSON_PARSE_STATE_NUMBER) {
|
||||
if (HandleData(cx, jp, JSON_DATA_NUMBER)) {
|
||||
PopState(jp);
|
||||
PopState(cx, jp);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -498,18 +594,32 @@ js_FinishJSONParse(JSContext *cx, JSONParser *jp)
|
||||
JSBool ok = *jp->statep == JSON_PARSE_STATE_FINISHED;
|
||||
JS_free(cx, jp);
|
||||
|
||||
if (!ok)
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
|
||||
if (reviver && !JSVAL_IS_PRIMITIVE(reviver) &&
|
||||
(JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(reviver)) || js_IsCallable(cx, JSVAL_TO_OBJECT(reviver)))) {
|
||||
ok = Revive(cx, reviver, jp->rootVal);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
PushState(JSONParser *jp, JSONParserState state)
|
||||
PushState(JSContext *cx, JSONParser *jp, JSONParserState state)
|
||||
{
|
||||
if (*jp->statep == JSON_PARSE_STATE_FINISHED)
|
||||
return JS_FALSE; // extra input
|
||||
if (*jp->statep == JSON_PARSE_STATE_FINISHED) {
|
||||
// extra input
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
jp->statep++;
|
||||
if ((uint32)(jp->statep - jp->stateStack) >= JS_ARRAY_LENGTH(jp->stateStack))
|
||||
return JS_FALSE; // too deep
|
||||
if ((uint32)(jp->statep - jp->stateStack) >= JS_ARRAY_LENGTH(jp->stateStack)) {
|
||||
// too deep
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
*jp->statep = state;
|
||||
|
||||
@ -517,11 +627,12 @@ PushState(JSONParser *jp, JSONParserState state)
|
||||
}
|
||||
|
||||
static JSBool
|
||||
PopState(JSONParser *jp)
|
||||
PopState(JSContext *cx, JSONParser *jp)
|
||||
{
|
||||
jp->statep--;
|
||||
if (jp->statep < jp->stateStack) {
|
||||
jp->statep = jp->stateStack;
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
@ -534,14 +645,15 @@ PopState(JSONParser *jp)
|
||||
static JSBool
|
||||
PushValue(JSContext *cx, JSONParser *jp, JSObject *parent, jsval value)
|
||||
{
|
||||
JSAutoTempValueRooter tvr(cx, 1, &value);
|
||||
|
||||
JSBool ok;
|
||||
if (OBJ_IS_ARRAY(cx, parent)) {
|
||||
jsuint len;
|
||||
ok = js_GetLengthProperty(cx, parent, &len);
|
||||
if (ok) {
|
||||
ok = OBJ_DEFINE_PROPERTY(cx, parent, INT_TO_JSID(len), value,
|
||||
jsid index;
|
||||
if (!js_IndexToId(cx, len, &index))
|
||||
return JS_FALSE;
|
||||
ok = OBJ_DEFINE_PROPERTY(cx, parent, index, value,
|
||||
NULL, NULL, JSPROP_ENUMERATE, NULL);
|
||||
}
|
||||
} else {
|
||||
@ -561,8 +673,10 @@ PushObject(JSContext *cx, JSONParser *jp, JSObject *obj)
|
||||
jsuint len;
|
||||
if (!js_GetLengthProperty(cx, jp->objectStack, &len))
|
||||
return JS_FALSE;
|
||||
if (len >= JSON_MAX_DEPTH)
|
||||
return JS_FALSE; // decoding error
|
||||
if (len >= JSON_MAX_DEPTH) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
jsval v = OBJECT_TO_JSVAL(obj);
|
||||
JSAutoTempValueRooter tvr(cx, v);
|
||||
@ -584,7 +698,7 @@ PushObject(JSContext *cx, JSONParser *jp, JSObject *obj)
|
||||
|
||||
JS_ASSERT(JSVAL_IS_OBJECT(p));
|
||||
JSObject *parent = JSVAL_TO_OBJECT(p);
|
||||
if (!PushValue(cx, jp, parent, OBJECT_TO_JSVAL(obj)))
|
||||
if (!PushValue(cx, jp, parent, v))
|
||||
return JS_FALSE;
|
||||
|
||||
// This property must be enumerable to keep the array dense
|
||||
@ -596,24 +710,6 @@ PushObject(JSContext *cx, JSONParser *jp, JSObject *obj)
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSObject *
|
||||
GetTopOfObjectStack(JSContext *cx, JSONParser *jp)
|
||||
{
|
||||
jsuint length;
|
||||
if (!js_GetLengthProperty(cx, jp->objectStack, &length))
|
||||
return NULL;
|
||||
|
||||
if (length == 0)
|
||||
return NULL;
|
||||
|
||||
jsval o;
|
||||
if (!OBJ_GET_PROPERTY(cx, jp->objectStack, INT_TO_JSID(length - 1), &o))
|
||||
return NULL;
|
||||
|
||||
JS_ASSERT(!JSVAL_IS_PRIMITIVE(o));
|
||||
return JSVAL_TO_OBJECT(o);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
OpenObject(JSContext *cx, JSONParser *jp)
|
||||
{
|
||||
@ -653,42 +749,57 @@ CloseArray(JSContext *cx, JSONParser *jp)
|
||||
return CloseObject(cx, jp);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
PushPrimitive(JSContext *cx, JSONParser *jp, jsval value)
|
||||
{
|
||||
JSAutoTempValueRooter tvr(cx, 1, &value);
|
||||
|
||||
jsuint len;
|
||||
if (!js_GetLengthProperty(cx, jp->objectStack, &len))
|
||||
return JS_FALSE;
|
||||
|
||||
if (len > 0) {
|
||||
jsval o;
|
||||
if (!OBJ_GET_PROPERTY(cx, jp->objectStack, INT_TO_JSID(len - 1), &o))
|
||||
return JS_FALSE;
|
||||
|
||||
JS_ASSERT(!JSVAL_IS_PRIMITIVE(o));
|
||||
return PushValue(cx, jp, JSVAL_TO_OBJECT(o), value);
|
||||
}
|
||||
|
||||
// root value must be primitive
|
||||
*jp->rootVal = value;
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
HandleNumber(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len)
|
||||
{
|
||||
const jschar *ep;
|
||||
double val;
|
||||
if (!js_strtod(cx, buf, buf + len, &ep, &val) || ep != buf + len)
|
||||
if (!js_strtod(cx, buf, buf + len, &ep, &val))
|
||||
return JS_FALSE;
|
||||
if (ep != buf + len) {
|
||||
// bad number input
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
jsval numVal;
|
||||
JSObject *obj = GetTopOfObjectStack(cx, jp);
|
||||
|
||||
jsval numVal;
|
||||
if (!JS_NewNumberValue(cx, val, &numVal))
|
||||
return JS_FALSE;
|
||||
|
||||
if (obj)
|
||||
return PushValue(cx, jp, obj, numVal);
|
||||
|
||||
// nothing on the object stack, so number value as root
|
||||
*jp->rootVal = numVal;
|
||||
return JS_TRUE;
|
||||
|
||||
return PushPrimitive(cx, jp, numVal);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
HandleString(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len)
|
||||
{
|
||||
JSObject *obj = GetTopOfObjectStack(cx, jp);
|
||||
JSString *str = js_NewStringCopyN(cx, buf, len);
|
||||
if (!str)
|
||||
return JS_FALSE;
|
||||
|
||||
if (obj)
|
||||
return PushValue(cx, jp, obj, STRING_TO_JSVAL(str));
|
||||
|
||||
// root value must be primitive
|
||||
*jp->rootVal = STRING_TO_JSVAL(str);
|
||||
return JS_TRUE;
|
||||
return PushPrimitive(cx, jp, STRING_TO_JSVAL(str));
|
||||
}
|
||||
|
||||
static JSBool
|
||||
@ -696,25 +807,24 @@ HandleKeyword(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len)
|
||||
{
|
||||
jsval keyword;
|
||||
JSTokenType tt = js_CheckKeyword(buf, len);
|
||||
if (tt != TOK_PRIMARY)
|
||||
if (tt != TOK_PRIMARY) {
|
||||
// bad keyword
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
if (buf[0] == 'n')
|
||||
if (buf[0] == 'n') {
|
||||
keyword = JSVAL_NULL;
|
||||
else if (buf[0] == 't')
|
||||
} else if (buf[0] == 't') {
|
||||
keyword = JSVAL_TRUE;
|
||||
else if (buf[0] == 'f')
|
||||
} else if (buf[0] == 'f') {
|
||||
keyword = JSVAL_FALSE;
|
||||
else
|
||||
} else {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
JSObject *obj = GetTopOfObjectStack(cx, jp);
|
||||
if (obj)
|
||||
return PushValue(cx, jp, obj, keyword);
|
||||
|
||||
// root value must be primitive
|
||||
*jp->rootVal = keyword;
|
||||
return JS_TRUE;
|
||||
return PushPrimitive(cx, jp, keyword);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
@ -722,8 +832,11 @@ HandleData(JSContext *cx, JSONParser *jp, JSONDataType type)
|
||||
{
|
||||
JSBool ok = JS_FALSE;
|
||||
|
||||
if (!STRING_BUFFER_OK(jp->buffer))
|
||||
if (!STRING_BUFFER_OK(jp->buffer)) {
|
||||
// what to call this error?
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case JSON_DATA_STRING:
|
||||
@ -759,206 +872,222 @@ js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len
|
||||
uint32 i;
|
||||
|
||||
if (*jp->statep == JSON_PARSE_STATE_INIT) {
|
||||
PushState(jp, JSON_PARSE_STATE_VALUE);
|
||||
PushState(cx, jp, JSON_PARSE_STATE_VALUE);
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
jschar c = data[i];
|
||||
switch (*jp->statep) {
|
||||
case JSON_PARSE_STATE_VALUE :
|
||||
if (c == ']') {
|
||||
// empty array
|
||||
if (!PopState(jp))
|
||||
return JS_FALSE;
|
||||
if (*jp->statep != JSON_PARSE_STATE_ARRAY)
|
||||
return JS_FALSE; // unexpected char
|
||||
if (!CloseArray(cx, jp) || !PopState(jp))
|
||||
return JS_FALSE;
|
||||
break;
|
||||
case JSON_PARSE_STATE_VALUE:
|
||||
if (c == ']') {
|
||||
// empty array
|
||||
if (!PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
|
||||
if (*jp->statep != JSON_PARSE_STATE_ARRAY) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
if (c == '}') {
|
||||
// we should only find these in OBJECT_KEY state
|
||||
return JS_FALSE; // unexpected failure
|
||||
}
|
||||
if (!CloseArray(cx, jp) || !PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
|
||||
if (c == '"') {
|
||||
*jp->statep = JSON_PARSE_STATE_STRING;
|
||||
break;
|
||||
}
|
||||
|
||||
if (IsNumChar(c)) {
|
||||
*jp->statep = JSON_PARSE_STATE_NUMBER;
|
||||
js_AppendChar(jp->buffer, c);
|
||||
break;
|
||||
}
|
||||
|
||||
if (JS7_ISLET(c)) {
|
||||
*jp->statep = JSON_PARSE_STATE_KEYWORD;
|
||||
js_AppendChar(jp->buffer, c);
|
||||
break;
|
||||
}
|
||||
|
||||
// fall through in case the value is an object or array
|
||||
case JSON_PARSE_STATE_OBJECT_VALUE :
|
||||
if (c == '{') {
|
||||
*jp->statep = JSON_PARSE_STATE_OBJECT;
|
||||
if (!OpenObject(cx, jp) || !PushState(jp, JSON_PARSE_STATE_OBJECT_PAIR))
|
||||
return JS_FALSE;
|
||||
} else if (c == '[') {
|
||||
*jp->statep = JSON_PARSE_STATE_ARRAY;
|
||||
if (!OpenArray(cx, jp) || !PushState(jp, JSON_PARSE_STATE_VALUE))
|
||||
return JS_FALSE;
|
||||
} else if (!JS_ISXMLSPACE(c)) {
|
||||
return JS_FALSE; // unexpected
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case JSON_PARSE_STATE_OBJECT :
|
||||
if (c == '}') {
|
||||
if (!CloseObject(cx, jp) || !PopState(jp))
|
||||
return JS_FALSE;
|
||||
} else if (c == ',') {
|
||||
if (!PushState(jp, JSON_PARSE_STATE_OBJECT_PAIR))
|
||||
return JS_FALSE;
|
||||
} else if (c == ']' || !JS_ISXMLSPACE(c)) {
|
||||
return JS_FALSE; // unexpected
|
||||
}
|
||||
break;
|
||||
if (c == '}') {
|
||||
// we should only find these in OBJECT_KEY state
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
case JSON_PARSE_STATE_ARRAY :
|
||||
if (c == ']') {
|
||||
if (!CloseArray(cx, jp) || !PopState(jp))
|
||||
return JS_FALSE;
|
||||
} else if (c == ',') {
|
||||
if (!PushState(jp, JSON_PARSE_STATE_VALUE))
|
||||
return JS_FALSE;
|
||||
} else if (!JS_ISXMLSPACE(c)) {
|
||||
return JS_FALSE; // unexpected
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_OBJECT_PAIR :
|
||||
if (c == '"') {
|
||||
// we want to be waiting for a : when the string has been read
|
||||
*jp->statep = JSON_PARSE_STATE_OBJECT_IN_PAIR;
|
||||
if (!PushState(jp, JSON_PARSE_STATE_STRING))
|
||||
return JS_FALSE;
|
||||
} else if (c == '}') {
|
||||
// pop off the object pair state and the object state
|
||||
if (!CloseObject(cx, jp) || !PopState(jp) || !PopState(jp))
|
||||
return JS_FALSE;
|
||||
} else if (c == ']' || !JS_ISXMLSPACE(c)) {
|
||||
return JS_FALSE; // unexpected
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_OBJECT_IN_PAIR:
|
||||
if (c == ':') {
|
||||
*jp->statep = JSON_PARSE_STATE_VALUE;
|
||||
} else if (!JS_ISXMLSPACE(c)) {
|
||||
return JS_FALSE; // unexpected
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_STRING:
|
||||
if (c == '"') {
|
||||
if (!PopState(jp))
|
||||
return JS_FALSE;
|
||||
JSONDataType jdt;
|
||||
if (*jp->statep == JSON_PARSE_STATE_OBJECT_IN_PAIR) {
|
||||
jdt = JSON_DATA_KEYSTRING;
|
||||
} else {
|
||||
jdt = JSON_DATA_STRING;
|
||||
}
|
||||
if (!HandleData(cx, jp, jdt))
|
||||
return JS_FALSE;
|
||||
} else if (c == '\\') {
|
||||
*jp->statep = JSON_PARSE_STATE_STRING_ESCAPE;
|
||||
} else {
|
||||
js_AppendChar(jp->buffer, c);
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_STRING_ESCAPE:
|
||||
switch (c) {
|
||||
case '"':
|
||||
case '\\':
|
||||
case '/':
|
||||
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;
|
||||
default :
|
||||
if (c == 'u') {
|
||||
jp->numHex = 0;
|
||||
jp->hexChar = 0;
|
||||
*jp->statep = JSON_PARSE_STATE_STRING_HEX;
|
||||
continue;
|
||||
} else {
|
||||
return JS_FALSE; // unexpected
|
||||
}
|
||||
}
|
||||
|
||||
js_AppendChar(jp->buffer, c);
|
||||
if (c == '"') {
|
||||
*jp->statep = JSON_PARSE_STATE_STRING;
|
||||
break;
|
||||
}
|
||||
|
||||
case JSON_PARSE_STATE_STRING_HEX:
|
||||
if (('0' <= c) && (c <= '9'))
|
||||
jp->hexChar = (jp->hexChar << 4) | (c - '0');
|
||||
else if (('a' <= c) && (c <= 'f'))
|
||||
jp->hexChar = (jp->hexChar << 4) | (c - 'a' + 0x0a);
|
||||
else if (('A' <= c) && (c <= 'F'))
|
||||
jp->hexChar = (jp->hexChar << 4) | (c - 'A' + 0x0a);
|
||||
else
|
||||
return JS_FALSE; // unexpected
|
||||
if (IsNumChar(c)) {
|
||||
*jp->statep = JSON_PARSE_STATE_NUMBER;
|
||||
js_AppendChar(jp->buffer, c);
|
||||
break;
|
||||
}
|
||||
|
||||
if (++(jp->numHex) == 4) {
|
||||
js_AppendChar(jp->buffer, jp->hexChar);
|
||||
jp->hexChar = 0;
|
||||
if (JS7_ISLET(c)) {
|
||||
*jp->statep = JSON_PARSE_STATE_KEYWORD;
|
||||
js_AppendChar(jp->buffer, c);
|
||||
break;
|
||||
}
|
||||
|
||||
// fall through in case the value is an object or array
|
||||
case JSON_PARSE_STATE_OBJECT_VALUE:
|
||||
if (c == '{') {
|
||||
*jp->statep = JSON_PARSE_STATE_OBJECT;
|
||||
if (!OpenObject(cx, jp) || !PushState(cx, jp, JSON_PARSE_STATE_OBJECT_PAIR))
|
||||
return JS_FALSE;
|
||||
} else if (c == '[') {
|
||||
*jp->statep = JSON_PARSE_STATE_ARRAY;
|
||||
if (!OpenArray(cx, jp) || !PushState(cx, jp, JSON_PARSE_STATE_VALUE))
|
||||
return JS_FALSE;
|
||||
} else if (!JS_ISXMLSPACE(c)) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_OBJECT:
|
||||
if (c == '}') {
|
||||
if (!CloseObject(cx, jp) || !PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
} else if (c == ',') {
|
||||
if (!PushState(cx, jp, JSON_PARSE_STATE_OBJECT_PAIR))
|
||||
return JS_FALSE;
|
||||
} else if (c == ']' || !JS_ISXMLSPACE(c)) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_ARRAY :
|
||||
if (c == ']') {
|
||||
if (!CloseArray(cx, jp) || !PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
} else if (c == ',') {
|
||||
if (!PushState(cx, jp, JSON_PARSE_STATE_VALUE))
|
||||
return JS_FALSE;
|
||||
} else if (!JS_ISXMLSPACE(c)) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_OBJECT_PAIR :
|
||||
if (c == '"') {
|
||||
// we want to be waiting for a : when the string has been read
|
||||
*jp->statep = JSON_PARSE_STATE_OBJECT_IN_PAIR;
|
||||
if (!PushState(cx, jp, JSON_PARSE_STATE_STRING))
|
||||
return JS_FALSE;
|
||||
} else if (c == '}') {
|
||||
// pop off the object pair state and the object state
|
||||
if (!CloseObject(cx, jp) || !PopState(cx, jp) || !PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
} else if (c == ']' || !JS_ISXMLSPACE(c)) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_OBJECT_IN_PAIR:
|
||||
if (c == ':') {
|
||||
*jp->statep = JSON_PARSE_STATE_VALUE;
|
||||
} else if (!JS_ISXMLSPACE(c)) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_STRING:
|
||||
if (c == '"') {
|
||||
if (!PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
JSONDataType jdt;
|
||||
if (*jp->statep == JSON_PARSE_STATE_OBJECT_IN_PAIR) {
|
||||
jdt = JSON_DATA_KEYSTRING;
|
||||
} else {
|
||||
jdt = JSON_DATA_STRING;
|
||||
}
|
||||
if (!HandleData(cx, jp, jdt))
|
||||
return JS_FALSE;
|
||||
} else if (c == '\\') {
|
||||
*jp->statep = JSON_PARSE_STATE_STRING_ESCAPE;
|
||||
} else {
|
||||
js_AppendChar(jp->buffer, c);
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_STRING_ESCAPE:
|
||||
switch (c) {
|
||||
case '"':
|
||||
case '\\':
|
||||
case '/':
|
||||
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;
|
||||
default :
|
||||
if (c == 'u') {
|
||||
jp->numHex = 0;
|
||||
*jp->statep = JSON_PARSE_STATE_STRING;
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_KEYWORD:
|
||||
if (JS7_ISLET(c)) {
|
||||
js_AppendChar(jp->buffer, c);
|
||||
jp->hexChar = 0;
|
||||
*jp->statep = JSON_PARSE_STATE_STRING_HEX;
|
||||
continue;
|
||||
} else {
|
||||
// this character isn't part of the keyword, process it again
|
||||
i--;
|
||||
if (!PopState(jp))
|
||||
return JS_FALSE;
|
||||
|
||||
if (!HandleData(cx, jp, JSON_DATA_KEYWORD))
|
||||
return JS_FALSE;
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case JSON_PARSE_STATE_NUMBER:
|
||||
if (IsNumChar(c)) {
|
||||
js_AppendChar(jp->buffer, c);
|
||||
} else {
|
||||
// this character isn't part of the number, process it again
|
||||
i--;
|
||||
if (!PopState(jp))
|
||||
return JS_FALSE;
|
||||
if (!HandleData(cx, jp, JSON_DATA_NUMBER))
|
||||
return JS_FALSE;
|
||||
}
|
||||
break;
|
||||
js_AppendChar(jp->buffer, c);
|
||||
*jp->statep = JSON_PARSE_STATE_STRING;
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_FINISHED:
|
||||
if (!JS_ISXMLSPACE(c))
|
||||
return JS_FALSE; // extra input
|
||||
case JSON_PARSE_STATE_STRING_HEX:
|
||||
if (('0' <= c) && (c <= '9')) {
|
||||
jp->hexChar = (jp->hexChar << 4) | (c - '0');
|
||||
} else if (('a' <= c) && (c <= 'f')) {
|
||||
jp->hexChar = (jp->hexChar << 4) | (c - 'a' + 0x0a);
|
||||
} else if (('A' <= c) && (c <= 'F')) {
|
||||
jp->hexChar = (jp->hexChar << 4) | (c - 'A' + 0x0a);
|
||||
} else {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
if (++(jp->numHex) == 4) {
|
||||
js_AppendChar(jp->buffer, jp->hexChar);
|
||||
jp->hexChar = 0;
|
||||
jp->numHex = 0;
|
||||
*jp->statep = JSON_PARSE_STATE_STRING;
|
||||
}
|
||||
break;
|
||||
|
||||
break;
|
||||
case JSON_PARSE_STATE_KEYWORD:
|
||||
if (JS7_ISLET(c)) {
|
||||
js_AppendChar(jp->buffer, c);
|
||||
} else {
|
||||
// this character isn't part of the keyword, process it again
|
||||
i--;
|
||||
if (!PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
|
||||
if (!HandleData(cx, jp, JSON_DATA_KEYWORD))
|
||||
return JS_FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
JS_NOT_REACHED("Invalid JSON parser state");
|
||||
case JSON_PARSE_STATE_NUMBER:
|
||||
if (IsNumChar(c)) {
|
||||
js_AppendChar(jp->buffer, c);
|
||||
} else {
|
||||
// this character isn't part of the number, process it again
|
||||
i--;
|
||||
if (!PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
if (!HandleData(cx, jp, JSON_DATA_NUMBER))
|
||||
return JS_FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_FINISHED:
|
||||
if (!JS_ISXMLSPACE(c)) {
|
||||
// extra input
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
JS_NOT_REACHED("Invalid JSON parser state");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,7 +100,7 @@ extern JSBool
|
||||
js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len);
|
||||
|
||||
extern JSBool
|
||||
js_FinishJSONParse(JSContext *cx, JSONParser *jp);
|
||||
js_FinishJSONParse(JSContext *cx, JSONParser *jp, jsval reviver);
|
||||
|
||||
JS_END_EXTERN_C
|
||||
|
||||
|
@ -241,6 +241,12 @@ js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp)
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_DumpScript(JSContext *cx, JSScript *script)
|
||||
{
|
||||
return js_Disassemble(cx, script, true, stdout);
|
||||
}
|
||||
|
||||
const char *
|
||||
ToDisassemblySource(JSContext *cx, jsval v)
|
||||
{
|
||||
|
@ -2042,7 +2042,6 @@ class RegExpNativeCompiler {
|
||||
JSRegExp* re;
|
||||
CompilerState* cs; /* RegExp to compile */
|
||||
Fragment* fragment;
|
||||
LirBuffer* lirbuf;
|
||||
LirWriter* lir;
|
||||
LirBufWriter* lirBufWriter; /* for skip */
|
||||
|
||||
@ -2050,7 +2049,7 @@ class RegExpNativeCompiler {
|
||||
LIns* gdata;
|
||||
LIns* cpend;
|
||||
|
||||
JSBool isCaseInsensitive() const { return cs->flags & JSREG_FOLD; }
|
||||
JSBool isCaseInsensitive() const { return (cs->flags & JSREG_FOLD) != 0; }
|
||||
|
||||
JSBool targetCurrentPoint(LIns* ins)
|
||||
{
|
||||
|
@ -49,3 +49,6 @@ MSG_DEF(JSSMSG_FILE_SCRIPTS_ONLY, 4, 0, JSEXN_NONE, "only works on JS scr
|
||||
MSG_DEF(JSSMSG_UNEXPECTED_EOF, 5, 1, JSEXN_NONE, "unexpected EOF in {0}")
|
||||
MSG_DEF(JSSMSG_DOEXP_USAGE, 6, 0, JSEXN_NONE, "usage: doexp obj id")
|
||||
MSG_DEF(JSSMSG_SCRIPTS_ONLY, 7, 0, JSEXN_NONE, "only works on scripts")
|
||||
MSG_DEF(JSSMSG_NOT_ENOUGH_ARGS, 8, 1, JSEXN_NONE, "{0}: not enough arguments")
|
||||
MSG_DEF(JSSMSG_TOO_MANY_ARGS, 9, 1, JSEXN_NONE, "{0}: too many arguments")
|
||||
MSG_DEF(JSSMSG_ASSERT_EQ_FAILED, 10, 2, JSEXN_NONE, "Assertion failed: got {0}, expected {1}")
|
||||
|
@ -1035,13 +1035,47 @@ out_of_range:
|
||||
}
|
||||
|
||||
#ifdef JS_TRACER
|
||||
extern jsdouble js_NaN;
|
||||
|
||||
jsdouble FASTCALL
|
||||
js_String_p_charCodeAt(JSString* str, jsdouble d)
|
||||
{
|
||||
d = js_DoubleToInteger(d);
|
||||
if (d < 0 || (int32)JSSTRING_LENGTH(str) <= d)
|
||||
return js_NaN;
|
||||
return jsdouble(JSSTRING_CHARS(str)[jsuint(d)]);
|
||||
}
|
||||
|
||||
int32 FASTCALL
|
||||
js_String_p_charCodeAt(JSString* str, int32 i)
|
||||
js_String_p_charCodeAt_int(JSString* str, jsint i)
|
||||
{
|
||||
if (i < 0 || (int32)JSSTRING_LENGTH(str) <= i)
|
||||
return -1;
|
||||
return 0;
|
||||
return JSSTRING_CHARS(str)[i];
|
||||
}
|
||||
|
||||
jsdouble FASTCALL
|
||||
js_String_p_charCodeAt0(JSString* str)
|
||||
{
|
||||
if ((int32)JSSTRING_LENGTH(str) == 0)
|
||||
return js_NaN;
|
||||
return jsdouble(JSSTRING_CHARS(str)[0]);
|
||||
}
|
||||
|
||||
int32 FASTCALL
|
||||
js_String_p_charCodeAt0_int(JSString* str)
|
||||
{
|
||||
if ((int32)JSSTRING_LENGTH(str) == 0)
|
||||
return 0;
|
||||
return JSSTRING_CHARS(str)[0];
|
||||
}
|
||||
|
||||
/*
|
||||
* The FuncFilter replaces the generic double version of charCodeAt with the
|
||||
* integer fast path if appropriate.
|
||||
*/
|
||||
JS_DEFINE_CALLINFO_1(extern, INT32, js_String_p_charCodeAt0_int, STRING, 1, 1)
|
||||
JS_DEFINE_CALLINFO_2(extern, INT32, js_String_p_charCodeAt_int, STRING, INT32, 1, 1)
|
||||
#endif
|
||||
|
||||
jsint
|
||||
@ -2506,8 +2540,9 @@ JS_DEFINE_TRCINFO_2(str_substring,
|
||||
(3, (static, STRING_RETRY, String_p_substring_1, CONTEXT, THIS_STRING, INT32, 1, 1)))
|
||||
JS_DEFINE_TRCINFO_1(str_charAt,
|
||||
(3, (extern, STRING_RETRY, js_String_getelem, CONTEXT, THIS_STRING, INT32, 1, 1)))
|
||||
JS_DEFINE_TRCINFO_1(str_charCodeAt,
|
||||
(2, (extern, INT32_RETRY, js_String_p_charCodeAt, THIS_STRING, INT32, 1, 1)))
|
||||
JS_DEFINE_TRCINFO_2(str_charCodeAt,
|
||||
(1, (extern, DOUBLE, js_String_p_charCodeAt0, THIS_STRING, 1, 1)),
|
||||
(2, (extern, DOUBLE, js_String_p_charCodeAt, THIS_STRING, DOUBLE, 1, 1)))
|
||||
JS_DEFINE_TRCINFO_4(str_concat,
|
||||
(3, (static, STRING_RETRY, String_p_concat_1int, CONTEXT, THIS_STRING, INT32, 1, 1)),
|
||||
(3, (extern, STRING_RETRY, js_ConcatStrings, CONTEXT, THIS_STRING, STRING, 1, 1)),
|
||||
|
@ -52,7 +52,8 @@
|
||||
#include <limits.h>
|
||||
|
||||
#include "nanojit/nanojit.h"
|
||||
#include "jsarray.h" // higher-level library and API headers
|
||||
#include "jsapi.h" // higher-level library and API headers
|
||||
#include "jsarray.h"
|
||||
#include "jsbool.h"
|
||||
#include "jscntxt.h"
|
||||
#include "jsdbgapi.h"
|
||||
@ -905,19 +906,33 @@ public:
|
||||
if (isi2f(s0) || isu2f(s0))
|
||||
return iu2fArg(s0);
|
||||
// XXX ARM -- check for qjoin(call(UnboxDouble),call(UnboxDouble))
|
||||
if (s0->isCall() && s0->callInfo() == &js_UnboxDouble_ci) {
|
||||
LIns* args2[] = { callArgN(s0, 0) };
|
||||
return out->insCall(&js_UnboxInt32_ci, args2);
|
||||
}
|
||||
if (s0->isCall() && s0->callInfo() == &js_StringToNumber_ci) {
|
||||
// callArgN's ordering is that as seen by the builtin, not as stored in args here.
|
||||
// True story!
|
||||
LIns* args2[] = { callArgN(s0, 1), callArgN(s0, 0) };
|
||||
return out->insCall(&js_StringToInt32_ci, args2);
|
||||
if (s0->isCall()) {
|
||||
const CallInfo* ci2 = s0->callInfo();
|
||||
if (ci2 == &js_UnboxDouble_ci) {
|
||||
LIns* args2[] = { callArgN(s0, 0) };
|
||||
return out->insCall(&js_UnboxInt32_ci, args2);
|
||||
} else if (ci2 == &js_StringToNumber_ci) {
|
||||
// callArgN's ordering is that as seen by the builtin, not as stored in
|
||||
// args here. True story!
|
||||
LIns* args2[] = { callArgN(s0, 1), callArgN(s0, 0) };
|
||||
return out->insCall(&js_StringToInt32_ci, args2);
|
||||
} else if (ci2 == &js_String_p_charCodeAt0_ci) {
|
||||
// Use a fast path builtin for a charCodeAt that converts to an int right away.
|
||||
LIns* args2[] = { callArgN(s0, 0) };
|
||||
return out->insCall(&js_String_p_charCodeAt0_int_ci, args2);
|
||||
} else if (ci2 == &js_String_p_charCodeAt_ci) {
|
||||
LIns* idx = callArgN(s0, 1);
|
||||
// If the index is not already an integer, force it to be an integer.
|
||||
idx = isPromote(idx)
|
||||
? demote(out, idx)
|
||||
: out->insCall(&js_DoubleToInt32_ci, &idx);
|
||||
LIns* args2[] = { idx, callArgN(s0, 0) };
|
||||
return out->insCall(&js_String_p_charCodeAt_int_ci, args2);
|
||||
}
|
||||
}
|
||||
} else if (ci == &js_BoxDouble_ci) {
|
||||
JS_ASSERT(s0->isQuad());
|
||||
if (s0->isop(LIR_i2f)) {
|
||||
if (isi2f(s0)) {
|
||||
LIns* args2[] = { s0->oprnd1(), args[1] };
|
||||
return out->insCall(&js_BoxInt32_ci, args2);
|
||||
}
|
||||
@ -6742,27 +6757,6 @@ TraceRecorder::functionCall(bool constructing, uintN argc)
|
||||
}
|
||||
argp--;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we got this far, and we have a charCodeAt, check that charCodeAt
|
||||
* isn't going to return a NaN.
|
||||
*/
|
||||
if (!constructing && known->builtin == &js_String_p_charCodeAt_ci) {
|
||||
JSString* str = JSVAL_TO_STRING(tval);
|
||||
jsval& arg = stackval(-1);
|
||||
|
||||
JS_ASSERT(JSVAL_IS_STRING(tval));
|
||||
JS_ASSERT(isNumber(arg));
|
||||
|
||||
if (JSVAL_IS_INT(arg)) {
|
||||
if (size_t(JSVAL_TO_INT(arg)) >= JSSTRING_LENGTH(str))
|
||||
ABORT_TRACE("invalid charCodeAt index");
|
||||
} else {
|
||||
double d = js_DoubleToInteger(*JSVAL_TO_DOUBLE(arg));
|
||||
if (d < 0 || JSSTRING_LENGTH(str) <= d)
|
||||
ABORT_TRACE("invalid charCodeAt index");
|
||||
}
|
||||
}
|
||||
goto success;
|
||||
|
||||
next_specialization:;
|
||||
@ -7019,30 +7013,6 @@ TraceRecorder::record_SetPropHit(JSPropCacheEntry* entry, JSScopeProperty* sprop
|
||||
ABORT_TRACE("lazy import of global slot failed");
|
||||
|
||||
LIns* r_ins = get(&r);
|
||||
|
||||
/*
|
||||
* Scope shape is determined by the ordered list of property names and
|
||||
* other attributes (flags, getter/setter, etc.) but not value or type
|
||||
* -- except for function-valued properties. This function-based
|
||||
* distinction enables various function-call optimizations.
|
||||
*
|
||||
* This shape requirement is addressed in the interpreter by branding
|
||||
* the scope and updating any branded scope's shape every time the
|
||||
* value changes to or from a function or when one function value is
|
||||
* modified to a different function value; see GC_WRITE_BARRIER.
|
||||
*
|
||||
* On trace the shape requirement is mostly handled by normal shape
|
||||
* guarding. However, the global object's shape is required to be
|
||||
* invariant at trace recording time, and since a function-to-function
|
||||
* transition can change shape, we must handle this edge case
|
||||
* separately with the following guard. See also bug 473256.
|
||||
*/
|
||||
if (VALUE_IS_FUNCTION(cx, r)) {
|
||||
guard(true,
|
||||
lir->ins2(LIR_eq, r_ins, INS_CONSTPTR(JSVAL_TO_OBJECT(r))),
|
||||
MISMATCH_EXIT);
|
||||
}
|
||||
|
||||
set(&STOBJ_GET_SLOT(obj, slot), r_ins);
|
||||
|
||||
JS_ASSERT(*pc != JSOP_INITPROP);
|
||||
|
@ -57,6 +57,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsj_private.h" /* LiveConnect internals */
|
||||
#include "jsj_hash.h" /* Hash table with Java object as key */
|
||||
|
@ -302,7 +302,11 @@ GetContextData(JSContext *cx)
|
||||
static JSBool
|
||||
ShellOperationCallback(JSContext *cx)
|
||||
{
|
||||
return !gCanceled;
|
||||
if (!gCanceled)
|
||||
return JS_TRUE;
|
||||
|
||||
JS_ClearPendingException(cx);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1017,6 +1021,48 @@ Quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
static const char *
|
||||
ToSource(JSContext *cx, jsval *vp)
|
||||
{
|
||||
JSString *str = JS_ValueToSource(cx, *vp);
|
||||
if (str) {
|
||||
*vp = STRING_TO_JSVAL(str);
|
||||
return JS_GetStringBytes(str);
|
||||
}
|
||||
JS_ClearPendingException(cx);
|
||||
return "<<error converting value to string>>";
|
||||
}
|
||||
|
||||
static JSBool
|
||||
AssertEq(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
if (argc != 2) {
|
||||
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
|
||||
(argc > 2) ? JSSMSG_TOO_MANY_ARGS : JSSMSG_NOT_ENOUGH_ARGS,
|
||||
"assertEq");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
jsval *argv = JS_ARGV(cx, vp);
|
||||
if (!js_StrictlyEqual(cx, argv[0], argv[1])) {
|
||||
const char *actual = ToSource(cx, &argv[0]);
|
||||
const char *expected = ToSource(cx, &argv[1]);
|
||||
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_ASSERT_EQ_FAILED,
|
||||
actual, expected);
|
||||
return JS_FALSE;
|
||||
}
|
||||
JS_SET_RVAL(cx, vp, JSVAL_VOID);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
#ifdef JS_TRACER
|
||||
static jsval JS_FASTCALL
|
||||
AssertEq_tn(JSContext *cx, jsval v1, jsval v2)
|
||||
{
|
||||
return (js_StrictlyEqual(cx, v1, v2)) ? JSVAL_VOID : JSVAL_ERROR_COOKIE;
|
||||
}
|
||||
#endif
|
||||
|
||||
static JSBool
|
||||
GC(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
@ -3362,6 +3408,7 @@ Elapsed(JSContext *cx, uintN argc, jsval *vp)
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
JS_DEFINE_TRCINFO_1(AssertEq, (3, (static, JSVAL_RETRY, AssertEq_tn, CONTEXT, JSVAL, JSVAL, 0, 0)))
|
||||
JS_DEFINE_TRCINFO_1(Print, (2, (static, JSVAL_FAIL, Print_tn, CONTEXT, STRING, 0, 0)))
|
||||
JS_DEFINE_TRCINFO_1(ShapeOf, (1, (static, INT32, ShapeOf_tn, OBJECT, 0, 0)))
|
||||
|
||||
@ -3494,6 +3541,7 @@ static JSFunctionSpec shell_functions[] = {
|
||||
JS_TN("print", Print, 0,0, Print_trcinfo),
|
||||
JS_FS("help", Help, 0,0,0),
|
||||
JS_FS("quit", Quit, 0,0,0),
|
||||
JS_TN("assertEq", AssertEq, 2,0, AssertEq_trcinfo),
|
||||
JS_FN("gc", GC, 0,0),
|
||||
JS_FN("gcparam", GCParameter, 2,0),
|
||||
JS_FN("countHeap", CountHeap, 0,0),
|
||||
@ -3572,6 +3620,8 @@ static const char *const shell_help_messages[] = {
|
||||
"print([exp ...]) Evaluate and print expressions",
|
||||
"help([name ...]) Display usage and help messages",
|
||||
"quit() Quit the shell",
|
||||
"assertEq(actual, expected)\n"
|
||||
" Throw if the two arguments are not ===",
|
||||
"gc() Run the garbage collector",
|
||||
"gcparam(name, value)\n"
|
||||
" Wrapper for JS_SetGCParameter. The name must be either 'maxBytes' or\n"
|
||||
|
@ -4403,23 +4403,6 @@ testConvertibleObjectEqUndefined.jitstats = {
|
||||
};
|
||||
test(testConvertibleObjectEqUndefined);
|
||||
|
||||
var globalProperty;
|
||||
function testFunctionToFunctionGlobalPropertyTransition()
|
||||
{
|
||||
function g() { return "g" }
|
||||
function h() { return "h"; }
|
||||
var a = [g, g, g, g, h];
|
||||
var results = [];
|
||||
for (i = 0; i < 5; i++)
|
||||
{
|
||||
globalProperty = a[i];
|
||||
results[i] = globalProperty();
|
||||
}
|
||||
return results.join("");
|
||||
}
|
||||
testFunctionToFunctionGlobalPropertyTransition.expected = "ggggh";
|
||||
test(testFunctionToFunctionGlobalPropertyTransition);
|
||||
|
||||
function testUndefinedPropertyAccess() {
|
||||
var x = [1,2,3];
|
||||
var y = {};
|
||||
|
Loading…
Reference in New Issue
Block a user