diff --git a/mozilla/js/src/js.msg b/mozilla/js/src/js.msg index c98e34359e7..c1783f3753e 100644 --- a/mozilla/js/src/js.msg +++ b/mozilla/js/src/js.msg @@ -297,4 +297,4 @@ MSG_DEF(JSMSG_BAD_GENERATOR_YIELD, 214, 1, JSEXN_TYPEERR, "yield from closing MSG_DEF(JSMSG_BAD_YIELD_SYNTAX, 215, 0, JSEXN_SYNTAXERR, "yield expression must be parenthesized") MSG_DEF(JSMSG_ARRAY_COMP_LEFTSIDE, 216, 0, JSEXN_SYNTAXERR, "invalid array comprehension left-hand side") MSG_DEF(JSMSG_YIELD_FROM_FILTER, 217, 0, JSEXN_INTERNALERR, "yield not yet supported from filtering predicate") -MSG_DEF(JSMSG_SELF_MODIFYING_SCRIPT, 218, 0, JSEXN_TYPEERR, "self-modifying script detected") +MSG_DEF(JSMSG_COMPILE_EXECED_SCRIPT, 218, 0, JSEXN_TYPEERR, "cannot compile over a script that is currently executing") diff --git a/mozilla/js/src/jsscript.c b/mozilla/js/src/jsscript.c index b57c6ff1b65..91742ef88b9 100644 --- a/mozilla/js/src/jsscript.c +++ b/mozilla/js/src/jsscript.c @@ -67,6 +67,31 @@ static const char js_script_exec[] = "Script.prototype.exec"; static const char js_script_compile[] = "Script.prototype.compile"; +/* + * This routine requires that obj has been locked previously. + */ +static jsint +GetScriptExecDepth(JSContext *cx, JSObject *obj) +{ + jsval v; + + JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj)); + v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_START(&js_ScriptClass)); + return JSVAL_TO_INT(v); +} + +static void +AdjustScriptExecDepth(JSContext *cx, JSObject *obj, jsint delta) +{ + jsint execDepth; + + JS_LOCK_OBJ(cx, obj); + execDepth = GetScriptExecDepth(cx, obj); + LOCKED_OBJ_SET_SLOT(obj, JSSLOT_START(&js_ScriptClass), + INT_TO_JSVAL(execDepth + delta)); + JS_UNLOCK_OBJ(cx, obj); +} + #if JS_HAS_TOSOURCE static JSBool script_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, @@ -165,11 +190,13 @@ script_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, { JSString *str; JSObject *scopeobj; - JSScript *oldscript, *script; + jsval v; + JSScript *script, *oldscript; JSStackFrame *fp, *caller; const char *file; uintN line; JSPrincipals *principals; + jsint execDepth; /* Make sure obj is a Script object. */ if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) @@ -192,18 +219,6 @@ script_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, argv[1] = OBJECT_TO_JSVAL(scopeobj); } - /* XXX thread safety was completely neglected in this function... */ - oldscript = (JSScript *) JS_GetPrivate(cx, obj); - if (oldscript) { - for (fp = cx->fp; fp; fp = fp->down) { - if (fp->script == oldscript) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_SELF_MODIFYING_SCRIPT); - return JS_FALSE; - } - } - } - /* Compile using the caller's scope chain, which js_Invoke passes to fp. */ fp = cx->fp; caller = JS_GetScriptedCaller(cx, fp); @@ -247,15 +262,32 @@ script_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, if (!script) return JS_FALSE; - /* Swap script for obj's old script, if any. */ - if (!JS_SetPrivate(cx, obj, script)) { - js_DestroyScript(cx, script); + JS_LOCK_OBJ(cx, obj); + execDepth = GetScriptExecDepth(cx, obj); + + /* + * execDepth must be 0 to allow compilation here, otherwise the JSScript + * struct can be released while running. + */ + if (execDepth > 0) { + JS_UNLOCK_OBJ(cx, obj); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_COMPILE_EXECED_SCRIPT); return JS_FALSE; } + + /* Swap script for obj's old script, if any. */ + v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PRIVATE); + oldscript = !JSVAL_IS_VOID(v) ? (JSScript *) JSVAL_TO_PRIVATE(v) : NULL; + LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(script)); + JS_UNLOCK_OBJ(cx, obj); + if (oldscript) js_DestroyScript(cx, oldscript); script->object = obj; + js_CallNewScriptHook(cx, script, NULL); + out: /* Return the object. */ *rval = OBJECT_TO_JSVAL(obj); @@ -267,8 +299,8 @@ script_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSObject *scopeobj, *parent; JSStackFrame *fp, *caller; - JSPrincipals *principals; JSScript *script; + JSBool ok; if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) return JS_FALSE; @@ -331,18 +363,27 @@ script_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if (!scopeobj) return JS_FALSE; - script = (JSScript *) JS_GetPrivate(cx, obj); - if (!script) - return JS_TRUE; + /* Keep track of nesting depth for the script. */ + AdjustScriptExecDepth(cx, obj, 1); - /* Belt-and-braces: check that this script object has access to scopeobj. */ - principals = script->principals; - if (!js_CheckPrincipalsAccess(cx, scopeobj, principals, - CLASS_ATOM(cx, Script))) { - return JS_FALSE; + /* Must get to out label after this */ + script = (JSScript *) JS_GetPrivate(cx, obj); + if (!script) { + ok = JS_FALSE; + goto out; } - return js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval); + /* Belt-and-braces: check that this script object has access to scopeobj. */ + ok = js_CheckPrincipalsAccess(cx, scopeobj, script->principals, + CLASS_ATOM(cx, Script)); + if (!ok) + goto out; + + ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval); + +out: + AdjustScriptExecDepth(cx, obj, -1); + return ok; } #if JS_HAS_XDR @@ -748,6 +789,7 @@ script_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, JSString *str; void *buf; uint32 len; + jsval v; JSScript *script, *oldscript; JSBool ok, hasMagic; @@ -797,13 +839,26 @@ script_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, goto out; } - /* Swap script for obj's old script, if any. */ - oldscript = (JSScript *) JS_GetPrivate(cx, obj); - ok = JS_SetPrivate(cx, obj, script); - if (!ok) { - JS_free(cx, script); + JS_LOCK_OBJ(cx, obj); + execDepth = GetScriptExecDepth(cx, obj); + + /* + * execDepth must be 0 to allow compilation here, otherwise the JSScript + * struct can be released while running. + */ + if (execDepth > 0) { + JS_UNLOCK_OBJ(cx, obj); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_COMPILE_EXECED_SCRIPT); goto out; } + + /* Swap script for obj's old script, if any. */ + v = LOCKED_OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + oldscript = !JSVAL_IS_VOID(v) ? (JSScript *) JSVAL_TO_PRIVATE(v) : NULL; + LOCKED_OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(script)); + JS_UNLOCK_OBJ(cx, obj); + if (oldscript) js_DestroyScript(cx, oldscript); @@ -884,7 +939,8 @@ const char js_Script_str[] = "Script"; JS_FRIEND_DATA(JSClass) js_ScriptClass = { js_Script_str, - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Script), + JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Script) | + JSCLASS_HAS_RESERVED_SLOTS(1), JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, script_finalize, NULL, NULL, script_call, NULL,/*XXXbe xdr*/ @@ -908,6 +964,10 @@ Script(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) */ *rval = OBJECT_TO_JSVAL(obj); } + + if (!JS_SetReservedSlot(cx, obj, 0, INT_TO_JSVAL(0))) + return JS_FALSE; + return script_compile(cx, obj, argc, argv, rval); }