mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-02 01:48:05 +00:00
Bug 631135 - Objects created by or on behalf of fast natives and property ops (getters or setters) are parented to the wrong proto and global. r=lw,jst,mrbkap,bz, a=jst
This commit is contained in:
parent
068460f52c
commit
77f31fb7fe
@ -13,7 +13,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=246699
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=246699">Mozilla Bug 246699</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
<iframe id="load-frame"></iframe>
|
||||
<iframe id="load-frame" src="http://example.com/"></iframe>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
@ -49,11 +49,13 @@ function tryComponentsClasses()
|
||||
return Components.classes["@mozilla.org/dummy;1"];
|
||||
}
|
||||
|
||||
|
||||
is(inciteCaps(tryChromeLoad), "denied-stack",
|
||||
"should get stack for content-loading-chrome rejection");
|
||||
is(inciteCaps(tryComponentsClasses), "denied-stack",
|
||||
"should get stack for Components.classes rejection");
|
||||
window.addEventListener("load", function()
|
||||
{
|
||||
is(inciteCaps(tryChromeLoad), "denied-stack",
|
||||
"should get stack for content-loading-chrome rejection");
|
||||
is(inciteCaps(tryComponentsClasses), "denied-stack",
|
||||
"should get stack for Components.classes rejection");
|
||||
}, false);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
@ -5799,9 +5799,13 @@ CloneSimpleValues(JSContext* cx,
|
||||
|
||||
// RegExp objects.
|
||||
if (js_ObjectIsRegExp(obj)) {
|
||||
JSObject* global = JS_GetGlobalForScopeChain(cx);
|
||||
if (!global) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
JSObject* proto;
|
||||
if (!js_GetClassPrototype(cx, JS_GetScopeChain(cx), JSProto_RegExp,
|
||||
&proto)) {
|
||||
if (!js_GetClassPrototype(cx, global, JSProto_RegExp, &proto)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
JSObject* newRegExp = js_CloneRegExpObject(cx, obj, proto);
|
||||
|
@ -5804,30 +5804,17 @@ nsGlobalWindow::CallerInnerWindow()
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
JSObject *scope = nsnull;
|
||||
JSStackFrame *fp = nsnull;
|
||||
JS_FrameIterator(cx, &fp);
|
||||
if (fp) {
|
||||
while (fp->isDummyFrame()) {
|
||||
if (!JS_FrameIterator(cx, &fp))
|
||||
break;
|
||||
}
|
||||
|
||||
if (fp)
|
||||
scope = &fp->scopeChain();
|
||||
}
|
||||
|
||||
if (!scope)
|
||||
scope = JS_GetScopeChain(cx);
|
||||
JSObject *callerGlobal;
|
||||
if (!::JS_GetGlobalForCallingScript(cx, &callerGlobal) || !callerGlobal)
|
||||
return nsnull;
|
||||
|
||||
JSAutoEnterCompartment ac;
|
||||
if (!ac.enter(cx, scope))
|
||||
if (!ac.enter(cx, callerGlobal))
|
||||
return nsnull;
|
||||
|
||||
nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
|
||||
nsContentUtils::XPConnect()->
|
||||
GetWrappedNativeOfJSObject(cx, ::JS_GetGlobalForObject(cx, scope),
|
||||
getter_AddRefs(wrapper));
|
||||
GetWrappedNativeOfJSObject(cx, callerGlobal, getter_AddRefs(wrapper));
|
||||
if (!wrapper)
|
||||
return nsnull;
|
||||
|
||||
|
@ -670,9 +670,25 @@ nsJSContext::DOMOperationCallback(JSContext *cx)
|
||||
|
||||
// Check the amount of time this script has been running, or if the
|
||||
// dialog is disabled.
|
||||
JSObject* global = ::JS_GetGlobalForScopeChain(cx);
|
||||
JSObject* global;
|
||||
if (!::JS_GetGlobalForCallingScript(cx, &global)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
if (!global) {
|
||||
global = JS_GetGlobalObject(cx);
|
||||
if (!global) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
OBJ_TO_INNER_OBJECT(cx, global);
|
||||
if (!global) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
NS_ABORT_IF_FALSE(global != NULL, "operation callback without script?");
|
||||
|
||||
PRBool isTrackingChromeCodeTime =
|
||||
global && xpc::AccessCheck::isChrome(global->getCompartment());
|
||||
xpc::AccessCheck::isChrome(global->getCompartment());
|
||||
if (duration < (isTrackingChromeCodeTime ?
|
||||
sMaxChromeScriptRunTime : sMaxScriptRunTime)) {
|
||||
return JS_TRUE;
|
||||
|
@ -190,19 +190,18 @@ nsJSUtils::GetCurrentlyRunningCodeWindowID(JSContext *aContext)
|
||||
if (!aContext)
|
||||
return 0;
|
||||
|
||||
PRUint64 windowID = 0;
|
||||
JSObject *jsGlobal;
|
||||
if (!JS_GetGlobalForCallingScript(aContext, &jsGlobal) || !jsGlobal)
|
||||
return 0;
|
||||
|
||||
JSObject *jsGlobal = JS_GetGlobalForScopeChain(aContext);
|
||||
if (jsGlobal) {
|
||||
nsIScriptGlobalObject *scriptGlobal = GetStaticScriptGlobal(aContext,
|
||||
jsGlobal);
|
||||
if (scriptGlobal) {
|
||||
nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(scriptGlobal);
|
||||
if (win)
|
||||
windowID = win->GetOuterWindow()->WindowID();
|
||||
}
|
||||
}
|
||||
JSAutoEnterCompartment ac;
|
||||
if (!ac.enter(aContext, jsGlobal))
|
||||
return 0;
|
||||
nsIScriptGlobalObject *sg = GetStaticScriptGlobal(aContext, jsGlobal);
|
||||
if (!sg)
|
||||
return 0;
|
||||
|
||||
return windowID;
|
||||
nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(sg);
|
||||
return win ? win->GetOuterWindow()->WindowID() : 0;
|
||||
}
|
||||
|
||||
|
@ -114,7 +114,7 @@ ReturnKeyRange(JSContext* aCx,
|
||||
nsIXPConnect* xpc = nsContentUtils::XPConnect();
|
||||
NS_ASSERTION(xpc, "This should never be null!");
|
||||
|
||||
JSObject* global = JS_GetGlobalForObject(aCx, JS_GetScopeChain(aCx));
|
||||
JSObject* global = JS_GetGlobalForScopeChain(aCx);
|
||||
NS_ENSURE_TRUE(global, JS_FALSE);
|
||||
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
|
||||
|
@ -634,7 +634,7 @@ nsDOMWorkerFunctions::GetInstanceCommon(JSContext* aCx,
|
||||
}
|
||||
}
|
||||
|
||||
JSObject* global = JS_GetGlobalForObject(aCx, JS_GetScopeChain(aCx));
|
||||
JSObject* global = JS_GetGlobalForScopeChain(aCx);
|
||||
if (!global) {
|
||||
NS_ASSERTION(JS_IsExceptionPending(aCx), "Need to set an exception!");
|
||||
return JS_FALSE;
|
||||
@ -2533,8 +2533,7 @@ nsDOMWorker::ReadStructuredClone(JSContext* aCx,
|
||||
if (JS_ReadBytes(aReader, &wrappedNative, sizeof(wrappedNative))) {
|
||||
NS_ASSERTION(wrappedNative, "Null pointer?!");
|
||||
|
||||
JSObject* global = JS_GetGlobalForObject(aCx, JS_GetScopeChain(aCx));
|
||||
if (global) {
|
||||
if (JSObject* global = JS_GetGlobalForScopeChain(aCx)) {
|
||||
jsval val;
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
|
||||
if (NS_SUCCEEDED(nsContentUtils::WrapNative(aCx, global, wrappedNative,
|
||||
|
@ -54,6 +54,7 @@ CPPSRCS = \
|
||||
testCloneScript.cpp \
|
||||
testConservativeGC.cpp \
|
||||
testContexts.cpp \
|
||||
testCrossGlobal.cpp \
|
||||
testDebugger.cpp \
|
||||
testDeepFreeze.cpp \
|
||||
testDefineGetterSetterNonEnumerable.cpp \
|
||||
|
90
js/src/jsapi-tests/testCrossGlobal.cpp
Normal file
90
js/src/jsapi-tests/testCrossGlobal.cpp
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/
|
||||
*/
|
||||
#include "tests.h"
|
||||
|
||||
static const char CODE[] =
|
||||
"function f() "
|
||||
"{ "
|
||||
" return new otherGlobal.Array() instanceof otherGlobal.Array; "
|
||||
"} "
|
||||
"f() && f() && f(); ";
|
||||
|
||||
BEGIN_TEST(testCrossGlobal_call)
|
||||
{
|
||||
JSObject *otherGlobal = JS_NewGlobalObject(cx, basicGlobalClass());
|
||||
CHECK(otherGlobal);
|
||||
|
||||
JSBool res = JS_InitStandardClasses(cx, otherGlobal);
|
||||
CHECK(res);
|
||||
|
||||
res = JS_DefineProperty(cx, global, "otherGlobal", OBJECT_TO_JSVAL(otherGlobal),
|
||||
JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE);
|
||||
CHECK(res);
|
||||
|
||||
uintN oldopts = JS_GetOptions(cx);
|
||||
uintN newopts = oldopts & ~(JSOPTION_JIT | JSOPTION_METHODJIT | JSOPTION_PROFILING);
|
||||
newopts |= JSOPTION_METHODJIT;
|
||||
JS_SetOptions(cx, newopts);
|
||||
|
||||
jsval rv;
|
||||
EVAL(CODE, &rv);
|
||||
|
||||
CHECK_SAME(rv, JSVAL_TRUE);
|
||||
|
||||
JS_SetOptions(cx, oldopts);
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testCrossGlobal_call)
|
||||
|
||||
BEGIN_TEST(testCrossGlobal_compileAndGo)
|
||||
{
|
||||
|
||||
JSObject *otherGlobal = JS_NewGlobalObject(cx, basicGlobalClass());
|
||||
CHECK(otherGlobal);
|
||||
|
||||
JSBool res = JS_InitStandardClasses(cx, otherGlobal);
|
||||
CHECK(res);
|
||||
|
||||
res = JS_DefineProperty(cx, global, "otherGlobal", OBJECT_TO_JSVAL(otherGlobal),
|
||||
JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE);
|
||||
CHECK(res);
|
||||
|
||||
res = JS_DefineProperty(cx, otherGlobal, "otherGlobal", OBJECT_TO_JSVAL(otherGlobal),
|
||||
JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE);
|
||||
CHECK(res);
|
||||
|
||||
uintN oldopts = JS_GetOptions(cx);
|
||||
uintN newopts =
|
||||
oldopts
|
||||
& ~(JSOPTION_COMPILE_N_GO | JSOPTION_JIT | JSOPTION_METHODJIT | JSOPTION_PROFILING);
|
||||
newopts |= JSOPTION_METHODJIT;
|
||||
JS_SetOptions(cx, newopts);
|
||||
|
||||
jsval rv;
|
||||
|
||||
// Compile script
|
||||
|
||||
JSScript *script = JS_CompileScript(cx, global, CODE, strlen(CODE), __FILE__, __LINE__);
|
||||
CHECK(script);
|
||||
JSObject *scriptObj = JS_NewScriptObject(cx, script);
|
||||
CHECK(scriptObj);
|
||||
JS::Anchor<JSObject *> anch(scriptObj);
|
||||
|
||||
// Run script against new other global
|
||||
res = JS_ExecuteScript(cx, otherGlobal, script, &rv);
|
||||
CHECK(res);
|
||||
CHECK_SAME(rv, JSVAL_TRUE);
|
||||
|
||||
// Run script against original global
|
||||
res = JS_ExecuteScript(cx, global, script, &rv);
|
||||
CHECK(res);
|
||||
CHECK_SAME(rv, JSVAL_TRUE);
|
||||
|
||||
JS_SetOptions(cx, oldopts);
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testCrossGlobal_compileAndGo)
|
@ -1930,13 +1930,6 @@ JS_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject **objp)
|
||||
return js_GetClassObject(cx, obj, key, objp);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject *)
|
||||
JS_GetScopeChain(JSContext *cx)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
return GetScopeChain(cx);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject *)
|
||||
JS_GetGlobalForObject(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
@ -1948,7 +1941,24 @@ JS_PUBLIC_API(JSObject *)
|
||||
JS_GetGlobalForScopeChain(JSContext *cx)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
return GetGlobalForScopeChain(cx);
|
||||
return cx->getGlobalFromScopeChain();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_GetGlobalForCallingScript(JSContext *cx, JSObject **objp)
|
||||
{
|
||||
JSStackFrame *fp = JS_GetScriptedCaller(cx, NULL);
|
||||
if (!fp) {
|
||||
*objp = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
JSObject *scope = JS_GetFrameScopeChain(cx, fp);
|
||||
if (!scope)
|
||||
return false;
|
||||
|
||||
*objp = scope->getGlobal();
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(jsval)
|
||||
|
@ -1098,15 +1098,40 @@ extern JS_PUBLIC_API(JSBool)
|
||||
JS_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
|
||||
JSObject **objp);
|
||||
|
||||
extern JS_PUBLIC_API(JSObject *)
|
||||
JS_GetScopeChain(JSContext *cx);
|
||||
|
||||
extern JS_PUBLIC_API(JSObject *)
|
||||
JS_GetGlobalForObject(JSContext *cx, JSObject *obj);
|
||||
|
||||
/*
|
||||
* Returns the global for the currently executing method or of the object
|
||||
* currently being accessed if a property access (get or set) is in progress.
|
||||
* If no action is currently in progress, the context's global object is
|
||||
* returned. If the context has no global, this method will report an error and
|
||||
* return null.
|
||||
*
|
||||
* To illustrate: suppose a global G with a method defined by a JSNative
|
||||
* corresponding to the property G.method. When JS_GetGlobalForScopeChain is
|
||||
* invoked during execution of G.method, the object returned will be G. This is
|
||||
* so if a script in G calls method, and it is also so if script in a different
|
||||
* global G2 calls |G1.method()|, or extracts |G.method| and calls it, and so
|
||||
* on. This behavior also applies for property accesses to an |obj.prop| if
|
||||
* that property is represented by a |JSPropertyOp| and |JSStrictPropertyOp|
|
||||
* pair: the global object will be that corresponding to |obj|.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSObject *)
|
||||
JS_GetGlobalForScopeChain(JSContext *cx);
|
||||
|
||||
/*
|
||||
* Returns the global of the most recent interpreted code if there is any.
|
||||
*
|
||||
* NB: This method ignores the current scope chain, any sense of privilege
|
||||
* separation encapsulated in intervening stack frames, and so on. If you
|
||||
* are attempting to determine the global object to expose it to script,
|
||||
* this is almost certainly not the method you want! You *probably* want
|
||||
* JS_GetGlobalForScopeChain instead.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSBool)
|
||||
JS_GetGlobalForCallingScript(JSContext *cx, JSObject **objp);
|
||||
|
||||
#ifdef JS_HAS_CTYPES
|
||||
/*
|
||||
* Initialize the 'ctypes' object on a global variable 'obj'. The 'ctypes'
|
||||
|
@ -231,6 +231,8 @@ StackSpace::mark(JSTracer *trc)
|
||||
/* This may be the only pointer to the initialVarObj. */
|
||||
if (seg->hasInitialVarObj())
|
||||
MarkObject(trc, seg->getInitialVarObj(), "varobj");
|
||||
if (seg->hasLastNativeCalleeScope())
|
||||
MarkObject(trc, *seg->lastNativeCalleeScope(), "lastNativeCalleeScope");
|
||||
|
||||
/* Mark slots/args trailing off of the last stack frame. */
|
||||
JSStackFrame *fp = seg->getCurrentFrame();
|
||||
@ -1991,6 +1993,12 @@ JSContext::JSContext(JSRuntime *rt)
|
||||
busyArrays()
|
||||
{}
|
||||
|
||||
void
|
||||
JSContext::reportInactive()
|
||||
{
|
||||
JS_ReportErrorNumber(this, js_GetErrorMessage, NULL, JSMSG_INACTIVE);
|
||||
}
|
||||
|
||||
void
|
||||
JSContext::resetCompartment()
|
||||
{
|
||||
@ -2084,7 +2092,9 @@ void
|
||||
JSContext::saveActiveSegment()
|
||||
{
|
||||
JS_ASSERT(hasActiveSegment());
|
||||
currentSegment->save(regs);
|
||||
currentSegment->save(regs, lastNativeCalleeScope, lastNativeCalleeScopePtr);
|
||||
lastNativeCalleeScope = NULL;
|
||||
lastNativeCalleeScopePtr = NULL;
|
||||
setCurrentRegs(NULL);
|
||||
resetCompartment();
|
||||
}
|
||||
@ -2092,9 +2102,11 @@ JSContext::saveActiveSegment()
|
||||
void
|
||||
JSContext::restoreSegment()
|
||||
{
|
||||
js::StackSegment *ccs = currentSegment;
|
||||
setCurrentRegs(ccs->getSuspendedRegs());
|
||||
ccs->restore();
|
||||
js::StackSegment *cs = currentSegment;
|
||||
setCurrentRegs(cs->getSuspendedRegs());
|
||||
lastNativeCalleeScopePtr = cs->lastNativeCalleeScopePtr();
|
||||
lastNativeCalleeScope = cs->lastNativeCalleeScope();
|
||||
cs->restore();
|
||||
resetCompartment();
|
||||
}
|
||||
|
||||
|
@ -245,6 +245,10 @@ class StackSegment
|
||||
/* Whether this segment was suspended by JS_SaveFrameChain. */
|
||||
bool saved;
|
||||
|
||||
/* Maintained to implement getGlobalFromScopeChain. */
|
||||
JSObject *savedLastNativeCalleeScope_;
|
||||
void *savedLastNativeCalleeScopePtr_;
|
||||
|
||||
/* Align at 8 bytes on all platforms. */
|
||||
#if JS_BITS_PER_WORD == 32
|
||||
void *padding;
|
||||
@ -260,7 +264,8 @@ class StackSegment
|
||||
StackSegment()
|
||||
: cx(NULL), previousInContext(NULL), previousInMemory(NULL),
|
||||
initialFrame(NULL), suspendedRegs(NON_NULL_SUSPENDED_REGS),
|
||||
initialVarObj(NULL), saved(false)
|
||||
initialVarObj(NULL), saved(false), savedLastNativeCalleeScope_(NULL),
|
||||
savedLastNativeCalleeScopePtr_(NULL)
|
||||
{
|
||||
JS_ASSERT(!inContext());
|
||||
}
|
||||
@ -348,19 +353,9 @@ class StackSegment
|
||||
|
||||
/* When isSuspended, transitioning isSaved <--> !isSaved */
|
||||
|
||||
void save(JSFrameRegs *regs) {
|
||||
JS_ASSERT(!isSuspended());
|
||||
suspend(regs);
|
||||
saved = true;
|
||||
JS_ASSERT(isSaved());
|
||||
}
|
||||
void save(JSFrameRegs *regs, JSObject *lastNativeCalleeScope, void *lastNativeCalleeScopePtr);
|
||||
|
||||
void restore() {
|
||||
JS_ASSERT(isSaved());
|
||||
saved = false;
|
||||
resume();
|
||||
JS_ASSERT(!isSuspended());
|
||||
}
|
||||
void restore();
|
||||
|
||||
/* Data available when inContext */
|
||||
|
||||
@ -416,6 +411,21 @@ class StackSegment
|
||||
return *initialVarObj;
|
||||
}
|
||||
|
||||
bool hasLastNativeCalleeScope() const {
|
||||
JS_ASSERT(inContext());
|
||||
return savedLastNativeCalleeScope_ != NULL;
|
||||
}
|
||||
|
||||
JSObject *lastNativeCalleeScope() const {
|
||||
JS_ASSERT(inContext());
|
||||
return savedLastNativeCalleeScope_;
|
||||
}
|
||||
|
||||
void *lastNativeCalleeScopePtr() const {
|
||||
JS_ASSERT(inContext());
|
||||
return savedLastNativeCalleeScopePtr_;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
JS_REQUIRES_STACK bool contains(const JSStackFrame *fp) const;
|
||||
#endif
|
||||
@ -623,8 +633,6 @@ class StackSpace
|
||||
inline void popInvokeArgs(const InvokeArgsGuard &args);
|
||||
inline void popInvokeFrame(const InvokeFrameGuard &ag);
|
||||
|
||||
inline Value *firstUnused() const;
|
||||
|
||||
inline bool isCurrentAndActive(JSContext *cx) const;
|
||||
friend class AllFramesIter;
|
||||
StackSegment *getCurrentSegment() const { return currentSegment; }
|
||||
@ -634,6 +642,11 @@ class StackSpace
|
||||
JS_FRIEND_API(bool) bumpCommit(Value *from, ptrdiff_t nvals) const;
|
||||
#endif
|
||||
|
||||
/* The first stack location available to push new values and frames. */
|
||||
inline js::Value *firstUnused() const;
|
||||
public:
|
||||
inline void *constFirstUnused() const;
|
||||
|
||||
public:
|
||||
static const size_t CAPACITY_VALS = 512 * 1024;
|
||||
static const size_t CAPACITY_BYTES = CAPACITY_VALS * sizeof(Value);
|
||||
@ -1684,6 +1697,32 @@ struct JSContext
|
||||
return !!regs;
|
||||
}
|
||||
|
||||
/*
|
||||
* An object created for the global of the currently executing native
|
||||
* method, native getter, or native setter (or null if none is executing),
|
||||
* used for scope chain computation. The exact identity of this value is
|
||||
* unspecified: function calls and property accesses fill this with
|
||||
* different values, and the trace JIT and method JIT don't update the
|
||||
* value during intra-global-object method calls and property accesses.
|
||||
*/
|
||||
JSObject *lastNativeCalleeScope;
|
||||
|
||||
/*
|
||||
* A JavaScript stack location associated with a native call/property
|
||||
* access.
|
||||
*/
|
||||
void *lastNativeCalleeScopePtr;
|
||||
|
||||
/*
|
||||
* Computes the global object corresponding to the current scope chain, as
|
||||
* determined by the current callee if one exists, by the object being
|
||||
* accessed if a native property operation is occurring, or by the context
|
||||
* global object if one does not.
|
||||
*/
|
||||
inline JSObject *getGlobalFromScopeChain();
|
||||
|
||||
void reportInactive();
|
||||
|
||||
public:
|
||||
friend class js::StackSpace;
|
||||
friend bool js::Interpret(JSContext *, JSStackFrame *, uintN, JSInterpMode);
|
||||
@ -1768,7 +1807,7 @@ struct JSContext
|
||||
return currentSegment;
|
||||
}
|
||||
|
||||
inline js::RegExpStatics *regExpStatics();
|
||||
inline js::RegExpStatics *getRegExpStatics();
|
||||
|
||||
/* Add the given segment to the list as the new active segment. */
|
||||
void pushSegmentAndFrame(js::StackSegment *newseg, JSFrameRegs ®s);
|
||||
|
@ -49,35 +49,6 @@
|
||||
#include "jsregexp.h"
|
||||
#include "jsgc.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
static inline JSObject *
|
||||
GetGlobalForScopeChain(JSContext *cx)
|
||||
{
|
||||
/*
|
||||
* This is essentially GetScopeChain(cx)->getGlobal(), but without
|
||||
* falling off trace.
|
||||
*
|
||||
* This use of cx->fp, possibly on trace, is deliberate:
|
||||
* cx->fp->scopeChain->getGlobal() returns the same object whether we're on
|
||||
* trace or not, since we do not trace calls across global objects.
|
||||
*/
|
||||
VOUCH_DOES_NOT_REQUIRE_STACK();
|
||||
|
||||
if (cx->hasfp())
|
||||
return cx->fp()->scopeChain().getGlobal();
|
||||
|
||||
JSObject *scope = cx->globalObject;
|
||||
if (!scope) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE);
|
||||
return NULL;
|
||||
}
|
||||
OBJ_TO_INNER_OBJECT(cx, scope);
|
||||
return scope;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#ifdef JS_METHODJIT
|
||||
inline js::mjit::JaegerCompartment *JSContext::jaegerCompartment()
|
||||
{
|
||||
@ -85,6 +56,45 @@ inline js::mjit::JaegerCompartment *JSContext::jaegerCompartment()
|
||||
}
|
||||
#endif
|
||||
|
||||
inline JSObject *
|
||||
JSContext::getGlobalFromScopeChain()
|
||||
{
|
||||
if (regs) {
|
||||
/*
|
||||
* It's fine if the frame is stale on-trace, because we don't trace
|
||||
* calls crossing globals.
|
||||
*/
|
||||
VOUCH_DOES_NOT_REQUIRE_STACK();
|
||||
|
||||
/* Consult the frame if it's newer than any last native call. */
|
||||
if (regs->fp > lastNativeCalleeScopePtr)
|
||||
return regs->fp->scopeChain().getGlobal();
|
||||
|
||||
/* Otherwise the callee gives us our global object. */
|
||||
JS_ASSERT(lastNativeCalleeScopePtr <= stack().constFirstUnused());
|
||||
JS_ASSERT(currentSegment < lastNativeCalleeScopePtr);
|
||||
return lastNativeCalleeScope->getGlobal();
|
||||
}
|
||||
|
||||
/* If we have only native calls, consult the last one. */
|
||||
if (lastNativeCalleeScope) {
|
||||
JS_ASSERT(lastNativeCalleeScopePtr <= stack().constFirstUnused());
|
||||
return lastNativeCalleeScope->getGlobal();
|
||||
}
|
||||
|
||||
/*
|
||||
* If no native call has occurred, this is a free-floating request for
|
||||
* a global: return the context globalObject if possible.
|
||||
*/
|
||||
JSObject *globalObj = globalObject;
|
||||
if (!globalObj) {
|
||||
reportInactive();
|
||||
return NULL;
|
||||
}
|
||||
OBJ_TO_INNER_OBJECT(this, globalObj);
|
||||
return globalObj;
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSContext::ensureGeneratorStackSpace()
|
||||
{
|
||||
@ -110,13 +120,37 @@ JSContext::computeNextFrame(JSStackFrame *fp)
|
||||
}
|
||||
|
||||
inline js::RegExpStatics *
|
||||
JSContext::regExpStatics()
|
||||
JSContext::getRegExpStatics()
|
||||
{
|
||||
return js::RegExpStatics::extractFrom(js::GetGlobalForScopeChain(this));
|
||||
JSObject *global = getGlobalFromScopeChain();
|
||||
if (!global)
|
||||
return NULL;
|
||||
return js::RegExpStatics::extractFrom(global);
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
JS_ALWAYS_INLINE void
|
||||
StackSegment::save(JSFrameRegs *regs, JSObject *lastNativeCalleeScope,
|
||||
void *lastNativeCalleeScopePtr)
|
||||
{
|
||||
JS_ASSERT(!isSuspended());
|
||||
suspend(regs);
|
||||
saved = true;
|
||||
savedLastNativeCalleeScope_ = lastNativeCalleeScope;
|
||||
savedLastNativeCalleeScopePtr_ = lastNativeCalleeScopePtr;
|
||||
JS_ASSERT(isSaved());
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE void
|
||||
StackSegment::restore()
|
||||
{
|
||||
JS_ASSERT(isSaved());
|
||||
saved = false;
|
||||
resume();
|
||||
JS_ASSERT(!isSuspended());
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK JS_ALWAYS_INLINE JSFrameRegs *
|
||||
StackSegment::getCurrentRegs() const
|
||||
{
|
||||
@ -153,6 +187,12 @@ StackSpace::firstUnused() const
|
||||
return invokeArgEnd;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK inline void *
|
||||
StackSpace::constFirstUnused() const
|
||||
{
|
||||
return firstUnused();
|
||||
}
|
||||
|
||||
|
||||
/* Inline so we don't need the friend API. */
|
||||
JS_ALWAYS_INLINE bool
|
||||
@ -532,7 +572,7 @@ class CompartmentChecker
|
||||
|
||||
public:
|
||||
explicit CompartmentChecker(JSContext *cx) : context(cx), compartment(cx->compartment) {
|
||||
check(cx->hasfp() ? JS_GetGlobalForScopeChain(cx) : cx->globalObject);
|
||||
check(cx->getGlobalFromScopeChain());
|
||||
VOUCH_DOES_NOT_REQUIRE_STACK();
|
||||
}
|
||||
|
||||
@ -690,6 +730,35 @@ assertSameCompartment(JSContext *cx, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
|
||||
|
||||
#undef START_ASSERT_SAME_COMPARTMENT
|
||||
|
||||
namespace detail {
|
||||
|
||||
class AutoScopeChainSetter {
|
||||
JSContext * const cx;
|
||||
JSObject * const oldLastNativeCalleeScope;
|
||||
void * const oldLastNativeCalleeScopePtr;
|
||||
JSObject * const scope;
|
||||
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
|
||||
public:
|
||||
AutoScopeChainSetter(JSContext *cx, JSObject *scope, void *scopePtr
|
||||
JS_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
: cx(cx),
|
||||
oldLastNativeCalleeScope(cx->lastNativeCalleeScope),
|
||||
oldLastNativeCalleeScopePtr(cx->lastNativeCalleeScopePtr),
|
||||
scope(scope)
|
||||
{
|
||||
JS_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
cx->lastNativeCalleeScope = scope;
|
||||
cx->lastNativeCalleeScopePtr = scopePtr;
|
||||
}
|
||||
~AutoScopeChainSetter() {
|
||||
cx->lastNativeCalleeScope = oldLastNativeCalleeScope;
|
||||
cx->lastNativeCalleeScopePtr = oldLastNativeCalleeScopePtr;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
STATIC_PRECONDITION_ASSUME(ubound(vp) >= argc + 2)
|
||||
JS_ALWAYS_INLINE bool
|
||||
CallJSNative(JSContext *cx, js::Native native, uintN argc, js::Value *vp)
|
||||
@ -697,6 +766,7 @@ CallJSNative(JSContext *cx, js::Native native, uintN argc, js::Value *vp)
|
||||
#ifdef DEBUG
|
||||
JSBool alreadyThrowing = cx->isExceptionPending();
|
||||
#endif
|
||||
detail::AutoScopeChainSetter scs(cx, &vp[0].toObject(), vp);
|
||||
assertSameCompartment(cx, ValueArray(vp, argc + 2));
|
||||
JSBool ok = native(cx, argc, vp);
|
||||
if (ok) {
|
||||
@ -747,6 +817,15 @@ JS_ALWAYS_INLINE bool
|
||||
CallJSPropertyOp(JSContext *cx, js::PropertyOp op, JSObject *obj, jsid id, js::Value *vp)
|
||||
{
|
||||
assertSameCompartment(cx, obj, id, *vp);
|
||||
|
||||
/*
|
||||
* This property access may occur not in relation to a stack location, so
|
||||
* we can't use |vp| here. However, since we use the location only in
|
||||
* comparisons, to determine when to refer to the last interpreted frame's
|
||||
* scope chain versus the last native callee's global object, the first
|
||||
* unused location at the end of the stack will suffice.
|
||||
*/
|
||||
detail::AutoScopeChainSetter scs(cx, obj, cx->stack().constFirstUnused());
|
||||
JSBool ok = op(cx, obj, id, vp);
|
||||
if (ok)
|
||||
assertSameCompartment(cx, obj, *vp);
|
||||
@ -758,6 +837,9 @@ CallJSPropertyOpSetter(JSContext *cx, js::StrictPropertyOp op, JSObject *obj, js
|
||||
JSBool strict, js::Value *vp)
|
||||
{
|
||||
assertSameCompartment(cx, obj, id, *vp);
|
||||
|
||||
/* See the comment in CallJSPropertyOp for an explanation. */
|
||||
detail::AutoScopeChainSetter scs(cx, obj, cx->stack().constFirstUnused());
|
||||
return op(cx, obj, id, strict, vp);
|
||||
}
|
||||
|
||||
|
@ -219,19 +219,12 @@ JSCompartment::wrap(JSContext *cx, Value *vp)
|
||||
* Wrappers should really be parented to the wrapped parent of the wrapped
|
||||
* object, but in that case a wrapped global object would have a NULL
|
||||
* parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead
|
||||
,
|
||||
* we parent all wrappers to the global object in their home compartment.
|
||||
* This loses us some transparency, and is generally very cheesy.
|
||||
*/
|
||||
JSObject *global;
|
||||
if (cx->hasfp()) {
|
||||
global = cx->fp()->scopeChain().getGlobal();
|
||||
} else {
|
||||
global = cx->globalObject;
|
||||
OBJ_TO_INNER_OBJECT(cx, global);
|
||||
if (!global)
|
||||
return false;
|
||||
}
|
||||
JSObject *global = cx->getGlobalFromScopeChain();
|
||||
if (!global)
|
||||
return false;
|
||||
|
||||
/* Unwrap incoming objects. */
|
||||
if (vp->isObject()) {
|
||||
|
@ -142,33 +142,6 @@ JSStackFrame::pc(JSContext *cx, JSStackFrame *next)
|
||||
#endif
|
||||
}
|
||||
|
||||
JSObject *
|
||||
js::GetScopeChain(JSContext *cx)
|
||||
{
|
||||
JSStackFrame *fp = js_GetTopStackFrame(cx);
|
||||
if (!fp) {
|
||||
/*
|
||||
* There is no code active on this context. In place of an actual
|
||||
* scope chain, use the context's global object, which is set in
|
||||
* js_InitFunctionAndObjectClasses, and which represents the default
|
||||
* scope chain for the embedding. See also js_FindClassObject.
|
||||
*
|
||||
* For embeddings that use the inner and outer object hooks, the inner
|
||||
* object represents the ultimate global object, with the outer object
|
||||
* acting as a stand-in.
|
||||
*/
|
||||
JSObject *obj = cx->globalObject;
|
||||
if (!obj) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
OBJ_TO_INNER_OBJECT(cx, obj);
|
||||
return obj;
|
||||
}
|
||||
return GetScopeChain(cx, fp);
|
||||
}
|
||||
|
||||
/*
|
||||
* This computes the blockChain by iterating through the bytecode
|
||||
* of the current script until it reaches the PC. Each time it sees
|
||||
|
@ -803,9 +803,6 @@ GetBlockChain(JSContext *cx, JSStackFrame *fp);
|
||||
extern JSObject *
|
||||
GetBlockChainFast(JSContext *cx, JSStackFrame *fp, JSOp op, size_t oplen);
|
||||
|
||||
extern JSObject *
|
||||
GetScopeChain(JSContext *cx);
|
||||
|
||||
/*
|
||||
* Refresh and return fp->scopeChain. It may be stale if block scopes are
|
||||
* active but not yet reflected by objects in the scope chain. If a block
|
||||
|
@ -4242,33 +4242,17 @@ JSBool
|
||||
js_FindClassObject(JSContext *cx, JSObject *start, JSProtoKey protoKey,
|
||||
Value *vp, Class *clasp)
|
||||
{
|
||||
JSStackFrame *fp;
|
||||
JSObject *obj, *cobj, *pobj;
|
||||
jsid id;
|
||||
JSProperty *prop;
|
||||
const Shape *shape;
|
||||
|
||||
/*
|
||||
* Find the global object. Use cx->fp() directly to avoid falling off
|
||||
* trace; all JIT-elided stack frames have the same global object as
|
||||
* cx->fp().
|
||||
*/
|
||||
VOUCH_DOES_NOT_REQUIRE_STACK();
|
||||
if (!start && (fp = cx->maybefp()) != NULL)
|
||||
start = &fp->scopeChain();
|
||||
|
||||
if (start) {
|
||||
/* Find the topmost object in the scope chain. */
|
||||
do {
|
||||
obj = start;
|
||||
start = obj->getParent();
|
||||
} while (start);
|
||||
obj = start->getGlobal();
|
||||
} else {
|
||||
obj = cx->globalObject;
|
||||
if (!obj) {
|
||||
vp->setUndefined();
|
||||
return JS_TRUE;
|
||||
}
|
||||
obj = cx->getGlobalFromScopeChain();
|
||||
if (!obj)
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
OBJ_TO_INNER_OBJECT(cx, obj);
|
||||
@ -6189,15 +6173,9 @@ js_GetClassPrototype(JSContext *cx, JSObject *scopeobj, JSProtoKey protoKey,
|
||||
|
||||
if (protoKey != JSProto_Null) {
|
||||
if (!scopeobj) {
|
||||
if (cx->hasfp())
|
||||
scopeobj = &cx->fp()->scopeChain();
|
||||
if (!scopeobj) {
|
||||
scopeobj = cx->globalObject;
|
||||
if (!scopeobj) {
|
||||
*protop = NULL;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
scopeobj = cx->getGlobalFromScopeChain();
|
||||
if (!scopeobj)
|
||||
return false;
|
||||
}
|
||||
scopeobj = scopeobj->getGlobal();
|
||||
if (scopeobj->isGlobal()) {
|
||||
|
@ -969,15 +969,9 @@ NewBuiltinClassInstance(JSContext *cx, Class *clasp, gc::FinalizeKind kind)
|
||||
JS_ASSERT(protoKey != JSProto_Null);
|
||||
|
||||
/* NB: inline-expanded and specialized version of js_GetClassPrototype. */
|
||||
JSObject *global;
|
||||
if (!cx->hasfp()) {
|
||||
global = cx->globalObject;
|
||||
OBJ_TO_INNER_OBJECT(cx, global);
|
||||
if (!global)
|
||||
return NULL;
|
||||
} else {
|
||||
global = cx->fp()->scopeChain().getGlobal();
|
||||
}
|
||||
JSObject *global = cx->getGlobalFromScopeChain();
|
||||
if (!global)
|
||||
return NULL;
|
||||
JS_ASSERT(global->isGlobal());
|
||||
|
||||
const Value &v = global->getReservedSlot(JSProto_LIMIT + protoKey);
|
||||
|
@ -8952,7 +8952,10 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot)
|
||||
|
||||
JSObject *obj;
|
||||
if (context->hasfp()) {
|
||||
obj = RegExp::createObject(context, context->regExpStatics(),
|
||||
RegExpStatics *res = context->getRegExpStatics();
|
||||
if (!res)
|
||||
return NULL;
|
||||
obj = RegExp::createObject(context, res,
|
||||
tokenStream.getTokenbuf().begin(),
|
||||
tokenStream.getTokenbuf().length(),
|
||||
tokenStream.currentToken().t_reflags);
|
||||
|
@ -150,7 +150,9 @@ js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto)
|
||||
*/
|
||||
assertSameCompartment(cx, obj, clone);
|
||||
|
||||
RegExpStatics *res = cx->regExpStatics();
|
||||
RegExpStatics *res = cx->getRegExpStatics();
|
||||
if (!res)
|
||||
return NULL;
|
||||
RegExp *re = RegExp::extractFrom(obj);
|
||||
{
|
||||
uint32 origFlags = re->getFlags();
|
||||
@ -401,7 +403,9 @@ regexp_resolve(JSContext *cx, JSObject *obj, jsid id, uint32 flags, JSObject **o
|
||||
static JSBool \
|
||||
name(JSContext *cx, JSObject *obj, jsid id, jsval *vp) \
|
||||
{ \
|
||||
RegExpStatics *res = cx->regExpStatics(); \
|
||||
RegExpStatics *res = cx->getRegExpStatics(); \
|
||||
if (!res) \
|
||||
return false; \
|
||||
code; \
|
||||
}
|
||||
|
||||
@ -427,7 +431,9 @@ DEFINE_STATIC_GETTER(static_paren9_getter, return res->createParen(cx, 9,
|
||||
static JSBool \
|
||||
name(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp) \
|
||||
{ \
|
||||
RegExpStatics *res = cx->regExpStatics(); \
|
||||
RegExpStatics *res = cx->getRegExpStatics(); \
|
||||
if (!res) \
|
||||
return false; \
|
||||
code; \
|
||||
return true; \
|
||||
}
|
||||
@ -672,7 +678,10 @@ EscapeNakedForwardSlashes(JSContext *cx, JSString *unescaped)
|
||||
static bool
|
||||
SwapRegExpInternals(JSContext *cx, JSObject *obj, Value *rval, JSString *str, uint32 flags = 0)
|
||||
{
|
||||
flags |= cx->regExpStatics()->getFlags();
|
||||
RegExpStatics *res = cx->getRegExpStatics();
|
||||
if (!res)
|
||||
return false;
|
||||
flags |= res->getFlags();
|
||||
AlreadyIncRefed<RegExp> re = RegExp::create(cx, str, flags);
|
||||
if (!re)
|
||||
return false;
|
||||
@ -713,7 +722,9 @@ regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, Value *argv, JSBool te
|
||||
lastIndex = 0;
|
||||
}
|
||||
|
||||
RegExpStatics *res = cx->regExpStatics();
|
||||
RegExpStatics *res = cx->getRegExpStatics();
|
||||
if (!res)
|
||||
return false;
|
||||
|
||||
JSString *input;
|
||||
if (argc) {
|
||||
|
@ -1876,8 +1876,8 @@ str_match(JSContext *cx, uintN argc, Value *vp)
|
||||
|
||||
AutoObjectRooter array(cx);
|
||||
MatchArgType arg = array.addr();
|
||||
RegExpStatics *res = cx->regExpStatics();
|
||||
if (!DoMatch(cx, res, vp, str, *rep, MatchCallback, arg, MATCH_ARGS))
|
||||
RegExpStatics *res = cx->getRegExpStatics();
|
||||
if (!res || !DoMatch(cx, res, vp, str, *rep, MatchCallback, arg, MATCH_ARGS))
|
||||
return false;
|
||||
|
||||
/* When not global, DoMatch will leave |RegExp.exec()| in *vp. */
|
||||
@ -1906,7 +1906,9 @@ str_search(JSContext *cx, uintN argc, Value *vp)
|
||||
if (!rep)
|
||||
return false;
|
||||
|
||||
RegExpStatics *res = cx->regExpStatics();
|
||||
RegExpStatics *res = cx->getRegExpStatics();
|
||||
if (!res)
|
||||
return false;
|
||||
size_t i = 0;
|
||||
if (!rep->re().execute(cx, res, str, &i, true, vp))
|
||||
return false;
|
||||
@ -2357,7 +2359,9 @@ str_replace_regexp(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata)
|
||||
rdata.leftIndex = 0;
|
||||
rdata.calledBack = false;
|
||||
|
||||
RegExpStatics *res = cx->regExpStatics();
|
||||
RegExpStatics *res = cx->getRegExpStatics();
|
||||
if (!res)
|
||||
return false;
|
||||
if (!DoMatch(cx, res, vp, rdata.str, *rep, ReplaceRegExpCallback, &rdata, REPLACE_ARGS))
|
||||
return false;
|
||||
|
||||
@ -2697,7 +2701,9 @@ str_split(JSContext *cx, uintN argc, Value *vp)
|
||||
|
||||
AutoValueVector splits(cx);
|
||||
|
||||
RegExpStatics *res = cx->regExpStatics();
|
||||
RegExpStatics *res = cx->getRegExpStatics();
|
||||
if (!res)
|
||||
return false;
|
||||
jsint i, j;
|
||||
uint32 len = i = 0;
|
||||
while ((j = find_split(cx, res, str, re, &i, sep)) >= 0) {
|
||||
|
@ -1677,6 +1677,32 @@ GetXMLSettingFlags(JSContext *cx, uintN *flagsp)
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSObject *
|
||||
GetXMLScopeChain(JSContext *cx)
|
||||
{
|
||||
if (JSStackFrame *fp = js_GetTopStackFrame(cx))
|
||||
return GetScopeChain(cx, fp);
|
||||
|
||||
/*
|
||||
* There is no code active on this context. In place of an actual scope
|
||||
* chain, use the context's global object, which is set in
|
||||
* s_InitFunctionAndObjectClasses, and which represents the default scope
|
||||
* chain for the embedding. See also js_FindClassObject.
|
||||
*
|
||||
* For embeddings that use the inner and outer object hooks, the inner
|
||||
* object represents the ultimate global object, with the outer object
|
||||
* acting as a stand-in.
|
||||
*/
|
||||
JSObject *obj = cx->globalObject;
|
||||
if (!obj) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
OBJ_TO_INNER_OBJECT(cx, obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
static JSXML *
|
||||
ParseXMLSource(JSContext *cx, JSString *src)
|
||||
{
|
||||
@ -1756,7 +1782,7 @@ ParseXMLSource(JSContext *cx, JSString *src)
|
||||
{
|
||||
Parser parser(cx);
|
||||
if (parser.init(chars, length, filename, lineno, cx->findVersion())) {
|
||||
JSObject *scopeChain = GetScopeChain(cx);
|
||||
JSObject *scopeChain = GetXMLScopeChain(cx);
|
||||
if (!scopeChain) {
|
||||
cx->free(chars);
|
||||
return NULL;
|
||||
@ -7231,7 +7257,7 @@ js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp)
|
||||
JSObject *ns, *obj, *tmp;
|
||||
jsval v;
|
||||
|
||||
JSObject *scopeChain = GetScopeChain(cx);
|
||||
JSObject *scopeChain = GetXMLScopeChain(cx);
|
||||
|
||||
obj = NULL;
|
||||
for (tmp = scopeChain; tmp; tmp = tmp->getParent()) {
|
||||
|
@ -808,9 +808,19 @@ class CallCompiler : public BaseCompiler
|
||||
if (!CallJSNative(cx, fun->u.n.native, ic.frameSize.getArgc(f), vp))
|
||||
THROWV(true);
|
||||
|
||||
/* Right now, take slow-path for IC misses or multiple stubs. */
|
||||
if (ic.fastGuardedNative || ic.hasJsFunCheck)
|
||||
/*
|
||||
* Right now, take slow-path for IC misses or multiple stubs. Also take
|
||||
* it if the call crosses, or might cross, globals: in that case
|
||||
* JSContext::getGlobalFromScopeChain requires we note the change of
|
||||
* global.
|
||||
*/
|
||||
if (ic.fastGuardedNative ||
|
||||
ic.hasJsFunCheck ||
|
||||
!f.regs.fp->script()->compileAndGo ||
|
||||
obj->getGlobal() != f.regs.fp->scopeChain().getGlobal())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Native MIC needs to warm up first. */
|
||||
if (!ic.hit) {
|
||||
|
@ -4356,6 +4356,40 @@ StringStats(JSContext *cx, uintN argc, jsval *vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
enum CompartmentKind { SAME_COMPARTMENT, NEW_COMPARTMENT };
|
||||
|
||||
static JSObject *
|
||||
NewGlobalObject(JSContext *cx, CompartmentKind compartment);
|
||||
|
||||
JSBool
|
||||
NewGlobal(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
if (argc != 1 || !JSVAL_IS_STRING(JS_ARGV(cx, vp)[0])) {
|
||||
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "newGlobal");
|
||||
return false;
|
||||
}
|
||||
|
||||
JSString *str = JSVAL_TO_STRING(JS_ARGV(cx, vp)[0]);
|
||||
|
||||
JSBool equalSame = JS_FALSE, equalNew = JS_FALSE;
|
||||
if (!JS_StringEqualsAscii(cx, str, "same-compartment", &equalSame) ||
|
||||
!JS_StringEqualsAscii(cx, str, "new-compartment", &equalNew)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!equalSame && !equalNew) {
|
||||
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "newGlobal");
|
||||
return false;
|
||||
}
|
||||
|
||||
JSObject *global = NewGlobalObject(cx, equalSame ? SAME_COMPARTMENT : NEW_COMPARTMENT);
|
||||
if (!global)
|
||||
return false;
|
||||
|
||||
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(global));
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSFunctionSpec shell_functions[] = {
|
||||
JS_FN("version", Version, 0,0),
|
||||
JS_FN("revertVersion", RevertVersion, 0,0),
|
||||
@ -4452,6 +4486,7 @@ static JSFunctionSpec shell_functions[] = {
|
||||
JS_FN("mjitstats", MJitStats, 0,0),
|
||||
#endif
|
||||
JS_FN("stringstats", StringStats, 0,0),
|
||||
JS_FN("newGlobal", NewGlobal, 1,0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
@ -4477,7 +4512,7 @@ static const char *const shell_help_messages[] = {
|
||||
"assertEq(actual, expected[, msg])\n"
|
||||
" Throw if the first two arguments are not the same (both +0 or both -0,\n"
|
||||
" both NaN, or non-zero and ===)",
|
||||
"assertJit() Throw if the calling function failed to JIT\n",
|
||||
"assertJit() Throw if the calling function failed to JIT",
|
||||
"gc() Run the garbage collector",
|
||||
#ifdef JS_GCMETER
|
||||
"gcstats() Print garbage collector statistics",
|
||||
@ -4522,7 +4557,7 @@ static const char *const shell_help_messages[] = {
|
||||
"dumpObject() Dump an internal representation of an object",
|
||||
"notes([fun]) Show source notes for functions",
|
||||
"tracing([true|false|filename]) Turn bytecode execution tracing on/off.\n"
|
||||
" With filename, send to file.\n",
|
||||
" With filename, send to file.",
|
||||
"stats([string ...]) Dump 'arena', 'atom', 'global' stats",
|
||||
#endif
|
||||
#ifdef TEST_CVTARGS
|
||||
@ -4573,24 +4608,37 @@ static const char *const shell_help_messages[] = {
|
||||
" Get/Set the limit in seconds for the execution time for the current context.\n"
|
||||
" A negative value (default) means that the execution time is unlimited.",
|
||||
"elapsed() Execution time elapsed for the current context.",
|
||||
"parent(obj) Returns the parent of obj.\n",
|
||||
"wrap(obj) Wrap an object into a noop wrapper.\n",
|
||||
"serialize(sd) Serialize sd using JS_WriteStructuredClone. Returns a TypedArray.\n",
|
||||
"deserialize(a) Deserialize data generated by serialize.\n",
|
||||
"parent(obj) Returns the parent of obj.",
|
||||
"wrap(obj) Wrap an object into a noop wrapper.",
|
||||
"serialize(sd) Serialize sd using JS_WriteStructuredClone. Returns a TypedArray.",
|
||||
"deserialize(a) Deserialize data generated by serialize.",
|
||||
#ifdef JS_METHODJIT
|
||||
"mjitstats() Return stats on mjit memory usage.\n",
|
||||
"mjitstats() Return stats on mjit memory usage.",
|
||||
#endif
|
||||
"stringstats() Return stats on string memory usage.\n"
|
||||
"stringstats() Return stats on string memory usage.",
|
||||
"newGlobal(kind) Return a new global object, in the current\n"
|
||||
" compartment if kind === 'same-compartment' or in a\n"
|
||||
" new compartment if kind === 'new-compartment'",
|
||||
|
||||
/* Keep these last: see the static assertion below. */
|
||||
#ifdef MOZ_PROFILING
|
||||
"startProfiling() Start a profiling session.\n"
|
||||
" Profiler must be running with programatic sampling\n"
|
||||
"stopProfiling() Stop a running profiling session"
|
||||
" Profiler must be running with programatic sampling",
|
||||
"stopProfiling() Stop a running profiling session\n"
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef MOZ_PROFILING
|
||||
#define PROFILING_FUNCTION_COUNT 2
|
||||
#else
|
||||
#define PROFILING_FUNCTION_COUNT 0
|
||||
#endif
|
||||
|
||||
/* Help messages must match shell functions. */
|
||||
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(shell_help_messages) + 1 ==
|
||||
JS_ARRAY_LENGTH(shell_functions));
|
||||
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(shell_help_messages) - PROFILING_FUNCTION_COUNT ==
|
||||
JS_ARRAY_LENGTH(shell_functions) - 1 /* JS_FS_END */);
|
||||
|
||||
#undef PROFILING_FUNCTION_COUNT
|
||||
|
||||
#ifdef DEBUG
|
||||
static void
|
||||
@ -4599,12 +4647,13 @@ CheckHelpMessages()
|
||||
const char *const *m;
|
||||
const char *lp;
|
||||
|
||||
/* Each message must begin with "function_name(" prefix. */
|
||||
/* Messages begin with "function_name(" prefix and don't end with \n. */
|
||||
for (m = shell_help_messages; m != JS_ARRAY_END(shell_help_messages); ++m) {
|
||||
lp = strchr(*m, '(');
|
||||
JS_ASSERT(lp);
|
||||
JS_ASSERT(memcmp(shell_functions[m - shell_help_messages].name,
|
||||
*m, lp - *m) == 0);
|
||||
JS_ASSERT((*m)[strlen(*m) - 1] != '\n');
|
||||
}
|
||||
}
|
||||
#else
|
||||
@ -5424,46 +5473,52 @@ DestroyContext(JSContext *cx, bool withGC)
|
||||
}
|
||||
|
||||
static JSObject *
|
||||
NewGlobalObject(JSContext *cx)
|
||||
NewGlobalObject(JSContext *cx, CompartmentKind compartment)
|
||||
{
|
||||
JSObject *glob = JS_NewCompartmentAndGlobalObject(cx, &global_class, NULL);
|
||||
JSObject *glob = (compartment == NEW_COMPARTMENT)
|
||||
? JS_NewCompartmentAndGlobalObject(cx, &global_class, NULL)
|
||||
: JS_NewGlobalObject(cx, &global_class);
|
||||
if (!glob)
|
||||
return NULL;
|
||||
|
||||
JSAutoEnterCompartment ac;
|
||||
if (!ac.enter(cx, glob))
|
||||
return NULL;
|
||||
{
|
||||
JSAutoEnterCompartment ac;
|
||||
if (!ac.enter(cx, glob))
|
||||
return NULL;
|
||||
|
||||
#ifdef LAZY_STANDARD_CLASSES
|
||||
JS_SetGlobalObject(cx, glob);
|
||||
#else
|
||||
if (!JS_InitStandardClasses(cx, glob))
|
||||
return NULL;
|
||||
#ifndef LAZY_STANDARD_CLASSES
|
||||
if (!JS_InitStandardClasses(cx, glob))
|
||||
return NULL;
|
||||
#endif
|
||||
|
||||
#ifdef JS_HAS_CTYPES
|
||||
if (!JS_InitCTypesClass(cx, glob))
|
||||
return NULL;
|
||||
if (!JS_InitCTypesClass(cx, glob))
|
||||
return NULL;
|
||||
#endif
|
||||
if (!JS::RegisterPerfMeasurement(cx, glob))
|
||||
return NULL;
|
||||
if (!JS_DefineFunctions(cx, glob, shell_functions) ||
|
||||
!JS_DefineProfilingFunctions(cx, glob)) {
|
||||
return NULL;
|
||||
if (!JS::RegisterPerfMeasurement(cx, glob))
|
||||
return NULL;
|
||||
if (!JS_DefineFunctions(cx, glob, shell_functions) ||
|
||||
!JS_DefineProfilingFunctions(cx, glob)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JSObject *it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0);
|
||||
if (!it)
|
||||
return NULL;
|
||||
if (!JS_DefineProperties(cx, it, its_props))
|
||||
return NULL;
|
||||
if (!JS_DefineFunctions(cx, it, its_methods))
|
||||
return NULL;
|
||||
|
||||
if (!JS_DefineProperty(cx, glob, "custom", JSVAL_VOID, its_getter,
|
||||
its_setter, 0))
|
||||
return NULL;
|
||||
if (!JS_DefineProperty(cx, glob, "customRdOnly", JSVAL_VOID, its_getter,
|
||||
its_setter, JSPROP_READONLY))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JSObject *it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0);
|
||||
if (!it)
|
||||
return NULL;
|
||||
if (!JS_DefineProperties(cx, it, its_props))
|
||||
return NULL;
|
||||
if (!JS_DefineFunctions(cx, it, its_methods))
|
||||
return NULL;
|
||||
|
||||
if (!JS_DefineProperty(cx, glob, "custom", JSVAL_VOID, its_getter,
|
||||
its_setter, 0))
|
||||
return NULL;
|
||||
if (!JS_DefineProperty(cx, glob, "customRdOnly", JSVAL_VOID, its_getter,
|
||||
its_setter, JSPROP_READONLY))
|
||||
if (compartment == NEW_COMPARTMENT && !JS_WrapObject(cx, &glob))
|
||||
return NULL;
|
||||
|
||||
return glob;
|
||||
@ -5474,7 +5529,7 @@ Shell(JSContext *cx, int argc, char **argv, char **envp)
|
||||
{
|
||||
JSAutoRequest ar(cx);
|
||||
|
||||
JSObject *glob = NewGlobalObject(cx);
|
||||
JSObject *glob = NewGlobalObject(cx, NEW_COMPARTMENT);
|
||||
if (!glob)
|
||||
return 1;
|
||||
|
||||
@ -5482,6 +5537,8 @@ Shell(JSContext *cx, int argc, char **argv, char **envp)
|
||||
if (!ac.enter(cx, glob))
|
||||
return 1;
|
||||
|
||||
JS_SetGlobalObject(cx, glob);
|
||||
|
||||
JSObject *envobj = JS_DefineObject(cx, glob, "environment", &env_class, NULL, 0);
|
||||
if (!envobj || !JS_SetPrivate(cx, envobj, envp))
|
||||
return 1;
|
||||
@ -5518,7 +5575,7 @@ Shell(JSContext *cx, int argc, char **argv, char **envp)
|
||||
class ShellWorkerHooks : public js::workers::WorkerHooks {
|
||||
public:
|
||||
JSObject *newGlobalObject(JSContext *cx) {
|
||||
return NewGlobalObject(cx);
|
||||
return NewGlobalObject(cx, NEW_COMPARTMENT);
|
||||
}
|
||||
};
|
||||
ShellWorkerHooks hooks;
|
||||
|
41
js/src/tests/e4x/extensions/cross-global-settings.js
Normal file
41
js/src/tests/e4x/extensions/cross-global-settings.js
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/
|
||||
*/
|
||||
|
||||
var gTestfile = 'cross-global-settings.js';
|
||||
//-----------------------------------------------------------------------------
|
||||
var BUGNUMBER = 631135;
|
||||
var summary =
|
||||
"Look at the callee's global.XML.* setting values, not the caller's";
|
||||
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
/**************
|
||||
* BEGIN TEST *
|
||||
**************/
|
||||
|
||||
var otherGlobal = newGlobal("same-compartment");
|
||||
otherGlobal.XML.prettyPrinting = true;
|
||||
otherGlobal.XML.prettyIndent = 2;
|
||||
otherGlobal.indent = " ";
|
||||
|
||||
XML.prettyPrinting = true;
|
||||
XML.prettyIndent = 16;
|
||||
|
||||
var indent = " " +
|
||||
" " +
|
||||
" " +
|
||||
" ";
|
||||
|
||||
var str = otherGlobal.XML("<a><b/><c/></a>").toXMLString();
|
||||
|
||||
assertEq(str.indexOf(indent), -1);
|
||||
assertEq(str.indexOf(otherGlobal.indent) > 0, true);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
||||
print("All tests passed!");
|
@ -22,3 +22,4 @@ script regress-450871-02.js
|
||||
script regress-462734-01.js
|
||||
script extensibility.js
|
||||
script regress-595207.js
|
||||
skip-if(!xulRuntime.shell) script cross-global-settings.js
|
||||
|
70
js/src/tests/ecma_5/extensions/cross-global-call.js
Normal file
70
js/src/tests/ecma_5/extensions/cross-global-call.js
Normal file
@ -0,0 +1,70 @@
|
||||
// Any copyright is dedicated to the Public Domain.
|
||||
// http://creativecommons.org/licenses/publicdomain/
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
var BUGNUMBER = 631135;
|
||||
var summary =
|
||||
"Objects created by/for natives are created for the caller's scope, not " +
|
||||
"for the native's scope";
|
||||
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
/**************
|
||||
* BEGIN TEST *
|
||||
**************/
|
||||
|
||||
var otherGlobal = newGlobal("same-compartment");
|
||||
|
||||
function makeArray()
|
||||
{
|
||||
return new otherGlobal.Array();
|
||||
}
|
||||
|
||||
var a;
|
||||
|
||||
a = makeArray();
|
||||
assertEq(a instanceof otherGlobal.Array, true);
|
||||
assertEq(a instanceof Array, false);
|
||||
a = makeArray();
|
||||
assertEq(a instanceof otherGlobal.Array, true);
|
||||
assertEq(a instanceof Array, false);
|
||||
a = makeArray();
|
||||
assertEq(a instanceof otherGlobal.Array, true);
|
||||
assertEq(a instanceof Array, false);
|
||||
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
a = new otherGlobal.Array();
|
||||
assertEq(a instanceof otherGlobal.Array, true);
|
||||
assertEq(a instanceof Array, false);
|
||||
}
|
||||
|
||||
|
||||
var res = otherGlobal.Array.prototype.slice.call([1, 2, 3], 0, 1);
|
||||
assertEq(res instanceof otherGlobal.Array, true);
|
||||
assertEq(res instanceof Array, false);
|
||||
|
||||
var obj = { p: 1 };
|
||||
var desc = otherGlobal.Object.getOwnPropertyDescriptor(obj, "p");
|
||||
assertEq(desc instanceof otherGlobal.Object, true);
|
||||
assertEq(desc instanceof Object, false);
|
||||
|
||||
try
|
||||
{
|
||||
otherGlobal.Function.prototype.call.call(null);
|
||||
var err = "no error";
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
err = e;
|
||||
}
|
||||
assertEq(err instanceof otherGlobal.TypeError, true,
|
||||
"bad error: " + err);
|
||||
assertEq(err instanceof TypeError, false, "bad error: " + err);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
||||
print("All tests passed!");
|
@ -9,6 +9,7 @@ script bug352085.js
|
||||
script bug472534.js
|
||||
script bug496985.js
|
||||
script bug566661.js
|
||||
skip-if(!xulRuntime.shell) script cross-global-call.js # needs newGlobal()
|
||||
script eval-native-callback-is-indirect.js
|
||||
script extension-methods-reject-null-undefined-this.js
|
||||
skip-if(!xulRuntime.shell) script function-definition-with.js # needs evaluate()
|
||||
|
@ -3630,11 +3630,17 @@ xpc_EvalInSandbox(JSContext *cx, JSObject *sandbox, const nsAString& source,
|
||||
{
|
||||
JSAutoRequest req(cx);
|
||||
|
||||
callingScope = JS_GetScopeChain(cx);
|
||||
if (!callingScope) {
|
||||
if (!JS_GetGlobalForCallingScript(cx, &callingScope)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
callingScope = JS_GetGlobalForObject(cx, callingScope);
|
||||
if (!callingScope) {
|
||||
callingScope = JS_GetGlobalObject(cx);
|
||||
if (!callingScope)
|
||||
return NS_ERROR_FAILURE;
|
||||
OBJ_TO_INNER_OBJECT(cx, callingScope);
|
||||
if (!callingScope)
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<ContextHolder> sandcx = new ContextHolder(cx, sandbox);
|
||||
|
@ -317,10 +317,9 @@ XPCThrower::ThrowExceptionObject(JSContext* cx, nsIException* e)
|
||||
}
|
||||
else if((xpc = nsXPConnect::GetXPConnect()))
|
||||
{
|
||||
JSObject* glob = JS_GetScopeChain(cx);
|
||||
JSObject* glob = JS_GetGlobalForScopeChain(cx);
|
||||
if(!glob)
|
||||
return JS_FALSE;
|
||||
glob = JS_GetGlobalForObject(cx, glob);
|
||||
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
|
||||
nsresult rv = xpc->WrapNative(cx, glob, e,
|
||||
|
@ -234,37 +234,28 @@ GetPrincipal(JSObject *obj)
|
||||
bool
|
||||
AccessCheck::documentDomainMakesSameOrigin(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JSObject *scope = nsnull;
|
||||
JSStackFrame *fp = nsnull;
|
||||
JS_FrameIterator(cx, &fp);
|
||||
if (fp) {
|
||||
while (fp->isDummyFrame()) {
|
||||
if (!JS_FrameIterator(cx, &fp))
|
||||
break;
|
||||
}
|
||||
|
||||
if (fp)
|
||||
scope = &fp->scopeChain();
|
||||
JSObject *scope;
|
||||
if (!JS_GetGlobalForCallingScript(cx, &scope))
|
||||
return false;
|
||||
if (!scope) {
|
||||
scope = JS_GetGlobalForScopeChain(cx);
|
||||
if (!scope)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!scope)
|
||||
scope = JS_GetScopeChain(cx);
|
||||
|
||||
nsIPrincipal *subject;
|
||||
nsIPrincipal *object;
|
||||
|
||||
{
|
||||
JSAutoEnterCompartment ac;
|
||||
|
||||
if (!ac.enter(cx, scope))
|
||||
return false;
|
||||
|
||||
subject = GetPrincipal(JS_GetGlobalForObject(cx, scope));
|
||||
subject = GetPrincipal(scope);
|
||||
}
|
||||
|
||||
if (!subject)
|
||||
return false;
|
||||
|
||||
nsIPrincipal *object;
|
||||
{
|
||||
JSAutoEnterCompartment ac;
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -112,11 +112,7 @@ Module::Call(nsIXPConnectWrappedNative* wrapper,
|
||||
jsval* vp,
|
||||
PRBool* _retval)
|
||||
{
|
||||
JSObject* scope = JS_GetScopeChain(cx);
|
||||
if (!scope)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
JSObject* global = JS_GetGlobalForObject(cx, scope);
|
||||
JSObject* global = JS_GetGlobalForScopeChain(cx);
|
||||
if (!global)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user