mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-11 20:35:50 +00:00
Bug 656462, part 4 - Simplify stack code, keep track of native calls, create new iterator over native/scripted callstack, make JS_SaveFrameChain fallible (r=waldo,mrbkap)
--HG-- extra : rebase_source : 756a640568464d11fe6bb00104a2fdb6f6d02d02
This commit is contained in:
parent
749dfad05a
commit
2afe24d969
@ -342,9 +342,10 @@ nsXBLProtoImplAnonymousMethod::Execute(nsIContent* aBoundElement)
|
||||
// anything else. We just report it. Note that we need to set aside the
|
||||
// frame chain here, since the constructor invocation is not related to
|
||||
// whatever is on the stack right now, really.
|
||||
JSStackFrame* frame = JS_SaveFrameChain(cx);
|
||||
::JS_ReportPendingException(cx);
|
||||
JS_RestoreFrameChain(cx, frame);
|
||||
JSBool saved = JS_SaveFrameChain(cx);
|
||||
JS_ReportPendingException(cx);
|
||||
if (saved)
|
||||
JS_RestoreFrameChain(cx);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -3520,9 +3520,10 @@ nsJSContext::ReportPendingException()
|
||||
// set aside the frame chain, since it has nothing to do with the
|
||||
// exception we're reporting.
|
||||
if (mIsInitialized && ::JS_IsExceptionPending(mContext)) {
|
||||
JSStackFrame* frame = JS_SaveFrameChain(mContext);
|
||||
PRBool saved = ::JS_SaveFrameChain(mContext);
|
||||
::JS_ReportPendingException(mContext);
|
||||
JS_RestoreFrameChain(mContext, frame);
|
||||
if (saved)
|
||||
::JS_RestoreFrameChain(mContext);
|
||||
}
|
||||
}
|
||||
|
||||
|
174
js/src/jit-test/tests/basic/testStackIter.js
Normal file
174
js/src/jit-test/tests/basic/testStackIter.js
Normal file
@ -0,0 +1,174 @@
|
||||
function stackToString(stack) {
|
||||
var str = "|";
|
||||
for (var i = 0; i < stack.length; ++i) {
|
||||
if (typeof stack[i] === "string")
|
||||
str += stack[i];
|
||||
else
|
||||
str += stack[i].name;
|
||||
str += "|";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
function assertStackIs(s1) {
|
||||
var s2 = dumpStack();
|
||||
var me = s2.shift();
|
||||
assertEq(me, assertStackIs);
|
||||
try {
|
||||
if (s1.length != s2.length)
|
||||
throw "Length " + s1.length + " not equal " + s2.length;
|
||||
for (var i = 0; i < s1.length; ++i) {
|
||||
var match;
|
||||
if (typeof s1[i] === "string" && (m = s1[i].match(/bound\((.*)\)/))) {
|
||||
if (s2[i].name != m[1] || s2[i].toSource().indexOf("[native code]") < 0)
|
||||
throw "Element " + i + " not bound function";
|
||||
} else if (s1[i] != s2[i]) {
|
||||
throw "Element " + i + " not equal";
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
print("Given = " + stackToString(s1));
|
||||
print("dumpStack = " + stackToString(s2));
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/***********/
|
||||
|
||||
assertStackIs(["global-code"]);
|
||||
(function f() { assertStackIs([f, "global-code"]) })();
|
||||
eval("assertStackIs(['eval-code', 'global-code'])");
|
||||
(function f() { eval("assertStackIs(['eval-code', f, 'global-code'])"); })();
|
||||
(function f() { eval("(function g() { assertStackIs([g, 'eval-code', f, 'global-code']); })()"); })();
|
||||
(function f() { assertStackIs([f, 'bound(f)', 'global-code']); }).bind()()
|
||||
this['eval']("assertStackIs(['eval-code', eval, 'global-code'])");
|
||||
eval.bind(null, "assertStackIs(['eval-code', eval, 'bound(eval)', 'global-code'])")();
|
||||
(function f() { assertStackIs([f, Function.prototype.call, 'global-code']) }).call(null);
|
||||
(function f() { assertStackIs([f, Function.prototype.apply, 'global-code']) }).apply(null, {});
|
||||
(function f() { (function g(x,y,z) { assertStackIs([g,f,'global-code']); })() })(1);
|
||||
|
||||
/***********/
|
||||
|
||||
var gen = (function g() { assertStackIs([g, gen.next, fun, 'global-code']); yield; })();
|
||||
var fun = function f() { gen.next() };
|
||||
fun();
|
||||
|
||||
var gen = (function g(x) { assertStackIs([g, gen.next, fun, 'global-code']); yield; })(1,2,3);
|
||||
var fun = function f() { gen.next() };
|
||||
fun();
|
||||
|
||||
var gen = (function g(x) { assertStackIs([g, gen.next, 'eval-code', fun, 'global-code']); yield; })(1,2,3);
|
||||
var fun = function f() { eval('gen.next()') };
|
||||
fun();
|
||||
|
||||
/***********/
|
||||
|
||||
const N = 100;
|
||||
|
||||
(function f(x) {
|
||||
if (x === 0) {
|
||||
var s = dumpStack();
|
||||
for (var i = 0; i < N; ++i)
|
||||
assertEq(s[i], f);
|
||||
return;
|
||||
}
|
||||
f(x-1);
|
||||
})(N);
|
||||
|
||||
/***********/
|
||||
|
||||
"abababab".replace(/b/g, function g() {
|
||||
assertStackIs([g, String.prototype.replace, "global-code"]);
|
||||
});
|
||||
|
||||
/***********/
|
||||
|
||||
var obj = {
|
||||
toString:function toString() {
|
||||
assertStackIs([toString, String.prototype.concat, "global-code"]);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
"a".concat(obj);
|
||||
|
||||
(function f() {
|
||||
var obj = {
|
||||
toString:(Array.prototype.sort.bind([1,2], function cb() {
|
||||
assertStackIs([cb, Array.prototype.sort, "bound(sort)",
|
||||
String.prototype.concat, f, "global-code"]);
|
||||
throw "OK";
|
||||
}))
|
||||
}
|
||||
|
||||
try {
|
||||
"a".concat(obj);
|
||||
} catch(e) {
|
||||
assertEq(e, "OK");
|
||||
}
|
||||
})();
|
||||
|
||||
/***********/
|
||||
|
||||
var obj = { valueOf:function valueOf() {
|
||||
assertStackIs([valueOf, Math.sin, Array.prototype.sort, "global-code"]);
|
||||
} };
|
||||
[obj, obj].sort(Math.sin);
|
||||
|
||||
var obj = { valueOf:(function valueOf() {
|
||||
assertStackIs([valueOf, "bound(valueOf)", Math.sin, Array.prototype.sort, "global-code"]);
|
||||
}).bind() };
|
||||
[obj, obj].sort(Math.sin);
|
||||
|
||||
var obj = { valueOf:(function valueOf() {
|
||||
assertStackIs([valueOf, "bound(valueOf)", "bound(valueOf)", "bound(valueOf)",
|
||||
Math.sin, Array.prototype.sort, "global-code"]);
|
||||
}).bind().bind().bind() };
|
||||
[obj, obj].sort(Math.sin);
|
||||
|
||||
/***********/
|
||||
|
||||
for (var i = 0; i < 10; ++i) {
|
||||
/* No loss for scripts. */
|
||||
(function f() {
|
||||
assertStackIs([f, Function.prototype.apply, 'global-code']);
|
||||
}).apply(null, {});
|
||||
(function f() {
|
||||
assertStackIs([f, Function.prototype.call, 'global-code']);
|
||||
}).call(null);
|
||||
|
||||
/* Loss for natives. */
|
||||
(function f() {
|
||||
var stack = dumpStack();
|
||||
assertEq(stack[0], f);
|
||||
if (stack.length === 4) {
|
||||
assertEq(stack[1].name, 'f');
|
||||
assertEq(stack[2], Function.prototype.call);
|
||||
} else {
|
||||
assertEq(stack.length, 3);
|
||||
assertEq(stack[1], Function.prototype.call);
|
||||
}
|
||||
}).bind().call(null);
|
||||
(function f() {
|
||||
var stack = dumpStack();
|
||||
assertEq(stack[0], f);
|
||||
if (stack.length === 4) {
|
||||
assertEq(stack[1].name, 'f');
|
||||
assertEq(stack[2], Function.prototype.apply);
|
||||
} else {
|
||||
assertEq(stack.length, 3);
|
||||
assertEq(stack[1], Function.prototype.apply);
|
||||
}
|
||||
}).bind().apply(null, {});
|
||||
(function f() {
|
||||
var stack = dumpStack();
|
||||
assertEq(stack[0], f);
|
||||
if (stack.length === 4) {
|
||||
assertEq(stack[1].name, 'f');
|
||||
assertEq(stack[2], Function.prototype.apply);
|
||||
} else {
|
||||
assertEq(stack.length, 3);
|
||||
assertEq(stack[1], Function.prototype.apply);
|
||||
}
|
||||
}).bind().apply(null, [1,2,3,4,5]);
|
||||
}
|
52
js/src/jit-test/tests/basic/testStackIterDebug.js
Normal file
52
js/src/jit-test/tests/basic/testStackIterDebug.js
Normal file
@ -0,0 +1,52 @@
|
||||
// |jit-test| mjitalways;debug
|
||||
|
||||
function stackToString(stack) {
|
||||
var str = "|";
|
||||
for (var i = 0; i < stack.length; ++i) {
|
||||
if (typeof stack[i] === "string")
|
||||
str += stack[i];
|
||||
else
|
||||
str += stack[i].name;
|
||||
str += "|";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
function assertStackIs(s1) {
|
||||
var s2 = dumpStack();
|
||||
var me = s2.shift();
|
||||
assertEq(me, assertStackIs);
|
||||
try {
|
||||
if (s1.length != s2.length)
|
||||
throw "Length " + s1.length + " not equal " + s2.length;
|
||||
for (var i = 0; i < s1.length; ++i) {
|
||||
var match;
|
||||
if (typeof s1[i] === "string" && (m = s1[i].match(/bound\((.*)\)/))) {
|
||||
if (s2[i].name != m[1] || s2[i].toSource().indexOf("[native code]") < 0)
|
||||
throw "Element " + i + " not bound function";
|
||||
} else if (s1[i] != s2[i]) {
|
||||
throw "Element " + i + " not equal";
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
print("Given = " + stackToString(s1));
|
||||
print("dumpStack = " + stackToString(s2));
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************/
|
||||
|
||||
(function f() { evalInFrame(0, "assertStackIs(['eval-code', evalInFrame, f, 'global-code'])"); })();
|
||||
(function f() { (function g() { evalInFrame(1, "assertStackIs(['eval-code', f, 'global-code'])"); })() })();
|
||||
(function f() { (function g() { evalInFrame(1, "assertStackIs(['eval-code', f, 'bound(f)', 'global-code'])"); })() }).bind()();
|
||||
|
||||
(function f() { evalInFrame(0, "assertStackIs(['eval-code', evalInFrame, f, 'global-code'])", true); })();
|
||||
(function f() { (function g() { evalInFrame(1, "assertStackIs(['eval-code', f, 'global-code'])", true); })() })();
|
||||
(function f() { (function g() { evalInFrame(1, "assertStackIs(['eval-code', f, 'bound(f)', 'global-code'])", true); })() }).bind()();
|
||||
(function f() { (function g() { evalInFrame(1, "assertStackIs(['eval-code', f, 'bound(f)', 'global-code'])", true); }).bind()() }).bind()();
|
||||
(function f() { (function g() { evalInFrame(1, "assertStackIs(['eval-code', f, 'bound(f)', 'global-code'])", true); }).bind().bind()() }).bind()();
|
||||
|
||||
(function f() { var o = { toString:function() { evalInFrame(1, "assertStackIs(['eval-code', f, 'global-code'])"); }}; [o,o].sort() })();
|
||||
(function f() { var o = { toString:function() { evalInFrame(1, "assertStackIs(['eval-code', f, 'global-code'])", true); }}; [o,o].sort() })();
|
@ -5201,26 +5201,20 @@ JS_IsRunning(JSContext *cx)
|
||||
return fp != NULL;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSStackFrame *)
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_SaveFrameChain(JSContext *cx)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
StackFrame *fp = js_GetTopStackFrame(cx);
|
||||
if (!fp)
|
||||
return NULL;
|
||||
cx->stack.saveActiveSegment();
|
||||
return Jsvalify(fp);
|
||||
LeaveTrace(cx);
|
||||
return cx->stack.saveFrameChain();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp)
|
||||
JS_RestoreFrameChain(JSContext *cx)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
JS_ASSERT_NOT_ON_TRACE(cx);
|
||||
JS_ASSERT(!cx->hasfp());
|
||||
if (!fp)
|
||||
return;
|
||||
cx->stack.restoreSegment();
|
||||
cx->stack.restoreFrameChain();
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
|
@ -2943,15 +2943,13 @@ JS_IsRunning(JSContext *cx);
|
||||
* must be balanced and all nested calls to JS_SaveFrameChain must have had
|
||||
* matching JS_RestoreFrameChain calls.
|
||||
*
|
||||
* JS_SaveFrameChain deals with cx not having any code running on it. A null
|
||||
* return does not signify an error, and JS_RestoreFrameChain handles a null
|
||||
* frame pointer argument safely.
|
||||
* JS_SaveFrameChain deals with cx not having any code running on it.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSStackFrame *)
|
||||
extern JS_PUBLIC_API(JSBool)
|
||||
JS_SaveFrameChain(JSContext *cx);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp);
|
||||
JS_RestoreFrameChain(JSContext *cx);
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
|
@ -1540,7 +1540,8 @@ JSContext::wrapPendingException()
|
||||
JSGenerator *
|
||||
JSContext::generatorFor(StackFrame *fp) const
|
||||
{
|
||||
JS_ASSERT(stack.contains(fp) && fp->isGeneratorFrame());
|
||||
JS_ASSERT(stack.containsSlow(fp));
|
||||
JS_ASSERT(fp->isGeneratorFrame());
|
||||
JS_ASSERT(!fp->isFloatingGenerator());
|
||||
JS_ASSERT(!genStack.empty());
|
||||
|
||||
|
@ -1186,10 +1186,11 @@ struct JSContext
|
||||
* default version.
|
||||
*/
|
||||
void maybeMigrateVersionOverride() {
|
||||
if (JS_LIKELY(!isVersionOverridden() && stack.empty()))
|
||||
return;
|
||||
defaultVersion = versionOverride;
|
||||
clearVersionOverride();
|
||||
JS_ASSERT(stack.empty());
|
||||
if (JS_UNLIKELY(isVersionOverridden())) {
|
||||
defaultVersion = versionOverride;
|
||||
clearVersionOverride();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -170,9 +170,9 @@ CompartmentHasLiveScripts(JSCompartment *comp)
|
||||
if (JS_GetContextThread(icx) != currentThreadId)
|
||||
continue;
|
||||
#endif
|
||||
for (AllFramesIter i(icx); !i.done(); ++i) {
|
||||
JSScript *script = i.fp()->maybeScript();
|
||||
if (script && script->compartment == comp)
|
||||
for (FrameRegsIter i(icx); !i.done(); ++i) {
|
||||
JSScript *script = i.fp()->script();
|
||||
if (script->compartment == comp)
|
||||
return JS_TRUE;
|
||||
}
|
||||
}
|
||||
@ -1491,7 +1491,7 @@ JS_PUBLIC_API(JSObject *)
|
||||
JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fpArg)
|
||||
{
|
||||
StackFrame *fp = Valueify(fpArg);
|
||||
JS_ASSERT(cx->stack.contains(fp));
|
||||
JS_ASSERT(cx->stack.containsSlow(fp));
|
||||
|
||||
js::AutoCompartment ac(cx, &fp->scopeChain());
|
||||
if (!ac.enter())
|
||||
@ -1506,7 +1506,7 @@ JS_PUBLIC_API(JSObject *)
|
||||
JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fpArg)
|
||||
{
|
||||
StackFrame *fp = Valueify(fpArg);
|
||||
JS_ASSERT(cx->stack.contains(fp));
|
||||
JS_ASSERT(cx->stack.containsSlow(fp));
|
||||
|
||||
if (!fp->isFunctionFrame())
|
||||
return NULL;
|
||||
|
@ -356,8 +356,7 @@ InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message,
|
||||
elem->filename = NULL;
|
||||
if (fp->isScriptFrame()) {
|
||||
elem->filename = fp->script()->filename;
|
||||
if (fp->isScriptFrame())
|
||||
elem->ulineno = js_FramePCToLineNumber(cx, fp, iter.pc());
|
||||
elem->ulineno = js_FramePCToLineNumber(cx, fp, iter.pc());
|
||||
}
|
||||
++elem;
|
||||
}
|
||||
|
@ -247,7 +247,7 @@ js_GetArgsObject(JSContext *cx, StackFrame *fp)
|
||||
*/
|
||||
JS_ASSERT_IF(fp->fun()->isHeavyweight(), fp->hasCallObj());
|
||||
|
||||
while (fp->isDirectEvalOrDebuggerFrame())
|
||||
while (fp->isEvalInFunction())
|
||||
fp = fp->prev();
|
||||
|
||||
/* Create an arguments object for fp only if it lacks one. */
|
||||
@ -1588,12 +1588,9 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
|
||||
JSFunction *fun = obj->getFunctionPrivate();
|
||||
|
||||
/* Find fun's top-most activation record. */
|
||||
StackFrame *fp;
|
||||
for (fp = js_GetTopStackFrame(cx);
|
||||
fp && (fp->maybeFun() != fun || fp->isDirectEvalOrDebuggerFrame());
|
||||
fp = fp->prev()) {
|
||||
continue;
|
||||
}
|
||||
StackFrame *fp = js_GetTopStackFrame(cx);
|
||||
while (fp && (fp->maybeFun() != fun || fp->isEvalInFunction()))
|
||||
fp = fp->prev();
|
||||
|
||||
switch (slot) {
|
||||
case FUN_ARGUMENTS:
|
||||
@ -3016,9 +3013,6 @@ js_ReportIsNotFunction(JSContext *cx, const Value *vp, uintN flags)
|
||||
ptrdiff_t spindex = 0;
|
||||
|
||||
FrameRegsIter i(cx);
|
||||
while (!i.done() && !i.pc())
|
||||
++i;
|
||||
|
||||
if (!i.done()) {
|
||||
uintN depth = js_ReconstructStackDepth(cx, i.fp()->script(), i.pc());
|
||||
Value *simsp = i.fp()->base() + depth;
|
||||
|
@ -217,6 +217,11 @@ struct JSFunction : public JSObject_Slots2
|
||||
return isInterpreted() ? NULL : u.n.native;
|
||||
}
|
||||
|
||||
js::Native native() const {
|
||||
JS_ASSERT(isNative());
|
||||
return u.n.native;
|
||||
}
|
||||
|
||||
JSScript *script() const {
|
||||
JS_ASSERT(isInterpreted());
|
||||
return u.i.script;
|
||||
@ -300,6 +305,19 @@ IsFunctionObject(const js::Value &v, JSFunction **fun)
|
||||
return b;
|
||||
}
|
||||
|
||||
static JS_ALWAYS_INLINE bool
|
||||
IsNativeFunction(const js::Value &v)
|
||||
{
|
||||
JSFunction *fun;
|
||||
return IsFunctionObject(v, &fun) && fun->isNative();
|
||||
}
|
||||
|
||||
static JS_ALWAYS_INLINE bool
|
||||
IsNativeFunction(const js::Value &v, JSFunction **fun)
|
||||
{
|
||||
return IsFunctionObject(v, fun) && (*fun)->isNative();
|
||||
}
|
||||
|
||||
extern JS_ALWAYS_INLINE bool
|
||||
SameTraceType(const Value &lhs, const Value &rhs)
|
||||
{
|
||||
|
@ -671,7 +671,7 @@ Invoke(JSContext *cx, const CallArgs &argsRef, MaybeConstruct construct)
|
||||
|
||||
/* Get pointer to new frame/slots, prepare arguments. */
|
||||
InvokeFrameGuard ifg;
|
||||
if (!cx->stack.pushInvokeFrame(cx, args, construct, callee, fun, script, &ifg))
|
||||
if (!cx->stack.pushInvokeFrame(cx, args, construct, &ifg))
|
||||
return false;
|
||||
|
||||
/* Now that the new frame is rooted, maybe create a call object. */
|
||||
@ -731,7 +731,7 @@ InvokeSessionGuard::start(JSContext *cx, const Value &calleev, const Value &this
|
||||
break;
|
||||
|
||||
/* Push the stack frame once for the session. */
|
||||
if (!stack.pushInvokeFrame(cx, args_, NO_CONSTRUCT, callee, fun, script_, &ifg_))
|
||||
if (!stack.pushInvokeFrame(cx, args_, NO_CONSTRUCT, &ifg_))
|
||||
return false;
|
||||
|
||||
StackFrame *fp = ifg_.fp();
|
||||
@ -858,7 +858,7 @@ InitSharpSlots(JSContext *cx, StackFrame *fp)
|
||||
Value *sharps = &fp->slots()[script->nfixed - SHARP_NSLOTS];
|
||||
if (!fp->isGlobalFrame() && prev->script()->hasSharps) {
|
||||
JS_ASSERT(prev->numFixed() >= SHARP_NSLOTS);
|
||||
int base = (prev->isFunctionFrame() && !prev->isDirectEvalOrDebuggerFrame())
|
||||
int base = prev->isNonEvalFunctionFrame()
|
||||
? prev->fun()->script()->bindings.sharpSlotBase(cx)
|
||||
: prev->numFixed() - SHARP_NSLOTS;
|
||||
if (base < 0)
|
||||
@ -1408,7 +1408,7 @@ js::GetUpvar(JSContext *cx, uintN closureLevel, UpvarCookie cookie)
|
||||
const uintN targetLevel = closureLevel - cookie.level();
|
||||
JS_ASSERT(targetLevel < UpvarCookie::UPVAR_LEVEL_LIMIT);
|
||||
|
||||
StackFrame *fp = cx->stack.findFrameAtLevel(targetLevel);
|
||||
StackFrame *fp = FindUpvarFrame(cx, targetLevel);
|
||||
uintN slot = cookie.slot();
|
||||
const Value *vp;
|
||||
|
||||
@ -1428,6 +1428,19 @@ js::GetUpvar(JSContext *cx, uintN closureLevel, UpvarCookie cookie)
|
||||
return vp[slot];
|
||||
}
|
||||
|
||||
extern StackFrame *
|
||||
js::FindUpvarFrame(JSContext *cx, uintN targetLevel)
|
||||
{
|
||||
StackFrame *fp = cx->fp();
|
||||
while (true) {
|
||||
JS_ASSERT(fp && fp->isScriptFrame());
|
||||
if (fp->script()->staticLevel == targetLevel)
|
||||
break;
|
||||
fp = fp->prev();
|
||||
}
|
||||
return fp;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
JS_STATIC_INTERPRET JS_REQUIRES_STACK void
|
||||
@ -2810,7 +2823,7 @@ BEGIN_CASE(JSOP_STOP)
|
||||
#ifdef JS_METHODJIT
|
||||
jit_return:
|
||||
#endif
|
||||
cx->stack.popInlineFrame();
|
||||
cx->stack.popInlineFrame(regs);
|
||||
|
||||
/* Sync interpreter locals. */
|
||||
script = regs.fp()->script();
|
||||
|
@ -152,9 +152,25 @@ ComputeThis(JSContext *cx, StackFrame *fp);
|
||||
* and the range [args.getvp() + 2, args.getvp() + 2 + args.getArgc()) should
|
||||
* be initialized actual arguments.
|
||||
*/
|
||||
extern JS_REQUIRES_STACK bool
|
||||
extern bool
|
||||
Invoke(JSContext *cx, const CallArgs &args, MaybeConstruct construct = NO_CONSTRUCT);
|
||||
|
||||
/*
|
||||
* For calls to natives, the InvokeArgsGuard object provides a record of the
|
||||
* call for the debugger's callstack. For this to work, the InvokeArgsGuard
|
||||
* record needs to know when the call is actually active (because the
|
||||
* InvokeArgsGuard can be pushed long before and popped long after the actual
|
||||
* call, during which time many stack-observing things can happen).
|
||||
*/
|
||||
inline bool
|
||||
Invoke(JSContext *cx, InvokeArgsGuard &args, MaybeConstruct construct = NO_CONSTRUCT)
|
||||
{
|
||||
args.setActive();
|
||||
bool ok = Invoke(cx, ImplicitCast<CallArgs>(args), construct);
|
||||
args.setInactive();
|
||||
return ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Natives like sort/forEach/replace call Invoke repeatedly with the same
|
||||
* callee, this, and number of arguments. To optimize this, such natives can
|
||||
@ -274,8 +290,12 @@ ValueToId(JSContext *cx, const Value &v, jsid *idp);
|
||||
* closure level.
|
||||
* @return The value of the upvar.
|
||||
*/
|
||||
extern const js::Value &
|
||||
GetUpvar(JSContext *cx, uintN level, js::UpvarCookie cookie);
|
||||
extern const Value &
|
||||
GetUpvar(JSContext *cx, uintN level, UpvarCookie cookie);
|
||||
|
||||
/* Search the call stack for the nearest frame with static level targetLevel. */
|
||||
extern StackFrame *
|
||||
FindUpvarFrame(JSContext *cx, uintN targetLevel);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
|
@ -89,7 +89,7 @@ class InvokeSessionGuard
|
||||
~InvokeSessionGuard() {}
|
||||
|
||||
bool start(JSContext *cx, const Value &callee, const Value &thisv, uintN argc);
|
||||
bool invoke(JSContext *cx) const;
|
||||
bool invoke(JSContext *cx);
|
||||
|
||||
bool started() const {
|
||||
return args_.pushed();
|
||||
@ -113,7 +113,7 @@ class InvokeSessionGuard
|
||||
};
|
||||
|
||||
inline bool
|
||||
InvokeSessionGuard::invoke(JSContext *cx) const
|
||||
InvokeSessionGuard::invoke(JSContext *cx)
|
||||
{
|
||||
/* N.B. Must be kept in sync with Invoke */
|
||||
|
||||
@ -139,6 +139,7 @@ InvokeSessionGuard::invoke(JSContext *cx) const
|
||||
JSBool ok;
|
||||
{
|
||||
AutoPreserveEnumerators preserve(cx);
|
||||
args_.setActive(); /* From js::Invoke(InvokeArgsGuard) overload. */
|
||||
Probes::enterJSFun(cx, fp->fun(), script_);
|
||||
#ifdef JS_METHODJIT
|
||||
ok = mjit::EnterMethodJIT(cx, fp, code, stackLimit_);
|
||||
@ -148,6 +149,7 @@ InvokeSessionGuard::invoke(JSContext *cx) const
|
||||
ok = Interpret(cx, cx->fp());
|
||||
#endif
|
||||
Probes::exitJSFun(cx, fp->fun(), script_);
|
||||
args_.setInactive();
|
||||
}
|
||||
|
||||
/* Don't clobber callee with rval; rval gets read from fp->rval. */
|
||||
|
@ -1181,7 +1181,7 @@ js_NewGenerator(JSContext *cx)
|
||||
gen->floating = genfp;
|
||||
|
||||
/* Copy from the stack to the generator's floating frame. */
|
||||
gen->regs.rebaseFromTo(stackRegs, genfp);
|
||||
gen->regs.rebaseFromTo(stackRegs, *genfp);
|
||||
genfp->stealFrameAndSlots(genvp, stackfp, stackvp, stackRegs.sp);
|
||||
genfp->initFloatingGenerator();
|
||||
|
||||
|
@ -214,7 +214,6 @@ js_NewGenerator(JSContext *cx);
|
||||
inline js::StackFrame *
|
||||
js_FloatingFrameIfGenerator(JSContext *cx, js::StackFrame *fp)
|
||||
{
|
||||
JS_ASSERT(cx->stack.contains(fp));
|
||||
if (JS_UNLIKELY(fp->isGeneratorFrame()))
|
||||
return cx->generatorFor(fp)->floatingFrame();
|
||||
return fp;
|
||||
|
@ -1741,8 +1741,8 @@ enum MatchControlFlags {
|
||||
|
||||
/* Factor out looping and matching logic. */
|
||||
static bool
|
||||
DoMatch(JSContext *cx, RegExpStatics *res, Value *vp, JSString *str, const RegExpPair &rep,
|
||||
DoMatchCallback callback, void *data, MatchControlFlags flags)
|
||||
DoMatch(JSContext *cx, RegExpStatics *res, JSString *str, const RegExpPair &rep,
|
||||
DoMatchCallback callback, void *data, MatchControlFlags flags, Value *rval)
|
||||
{
|
||||
RegExp &re = rep.re();
|
||||
if (re.global()) {
|
||||
@ -1751,9 +1751,9 @@ DoMatch(JSContext *cx, RegExpStatics *res, Value *vp, JSString *str, const RegEx
|
||||
if (rep.reobj())
|
||||
rep.reobj()->zeroRegExpLastIndex();
|
||||
for (size_t count = 0, i = 0, length = str->length(); i <= length; ++count) {
|
||||
if (!re.execute(cx, res, str, &i, testGlobal, vp))
|
||||
if (!re.execute(cx, res, str, &i, testGlobal, rval))
|
||||
return false;
|
||||
if (!Matched(testGlobal, *vp))
|
||||
if (!Matched(testGlobal, *rval))
|
||||
break;
|
||||
if (!callback(cx, res, count, data))
|
||||
return false;
|
||||
@ -1765,9 +1765,9 @@ DoMatch(JSContext *cx, RegExpStatics *res, Value *vp, JSString *str, const RegEx
|
||||
bool testSingle = !!(flags & TEST_SINGLE_BIT),
|
||||
callbackOnSingle = !!(flags & CALLBACK_ON_SINGLE_BIT);
|
||||
size_t i = 0;
|
||||
if (!re.execute(cx, res, str, &i, testSingle, vp))
|
||||
if (!re.execute(cx, res, str, &i, testSingle, rval))
|
||||
return false;
|
||||
if (callbackOnSingle && Matched(testSingle, *vp) && !callback(cx, res, 0, data))
|
||||
if (callbackOnSingle && Matched(testSingle, *rval) && !callback(cx, res, 0, data))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -1842,12 +1842,14 @@ 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))
|
||||
Value rval;
|
||||
if (!DoMatch(cx, res, str, *rep, MatchCallback, arg, MATCH_ARGS, &rval))
|
||||
return false;
|
||||
|
||||
/* When not global, DoMatch will leave |RegExp.exec()| in *vp. */
|
||||
if (rep->re().global())
|
||||
vp->setObjectOrNull(array.object());
|
||||
else
|
||||
*vp = rval;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2323,7 +2325,8 @@ str_replace_regexp(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata)
|
||||
rdata.calledBack = false;
|
||||
|
||||
RegExpStatics *res = cx->regExpStatics();
|
||||
if (!DoMatch(cx, res, vp, rdata.str, *rep, ReplaceRegExpCallback, &rdata, REPLACE_ARGS))
|
||||
Value tmp;
|
||||
if (!DoMatch(cx, res, rdata.str, *rep, ReplaceRegExpCallback, &rdata, REPLACE_ARGS, &tmp))
|
||||
return false;
|
||||
|
||||
if (!rdata.calledBack) {
|
||||
@ -2849,23 +2852,18 @@ str_concat(JSContext *cx, uintN argc, Value *vp)
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
/* Set vp (aka rval) early to handle the argc == 0 case. */
|
||||
vp->setString(str);
|
||||
|
||||
Value *argv;
|
||||
uintN i;
|
||||
for (i = 0, argv = vp + 2; i < argc; i++) {
|
||||
Value *argv = JS_ARGV(cx, vp);
|
||||
for (uintN i = 0; i < argc; i++) {
|
||||
JSString *str2 = js_ValueToString(cx, argv[i]);
|
||||
if (!str2)
|
||||
return false;
|
||||
argv[i].setString(str2);
|
||||
|
||||
str = js_ConcatStrings(cx, str, str2);
|
||||
if (!str)
|
||||
return false;
|
||||
vp->setString(str);
|
||||
}
|
||||
|
||||
JS_SET_RVAL(cx, vp, StringValue(str));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -315,6 +315,7 @@ nanojit::LInsPrinter::accNames[] = {
|
||||
"typemap", // (1 << 25) == ACCSET_TYPEMAP
|
||||
"fcslots", // (1 << 26) == ACCSET_FCSLOTS
|
||||
"argsdata", // (1 << 27) == ACCSET_ARGS_DATA
|
||||
"seg", // (1 << 28) == ACCSET_SEG
|
||||
|
||||
"?!" // this entry should never be used, have it just in case
|
||||
};
|
||||
@ -382,6 +383,13 @@ ValueToTypeChar(const Value &v)
|
||||
if (v.isMagic()) return 'M';
|
||||
return '?';
|
||||
}
|
||||
|
||||
static inline uintN
|
||||
FramePCOffset(JSContext *cx, js::StackFrame* fp)
|
||||
{
|
||||
jsbytecode *pc = fp->pcQuadratic(cx);
|
||||
return uintN(pc - fp->script()->code);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline uintN
|
||||
@ -3309,7 +3317,7 @@ GetUpvarOnTrace(JSContext* cx, uint32 upvarLevel, int32 slot, uint32 callDepth,
|
||||
* then we simply get the value from the interpreter state.
|
||||
*/
|
||||
JS_ASSERT(upvarLevel < UpvarCookie::UPVAR_LEVEL_LIMIT);
|
||||
StackFrame* fp = cx->stack.findFrameAtLevel(upvarLevel);
|
||||
StackFrame* fp = FindUpvarFrame(cx, upvarLevel);
|
||||
Value v = T::interp_get(fp, slot);
|
||||
JSValueType type = getCoercedType(v);
|
||||
ValueToNative(v, type, result);
|
||||
@ -13448,7 +13456,7 @@ TraceRecorder::upvar(JSScript* script, JSUpvarArray* uva, uintN index, Value& v)
|
||||
*/
|
||||
uint32 level = script->staticLevel - cookie.level();
|
||||
uint32 cookieSlot = cookie.slot();
|
||||
StackFrame* fp = cx->stack.findFrameAtLevel(level);
|
||||
StackFrame* fp = FindUpvarFrame(cx, level);
|
||||
const CallInfo* ci;
|
||||
int32 slot;
|
||||
if (!fp->isFunctionFrame() || fp->isEvalFrame()) {
|
||||
@ -15086,25 +15094,13 @@ TraceRecorder::record_JSOP_BINDNAME()
|
||||
if (!fp->isFunctionFrame()) {
|
||||
obj = &fp->scopeChain();
|
||||
|
||||
#ifdef DEBUG
|
||||
StackFrame *fp2 = fp;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* In global code, fp->scopeChain can only contain blocks whose values
|
||||
* are still on the stack. We never use BINDNAME to refer to these.
|
||||
*/
|
||||
while (obj->isBlock()) {
|
||||
// The block's values are still on the stack.
|
||||
#ifdef DEBUG
|
||||
// NB: fp2 can't be a generator frame, because !fp->hasFunction.
|
||||
while (obj->getPrivate() != fp2) {
|
||||
JS_ASSERT(fp2->isDirectEvalOrDebuggerFrame());
|
||||
fp2 = fp2->prev();
|
||||
if (!fp2)
|
||||
JS_NOT_REACHED("bad stack frame");
|
||||
}
|
||||
#endif
|
||||
JS_ASSERT(obj->getPrivate() == fp);
|
||||
obj = obj->getParent();
|
||||
// Blocks always have parents.
|
||||
JS_ASSERT(obj);
|
||||
|
@ -1736,11 +1736,9 @@ ParseXMLSource(JSContext *cx, JSString *src)
|
||||
|
||||
LeaveTrace(cx);
|
||||
xml = NULL;
|
||||
FrameRegsIter i(cx);
|
||||
for (; !i.done() && !i.pc(); ++i)
|
||||
JS_ASSERT(!i.fp()->isScriptFrame());
|
||||
filename = NULL;
|
||||
lineno = 1;
|
||||
FrameRegsIter i(cx);
|
||||
if (!i.done()) {
|
||||
op = (JSOp) *i.pc();
|
||||
if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) {
|
||||
|
@ -178,7 +178,7 @@ InlineReturn(VMFrame &f)
|
||||
{
|
||||
JS_ASSERT(f.fp() != f.entryfp);
|
||||
JS_ASSERT(!js_IsActiveWithOrBlock(f.cx, &f.fp()->scopeChain(), 0));
|
||||
f.cx->stack.popInlineFrame();
|
||||
f.cx->stack.popInlineFrame(f.regs);
|
||||
}
|
||||
|
||||
void JS_FASTCALL
|
||||
@ -195,16 +195,6 @@ stubs::SlowNew(VMFrame &f, uint32 argc)
|
||||
THROW();
|
||||
}
|
||||
|
||||
/*
|
||||
* This function must only be called after the early prologue, since it depends
|
||||
* on fp->exec.fun.
|
||||
*/
|
||||
static inline void
|
||||
RemovePartialFrame(JSContext *cx, StackFrame *fp)
|
||||
{
|
||||
cx->stack.popInlineFrame();
|
||||
}
|
||||
|
||||
/*
|
||||
* HitStackQuota is called after the early prologue pushing the new frame would
|
||||
* overflow f.stackLimit.
|
||||
@ -218,8 +208,7 @@ stubs::HitStackQuota(VMFrame &f)
|
||||
if (f.cx->stack.space().tryBumpLimit(NULL, f.regs.sp, nvals, &f.stackLimit))
|
||||
return;
|
||||
|
||||
/* Remove the current partially-constructed frame before throwing. */
|
||||
RemovePartialFrame(f.cx, f.fp());
|
||||
f.cx->stack.popFrameAfterOverflow();
|
||||
js_ReportOverRecursed(f.cx);
|
||||
THROW();
|
||||
}
|
||||
@ -285,10 +274,7 @@ stubs::CompileFunction(VMFrame &f, uint32 nactual)
|
||||
JSFunction *fun = callee.getFunctionPrivate();
|
||||
JSScript *script = fun->script();
|
||||
|
||||
/*
|
||||
* FixupArity/RemovePartialFrame expect to be called after the early
|
||||
* prologue.
|
||||
*/
|
||||
/* FixupArity expect to be called after the early prologue. */
|
||||
fp->initJitFrameEarlyPrologue(fun, nactual);
|
||||
|
||||
if (nactual != fp->numFormalArgs()) {
|
||||
@ -301,7 +287,7 @@ stubs::CompileFunction(VMFrame &f, uint32 nactual)
|
||||
fp->initJitFrameLatePrologue();
|
||||
|
||||
/* These would have been initialized by the prologue. */
|
||||
f.regs.prepareToRun(fp, script);
|
||||
f.regs.prepareToRun(*fp, script);
|
||||
|
||||
if (fun->isHeavyweight() && !js::CreateFunCallObject(cx, fp))
|
||||
THROWV(NULL);
|
||||
|
@ -2830,6 +2830,52 @@ DumpObject(JSContext *cx, uintN argc, jsval *vp)
|
||||
|
||||
#endif /* DEBUG */
|
||||
|
||||
/*
|
||||
* This shell function is temporary (used by testStackIter.js) and should be
|
||||
* removed once JSD2 lands wholly subsumes the functionality here.
|
||||
*/
|
||||
JSBool
|
||||
DumpStack(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
JSObject *arr = JS_NewArrayObject(cx, 0, NULL);
|
||||
if (!arr)
|
||||
return false;
|
||||
|
||||
JSString *evalStr = JS_NewStringCopyZ(cx, "eval-code");
|
||||
if (!evalStr)
|
||||
return false;
|
||||
|
||||
JSString *globalStr = JS_NewStringCopyZ(cx, "global-code");
|
||||
if (!globalStr)
|
||||
return false;
|
||||
|
||||
StackIter iter(cx);
|
||||
JS_ASSERT(iter.nativeArgs().callee().getFunctionPrivate()->native() == DumpStack);
|
||||
++iter;
|
||||
|
||||
jsint index = 0;
|
||||
for (; !iter.done(); ++index, ++iter) {
|
||||
Value v;
|
||||
if (iter.isScript()) {
|
||||
if (iter.fp()->isNonEvalFunctionFrame()) {
|
||||
if (!iter.fp()->getValidCalleeObject(cx, &v))
|
||||
return false;
|
||||
} else if (iter.fp()->isEvalFrame()) {
|
||||
v = StringValue(evalStr);
|
||||
} else {
|
||||
v = StringValue(globalStr);
|
||||
}
|
||||
} else {
|
||||
v = iter.nativeArgs().calleev();
|
||||
}
|
||||
if (!JS_SetElement(cx, arr, index, Jsvalify(&v)))
|
||||
return false;
|
||||
}
|
||||
|
||||
JS_SET_RVAL(cx, vp, ObjectValue(*arr));
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef TEST_CVTARGS
|
||||
#include <ctype.h>
|
||||
|
||||
@ -3736,9 +3782,9 @@ EvalInFrame(JSContext *cx, uintN argc, jsval *vp)
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
JSStackFrame *oldfp = NULL;
|
||||
JSBool saved = JS_FALSE;;
|
||||
if (saveCurrent)
|
||||
oldfp = JS_SaveFrameChain(cx);
|
||||
saved = JS_SaveFrameChain(cx);
|
||||
|
||||
size_t length;
|
||||
const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
|
||||
@ -3751,8 +3797,8 @@ EvalInFrame(JSContext *cx, uintN argc, jsval *vp)
|
||||
fi.pc()),
|
||||
vp);
|
||||
|
||||
if (saveCurrent)
|
||||
JS_RestoreFrameChain(cx, oldfp);
|
||||
if (saved)
|
||||
JS_RestoreFrameChain(cx);
|
||||
|
||||
return ok;
|
||||
}
|
||||
@ -4847,6 +4893,7 @@ static JSFunctionSpec shell_functions[] = {
|
||||
JS_FN("tracing", Tracing, 0,0),
|
||||
JS_FN("stats", DumpStats, 1,0),
|
||||
#endif
|
||||
JS_FN("dumpStack", DumpStack, 1,0),
|
||||
#ifdef TEST_CVTARGS
|
||||
JS_FN("cvtargs", ConvertArgs, 0,0),
|
||||
#endif
|
||||
@ -4980,6 +5027,7 @@ static const char *const shell_help_messages[] = {
|
||||
" With filename, send to file.",
|
||||
"stats([string ...]) Dump 'arena', 'atom', 'global' stats",
|
||||
#endif
|
||||
"dumpStack() Dump the stack as an array of callees (youngest first)",
|
||||
#ifdef TEST_CVTARGS
|
||||
"cvtargs(arg1..., arg12) Test argument formatter",
|
||||
#endif
|
||||
|
@ -376,7 +376,7 @@ void ValidateWriter::checkAccSet(LOpcode op, LIns *base, int32_t disp, AccSet ac
|
||||
// ins = ldp.regs base[<disp within FrameRegs>]
|
||||
ok = op == LIR_ldp &&
|
||||
dispWithin(FrameRegs) &&
|
||||
match(base, LIR_ldp, ACCSET_CX, offsetof(JSContext, stack) + ContextStack::offsetOfRegs());
|
||||
match(base, LIR_ldp, ACCSET_SEG, StackSegment::offsetOfRegs());
|
||||
break;
|
||||
|
||||
case ACCSET_STACKFRAME:
|
||||
@ -552,6 +552,12 @@ void ValidateWriter::checkAccSet(LOpcode op, LIns *base, int32_t disp, AccSet ac
|
||||
isConstPrivatePtr(base->oprnd1(), ArgumentsObject::DATA_SLOT)));
|
||||
break;
|
||||
|
||||
case ACCSET_SEG:
|
||||
// Match the ACCSET_SEG load that comes out of ldpContextRegs
|
||||
ok = dispWithin(StackSegment) &&
|
||||
match(base, LIR_ldp, ACCSET_CX, offsetof(JSContext, stack) + ContextStack::offsetOfSeg());
|
||||
break;
|
||||
|
||||
default:
|
||||
// This assertion will fail if any single-region AccSets aren't covered
|
||||
// by the switch -- only multi-region AccSets should be handled here.
|
||||
|
@ -126,6 +126,7 @@ enum LC_TMBits {
|
||||
* - ACCSET_TYPEMAP: All typemaps form a single region.
|
||||
* - ACCSET_FCSLOTS: All fcslots arrays form a single region.
|
||||
* - ACCSET_ARGS_DATA: All Arguments data arrays form a single region.
|
||||
* - ACCSET_SEG: All StackSegment structs.
|
||||
*/
|
||||
static const nanojit::AccSet ACCSET_STATE = (1 << 0);
|
||||
static const nanojit::AccSet ACCSET_STACK = (1 << 1);
|
||||
@ -158,8 +159,9 @@ static const nanojit::AccSet ACCSET_STRING_MCHARS = (1 << 24);
|
||||
static const nanojit::AccSet ACCSET_TYPEMAP = (1 << 25);
|
||||
static const nanojit::AccSet ACCSET_FCSLOTS = (1 << 26);
|
||||
static const nanojit::AccSet ACCSET_ARGS_DATA = (1 << 27);
|
||||
static const nanojit::AccSet ACCSET_SEG = (1 << 28);
|
||||
|
||||
static const uint8_t TM_NUM_USED_ACCS = 28; // number of access regions used by TraceMonkey
|
||||
static const uint8_t TM_NUM_USED_ACCS = 29; // number of access regions used by TraceMonkey
|
||||
|
||||
/*
|
||||
* An Address describes everything about a loaded/stored memory location. One
|
||||
@ -427,8 +429,11 @@ class Writer
|
||||
name(w.ldpContextFieldHelper(cx_ins, offsetof(JSContext, fieldname), LOAD_CONST), \
|
||||
#fieldname)
|
||||
nj::LIns *ldpContextRegs(nj::LIns *cx) const {
|
||||
int32 offset = offsetof(JSContext, stack) + ContextStack::offsetOfRegs();
|
||||
return name(ldpContextFieldHelper(cx, offset, nj::LOAD_NORMAL),"regs");
|
||||
int32 segOff = offsetof(JSContext, stack) + ContextStack::offsetOfSeg();
|
||||
nj::LIns *seg = ldpContextFieldHelper(cx, segOff, nj::LOAD_CONST);
|
||||
int32 regsOff = StackSegment::offsetOfRegs();
|
||||
return name(lir->insLoad(nj::LIR_ldp, seg, regsOff, ACCSET_SEG, nj::LOAD_CONST), "cx->regs()");
|
||||
|
||||
}
|
||||
|
||||
nj::LIns *stContextField(nj::LIns *value, nj::LIns *cx, int32 offset) const {
|
||||
|
@ -50,215 +50,6 @@
|
||||
|
||||
namespace js {
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* See VM stack layout comment in Stack.h. */
|
||||
class StackSegment
|
||||
{
|
||||
/* The context to which this segment belongs. */
|
||||
ContextStack *stack_;
|
||||
|
||||
/* Link for JSContext segment stack mentioned in big comment above. */
|
||||
StackSegment *previousInContext_;
|
||||
|
||||
/* Link for StackSpace segment stack mentioned in StackSpace comment. */
|
||||
StackSegment *previousInMemory_;
|
||||
|
||||
/* The first frame executed in this segment. null iff cx is null */
|
||||
StackFrame *initialFrame_;
|
||||
|
||||
/* If this segment is suspended, |cx->regs| when it was suspended. */
|
||||
FrameRegs *suspendedRegs_;
|
||||
|
||||
/* Whether this segment was suspended by JS_SaveFrameChain. */
|
||||
bool saved_;
|
||||
|
||||
/*
|
||||
* To make isActive a single null-ness check, this non-null constant is
|
||||
* assigned to suspendedRegs when empty.
|
||||
*/
|
||||
#define NON_NULL_SUSPENDED_REGS ((FrameRegs *)0x1)
|
||||
|
||||
public:
|
||||
StackSegment()
|
||||
: stack_(NULL), previousInContext_(NULL), previousInMemory_(NULL),
|
||||
initialFrame_(NULL), suspendedRegs_(NON_NULL_SUSPENDED_REGS),
|
||||
saved_(false)
|
||||
{
|
||||
JS_ASSERT(empty());
|
||||
}
|
||||
|
||||
/* Safe casts guaranteed by the contiguous-stack layout. */
|
||||
|
||||
Value *valueRangeBegin() const {
|
||||
return (Value *)(this + 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* The set of fields provided by a segment depend on its state. In addition
|
||||
* to the "active" and "suspended" states described in Stack.h, segments
|
||||
* have a third state: empty. An empty segment contains no frames and is
|
||||
* pushed for the purpose of preparing the args to Invoke. Invoke args
|
||||
* requires special handling because anything can happen between pushing
|
||||
* Invoke args and calling Invoke. Since an empty segment contains no
|
||||
* frames, it cannot become the "current segment" of a ContextStack (for
|
||||
* various arcane and hopefully temporary reasons). Thus, an empty segment
|
||||
* is pushed onto the StackSpace but only pushed onto a ContextStack when it
|
||||
* gets its first frame pushed from js::Invoke.
|
||||
*
|
||||
* Finally, (to support JS_SaveFrameChain/JS_RestoreFrameChain) a suspended
|
||||
* segment may or may not be "saved". Normally, when the active segment is
|
||||
* popped, the previous segment (which is necessarily suspended) becomes
|
||||
* active. If the previous segment was saved, however, then it stays
|
||||
* suspended until it is made active by a call to JS_RestoreFrameChain. This
|
||||
* is why a context may have a current segment, but not an active segment.
|
||||
* Hopefully, this feature will be removed.
|
||||
*/
|
||||
|
||||
bool empty() const {
|
||||
JS_ASSERT(!!stack_ == !!initialFrame_);
|
||||
JS_ASSERT_IF(!stack_, suspendedRegs_ == NON_NULL_SUSPENDED_REGS && !saved_);
|
||||
return !stack_;
|
||||
}
|
||||
|
||||
bool isActive() const {
|
||||
JS_ASSERT_IF(!suspendedRegs_, stack_ && !saved_);
|
||||
JS_ASSERT_IF(!stack_, suspendedRegs_ == NON_NULL_SUSPENDED_REGS);
|
||||
return !suspendedRegs_;
|
||||
}
|
||||
|
||||
bool isSuspended() const {
|
||||
JS_ASSERT_IF(!stack_ || !suspendedRegs_, !saved_);
|
||||
JS_ASSERT_IF(!stack_, suspendedRegs_ == NON_NULL_SUSPENDED_REGS);
|
||||
return stack_ && suspendedRegs_;
|
||||
}
|
||||
|
||||
/* Substate of suspended, queryable in any state. */
|
||||
|
||||
bool isSaved() const {
|
||||
JS_ASSERT_IF(saved_, isSuspended());
|
||||
return saved_;
|
||||
}
|
||||
|
||||
/* Transitioning between empty <--> isActive */
|
||||
|
||||
void joinContext(ContextStack &stack, StackFrame &frame) {
|
||||
JS_ASSERT(empty());
|
||||
stack_ = &stack;
|
||||
initialFrame_ = &frame;
|
||||
suspendedRegs_ = NULL;
|
||||
JS_ASSERT(isActive());
|
||||
}
|
||||
|
||||
void leaveContext() {
|
||||
JS_ASSERT(isActive());
|
||||
stack_ = NULL;
|
||||
initialFrame_ = NULL;
|
||||
suspendedRegs_ = NON_NULL_SUSPENDED_REGS;
|
||||
JS_ASSERT(empty());
|
||||
}
|
||||
|
||||
ContextStack &stack() const {
|
||||
JS_ASSERT(!empty());
|
||||
return *stack_;
|
||||
}
|
||||
|
||||
ContextStack *maybeStack() const {
|
||||
return stack_;
|
||||
}
|
||||
|
||||
#undef NON_NULL_SUSPENDED_REGS
|
||||
|
||||
/* Transitioning between isActive <--> isSuspended */
|
||||
|
||||
void suspend(FrameRegs ®s) {
|
||||
JS_ASSERT(isActive());
|
||||
JS_ASSERT(contains(regs.fp()));
|
||||
suspendedRegs_ = ®s;
|
||||
JS_ASSERT(isSuspended());
|
||||
}
|
||||
|
||||
void resume() {
|
||||
JS_ASSERT(isSuspended());
|
||||
suspendedRegs_ = NULL;
|
||||
JS_ASSERT(isActive());
|
||||
}
|
||||
|
||||
/* When isSuspended, transitioning isSaved <--> !isSaved */
|
||||
|
||||
void save(FrameRegs ®s) {
|
||||
JS_ASSERT(!isSuspended());
|
||||
suspend(regs);
|
||||
saved_ = true;
|
||||
JS_ASSERT(isSaved());
|
||||
}
|
||||
|
||||
void restore() {
|
||||
JS_ASSERT(isSaved());
|
||||
saved_ = false;
|
||||
resume();
|
||||
JS_ASSERT(!isSuspended());
|
||||
}
|
||||
|
||||
/* Data available when !empty */
|
||||
|
||||
StackFrame *initialFrame() const {
|
||||
JS_ASSERT(!empty());
|
||||
return initialFrame_;
|
||||
}
|
||||
|
||||
FrameRegs ¤tRegs() const {
|
||||
JS_ASSERT(!empty());
|
||||
return isActive() ? stack_->regs() : suspendedRegs();
|
||||
}
|
||||
|
||||
StackFrame *currentFrame() const {
|
||||
return currentRegs().fp();
|
||||
}
|
||||
|
||||
StackFrame *currentFrameOrNull() const {
|
||||
return empty() ? NULL : currentFrame();
|
||||
}
|
||||
|
||||
/* Data available when isSuspended. */
|
||||
|
||||
FrameRegs &suspendedRegs() const {
|
||||
JS_ASSERT(isSuspended());
|
||||
return *suspendedRegs_;
|
||||
}
|
||||
|
||||
StackFrame *suspendedFrame() const {
|
||||
return suspendedRegs_->fp();
|
||||
}
|
||||
|
||||
/* JSContext / js::StackSpace bookkeeping. */
|
||||
|
||||
void setPreviousInContext(StackSegment *seg) {
|
||||
previousInContext_ = seg;
|
||||
}
|
||||
|
||||
StackSegment *previousInContext() const {
|
||||
return previousInContext_;
|
||||
}
|
||||
|
||||
void setPreviousInMemory(StackSegment *seg) {
|
||||
previousInMemory_ = seg;
|
||||
}
|
||||
|
||||
StackSegment *previousInMemory() const {
|
||||
return previousInMemory_;
|
||||
}
|
||||
|
||||
bool contains(const StackFrame *fp) const;
|
||||
|
||||
StackFrame *computeNextFrame(StackFrame *fp) const;
|
||||
};
|
||||
|
||||
static const size_t VALUES_PER_STACK_SEGMENT = sizeof(StackSegment) / sizeof(Value);
|
||||
JS_STATIC_ASSERT(sizeof(StackSegment) % sizeof(Value) == 0);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
inline void
|
||||
StackFrame::initPrev(JSContext *cx)
|
||||
{
|
||||
@ -387,101 +178,6 @@ StackFrame::initJitFrameLatePrologue()
|
||||
SetValueRangeToUndefined(slots(), script()->nfixed);
|
||||
}
|
||||
|
||||
inline void
|
||||
StackFrame::initExecuteFrame(JSScript *script, StackFrame *prev, const Value &thisv,
|
||||
JSObject &scopeChain, ExecuteType type)
|
||||
{
|
||||
/*
|
||||
* See encoding of ExecuteType. When GLOBAL isn't set, we are executing a
|
||||
* script in the context of another frame and the frame type is determined
|
||||
* by the context.
|
||||
*/
|
||||
flags_ = type | HAS_SCOPECHAIN;
|
||||
if (!(flags_ & GLOBAL))
|
||||
flags_ |= (prev->flags_ & (FUNCTION | GLOBAL));
|
||||
|
||||
Value *dstvp = (Value *)this - 2;
|
||||
dstvp[1] = thisv;
|
||||
|
||||
if (isFunctionFrame()) {
|
||||
dstvp[0] = prev->calleev();
|
||||
exec = prev->exec;
|
||||
args.script = script;
|
||||
} else {
|
||||
JS_ASSERT(isGlobalFrame());
|
||||
dstvp[0] = NullValue();
|
||||
exec.script = script;
|
||||
#ifdef DEBUG
|
||||
args.script = (JSScript *)0xbad;
|
||||
#endif
|
||||
}
|
||||
|
||||
scopeChain_ = &scopeChain;
|
||||
prev_ = prev;
|
||||
#ifdef DEBUG
|
||||
ncode_ = (void *)0xbad;
|
||||
Debug_SetValueRangeToCrashOnTouch(&rval_, 1);
|
||||
prevpc_ = (jsbytecode *)0xbad;
|
||||
hookData_ = (void *)0xbad;
|
||||
annotation_ = (void *)0xbad;
|
||||
#endif
|
||||
|
||||
if (flags_ & HAS_ANNOTATION)
|
||||
annotation_ = prev->annotation_;
|
||||
}
|
||||
|
||||
inline void
|
||||
StackFrame::initDummyFrame(JSContext *cx, JSObject &chain)
|
||||
{
|
||||
PodZero(this);
|
||||
flags_ = DUMMY | HAS_PREVPC | HAS_SCOPECHAIN;
|
||||
initPrev(cx);
|
||||
chain.isGlobal();
|
||||
setScopeChainNoCallObj(chain);
|
||||
}
|
||||
|
||||
inline void
|
||||
StackFrame::stealFrameAndSlots(Value *vp, StackFrame *otherfp,
|
||||
Value *othervp, Value *othersp)
|
||||
{
|
||||
JS_ASSERT(vp == (Value *)this - ((Value *)otherfp - othervp));
|
||||
JS_ASSERT(othervp == otherfp->actualArgs() - 2);
|
||||
JS_ASSERT(othersp >= otherfp->slots());
|
||||
JS_ASSERT(othersp <= otherfp->base() + otherfp->numSlots());
|
||||
|
||||
PodCopy(vp, othervp, othersp - othervp);
|
||||
JS_ASSERT(vp == this->actualArgs() - 2);
|
||||
|
||||
/* Catch bad-touching of non-canonical args (e.g., generator_trace). */
|
||||
if (otherfp->hasOverflowArgs())
|
||||
Debug_SetValueRangeToCrashOnTouch(othervp, othervp + 2 + otherfp->numFormalArgs());
|
||||
|
||||
/*
|
||||
* Repoint Call, Arguments, Block and With objects to the new live frame.
|
||||
* Call and Arguments are done directly because we have pointers to them.
|
||||
* Block and With objects are done indirectly through 'liveFrame'. See
|
||||
* js_LiveFrameToFloating comment in jsiter.h.
|
||||
*/
|
||||
if (hasCallObj()) {
|
||||
JSObject &obj = callObj();
|
||||
obj.setPrivate(this);
|
||||
otherfp->flags_ &= ~HAS_CALL_OBJ;
|
||||
if (js_IsNamedLambda(fun())) {
|
||||
JSObject *env = obj.getParent();
|
||||
JS_ASSERT(env->getClass() == &js_DeclEnvClass);
|
||||
env->setPrivate(this);
|
||||
}
|
||||
}
|
||||
if (hasArgsObj()) {
|
||||
ArgumentsObject &argsobj = argsObj();
|
||||
if (argsobj.isNormalArguments())
|
||||
argsobj.setPrivate(this);
|
||||
else
|
||||
JS_ASSERT(!argsobj.getPrivate());
|
||||
otherfp->flags_ &= ~HAS_ARGS_OBJ;
|
||||
}
|
||||
}
|
||||
|
||||
inline Value &
|
||||
StackFrame::canonicalActualArg(uintN i) const
|
||||
{
|
||||
@ -672,42 +368,6 @@ StackFrame::markActivationObjectsAsPut()
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
JS_ALWAYS_INLINE void
|
||||
StackSpace::pushOverride(Value *top, StackOverride *prev)
|
||||
{
|
||||
*prev = override_;
|
||||
|
||||
override_.top = top;
|
||||
#ifdef DEBUG
|
||||
override_.seg = seg_;
|
||||
override_.frame = seg_->currentFrameOrNull();
|
||||
#endif
|
||||
|
||||
JS_ASSERT(prev->top < override_.top);
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE void
|
||||
StackSpace::popOverride(const StackOverride &prev)
|
||||
{
|
||||
JS_ASSERT(prev.top < override_.top);
|
||||
|
||||
JS_ASSERT_IF(seg_->empty(), override_.frame == NULL);
|
||||
JS_ASSERT_IF(!seg_->empty(), override_.frame == seg_->currentFrame());
|
||||
JS_ASSERT(override_.seg == seg_);
|
||||
|
||||
override_ = prev;
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE Value *
|
||||
StackSpace::activeFirstUnused() const
|
||||
{
|
||||
JS_ASSERT(seg_->isActive());
|
||||
|
||||
Value *max = Max(seg_->stack().regs().sp, override_.top);
|
||||
JS_ASSERT(max == firstUnused());
|
||||
return max;
|
||||
}
|
||||
|
||||
#ifdef JS_TRACER
|
||||
JS_ALWAYS_INLINE bool
|
||||
StackSpace::ensureEnoughSpaceToEnterTrace()
|
||||
@ -764,13 +424,6 @@ StackSpace::getStackLimit(JSContext *cx)
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
JS_ALWAYS_INLINE bool
|
||||
ContextStack::isCurrentAndActive() const
|
||||
{
|
||||
assertSegmentsInSync();
|
||||
return seg_ && seg_->isActive() && seg_ == space().currentSegment();
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE bool
|
||||
OOMCheck::operator()(JSContext *cx, StackSpace &space, Value *from, uintN nvals)
|
||||
{
|
||||
@ -829,7 +482,6 @@ ContextStack::getCallFrame(JSContext *cx, const CallArgs &args,
|
||||
Value *dst = firstUnused;
|
||||
Value *src = args.base();
|
||||
PodCopy(dst, src, ncopy);
|
||||
Debug_SetValueRangeToCrashOnTouch(src, ncopy);
|
||||
return reinterpret_cast<StackFrame *>(firstUnused + ncopy);
|
||||
}
|
||||
|
||||
@ -839,8 +491,8 @@ ContextStack::pushInlineFrame(JSContext *cx, FrameRegs ®s, const CallArgs &ar
|
||||
JSObject &callee, JSFunction *fun, JSScript *script,
|
||||
MaybeConstruct construct, Check check)
|
||||
{
|
||||
JS_ASSERT(isCurrentAndActive());
|
||||
JS_ASSERT(®s == &cx->regs());
|
||||
JS_ASSERT(onTop());
|
||||
JS_ASSERT(®s == &seg_->regs());
|
||||
JS_ASSERT(regs.sp == args.end());
|
||||
/* Cannot assert callee == args.callee() since this is called from LeaveTree. */
|
||||
JS_ASSERT(callee.getFunctionPrivate() == fun);
|
||||
@ -853,7 +505,7 @@ ContextStack::pushInlineFrame(JSContext *cx, FrameRegs ®s, const CallArgs &ar
|
||||
|
||||
/* Initialize frame, locals, regs. */
|
||||
fp->initCallFrame(cx, callee, fun, script, args.argc(), flags);
|
||||
regs.prepareToRun(fp, script);
|
||||
regs.prepareToRun(*fp, script);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -862,7 +514,7 @@ ContextStack::getFixupFrame(JSContext *cx, FrameRegs ®s, const CallArgs &args
|
||||
JSFunction *fun, JSScript *script, void *ncode,
|
||||
MaybeConstruct construct, LimitCheck check)
|
||||
{
|
||||
JS_ASSERT(isCurrentAndActive());
|
||||
JS_ASSERT(onTop());
|
||||
JS_ASSERT(®s == &cx->regs());
|
||||
JS_ASSERT(regs.sp == args.end());
|
||||
JS_ASSERT(args.callee().getFunctionPrivate() == fun);
|
||||
@ -880,143 +532,30 @@ ContextStack::getFixupFrame(JSContext *cx, FrameRegs ®s, const CallArgs &args
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE void
|
||||
ContextStack::popInlineFrame()
|
||||
ContextStack::popInlineFrame(FrameRegs ®s)
|
||||
{
|
||||
JS_ASSERT(isCurrentAndActive());
|
||||
JS_ASSERT(onTop());
|
||||
JS_ASSERT(®s == &seg_->regs());
|
||||
|
||||
StackFrame *fp = regs_->fp();
|
||||
StackFrame *fp = regs.fp();
|
||||
fp->putActivationObjects();
|
||||
|
||||
Value *newsp = fp->actualArgs() - 1;
|
||||
JS_ASSERT(newsp >= fp->prev()->base());
|
||||
|
||||
newsp[-1] = fp->returnValue();
|
||||
regs_->popFrame(newsp);
|
||||
regs.popFrame(newsp);
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE bool
|
||||
ContextStack::pushInvokeArgs(JSContext *cx, uintN argc, InvokeArgsGuard *argsGuard)
|
||||
inline void
|
||||
ContextStack::popFrameAfterOverflow()
|
||||
{
|
||||
if (!isCurrentAndActive())
|
||||
return pushInvokeArgsSlow(cx, argc, argsGuard);
|
||||
|
||||
Value *start = space().activeFirstUnused();
|
||||
uintN vplen = 2 + argc;
|
||||
if (!space().ensureSpace(cx, start, vplen))
|
||||
return false;
|
||||
|
||||
Value *vp = start;
|
||||
ImplicitCast<CallArgs>(*argsGuard) = CallArgsFromVp(argc, vp);
|
||||
|
||||
/*
|
||||
* Use stack override to root vp until the frame is pushed. Don't need to
|
||||
* MakeRangeGCSafe: the VM stack is conservatively marked.
|
||||
*/
|
||||
space().pushOverride(vp + vplen, &argsGuard->prevOverride_);
|
||||
|
||||
argsGuard->stack_ = this;
|
||||
return true;
|
||||
/* Restore the regs to what they were on entry to JSOP_CALL. */
|
||||
FrameRegs ®s = seg_->regs();
|
||||
StackFrame *fp = regs.fp();
|
||||
regs.popFrame(fp->actualArgsEnd());
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE void
|
||||
ContextStack::popInvokeArgs(const InvokeArgsGuard &argsGuard)
|
||||
{
|
||||
if (argsGuard.seg_) {
|
||||
popInvokeArgsSlow(argsGuard);
|
||||
return;
|
||||
}
|
||||
|
||||
JS_ASSERT(isCurrentAndActive());
|
||||
space().popOverride(argsGuard.prevOverride_);
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE
|
||||
InvokeArgsGuard::~InvokeArgsGuard()
|
||||
{
|
||||
if (JS_UNLIKELY(!pushed()))
|
||||
return;
|
||||
stack_->popInvokeArgs(*this);
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE bool
|
||||
ContextStack::pushInvokeFrame(JSContext *cx, const CallArgs &args, MaybeConstruct construct,
|
||||
JSObject &callee, JSFunction *fun, JSScript *script,
|
||||
InvokeFrameGuard *ifg)
|
||||
{
|
||||
JS_ASSERT(callee == args.callee());
|
||||
JS_ASSERT(callee.getFunctionPrivate() == fun);
|
||||
JS_ASSERT(fun->script() == script);
|
||||
JS_ASSERT(args.end() == space().firstUnused());
|
||||
|
||||
/* Get pointers into the stack; check for overflow. */
|
||||
StackFrame::Flags flags = ToFrameFlags(construct);
|
||||
StackFrame *fp = getCallFrame(cx, args, fun, script, &flags, OOMCheck());
|
||||
if (!fp)
|
||||
return false;
|
||||
|
||||
/* Initialize regs, frame, locals. */
|
||||
ifg->regs_.prepareToRun(fp, script);
|
||||
fp->initCallFrame(cx, callee, fun, script, args.argc(), flags);
|
||||
|
||||
if (JS_UNLIKELY(space().seg_->empty())) {
|
||||
pushInvokeFrameSlow(ifg);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Offically push onto the stack. */
|
||||
ifg->prevRegs_ = regs_;
|
||||
regs_ = &ifg->regs_;
|
||||
|
||||
/* Mark as pushed. */
|
||||
ifg->stack_ = this;
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE void
|
||||
ContextStack::popInvokeFrame(const InvokeFrameGuard &frameGuard)
|
||||
{
|
||||
JS_ASSERT(isCurrentAndActive());
|
||||
JS_ASSERT(&frameGuard.regs_ == regs_);
|
||||
|
||||
if (JS_UNLIKELY(seg_->initialFrame() == regs_->fp())) {
|
||||
popInvokeFrameSlow(frameGuard);
|
||||
return;
|
||||
}
|
||||
|
||||
regs_->fp()->putActivationObjects();
|
||||
regs_ = frameGuard.prevRegs_;
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE void
|
||||
InvokeFrameGuard::pop()
|
||||
{
|
||||
JS_ASSERT(pushed());
|
||||
stack_->popInvokeFrame(*this);
|
||||
stack_ = NULL;
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE
|
||||
InvokeFrameGuard::~InvokeFrameGuard()
|
||||
{
|
||||
if (pushed())
|
||||
pop();
|
||||
}
|
||||
|
||||
inline StackFrame *
|
||||
ContextStack::findFrameAtLevel(uintN targetLevel) const
|
||||
{
|
||||
StackFrame *fp = regs_->fp();
|
||||
while (true) {
|
||||
JS_ASSERT(fp && fp->isScriptFrame());
|
||||
if (fp->script()->staticLevel == targetLevel)
|
||||
break;
|
||||
fp = fp->prev();
|
||||
}
|
||||
return fp;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct STATIC_SKIP_INFERENCE CopyNonHoleArgsTo
|
||||
@ -1101,5 +640,4 @@ ArgumentsObject::getElements(uint32 start, uint32 count, Value *vp)
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* Stack_inl_h__ */
|
||||
|
1046
js/src/vm/Stack.cpp
1046
js/src/vm/Stack.cpp
File diff suppressed because it is too large
Load Diff
@ -60,6 +60,10 @@ class ExecuteFrameGuard;
|
||||
class DummyFrameGuard;
|
||||
class GeneratorFrameGuard;
|
||||
|
||||
class CallIter;
|
||||
class FrameRegsIter;
|
||||
class AllFramesIter;
|
||||
|
||||
class ArgumentsObject;
|
||||
|
||||
namespace mjit { struct JITScript; }
|
||||
@ -74,20 +78,18 @@ namespace mjit { struct JITScript; }
|
||||
*
|
||||
* The per-thread stack is subdivided into contiguous segments of memory which
|
||||
* have a memory layout invariant that allows fixed offsets to be used for stack
|
||||
* access (by the JIT) as well as fast call/return. This memory layout is
|
||||
* encapsulated by a set of types that describe different regions of memory:
|
||||
* StackSegment, StackFrame, FrameRegs and CallArgs. To avoid calling into C++,
|
||||
* the JIT compiler generates code that simulates C++ stack operations.
|
||||
* access (by jit code) as well as fast call/return. This memory layout is
|
||||
* encapsulated by a set of types that describe different regions of memory.
|
||||
* This encapsulation has holes: to avoid calling into C++ from generated code,
|
||||
* JIT compilers generate code that simulates analogous operations in C++.
|
||||
*
|
||||
* The memory layout of a segment looks like:
|
||||
* A sample memory layout of a segment looks like:
|
||||
*
|
||||
* current regs
|
||||
* .------------------------------------------------------.
|
||||
* | current frame |
|
||||
* | .-------------------------------------. V
|
||||
* | | initial frame | FrameRegs
|
||||
* | | .------------. | |
|
||||
* | | | V V V
|
||||
* regs
|
||||
* .---------------------------------------------.
|
||||
* | V
|
||||
* | fp .--FrameRegs--. sp
|
||||
* | V V
|
||||
* |StackSegment| slots |StackFrame| slots |StackFrame| slots |
|
||||
* | ^ |
|
||||
* ? <----------' `-----------'
|
||||
@ -95,16 +97,16 @@ namespace mjit { struct JITScript; }
|
||||
*
|
||||
* A segment starts with a fixed-size header (js::StackSegment) which logically
|
||||
* describes the segment, links it to the rest of the stack, and points to the
|
||||
* first and last frames in the segment.
|
||||
* end of the stack.
|
||||
*
|
||||
* Each script activation (global or function code) is given a fixed-size header
|
||||
* (js::StackFrame) which is associated with the values (called "slots") before
|
||||
* and after it. The frame contains bookkeeping information about the activation
|
||||
* and links to the previous frame.
|
||||
*
|
||||
* The slots preceeding a (function) StackFrame in memory are the arguments of
|
||||
* the call. The slots after a StackFrame in memory are its locals followed
|
||||
* by its expression stack. There is no clean line between the arguments of a
|
||||
* The slots preceding a (function) StackFrame in memory are the arguments of
|
||||
* the call. The slots after a StackFrame in memory are its locals followed by
|
||||
* its expression stack. There is no clean line between the arguments of a
|
||||
* frame and the expression stack of the previous frame since the top slots of
|
||||
* the expression become the arguments of a call. There are also layout
|
||||
* invariants concerning the arguments and StackFrame; see "Arguments" comment
|
||||
@ -119,24 +121,45 @@ namespace mjit { struct JITScript; }
|
||||
* dependence on FrameRegs outside the interpreter.
|
||||
*
|
||||
* A call to a native (C++) function does not push a frame. Instead, an array
|
||||
* of values (possibly from the top of a calling frame's expression stack) is
|
||||
* passed to the native. The layout of this array is abstracted by js::CallArgs.
|
||||
* Note that, between any two StackFrames there may be any number of native
|
||||
* calls, so the meaning of 'prev' is not 'directly called by'.
|
||||
* of values is passed to the native. The layout of this array is abstracted by
|
||||
* js::CallArgs. With respect to the StackSegment layout above, the args to a
|
||||
* native call are inserted anywhere there can be slots. A sample memory layout
|
||||
* looks like:
|
||||
*
|
||||
* regs
|
||||
* .----------------------------------------.
|
||||
* | V
|
||||
* | fp .--FrameRegs--. sp
|
||||
* | V V
|
||||
* |StackSegment| native call | slots |StackFrame| slots | native call |
|
||||
* | vp <--argc--> end vp <--argc--> end
|
||||
* | CallArgs <------------------------------ CallArgs
|
||||
* | prev ^
|
||||
* `-----------------------------------------------------'
|
||||
* calls
|
||||
*
|
||||
* Here there are two native calls on the stack. The start of each native arg
|
||||
* range is recorded by a CallArgs element which is prev-linked like stack
|
||||
* frames. Note that, in full generality, native and scripted calls can
|
||||
* interleave arbitrarily. Thus, the end of a segment is the maximum of its
|
||||
* current frame and its current native call. Similarly, the top of the entire
|
||||
* thread stack is the end of its current segment.
|
||||
*
|
||||
* Note that, between any two StackFrames there may be any number
|
||||
* of native calls, so the meaning of 'prev' is not 'directly called by'.
|
||||
*
|
||||
* An additional feature (perhaps not for much longer: bug 650361) is that
|
||||
* multiple independent "contexts" can interleave (LIFO) on a single contiguous
|
||||
* stack. "Independent" here means that neither context sees the other's frames.
|
||||
* Concretely, an embedding may enter the JS engine on cx1 and then, from a
|
||||
* native called by the JS engine, reenter the VM on cx2. Changing from cx1 to
|
||||
* cx2 causes cx1's segment to be "suspended" and a new segment started to be
|
||||
* started for cx2. These two segments are linked from the perspective of
|
||||
* stack. "Independent" here means that neither context sees the other's
|
||||
* frames. Concretely, an embedding may enter the JS engine on cx1 and then,
|
||||
* from a native called by the JS engine, reenter the VM on cx2. Changing from
|
||||
* cx1 to cx2 causes a new segment to be started for cx2's stack on top of
|
||||
* cx1's current segment. These two segments are linked from the perspective of
|
||||
* StackSpace, since they are adjacent on the thread's stack, but not from the
|
||||
* perspective of cx1 and cx2. Thus, each segment has two prev-links:
|
||||
* previousInMemory and previousInContext. A context's apparent stack is
|
||||
* encapsulated and managed by the js::ContextStack object stored in JSContext.
|
||||
* ContextStack is the primary interface to the rest of the engine for pushing
|
||||
* and popping args (for js::Invoke calls) and frames.
|
||||
* perspective of cx1 and cx2. Thus, each segment has two links: prevInMemory
|
||||
* and prevInContext. Each independent stack is encapsulated and managed by
|
||||
* the js::ContextStack object stored in JSContext. ContextStack is the primary
|
||||
* interface to the rest of the engine for pushing and popping the stack.
|
||||
*/
|
||||
|
||||
/*****************************************************************************/
|
||||
@ -195,6 +218,7 @@ CallReceiverFromVp(Value *vp)
|
||||
|
||||
class CallArgs : public CallReceiver
|
||||
{
|
||||
protected:
|
||||
uintN argc_;
|
||||
public:
|
||||
friend CallArgs CallArgsFromVp(uintN, Value *);
|
||||
@ -230,6 +254,42 @@ CallArgsFromSp(uintN argc, Value *sp)
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
class CallArgsList : public CallArgs
|
||||
{
|
||||
friend class StackSegment;
|
||||
CallArgsList *prev_;
|
||||
bool active_;
|
||||
public:
|
||||
friend CallArgsList CallArgsListFromVp(uintN, Value *, CallArgsList *);
|
||||
friend CallArgsList CallArgsListFromArgv(uintN, Value *, CallArgsList *);
|
||||
CallArgsList *prev() const { return prev_; }
|
||||
bool active() const { return active_; }
|
||||
void setActive() { active_ = true; }
|
||||
void setInactive() { active_ = false; }
|
||||
};
|
||||
|
||||
JS_ALWAYS_INLINE CallArgsList
|
||||
CallArgsListFromArgv(uintN argc, Value *argv, CallArgsList *prev)
|
||||
{
|
||||
CallArgsList args;
|
||||
#ifdef DEBUG
|
||||
args.usedRval_ = false;
|
||||
#endif
|
||||
args.argv_ = argv;
|
||||
args.argc_ = argc;
|
||||
args.prev_ = prev;
|
||||
args.active_ = false;
|
||||
return args;
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE CallArgsList
|
||||
CallArgsListFromVp(uintN argc, Value *vp, CallArgsList *prev)
|
||||
{
|
||||
return CallArgsListFromArgv(argc, vp + 2, prev);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
enum MaybeConstruct {
|
||||
NO_CONSTRUCT = 0, /* == false */
|
||||
CONSTRUCT = 0x80 /* == StackFrame::CONSTRUCTING, asserted below */
|
||||
@ -332,8 +392,8 @@ class StackFrame
|
||||
void initJitFrameLatePrologue();
|
||||
|
||||
/* Used for eval. */
|
||||
void initExecuteFrame(JSScript *script, StackFrame *prev, const Value &thisv,
|
||||
JSObject &scopeChain, ExecuteType type);
|
||||
void initExecuteFrame(JSScript *script, StackFrame *prev, FrameRegs *regs,
|
||||
const Value &thisv, JSObject &scopeChain, ExecuteType type);
|
||||
|
||||
/* Used when activating generators. */
|
||||
void stealFrameAndSlots(Value *vp, StackFrame *otherfp, Value *othervp, Value *othersp);
|
||||
@ -387,6 +447,10 @@ class StackFrame
|
||||
return flags_ & EVAL;
|
||||
}
|
||||
|
||||
bool isEvalInFunction() const {
|
||||
return (flags_ & (EVAL | FUNCTION)) == (EVAL | FUNCTION);
|
||||
}
|
||||
|
||||
bool isNonEvalFunctionFrame() const {
|
||||
return (flags_ & (FUNCTION | EVAL)) == FUNCTION;
|
||||
}
|
||||
@ -462,7 +526,7 @@ class StackFrame
|
||||
* Where I stop and I turn and I go right back
|
||||
* Till I get to the bottom and I see you again...
|
||||
*/
|
||||
jsbytecode *pcQuadratic(JSContext *cx);
|
||||
jsbytecode *pcQuadratic(JSContext *cx) const;
|
||||
|
||||
jsbytecode *prevpc() {
|
||||
if (flags_ & HAS_PREVPC)
|
||||
@ -926,10 +990,6 @@ class StackFrame
|
||||
return !!(flags_ & DEBUGGER);
|
||||
}
|
||||
|
||||
bool isDirectEvalOrDebuggerFrame() const {
|
||||
return (flags_ & (EVAL | DEBUGGER)) && !(flags_ & GLOBAL);
|
||||
}
|
||||
|
||||
bool hasOverriddenArgs() const {
|
||||
return !!(flags_ & OVERRIDE_ARGS);
|
||||
}
|
||||
@ -1076,10 +1136,11 @@ class FrameRegs
|
||||
}
|
||||
|
||||
/* For generator: */
|
||||
void rebaseFromTo(const FrameRegs &from, StackFrame *to) {
|
||||
fp_ = to;
|
||||
sp = to->slots() + (from.sp - from.fp_->slots());
|
||||
void rebaseFromTo(const FrameRegs &from, StackFrame &to) {
|
||||
fp_ = &to;
|
||||
sp = to.slots() + (from.sp - from.fp_->slots());
|
||||
pc = from.pc;
|
||||
JS_ASSERT(fp_);
|
||||
}
|
||||
|
||||
/* For ContextStack: */
|
||||
@ -1087,40 +1148,140 @@ class FrameRegs
|
||||
pc = fp_->prevpc();
|
||||
sp = newsp;
|
||||
fp_ = fp_->prev();
|
||||
JS_ASSERT(fp_);
|
||||
}
|
||||
|
||||
/* For FixupArity: */
|
||||
void popPartialFrame(Value *newsp) {
|
||||
sp = newsp;
|
||||
fp_ = fp_->prev();
|
||||
JS_ASSERT(fp_);
|
||||
}
|
||||
|
||||
/* For stubs::CompileFunction, ContextStack: */
|
||||
void prepareToRun(StackFrame *fp, JSScript *script) {
|
||||
void prepareToRun(StackFrame &fp, JSScript *script) {
|
||||
pc = script->code;
|
||||
sp = fp->slots() + script->nfixed;
|
||||
fp_ = fp;
|
||||
sp = fp.slots() + script->nfixed;
|
||||
fp_ = &fp;
|
||||
JS_ASSERT(fp_);
|
||||
}
|
||||
|
||||
/* For pushDummyFrame: */
|
||||
void initDummyFrame(StackFrame *fp) {
|
||||
void initDummyFrame(StackFrame &fp) {
|
||||
pc = NULL;
|
||||
sp = fp->slots();
|
||||
fp_ = fp;
|
||||
sp = fp.slots();
|
||||
fp_ = &fp;
|
||||
JS_ASSERT(fp_);
|
||||
}
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
struct StackOverride
|
||||
class StackSegment
|
||||
{
|
||||
Value *top;
|
||||
#ifdef DEBUG
|
||||
StackSegment *seg;
|
||||
StackFrame *frame;
|
||||
#endif
|
||||
/* Previous segment within same context stack. */
|
||||
StackSegment *const prevInContext_;
|
||||
|
||||
/* Previous segment sequentially in memory. */
|
||||
StackSegment *const prevInMemory_;
|
||||
|
||||
/* Execution registers for most recent script in this segment (or null). */
|
||||
FrameRegs *regs_;
|
||||
|
||||
/* Call args for most recent native call in this segment (or null). */
|
||||
CallArgsList *calls_;
|
||||
|
||||
public:
|
||||
StackSegment(StackSegment *prevInContext,
|
||||
StackSegment *prevInMemory,
|
||||
FrameRegs *regs,
|
||||
CallArgsList *calls)
|
||||
: prevInContext_(prevInContext),
|
||||
prevInMemory_(prevInMemory),
|
||||
regs_(regs),
|
||||
calls_(calls)
|
||||
{}
|
||||
|
||||
/* A segment is followed in memory by the arguments of the first call. */
|
||||
|
||||
Value *slotsBegin() const {
|
||||
return (Value *)(this + 1);
|
||||
}
|
||||
|
||||
/* Accessors. */
|
||||
|
||||
FrameRegs ®s() const {
|
||||
JS_ASSERT(regs_);
|
||||
return *regs_;
|
||||
}
|
||||
|
||||
FrameRegs *maybeRegs() const {
|
||||
return regs_;
|
||||
}
|
||||
|
||||
StackFrame *fp() const {
|
||||
return regs_->fp();
|
||||
}
|
||||
|
||||
StackFrame *maybefp() const {
|
||||
return regs_ ? regs_->fp() : NULL;
|
||||
}
|
||||
|
||||
CallArgsList &calls() const {
|
||||
JS_ASSERT(calls_);
|
||||
return *calls_;
|
||||
}
|
||||
|
||||
CallArgsList *maybeCalls() const {
|
||||
return calls_;
|
||||
}
|
||||
|
||||
Value *callArgv() const {
|
||||
return calls_->argv();
|
||||
}
|
||||
|
||||
Value *maybeCallArgv() const {
|
||||
return calls_ ? calls_->argv() : NULL;
|
||||
}
|
||||
|
||||
StackSegment *prevInContext() const {
|
||||
return prevInContext_;
|
||||
}
|
||||
|
||||
StackSegment *prevInMemory() const {
|
||||
return prevInMemory_;
|
||||
}
|
||||
|
||||
void repointRegs(FrameRegs *regs) {
|
||||
JS_ASSERT_IF(regs, regs->fp());
|
||||
regs_ = regs;
|
||||
}
|
||||
|
||||
bool isEmpty() const {
|
||||
return !calls_ && !regs_;
|
||||
}
|
||||
|
||||
bool contains(const StackFrame *fp) const;
|
||||
bool contains(const FrameRegs *regs) const;
|
||||
bool contains(const CallArgsList *call) const;
|
||||
|
||||
StackFrame *computeNextFrame(const StackFrame *fp) const;
|
||||
|
||||
Value *end() const;
|
||||
|
||||
FrameRegs *pushRegs(FrameRegs ®s);
|
||||
void popRegs(FrameRegs *regs);
|
||||
void pushCall(CallArgsList &callList);
|
||||
void popCall();
|
||||
|
||||
/* For jit access: */
|
||||
|
||||
static const size_t offsetOfRegs() { return offsetof(StackSegment, regs_); }
|
||||
};
|
||||
|
||||
static const size_t VALUES_PER_STACK_SEGMENT = sizeof(StackSegment) / sizeof(Value);
|
||||
JS_STATIC_ASSERT(sizeof(StackSegment) % sizeof(Value) == 0);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
class StackSpace
|
||||
@ -1129,7 +1290,6 @@ class StackSpace
|
||||
mutable Value *commitEnd_;
|
||||
Value *end_;
|
||||
StackSegment *seg_;
|
||||
StackOverride override_;
|
||||
|
||||
static const size_t CAPACITY_VALS = 512 * 1024;
|
||||
static const size_t CAPACITY_BYTES = CAPACITY_VALS * sizeof(Value);
|
||||
@ -1144,13 +1304,12 @@ class StackSpace
|
||||
JS_FRIEND_API(bool) bumpCommit(JSContext *maybecx, Value *from, ptrdiff_t nvals) const;
|
||||
#endif
|
||||
|
||||
friend class AllFramesIter;
|
||||
friend class ContextStack;
|
||||
friend class StackFrame;
|
||||
friend class OOMCheck;
|
||||
inline bool ensureSpace(JSContext *maybecx, Value *from, ptrdiff_t nvals) const;
|
||||
void pushSegment(StackSegment &seg);
|
||||
void popSegment();
|
||||
inline void pushOverride(Value *top, StackOverride *prev);
|
||||
inline void popOverride(const StackOverride &prev);
|
||||
StackSegment &findContainingSegment(const StackFrame *target) const;
|
||||
|
||||
public:
|
||||
StackSpace();
|
||||
@ -1158,14 +1317,8 @@ class StackSpace
|
||||
~StackSpace();
|
||||
|
||||
/* See stack layout comment above. */
|
||||
StackSegment *currentSegment() const { return seg_; }
|
||||
Value *firstUnused() const;
|
||||
|
||||
/* Optimization of firstUnused when currentSegment() is known active. */
|
||||
inline Value *activeFirstUnused() const;
|
||||
|
||||
/* Get the segment containing the target frame. */
|
||||
StackSegment &containingSegment(const StackFrame *target) const;
|
||||
Value *firstUnused() const { return seg_ ? seg_->end() : base_; }
|
||||
Value *endOfSpace() const { return end_; }
|
||||
|
||||
#ifdef JS_TRACER
|
||||
/*
|
||||
@ -1235,70 +1388,59 @@ class LimitCheck
|
||||
|
||||
class ContextStack
|
||||
{
|
||||
FrameRegs *regs_;
|
||||
StackSegment *seg_;
|
||||
StackSpace *space_;
|
||||
JSContext *cx_;
|
||||
|
||||
/*
|
||||
* This is the collecting-point for code that wants to know when there is
|
||||
* no JS active. Note that "no JS active" does not mean the stack is empty
|
||||
* because of JS_(Save|Restore)FrameChain. If code really wants to know
|
||||
* when the stack is empty, test |cx->stack.empty()|.
|
||||
* Return whether this ContextStack is at the top of the contiguous stack.
|
||||
* This is a precondition for extending the current segment by pushing
|
||||
* stack frames or overrides etc.
|
||||
*
|
||||
* NB: Just because a stack is onTop() doesn't mean there is necessarily
|
||||
* a frame pushed on the stack. For this, use hasfp().
|
||||
*/
|
||||
void notifyIfNoCodeRunning();
|
||||
|
||||
/*
|
||||
* Return whether this ContextStack is running code at the top of the
|
||||
* contiguous stack. This is a precondition for extending the current
|
||||
* segment by pushing stack frames or overrides etc.
|
||||
*/
|
||||
inline bool isCurrentAndActive() const;
|
||||
bool onTop() const;
|
||||
|
||||
#ifdef DEBUG
|
||||
void assertSegmentsInSync() const;
|
||||
void assertSpaceInSync() const;
|
||||
#else
|
||||
void assertSegmentsInSync() const {}
|
||||
void assertSpaceInSync() const {}
|
||||
#endif
|
||||
|
||||
friend class FrameGuard;
|
||||
StackFrame *getSegmentAndFrame(JSContext *cx, uintN vplen, uintN nslots,
|
||||
FrameGuard *frameGuard) const;
|
||||
void pushSegmentAndFrame(FrameGuard *frameGuard);
|
||||
void pushSegmentAndFrameImpl(FrameRegs ®s, StackSegment &seg);
|
||||
void popSegmentAndFrame();
|
||||
void popSegmentAndFrameImpl();
|
||||
|
||||
friend class GeneratorFrameGuard;
|
||||
void popGeneratorFrame(GeneratorFrameGuard *gfg);
|
||||
/* Implementation details of push* public interface. */
|
||||
StackSegment *pushSegment(JSContext *cx);
|
||||
enum MaybeExtend { CAN_EXTEND = true, CANT_EXTEND = false };
|
||||
Value *ensureOnTop(JSContext *cx, uintN nvars, MaybeExtend extend, bool *pushedSeg);
|
||||
|
||||
/* Check = { OOMCheck, LimitCheck } */
|
||||
template <class Check>
|
||||
inline StackFrame *getCallFrame(JSContext *cx, const CallArgs &args,
|
||||
JSFunction *fun, JSScript *script,
|
||||
StackFrame::Flags *pflags,
|
||||
Check check) const;
|
||||
inline StackFrame *
|
||||
getCallFrame(JSContext *cx, const CallArgs &args, JSFunction *fun, JSScript *script,
|
||||
StackFrame::Flags *pflags, Check check) const;
|
||||
|
||||
/* Make pop* functions private since only called by guard classes. */
|
||||
void popSegment();
|
||||
friend class InvokeArgsGuard;
|
||||
bool pushInvokeArgsSlow(JSContext *cx, uintN argc, InvokeArgsGuard *argsGuard);
|
||||
void popInvokeArgsSlow(const InvokeArgsGuard &argsGuard);
|
||||
inline void popInvokeArgs(const InvokeArgsGuard &argsGuard);
|
||||
void popInvokeArgs(const InvokeArgsGuard &iag);
|
||||
friend class FrameGuard;
|
||||
void popFrame(const FrameGuard &fg);
|
||||
friend class GeneratorFrameGuard;
|
||||
void popGeneratorFrame(const GeneratorFrameGuard &gfg);
|
||||
|
||||
friend class InvokeFrameGuard;
|
||||
void pushInvokeFrameSlow(InvokeFrameGuard *frameGuard);
|
||||
void popInvokeFrameSlow(const InvokeFrameGuard &frameGuard);
|
||||
inline void popInvokeFrame(const InvokeFrameGuard &frameGuard);
|
||||
friend class StackIter;
|
||||
|
||||
public:
|
||||
ContextStack(JSContext *cx);
|
||||
~ContextStack();
|
||||
|
||||
/*** Stack accessors ***/
|
||||
|
||||
/*
|
||||
* A context's stack is "empty" if there are no scripts or natives
|
||||
* executing. Note that JS_SaveFrameChain does factor into this definition.
|
||||
*/
|
||||
bool empty() const { JS_ASSERT_IF(regs_, seg_); return !seg_; }
|
||||
bool empty() const { return !seg_; }
|
||||
|
||||
/*
|
||||
* Return whether there has been at least one frame pushed since the most
|
||||
@ -1306,54 +1448,26 @@ class ContextStack
|
||||
* and dummy frames are frames that do not represent script execution hence
|
||||
* this query has little semantic meaning past "you can call fp()".
|
||||
*/
|
||||
bool hasfp() const { JS_ASSERT_IF(regs_, regs_->fp()); return !!regs_; }
|
||||
bool hasfp() const { return seg_ && seg_->maybeRegs(); }
|
||||
|
||||
/* Current regs of the current segment (see VM stack layout comment). */
|
||||
FrameRegs ®s() const { JS_ASSERT(regs_); return *regs_; }
|
||||
/*
|
||||
* Return the most recent script activation's registers with the same
|
||||
* caveat as hasfp regarding JS_SaveFrameChain.
|
||||
*/
|
||||
FrameRegs *maybeRegs() const { return seg_ ? seg_->maybeRegs() : NULL; }
|
||||
StackFrame *maybefp() const { return seg_ ? seg_->maybefp() : NULL; }
|
||||
|
||||
/* Convenience helpers. */
|
||||
FrameRegs *maybeRegs() const { return regs_; }
|
||||
StackFrame *fp() const { return regs_->fp(); }
|
||||
StackFrame *maybefp() const { return regs_ ? regs_->fp() : NULL; }
|
||||
/* Faster alternatives to maybe* functions. */
|
||||
FrameRegs ®s() const { JS_ASSERT(hasfp()); return seg_->regs(); }
|
||||
StackFrame *fp() const { JS_ASSERT(hasfp()); return seg_->fp(); }
|
||||
|
||||
/* The StackSpace currently hosting this ContextStack. */
|
||||
StackSpace &space() const { assertSpaceInSync(); return *space_; }
|
||||
|
||||
/*
|
||||
* To avoid indirection, ContextSpace caches a pointers to the StackSpace.
|
||||
* This must be kept coherent with cx->thread->data.space by calling
|
||||
* 'threadReset' whenver cx->thread changes.
|
||||
*/
|
||||
void threadReset();
|
||||
|
||||
/*
|
||||
* As an optimization, the interpreter/mjit can operate on a local
|
||||
* FrameRegs instance repoint the ContextStack to this local instance.
|
||||
*/
|
||||
void repointRegs(FrameRegs *regs) {
|
||||
JS_ASSERT_IF(regs, regs->fp());
|
||||
regs_ = regs;
|
||||
}
|
||||
|
||||
/* Return the current segment, which may or may not be active. */
|
||||
StackSegment *currentSegment() const {
|
||||
assertSegmentsInSync();
|
||||
return seg_;
|
||||
}
|
||||
|
||||
/* Search the call stack for the nearest frame with static level targetLevel. */
|
||||
inline StackFrame *findFrameAtLevel(uintN targetLevel) const;
|
||||
|
||||
#ifdef DEBUG
|
||||
/* Return whether the given frame is in this context's stack. */
|
||||
bool contains(const StackFrame *fp) const;
|
||||
#endif
|
||||
bool containsSlow(const StackFrame *target) const;
|
||||
|
||||
/* Mark the top segment as suspended, without pushing a new one. */
|
||||
void saveActiveSegment();
|
||||
|
||||
/* Undoes calls to suspendActiveSegment. */
|
||||
void restoreSegment();
|
||||
/*** Stack manipulation ***/
|
||||
|
||||
/*
|
||||
* pushInvokeArgs allocates |argc + 2| rooted values that will be passed as
|
||||
@ -1364,16 +1478,20 @@ class ContextStack
|
||||
bool pushInvokeArgs(JSContext *cx, uintN argc, InvokeArgsGuard *ag);
|
||||
|
||||
/* Called by Invoke for a scripted function call. */
|
||||
bool pushInvokeFrame(JSContext *cx, const CallArgs &args, MaybeConstruct,
|
||||
JSObject &callee, JSFunction *fun, JSScript *script,
|
||||
InvokeFrameGuard *ifg);
|
||||
bool pushInvokeFrame(JSContext *cx, const CallArgs &args,
|
||||
MaybeConstruct construct, InvokeFrameGuard *ifg);
|
||||
|
||||
/* Called by Execute for execution of eval or global code. */
|
||||
bool pushExecuteFrame(JSContext *cx, JSScript *script, const Value &thisv,
|
||||
JSObject &scopeChain, ExecuteType type,
|
||||
StackFrame *evalInFrame, ExecuteFrameGuard *efg);
|
||||
|
||||
/* Called by SendToGenerator to resume a yielded generator. */
|
||||
/*
|
||||
* Called by SendToGenerator to resume a yielded generator. In addition to
|
||||
* pushing a frame onto the VM stack, this function copies over the
|
||||
* floating frame stored in 'gen'. When 'gfg' is destroyed, the destructor
|
||||
* will copy the frame back to the floating frame.
|
||||
*/
|
||||
bool pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg);
|
||||
|
||||
/* Pushes a "dummy" frame; should be removed one day. */
|
||||
@ -1388,7 +1506,10 @@ class ContextStack
|
||||
bool pushInlineFrame(JSContext *cx, FrameRegs ®s, const CallArgs &args,
|
||||
JSObject &callee, JSFunction *fun, JSScript *script,
|
||||
MaybeConstruct construct, Check check);
|
||||
void popInlineFrame();
|
||||
void popInlineFrame(FrameRegs ®s);
|
||||
|
||||
/* Pop a partially-pushed frame after hitting the limit before throwing. */
|
||||
void popFrameAfterOverflow();
|
||||
|
||||
/*
|
||||
* Called by the methodjit for an arity mismatch. Arity mismatch can be
|
||||
@ -1402,54 +1523,65 @@ class ContextStack
|
||||
JSFunction *fun, JSScript *script, void *ncode,
|
||||
MaybeConstruct construct, LimitCheck check);
|
||||
|
||||
/* For jit use: */
|
||||
static size_t offsetOfRegs() { return offsetof(ContextStack, regs_); }
|
||||
bool saveFrameChain();
|
||||
void restoreFrameChain();
|
||||
|
||||
/*
|
||||
* As an optimization, the interpreter/mjit can operate on a local
|
||||
* FrameRegs instance repoint the ContextStack to this local instance.
|
||||
*/
|
||||
void repointRegs(FrameRegs *regs) { JS_ASSERT(hasfp()); seg_->repointRegs(regs); }
|
||||
|
||||
/*** For JSContext: ***/
|
||||
|
||||
/*
|
||||
* To avoid indirection, ContextSpace caches a pointer to the StackSpace.
|
||||
* This must be kept coherent with cx->thread->data.space by calling
|
||||
* 'threadReset' whenver cx->thread changes.
|
||||
*/
|
||||
void threadReset();
|
||||
|
||||
/*** For jit compiler: ***/
|
||||
|
||||
static size_t offsetOfSeg() { return offsetof(ContextStack, seg_); }
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
class InvokeArgsGuard : public CallArgs
|
||||
class InvokeArgsGuard : public CallArgsList
|
||||
{
|
||||
friend class ContextStack;
|
||||
ContextStack *stack_; /* null implies nothing pushed */
|
||||
StackSegment *seg_; /* null implies no segment pushed */
|
||||
StackOverride prevOverride_;
|
||||
ContextStack *stack_;
|
||||
bool pushedSeg_;
|
||||
void setPushed(ContextStack &stack) { JS_ASSERT(!pushed()); stack_ = &stack; }
|
||||
public:
|
||||
InvokeArgsGuard() : stack_(NULL), seg_(NULL) {}
|
||||
~InvokeArgsGuard();
|
||||
bool pushed() const { return stack_ != NULL; }
|
||||
InvokeArgsGuard() : CallArgsList(), stack_(NULL), pushedSeg_(false) {}
|
||||
~InvokeArgsGuard() { if (pushed()) stack_->popInvokeArgs(*this); }
|
||||
bool pushed() const { return !!stack_; }
|
||||
void pop() { stack_->popInvokeArgs(*this); stack_ = NULL; }
|
||||
};
|
||||
|
||||
class InvokeFrameGuard
|
||||
|
||||
{
|
||||
friend class ContextStack;
|
||||
ContextStack *stack_; /* null implies nothing pushed */
|
||||
FrameRegs regs_;
|
||||
FrameRegs *prevRegs_;
|
||||
public:
|
||||
InvokeFrameGuard() : stack_(NULL) {}
|
||||
~InvokeFrameGuard();
|
||||
bool pushed() const { return stack_ != NULL; }
|
||||
void pop();
|
||||
StackFrame *fp() const { return regs_.fp(); }
|
||||
};
|
||||
|
||||
/* Reusable base; not for direct use. */
|
||||
class FrameGuard
|
||||
{
|
||||
protected:
|
||||
friend class ContextStack;
|
||||
ContextStack *stack_; /* null implies nothing pushed */
|
||||
StackSegment *seg_;
|
||||
ContextStack *stack_;
|
||||
bool pushedSeg_;
|
||||
FrameRegs regs_;
|
||||
FrameRegs *prevRegs_;
|
||||
void setPushed(ContextStack &stack) { stack_ = &stack; }
|
||||
public:
|
||||
FrameGuard() : stack_(NULL) {}
|
||||
~FrameGuard();
|
||||
bool pushed() const { return stack_ != NULL; }
|
||||
FrameGuard() : stack_(NULL), pushedSeg_(false) {}
|
||||
~FrameGuard() { if (pushed()) stack_->popFrame(*this); }
|
||||
bool pushed() const { return !!stack_; }
|
||||
void pop() { stack_->popFrame(*this); stack_ = NULL; }
|
||||
|
||||
StackFrame *fp() const { return regs_.fp(); }
|
||||
};
|
||||
|
||||
class InvokeFrameGuard : public FrameGuard
|
||||
{};
|
||||
|
||||
class ExecuteFrameGuard : public FrameGuard
|
||||
{};
|
||||
|
||||
@ -1462,59 +1594,114 @@ class GeneratorFrameGuard : public FrameGuard
|
||||
JSGenerator *gen_;
|
||||
Value *stackvp_;
|
||||
public:
|
||||
~GeneratorFrameGuard();
|
||||
~GeneratorFrameGuard() { if (pushed()) stack_->popGeneratorFrame(*this); }
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
* While |cx->fp|'s pc/sp are available in |cx->regs|, to compute the saved
|
||||
* value of pc/sp for any other frame, it is necessary to know about that
|
||||
* frame's next-frame. This iterator maintains this information when walking
|
||||
* a chain of stack frames starting at |cx->fp|.
|
||||
* Iterate through the callstack of the given context. Each element of said
|
||||
* callstack can either be the execution of a script (scripted function call,
|
||||
* global code, eval code, debugger code) or the invocation of a (C++) native.
|
||||
* Example usage:
|
||||
*
|
||||
* Usage:
|
||||
* for (FrameRegsIter i(cx); !i.done(); ++i)
|
||||
* ... i.fp() ... i.sp() ... i.pc()
|
||||
* for (Stackiter i(cx); !i.done(); ++i) {
|
||||
* if (i.isScript()) {
|
||||
* ... i.fp() ... i.sp() ... i.pc()
|
||||
* } else {
|
||||
* JS_ASSERT(i.isNativeCall());
|
||||
* ... i.args();
|
||||
* }
|
||||
*
|
||||
* The SavedOption parameter additionally lets the iterator continue through
|
||||
* breaks in the callstack (from JS_SaveFrameChain). The default is to stop.
|
||||
*/
|
||||
class FrameRegsIter
|
||||
class StackIter
|
||||
{
|
||||
friend class ContextStack;
|
||||
JSContext *cx_;
|
||||
StackSegment *seg_;
|
||||
public:
|
||||
enum SavedOption { STOP_AT_SAVED, GO_THROUGH_SAVED };
|
||||
private:
|
||||
SavedOption savedOption_;
|
||||
|
||||
enum State { DONE, SCRIPTED, NATIVE, IMPLICIT_NATIVE };
|
||||
State state_;
|
||||
|
||||
StackFrame *fp_;
|
||||
CallArgsList *calls_;
|
||||
|
||||
StackSegment *seg_;
|
||||
Value *sp_;
|
||||
jsbytecode *pc_;
|
||||
CallArgs args_;
|
||||
|
||||
void initSlow();
|
||||
void incSlow(StackFrame *oldfp);
|
||||
void poisonRegs();
|
||||
void popFrame();
|
||||
void popCall();
|
||||
void settleOnNewSegment();
|
||||
void settleOnNewState();
|
||||
void startOnSegment(StackSegment *seg);
|
||||
|
||||
public:
|
||||
FrameRegsIter(JSContext *cx);
|
||||
StackIter(JSContext *cx, SavedOption = STOP_AT_SAVED);
|
||||
|
||||
bool done() const { return fp_ == NULL; }
|
||||
FrameRegsIter &operator++();
|
||||
bool operator==(const FrameRegsIter &rhs) const;
|
||||
bool operator!=(const FrameRegsIter &rhs) const { return !(*this == rhs); }
|
||||
bool done() const { return state_ == DONE; }
|
||||
StackIter &operator++();
|
||||
|
||||
StackFrame *fp() const { return fp_; }
|
||||
Value *sp() const { return sp_; }
|
||||
jsbytecode *pc() const { return pc_; }
|
||||
bool operator==(const StackIter &rhs) const;
|
||||
bool operator!=(const StackIter &rhs) const { return !(*this == rhs); }
|
||||
|
||||
bool isScript() const { JS_ASSERT(!done()); return state_ == SCRIPTED; }
|
||||
StackFrame *fp() const { JS_ASSERT(!done() && isScript()); return fp_; }
|
||||
Value *sp() const { JS_ASSERT(!done() && isScript()); return sp_; }
|
||||
jsbytecode *pc() const { JS_ASSERT(!done() && isScript()); return pc_; }
|
||||
|
||||
bool isNativeCall() const { JS_ASSERT(!done()); return state_ != SCRIPTED; }
|
||||
CallArgs nativeArgs() const { JS_ASSERT(!done() && isNativeCall()); return args_; }
|
||||
};
|
||||
|
||||
/* A filtering of the StackIter to only stop at scripts. */
|
||||
class FrameRegsIter
|
||||
{
|
||||
StackIter iter_;
|
||||
|
||||
void settle() {
|
||||
while (!iter_.done() && !iter_.isScript())
|
||||
++iter_;
|
||||
}
|
||||
|
||||
public:
|
||||
FrameRegsIter(JSContext *cx) : iter_(cx) { settle(); }
|
||||
|
||||
bool done() const { return iter_.done(); }
|
||||
FrameRegsIter &operator++() { ++iter_; settle(); return *this; }
|
||||
|
||||
bool operator==(const FrameRegsIter &rhs) const { return iter_ == rhs.iter_; }
|
||||
bool operator!=(const FrameRegsIter &rhs) const { return iter_ != rhs.iter_; }
|
||||
|
||||
StackFrame *fp() const { return iter_.fp(); }
|
||||
Value *sp() const { return iter_.sp(); }
|
||||
jsbytecode *pc() const { return iter_.pc(); }
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
* Utility class for iteration over all active stack frames.
|
||||
* Blindly iterate over all frames in the current thread's stack. These frames
|
||||
* can be from different contexts and compartments, so beware.
|
||||
*/
|
||||
class AllFramesIter
|
||||
{
|
||||
public:
|
||||
AllFramesIter(JSContext *cx);
|
||||
public:
|
||||
AllFramesIter(StackSpace &space);
|
||||
|
||||
bool done() const { return fp_ == NULL; }
|
||||
AllFramesIter& operator++();
|
||||
|
||||
StackFrame *fp() const { return fp_; }
|
||||
|
||||
private:
|
||||
private:
|
||||
StackSegment *seg_;
|
||||
StackFrame *fp_;
|
||||
};
|
||||
|
@ -259,9 +259,9 @@ class JSCLContextHelper
|
||||
{
|
||||
public:
|
||||
JSCLContextHelper(mozJSComponentLoader* loader);
|
||||
~JSCLContextHelper() { Pop(); }
|
||||
~JSCLContextHelper();
|
||||
|
||||
JSContext* Pop();
|
||||
void reportErrorAfterPop(char *buf);
|
||||
|
||||
operator JSContext*() const {return mContext;}
|
||||
|
||||
@ -269,6 +269,7 @@ private:
|
||||
JSContext* mContext;
|
||||
intN mContextThread;
|
||||
nsIThreadJSContextStack* mContextStack;
|
||||
char* mBuf;
|
||||
|
||||
// prevent copying and assignment
|
||||
JSCLContextHelper(const JSCLContextHelper &); // not implemented
|
||||
@ -291,22 +292,6 @@ private:
|
||||
const JSCLAutoErrorReporterSetter& operator=(const JSCLAutoErrorReporterSetter &); // not implemented
|
||||
};
|
||||
|
||||
static nsresult
|
||||
OutputError(JSContext *cx,
|
||||
const char *format,
|
||||
va_list ap)
|
||||
{
|
||||
char *buf = JS_vsmprintf(format, ap);
|
||||
if (!buf) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
JS_ReportError(cx, buf);
|
||||
JS_smprintf_free(buf);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static nsresult
|
||||
ReportOnCaller(nsAXPCNativeCallContext *cc,
|
||||
const char *format, ...) {
|
||||
@ -322,7 +307,15 @@ ReportOnCaller(nsAXPCNativeCallContext *cc,
|
||||
rv = cc->GetJSContext(&callerContext);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return OutputError(callerContext, format, ap);
|
||||
char *buf = JS_vsmprintf(format, ap);
|
||||
if (!buf) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
JS_ReportError(callerContext, buf);
|
||||
JS_smprintf_free(buf);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static nsresult
|
||||
@ -332,12 +325,14 @@ ReportOnCaller(JSCLContextHelper &helper,
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
|
||||
JSContext *cx = helper.Pop();
|
||||
if (!cx) {
|
||||
return NS_ERROR_FAILURE;
|
||||
char *buf = JS_vsmprintf(format, ap);
|
||||
if (!buf) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
return OutputError(cx, format, ap);
|
||||
helper.reportErrorAfterPop(buf);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static nsresult
|
||||
@ -1639,7 +1634,8 @@ mozJSComponentLoader::ModuleEntry::GetFactory(const mozilla::Module& module,
|
||||
|
||||
JSCLContextHelper::JSCLContextHelper(mozJSComponentLoader *loader)
|
||||
: mContext(loader->mContext), mContextThread(0),
|
||||
mContextStack(loader->mContextStack)
|
||||
mContextStack(loader->mContextStack),
|
||||
mBuf(nsnull)
|
||||
{
|
||||
mContextStack->Push(mContext);
|
||||
mContextThread = JS_GetContextThread(mContext);
|
||||
@ -1648,20 +1644,33 @@ JSCLContextHelper::JSCLContextHelper(mozJSComponentLoader *loader)
|
||||
}
|
||||
}
|
||||
|
||||
// Pops the context that was pushed and then returns the context that is now at
|
||||
// the top of the stack.
|
||||
JSContext*
|
||||
JSCLContextHelper::Pop()
|
||||
JSCLContextHelper::~JSCLContextHelper()
|
||||
{
|
||||
JSContext* cx = nsnull;
|
||||
if (mContextStack) {
|
||||
if (mContextThread) {
|
||||
JS_EndRequest(mContext);
|
||||
}
|
||||
|
||||
mContextStack->Pop(nsnull);
|
||||
|
||||
JSContext* cx = nsnull;
|
||||
mContextStack->Peek(&cx);
|
||||
|
||||
mContextStack = nsnull;
|
||||
|
||||
if (cx && mBuf) {
|
||||
JS_ReportError(cx, mBuf);
|
||||
}
|
||||
}
|
||||
|
||||
if (mBuf) {
|
||||
JS_smprintf_free(mBuf);
|
||||
}
|
||||
return cx;
|
||||
}
|
||||
|
||||
void
|
||||
JSCLContextHelper::reportErrorAfterPop(char *buf)
|
||||
{
|
||||
NS_ASSERTION(!mBuf, "Already called reportErrorAfterPop");
|
||||
mBuf = buf;
|
||||
}
|
||||
|
@ -967,23 +967,6 @@ inline nsresult UnexpectedFailure(nsresult rv)
|
||||
return rv;
|
||||
}
|
||||
|
||||
class SaveFrame
|
||||
{
|
||||
public:
|
||||
SaveFrame(JSContext *cx)
|
||||
: mJSContext(cx) {
|
||||
mFrame = JS_SaveFrameChain(mJSContext);
|
||||
}
|
||||
|
||||
~SaveFrame() {
|
||||
JS_RestoreFrameChain(mJSContext, mFrame);
|
||||
}
|
||||
|
||||
private:
|
||||
JSContext *mJSContext;
|
||||
JSStackFrame *mFrame;
|
||||
};
|
||||
|
||||
/* void initClasses (in JSContextPtr aJSContext, in JSObjectPtr aGlobalJSObj); */
|
||||
NS_IMETHODIMP
|
||||
nsXPConnect::InitClasses(JSContext * aJSContext, JSObject * aGlobalJSObj)
|
||||
|
@ -3559,14 +3559,13 @@ private:
|
||||
struct XPCJSContextInfo {
|
||||
XPCJSContextInfo(JSContext* aCx) :
|
||||
cx(aCx),
|
||||
frame(nsnull),
|
||||
savedFrameChain(false),
|
||||
suspendDepth(0)
|
||||
{}
|
||||
JSContext* cx;
|
||||
|
||||
// Frame to be restored when this JSContext becomes the topmost
|
||||
// one.
|
||||
JSStackFrame* frame;
|
||||
// Whether the frame chain was saved
|
||||
bool savedFrameChain;
|
||||
|
||||
// Greater than 0 if a request was suspended.
|
||||
jsrefcount suspendDepth;
|
||||
|
@ -93,9 +93,6 @@ XPCJSContextStack::Pop(JSContext * *_retval)
|
||||
NS_ASSERTION(!mStack.IsEmpty(), "ThreadJSContextStack underflow");
|
||||
|
||||
PRUint32 idx = mStack.Length() - 1; // The thing we're popping
|
||||
NS_ASSERTION(!mStack[idx].frame,
|
||||
"Shouldn't have a pending frame to restore on the context "
|
||||
"we're popping!");
|
||||
|
||||
if(_retval)
|
||||
*_retval = mStack[idx].cx;
|
||||
@ -106,7 +103,6 @@ XPCJSContextStack::Pop(JSContext * *_retval)
|
||||
--idx; // Advance to new top of the stack
|
||||
|
||||
XPCJSContextInfo & e = mStack[idx];
|
||||
NS_ASSERTION(!e.frame || e.cx, "Shouldn't have frame without a cx!");
|
||||
NS_ASSERTION(!e.suspendDepth || e.cx, "Shouldn't have suspendDepth without a cx!");
|
||||
if(e.cx)
|
||||
{
|
||||
@ -116,12 +112,12 @@ XPCJSContextStack::Pop(JSContext * *_retval)
|
||||
e.suspendDepth = 0;
|
||||
}
|
||||
|
||||
if(e.frame)
|
||||
if(e.savedFrameChain)
|
||||
{
|
||||
// Pop() can be called outside any request for e.cx.
|
||||
JSAutoRequest ar(e.cx);
|
||||
JS_RestoreFrameChain(e.cx, e.frame);
|
||||
e.frame = nsnull;
|
||||
JS_RestoreFrameChain(e.cx);
|
||||
e.savedFrameChain = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -146,11 +142,9 @@ NS_IMETHODIMP
|
||||
XPCJSContextStack::Push(JSContext * cx)
|
||||
{
|
||||
JS_ASSERT_IF(cx, JS_GetContextThread(cx));
|
||||
if(!mStack.AppendElement(cx))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
if(mStack.Length() > 1)
|
||||
if(mStack.Length() > 0)
|
||||
{
|
||||
XPCJSContextInfo & e = mStack[mStack.Length() - 2];
|
||||
XPCJSContextInfo & e = mStack[mStack.Length() - 1];
|
||||
if(e.cx)
|
||||
{
|
||||
if(e.cx == cx)
|
||||
@ -158,16 +152,14 @@ XPCJSContextStack::Push(JSContext * cx)
|
||||
nsIScriptSecurityManager* ssm = XPCWrapper::GetSecurityManager();
|
||||
if(ssm)
|
||||
{
|
||||
nsIPrincipal* globalObjectPrincipal =
|
||||
GetPrincipalFromCx(cx);
|
||||
if(globalObjectPrincipal)
|
||||
if(nsIPrincipal* globalObjectPrincipal = GetPrincipalFromCx(cx))
|
||||
{
|
||||
nsIPrincipal* subjectPrincipal = ssm->GetCxSubjectPrincipal(cx);
|
||||
PRBool equals = PR_FALSE;
|
||||
globalObjectPrincipal->Equals(subjectPrincipal, &equals);
|
||||
if(equals)
|
||||
{
|
||||
return NS_OK;
|
||||
goto append;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -176,13 +168,19 @@ XPCJSContextStack::Push(JSContext * cx)
|
||||
{
|
||||
// Push() can be called outside any request for e.cx.
|
||||
JSAutoRequest ar(e.cx);
|
||||
e.frame = JS_SaveFrameChain(e.cx);
|
||||
if(!JS_SaveFrameChain(e.cx))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
e.savedFrameChain = true;
|
||||
}
|
||||
|
||||
if(!cx)
|
||||
e.suspendDepth = JS_SuspendRequest(e.cx);
|
||||
}
|
||||
}
|
||||
|
||||
append:
|
||||
if(!mStack.AppendElement(cx))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user