mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-12 04:45:45 +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
|
// 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
|
// frame chain here, since the constructor invocation is not related to
|
||||||
// whatever is on the stack right now, really.
|
// whatever is on the stack right now, really.
|
||||||
JSStackFrame* frame = JS_SaveFrameChain(cx);
|
JSBool saved = JS_SaveFrameChain(cx);
|
||||||
::JS_ReportPendingException(cx);
|
JS_ReportPendingException(cx);
|
||||||
JS_RestoreFrameChain(cx, frame);
|
if (saved)
|
||||||
|
JS_RestoreFrameChain(cx);
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3520,9 +3520,10 @@ nsJSContext::ReportPendingException()
|
|||||||
// set aside the frame chain, since it has nothing to do with the
|
// set aside the frame chain, since it has nothing to do with the
|
||||||
// exception we're reporting.
|
// exception we're reporting.
|
||||||
if (mIsInitialized && ::JS_IsExceptionPending(mContext)) {
|
if (mIsInitialized && ::JS_IsExceptionPending(mContext)) {
|
||||||
JSStackFrame* frame = JS_SaveFrameChain(mContext);
|
PRBool saved = ::JS_SaveFrameChain(mContext);
|
||||||
::JS_ReportPendingException(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;
|
return fp != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_PUBLIC_API(JSStackFrame *)
|
JS_PUBLIC_API(JSBool)
|
||||||
JS_SaveFrameChain(JSContext *cx)
|
JS_SaveFrameChain(JSContext *cx)
|
||||||
{
|
{
|
||||||
CHECK_REQUEST(cx);
|
CHECK_REQUEST(cx);
|
||||||
StackFrame *fp = js_GetTopStackFrame(cx);
|
LeaveTrace(cx);
|
||||||
if (!fp)
|
return cx->stack.saveFrameChain();
|
||||||
return NULL;
|
|
||||||
cx->stack.saveActiveSegment();
|
|
||||||
return Jsvalify(fp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_PUBLIC_API(void)
|
JS_PUBLIC_API(void)
|
||||||
JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp)
|
JS_RestoreFrameChain(JSContext *cx)
|
||||||
{
|
{
|
||||||
CHECK_REQUEST(cx);
|
CHECK_REQUEST(cx);
|
||||||
JS_ASSERT_NOT_ON_TRACE(cx);
|
JS_ASSERT_NOT_ON_TRACE(cx);
|
||||||
JS_ASSERT(!cx->hasfp());
|
cx->stack.restoreFrameChain();
|
||||||
if (!fp)
|
|
||||||
return;
|
|
||||||
cx->stack.restoreSegment();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
|
@ -2943,15 +2943,13 @@ JS_IsRunning(JSContext *cx);
|
|||||||
* must be balanced and all nested calls to JS_SaveFrameChain must have had
|
* must be balanced and all nested calls to JS_SaveFrameChain must have had
|
||||||
* matching JS_RestoreFrameChain calls.
|
* matching JS_RestoreFrameChain calls.
|
||||||
*
|
*
|
||||||
* JS_SaveFrameChain deals with cx not having any code running on it. A null
|
* JS_SaveFrameChain deals with cx not having any code running on it.
|
||||||
* return does not signify an error, and JS_RestoreFrameChain handles a null
|
|
||||||
* frame pointer argument safely.
|
|
||||||
*/
|
*/
|
||||||
extern JS_PUBLIC_API(JSStackFrame *)
|
extern JS_PUBLIC_API(JSBool)
|
||||||
JS_SaveFrameChain(JSContext *cx);
|
JS_SaveFrameChain(JSContext *cx);
|
||||||
|
|
||||||
extern JS_PUBLIC_API(void)
|
extern JS_PUBLIC_API(void)
|
||||||
JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp);
|
JS_RestoreFrameChain(JSContext *cx);
|
||||||
|
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
|
|
||||||
|
@ -1540,7 +1540,8 @@ JSContext::wrapPendingException()
|
|||||||
JSGenerator *
|
JSGenerator *
|
||||||
JSContext::generatorFor(StackFrame *fp) const
|
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(!fp->isFloatingGenerator());
|
||||||
JS_ASSERT(!genStack.empty());
|
JS_ASSERT(!genStack.empty());
|
||||||
|
|
||||||
|
@ -1186,10 +1186,11 @@ struct JSContext
|
|||||||
* default version.
|
* default version.
|
||||||
*/
|
*/
|
||||||
void maybeMigrateVersionOverride() {
|
void maybeMigrateVersionOverride() {
|
||||||
if (JS_LIKELY(!isVersionOverridden() && stack.empty()))
|
JS_ASSERT(stack.empty());
|
||||||
return;
|
if (JS_UNLIKELY(isVersionOverridden())) {
|
||||||
defaultVersion = versionOverride;
|
defaultVersion = versionOverride;
|
||||||
clearVersionOverride();
|
clearVersionOverride();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -170,9 +170,9 @@ CompartmentHasLiveScripts(JSCompartment *comp)
|
|||||||
if (JS_GetContextThread(icx) != currentThreadId)
|
if (JS_GetContextThread(icx) != currentThreadId)
|
||||||
continue;
|
continue;
|
||||||
#endif
|
#endif
|
||||||
for (AllFramesIter i(icx); !i.done(); ++i) {
|
for (FrameRegsIter i(icx); !i.done(); ++i) {
|
||||||
JSScript *script = i.fp()->maybeScript();
|
JSScript *script = i.fp()->script();
|
||||||
if (script && script->compartment == comp)
|
if (script->compartment == comp)
|
||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1491,7 +1491,7 @@ JS_PUBLIC_API(JSObject *)
|
|||||||
JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fpArg)
|
JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fpArg)
|
||||||
{
|
{
|
||||||
StackFrame *fp = Valueify(fpArg);
|
StackFrame *fp = Valueify(fpArg);
|
||||||
JS_ASSERT(cx->stack.contains(fp));
|
JS_ASSERT(cx->stack.containsSlow(fp));
|
||||||
|
|
||||||
js::AutoCompartment ac(cx, &fp->scopeChain());
|
js::AutoCompartment ac(cx, &fp->scopeChain());
|
||||||
if (!ac.enter())
|
if (!ac.enter())
|
||||||
@ -1506,7 +1506,7 @@ JS_PUBLIC_API(JSObject *)
|
|||||||
JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fpArg)
|
JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fpArg)
|
||||||
{
|
{
|
||||||
StackFrame *fp = Valueify(fpArg);
|
StackFrame *fp = Valueify(fpArg);
|
||||||
JS_ASSERT(cx->stack.contains(fp));
|
JS_ASSERT(cx->stack.containsSlow(fp));
|
||||||
|
|
||||||
if (!fp->isFunctionFrame())
|
if (!fp->isFunctionFrame())
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -356,8 +356,7 @@ InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message,
|
|||||||
elem->filename = NULL;
|
elem->filename = NULL;
|
||||||
if (fp->isScriptFrame()) {
|
if (fp->isScriptFrame()) {
|
||||||
elem->filename = fp->script()->filename;
|
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;
|
++elem;
|
||||||
}
|
}
|
||||||
|
@ -247,7 +247,7 @@ js_GetArgsObject(JSContext *cx, StackFrame *fp)
|
|||||||
*/
|
*/
|
||||||
JS_ASSERT_IF(fp->fun()->isHeavyweight(), fp->hasCallObj());
|
JS_ASSERT_IF(fp->fun()->isHeavyweight(), fp->hasCallObj());
|
||||||
|
|
||||||
while (fp->isDirectEvalOrDebuggerFrame())
|
while (fp->isEvalInFunction())
|
||||||
fp = fp->prev();
|
fp = fp->prev();
|
||||||
|
|
||||||
/* Create an arguments object for fp only if it lacks one. */
|
/* 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();
|
JSFunction *fun = obj->getFunctionPrivate();
|
||||||
|
|
||||||
/* Find fun's top-most activation record. */
|
/* Find fun's top-most activation record. */
|
||||||
StackFrame *fp;
|
StackFrame *fp = js_GetTopStackFrame(cx);
|
||||||
for (fp = js_GetTopStackFrame(cx);
|
while (fp && (fp->maybeFun() != fun || fp->isEvalInFunction()))
|
||||||
fp && (fp->maybeFun() != fun || fp->isDirectEvalOrDebuggerFrame());
|
fp = fp->prev();
|
||||||
fp = fp->prev()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (slot) {
|
switch (slot) {
|
||||||
case FUN_ARGUMENTS:
|
case FUN_ARGUMENTS:
|
||||||
@ -3016,9 +3013,6 @@ js_ReportIsNotFunction(JSContext *cx, const Value *vp, uintN flags)
|
|||||||
ptrdiff_t spindex = 0;
|
ptrdiff_t spindex = 0;
|
||||||
|
|
||||||
FrameRegsIter i(cx);
|
FrameRegsIter i(cx);
|
||||||
while (!i.done() && !i.pc())
|
|
||||||
++i;
|
|
||||||
|
|
||||||
if (!i.done()) {
|
if (!i.done()) {
|
||||||
uintN depth = js_ReconstructStackDepth(cx, i.fp()->script(), i.pc());
|
uintN depth = js_ReconstructStackDepth(cx, i.fp()->script(), i.pc());
|
||||||
Value *simsp = i.fp()->base() + depth;
|
Value *simsp = i.fp()->base() + depth;
|
||||||
|
@ -217,6 +217,11 @@ struct JSFunction : public JSObject_Slots2
|
|||||||
return isInterpreted() ? NULL : u.n.native;
|
return isInterpreted() ? NULL : u.n.native;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
js::Native native() const {
|
||||||
|
JS_ASSERT(isNative());
|
||||||
|
return u.n.native;
|
||||||
|
}
|
||||||
|
|
||||||
JSScript *script() const {
|
JSScript *script() const {
|
||||||
JS_ASSERT(isInterpreted());
|
JS_ASSERT(isInterpreted());
|
||||||
return u.i.script;
|
return u.i.script;
|
||||||
@ -300,6 +305,19 @@ IsFunctionObject(const js::Value &v, JSFunction **fun)
|
|||||||
return b;
|
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
|
extern JS_ALWAYS_INLINE bool
|
||||||
SameTraceType(const Value &lhs, const Value &rhs)
|
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. */
|
/* Get pointer to new frame/slots, prepare arguments. */
|
||||||
InvokeFrameGuard ifg;
|
InvokeFrameGuard ifg;
|
||||||
if (!cx->stack.pushInvokeFrame(cx, args, construct, callee, fun, script, &ifg))
|
if (!cx->stack.pushInvokeFrame(cx, args, construct, &ifg))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* Now that the new frame is rooted, maybe create a call object. */
|
/* 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;
|
break;
|
||||||
|
|
||||||
/* Push the stack frame once for the session. */
|
/* 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;
|
return false;
|
||||||
|
|
||||||
StackFrame *fp = ifg_.fp();
|
StackFrame *fp = ifg_.fp();
|
||||||
@ -858,7 +858,7 @@ InitSharpSlots(JSContext *cx, StackFrame *fp)
|
|||||||
Value *sharps = &fp->slots()[script->nfixed - SHARP_NSLOTS];
|
Value *sharps = &fp->slots()[script->nfixed - SHARP_NSLOTS];
|
||||||
if (!fp->isGlobalFrame() && prev->script()->hasSharps) {
|
if (!fp->isGlobalFrame() && prev->script()->hasSharps) {
|
||||||
JS_ASSERT(prev->numFixed() >= SHARP_NSLOTS);
|
JS_ASSERT(prev->numFixed() >= SHARP_NSLOTS);
|
||||||
int base = (prev->isFunctionFrame() && !prev->isDirectEvalOrDebuggerFrame())
|
int base = prev->isNonEvalFunctionFrame()
|
||||||
? prev->fun()->script()->bindings.sharpSlotBase(cx)
|
? prev->fun()->script()->bindings.sharpSlotBase(cx)
|
||||||
: prev->numFixed() - SHARP_NSLOTS;
|
: prev->numFixed() - SHARP_NSLOTS;
|
||||||
if (base < 0)
|
if (base < 0)
|
||||||
@ -1408,7 +1408,7 @@ js::GetUpvar(JSContext *cx, uintN closureLevel, UpvarCookie cookie)
|
|||||||
const uintN targetLevel = closureLevel - cookie.level();
|
const uintN targetLevel = closureLevel - cookie.level();
|
||||||
JS_ASSERT(targetLevel < UpvarCookie::UPVAR_LEVEL_LIMIT);
|
JS_ASSERT(targetLevel < UpvarCookie::UPVAR_LEVEL_LIMIT);
|
||||||
|
|
||||||
StackFrame *fp = cx->stack.findFrameAtLevel(targetLevel);
|
StackFrame *fp = FindUpvarFrame(cx, targetLevel);
|
||||||
uintN slot = cookie.slot();
|
uintN slot = cookie.slot();
|
||||||
const Value *vp;
|
const Value *vp;
|
||||||
|
|
||||||
@ -1428,6 +1428,19 @@ js::GetUpvar(JSContext *cx, uintN closureLevel, UpvarCookie cookie)
|
|||||||
return vp[slot];
|
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
|
#ifdef DEBUG
|
||||||
|
|
||||||
JS_STATIC_INTERPRET JS_REQUIRES_STACK void
|
JS_STATIC_INTERPRET JS_REQUIRES_STACK void
|
||||||
@ -2810,7 +2823,7 @@ BEGIN_CASE(JSOP_STOP)
|
|||||||
#ifdef JS_METHODJIT
|
#ifdef JS_METHODJIT
|
||||||
jit_return:
|
jit_return:
|
||||||
#endif
|
#endif
|
||||||
cx->stack.popInlineFrame();
|
cx->stack.popInlineFrame(regs);
|
||||||
|
|
||||||
/* Sync interpreter locals. */
|
/* Sync interpreter locals. */
|
||||||
script = regs.fp()->script();
|
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
|
* and the range [args.getvp() + 2, args.getvp() + 2 + args.getArgc()) should
|
||||||
* be initialized actual arguments.
|
* be initialized actual arguments.
|
||||||
*/
|
*/
|
||||||
extern JS_REQUIRES_STACK bool
|
extern bool
|
||||||
Invoke(JSContext *cx, const CallArgs &args, MaybeConstruct construct = NO_CONSTRUCT);
|
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
|
* Natives like sort/forEach/replace call Invoke repeatedly with the same
|
||||||
* callee, this, and number of arguments. To optimize this, such natives can
|
* 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.
|
* closure level.
|
||||||
* @return The value of the upvar.
|
* @return The value of the upvar.
|
||||||
*/
|
*/
|
||||||
extern const js::Value &
|
extern const Value &
|
||||||
GetUpvar(JSContext *cx, uintN level, js::UpvarCookie cookie);
|
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 */
|
} /* namespace js */
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ class InvokeSessionGuard
|
|||||||
~InvokeSessionGuard() {}
|
~InvokeSessionGuard() {}
|
||||||
|
|
||||||
bool start(JSContext *cx, const Value &callee, const Value &thisv, uintN argc);
|
bool start(JSContext *cx, const Value &callee, const Value &thisv, uintN argc);
|
||||||
bool invoke(JSContext *cx) const;
|
bool invoke(JSContext *cx);
|
||||||
|
|
||||||
bool started() const {
|
bool started() const {
|
||||||
return args_.pushed();
|
return args_.pushed();
|
||||||
@ -113,7 +113,7 @@ class InvokeSessionGuard
|
|||||||
};
|
};
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
InvokeSessionGuard::invoke(JSContext *cx) const
|
InvokeSessionGuard::invoke(JSContext *cx)
|
||||||
{
|
{
|
||||||
/* N.B. Must be kept in sync with Invoke */
|
/* N.B. Must be kept in sync with Invoke */
|
||||||
|
|
||||||
@ -139,6 +139,7 @@ InvokeSessionGuard::invoke(JSContext *cx) const
|
|||||||
JSBool ok;
|
JSBool ok;
|
||||||
{
|
{
|
||||||
AutoPreserveEnumerators preserve(cx);
|
AutoPreserveEnumerators preserve(cx);
|
||||||
|
args_.setActive(); /* From js::Invoke(InvokeArgsGuard) overload. */
|
||||||
Probes::enterJSFun(cx, fp->fun(), script_);
|
Probes::enterJSFun(cx, fp->fun(), script_);
|
||||||
#ifdef JS_METHODJIT
|
#ifdef JS_METHODJIT
|
||||||
ok = mjit::EnterMethodJIT(cx, fp, code, stackLimit_);
|
ok = mjit::EnterMethodJIT(cx, fp, code, stackLimit_);
|
||||||
@ -148,6 +149,7 @@ InvokeSessionGuard::invoke(JSContext *cx) const
|
|||||||
ok = Interpret(cx, cx->fp());
|
ok = Interpret(cx, cx->fp());
|
||||||
#endif
|
#endif
|
||||||
Probes::exitJSFun(cx, fp->fun(), script_);
|
Probes::exitJSFun(cx, fp->fun(), script_);
|
||||||
|
args_.setInactive();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Don't clobber callee with rval; rval gets read from fp->rval. */
|
/* Don't clobber callee with rval; rval gets read from fp->rval. */
|
||||||
|
@ -1181,7 +1181,7 @@ js_NewGenerator(JSContext *cx)
|
|||||||
gen->floating = genfp;
|
gen->floating = genfp;
|
||||||
|
|
||||||
/* Copy from the stack to the generator's floating frame. */
|
/* 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->stealFrameAndSlots(genvp, stackfp, stackvp, stackRegs.sp);
|
||||||
genfp->initFloatingGenerator();
|
genfp->initFloatingGenerator();
|
||||||
|
|
||||||
|
@ -214,7 +214,6 @@ js_NewGenerator(JSContext *cx);
|
|||||||
inline js::StackFrame *
|
inline js::StackFrame *
|
||||||
js_FloatingFrameIfGenerator(JSContext *cx, js::StackFrame *fp)
|
js_FloatingFrameIfGenerator(JSContext *cx, js::StackFrame *fp)
|
||||||
{
|
{
|
||||||
JS_ASSERT(cx->stack.contains(fp));
|
|
||||||
if (JS_UNLIKELY(fp->isGeneratorFrame()))
|
if (JS_UNLIKELY(fp->isGeneratorFrame()))
|
||||||
return cx->generatorFor(fp)->floatingFrame();
|
return cx->generatorFor(fp)->floatingFrame();
|
||||||
return fp;
|
return fp;
|
||||||
|
@ -1741,8 +1741,8 @@ enum MatchControlFlags {
|
|||||||
|
|
||||||
/* Factor out looping and matching logic. */
|
/* Factor out looping and matching logic. */
|
||||||
static bool
|
static bool
|
||||||
DoMatch(JSContext *cx, RegExpStatics *res, Value *vp, JSString *str, const RegExpPair &rep,
|
DoMatch(JSContext *cx, RegExpStatics *res, JSString *str, const RegExpPair &rep,
|
||||||
DoMatchCallback callback, void *data, MatchControlFlags flags)
|
DoMatchCallback callback, void *data, MatchControlFlags flags, Value *rval)
|
||||||
{
|
{
|
||||||
RegExp &re = rep.re();
|
RegExp &re = rep.re();
|
||||||
if (re.global()) {
|
if (re.global()) {
|
||||||
@ -1751,9 +1751,9 @@ DoMatch(JSContext *cx, RegExpStatics *res, Value *vp, JSString *str, const RegEx
|
|||||||
if (rep.reobj())
|
if (rep.reobj())
|
||||||
rep.reobj()->zeroRegExpLastIndex();
|
rep.reobj()->zeroRegExpLastIndex();
|
||||||
for (size_t count = 0, i = 0, length = str->length(); i <= length; ++count) {
|
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;
|
return false;
|
||||||
if (!Matched(testGlobal, *vp))
|
if (!Matched(testGlobal, *rval))
|
||||||
break;
|
break;
|
||||||
if (!callback(cx, res, count, data))
|
if (!callback(cx, res, count, data))
|
||||||
return false;
|
return false;
|
||||||
@ -1765,9 +1765,9 @@ DoMatch(JSContext *cx, RegExpStatics *res, Value *vp, JSString *str, const RegEx
|
|||||||
bool testSingle = !!(flags & TEST_SINGLE_BIT),
|
bool testSingle = !!(flags & TEST_SINGLE_BIT),
|
||||||
callbackOnSingle = !!(flags & CALLBACK_ON_SINGLE_BIT);
|
callbackOnSingle = !!(flags & CALLBACK_ON_SINGLE_BIT);
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
if (!re.execute(cx, res, str, &i, testSingle, vp))
|
if (!re.execute(cx, res, str, &i, testSingle, rval))
|
||||||
return false;
|
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 false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -1842,12 +1842,14 @@ str_match(JSContext *cx, uintN argc, Value *vp)
|
|||||||
AutoObjectRooter array(cx);
|
AutoObjectRooter array(cx);
|
||||||
MatchArgType arg = array.addr();
|
MatchArgType arg = array.addr();
|
||||||
RegExpStatics *res = cx->regExpStatics();
|
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;
|
return false;
|
||||||
|
|
||||||
/* When not global, DoMatch will leave |RegExp.exec()| in *vp. */
|
|
||||||
if (rep->re().global())
|
if (rep->re().global())
|
||||||
vp->setObjectOrNull(array.object());
|
vp->setObjectOrNull(array.object());
|
||||||
|
else
|
||||||
|
*vp = rval;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2323,7 +2325,8 @@ str_replace_regexp(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata)
|
|||||||
rdata.calledBack = false;
|
rdata.calledBack = false;
|
||||||
|
|
||||||
RegExpStatics *res = cx->regExpStatics();
|
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;
|
return false;
|
||||||
|
|
||||||
if (!rdata.calledBack) {
|
if (!rdata.calledBack) {
|
||||||
@ -2849,23 +2852,18 @@ str_concat(JSContext *cx, uintN argc, Value *vp)
|
|||||||
if (!str)
|
if (!str)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* Set vp (aka rval) early to handle the argc == 0 case. */
|
Value *argv = JS_ARGV(cx, vp);
|
||||||
vp->setString(str);
|
for (uintN i = 0; i < argc; i++) {
|
||||||
|
|
||||||
Value *argv;
|
|
||||||
uintN i;
|
|
||||||
for (i = 0, argv = vp + 2; i < argc; i++) {
|
|
||||||
JSString *str2 = js_ValueToString(cx, argv[i]);
|
JSString *str2 = js_ValueToString(cx, argv[i]);
|
||||||
if (!str2)
|
if (!str2)
|
||||||
return false;
|
return false;
|
||||||
argv[i].setString(str2);
|
|
||||||
|
|
||||||
str = js_ConcatStrings(cx, str, str2);
|
str = js_ConcatStrings(cx, str, str2);
|
||||||
if (!str)
|
if (!str)
|
||||||
return false;
|
return false;
|
||||||
vp->setString(str);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JS_SET_RVAL(cx, vp, StringValue(str));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,6 +315,7 @@ nanojit::LInsPrinter::accNames[] = {
|
|||||||
"typemap", // (1 << 25) == ACCSET_TYPEMAP
|
"typemap", // (1 << 25) == ACCSET_TYPEMAP
|
||||||
"fcslots", // (1 << 26) == ACCSET_FCSLOTS
|
"fcslots", // (1 << 26) == ACCSET_FCSLOTS
|
||||||
"argsdata", // (1 << 27) == ACCSET_ARGS_DATA
|
"argsdata", // (1 << 27) == ACCSET_ARGS_DATA
|
||||||
|
"seg", // (1 << 28) == ACCSET_SEG
|
||||||
|
|
||||||
"?!" // this entry should never be used, have it just in case
|
"?!" // this entry should never be used, have it just in case
|
||||||
};
|
};
|
||||||
@ -382,6 +383,13 @@ ValueToTypeChar(const Value &v)
|
|||||||
if (v.isMagic()) return 'M';
|
if (v.isMagic()) return 'M';
|
||||||
return '?';
|
return '?';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline uintN
|
||||||
|
FramePCOffset(JSContext *cx, js::StackFrame* fp)
|
||||||
|
{
|
||||||
|
jsbytecode *pc = fp->pcQuadratic(cx);
|
||||||
|
return uintN(pc - fp->script()->code);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static inline uintN
|
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.
|
* then we simply get the value from the interpreter state.
|
||||||
*/
|
*/
|
||||||
JS_ASSERT(upvarLevel < UpvarCookie::UPVAR_LEVEL_LIMIT);
|
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);
|
Value v = T::interp_get(fp, slot);
|
||||||
JSValueType type = getCoercedType(v);
|
JSValueType type = getCoercedType(v);
|
||||||
ValueToNative(v, type, result);
|
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 level = script->staticLevel - cookie.level();
|
||||||
uint32 cookieSlot = cookie.slot();
|
uint32 cookieSlot = cookie.slot();
|
||||||
StackFrame* fp = cx->stack.findFrameAtLevel(level);
|
StackFrame* fp = FindUpvarFrame(cx, level);
|
||||||
const CallInfo* ci;
|
const CallInfo* ci;
|
||||||
int32 slot;
|
int32 slot;
|
||||||
if (!fp->isFunctionFrame() || fp->isEvalFrame()) {
|
if (!fp->isFunctionFrame() || fp->isEvalFrame()) {
|
||||||
@ -15086,25 +15094,13 @@ TraceRecorder::record_JSOP_BINDNAME()
|
|||||||
if (!fp->isFunctionFrame()) {
|
if (!fp->isFunctionFrame()) {
|
||||||
obj = &fp->scopeChain();
|
obj = &fp->scopeChain();
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
StackFrame *fp2 = fp;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In global code, fp->scopeChain can only contain blocks whose values
|
* In global code, fp->scopeChain can only contain blocks whose values
|
||||||
* are still on the stack. We never use BINDNAME to refer to these.
|
* are still on the stack. We never use BINDNAME to refer to these.
|
||||||
*/
|
*/
|
||||||
while (obj->isBlock()) {
|
while (obj->isBlock()) {
|
||||||
// The block's values are still on the stack.
|
// The block's values are still on the stack.
|
||||||
#ifdef DEBUG
|
JS_ASSERT(obj->getPrivate() == fp);
|
||||||
// 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
|
|
||||||
obj = obj->getParent();
|
obj = obj->getParent();
|
||||||
// Blocks always have parents.
|
// Blocks always have parents.
|
||||||
JS_ASSERT(obj);
|
JS_ASSERT(obj);
|
||||||
|
@ -1736,11 +1736,9 @@ ParseXMLSource(JSContext *cx, JSString *src)
|
|||||||
|
|
||||||
LeaveTrace(cx);
|
LeaveTrace(cx);
|
||||||
xml = NULL;
|
xml = NULL;
|
||||||
FrameRegsIter i(cx);
|
|
||||||
for (; !i.done() && !i.pc(); ++i)
|
|
||||||
JS_ASSERT(!i.fp()->isScriptFrame());
|
|
||||||
filename = NULL;
|
filename = NULL;
|
||||||
lineno = 1;
|
lineno = 1;
|
||||||
|
FrameRegsIter i(cx);
|
||||||
if (!i.done()) {
|
if (!i.done()) {
|
||||||
op = (JSOp) *i.pc();
|
op = (JSOp) *i.pc();
|
||||||
if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) {
|
if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) {
|
||||||
|
@ -178,7 +178,7 @@ InlineReturn(VMFrame &f)
|
|||||||
{
|
{
|
||||||
JS_ASSERT(f.fp() != f.entryfp);
|
JS_ASSERT(f.fp() != f.entryfp);
|
||||||
JS_ASSERT(!js_IsActiveWithOrBlock(f.cx, &f.fp()->scopeChain(), 0));
|
JS_ASSERT(!js_IsActiveWithOrBlock(f.cx, &f.fp()->scopeChain(), 0));
|
||||||
f.cx->stack.popInlineFrame();
|
f.cx->stack.popInlineFrame(f.regs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JS_FASTCALL
|
void JS_FASTCALL
|
||||||
@ -195,16 +195,6 @@ stubs::SlowNew(VMFrame &f, uint32 argc)
|
|||||||
THROW();
|
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
|
* HitStackQuota is called after the early prologue pushing the new frame would
|
||||||
* overflow f.stackLimit.
|
* overflow f.stackLimit.
|
||||||
@ -218,8 +208,7 @@ stubs::HitStackQuota(VMFrame &f)
|
|||||||
if (f.cx->stack.space().tryBumpLimit(NULL, f.regs.sp, nvals, &f.stackLimit))
|
if (f.cx->stack.space().tryBumpLimit(NULL, f.regs.sp, nvals, &f.stackLimit))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Remove the current partially-constructed frame before throwing. */
|
f.cx->stack.popFrameAfterOverflow();
|
||||||
RemovePartialFrame(f.cx, f.fp());
|
|
||||||
js_ReportOverRecursed(f.cx);
|
js_ReportOverRecursed(f.cx);
|
||||||
THROW();
|
THROW();
|
||||||
}
|
}
|
||||||
@ -285,10 +274,7 @@ stubs::CompileFunction(VMFrame &f, uint32 nactual)
|
|||||||
JSFunction *fun = callee.getFunctionPrivate();
|
JSFunction *fun = callee.getFunctionPrivate();
|
||||||
JSScript *script = fun->script();
|
JSScript *script = fun->script();
|
||||||
|
|
||||||
/*
|
/* FixupArity expect to be called after the early prologue. */
|
||||||
* FixupArity/RemovePartialFrame expect to be called after the early
|
|
||||||
* prologue.
|
|
||||||
*/
|
|
||||||
fp->initJitFrameEarlyPrologue(fun, nactual);
|
fp->initJitFrameEarlyPrologue(fun, nactual);
|
||||||
|
|
||||||
if (nactual != fp->numFormalArgs()) {
|
if (nactual != fp->numFormalArgs()) {
|
||||||
@ -301,7 +287,7 @@ stubs::CompileFunction(VMFrame &f, uint32 nactual)
|
|||||||
fp->initJitFrameLatePrologue();
|
fp->initJitFrameLatePrologue();
|
||||||
|
|
||||||
/* These would have been initialized by the prologue. */
|
/* 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))
|
if (fun->isHeavyweight() && !js::CreateFunCallObject(cx, fp))
|
||||||
THROWV(NULL);
|
THROWV(NULL);
|
||||||
|
@ -2830,6 +2830,52 @@ DumpObject(JSContext *cx, uintN argc, jsval *vp)
|
|||||||
|
|
||||||
#endif /* DEBUG */
|
#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
|
#ifdef TEST_CVTARGS
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
@ -3736,9 +3782,9 @@ EvalInFrame(JSContext *cx, uintN argc, jsval *vp)
|
|||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSStackFrame *oldfp = NULL;
|
JSBool saved = JS_FALSE;;
|
||||||
if (saveCurrent)
|
if (saveCurrent)
|
||||||
oldfp = JS_SaveFrameChain(cx);
|
saved = JS_SaveFrameChain(cx);
|
||||||
|
|
||||||
size_t length;
|
size_t length;
|
||||||
const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
|
const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
|
||||||
@ -3751,8 +3797,8 @@ EvalInFrame(JSContext *cx, uintN argc, jsval *vp)
|
|||||||
fi.pc()),
|
fi.pc()),
|
||||||
vp);
|
vp);
|
||||||
|
|
||||||
if (saveCurrent)
|
if (saved)
|
||||||
JS_RestoreFrameChain(cx, oldfp);
|
JS_RestoreFrameChain(cx);
|
||||||
|
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
@ -4847,6 +4893,7 @@ static JSFunctionSpec shell_functions[] = {
|
|||||||
JS_FN("tracing", Tracing, 0,0),
|
JS_FN("tracing", Tracing, 0,0),
|
||||||
JS_FN("stats", DumpStats, 1,0),
|
JS_FN("stats", DumpStats, 1,0),
|
||||||
#endif
|
#endif
|
||||||
|
JS_FN("dumpStack", DumpStack, 1,0),
|
||||||
#ifdef TEST_CVTARGS
|
#ifdef TEST_CVTARGS
|
||||||
JS_FN("cvtargs", ConvertArgs, 0,0),
|
JS_FN("cvtargs", ConvertArgs, 0,0),
|
||||||
#endif
|
#endif
|
||||||
@ -4980,6 +5027,7 @@ static const char *const shell_help_messages[] = {
|
|||||||
" With filename, send to file.",
|
" With filename, send to file.",
|
||||||
"stats([string ...]) Dump 'arena', 'atom', 'global' stats",
|
"stats([string ...]) Dump 'arena', 'atom', 'global' stats",
|
||||||
#endif
|
#endif
|
||||||
|
"dumpStack() Dump the stack as an array of callees (youngest first)",
|
||||||
#ifdef TEST_CVTARGS
|
#ifdef TEST_CVTARGS
|
||||||
"cvtargs(arg1..., arg12) Test argument formatter",
|
"cvtargs(arg1..., arg12) Test argument formatter",
|
||||||
#endif
|
#endif
|
||||||
|
@ -376,7 +376,7 @@ void ValidateWriter::checkAccSet(LOpcode op, LIns *base, int32_t disp, AccSet ac
|
|||||||
// ins = ldp.regs base[<disp within FrameRegs>]
|
// ins = ldp.regs base[<disp within FrameRegs>]
|
||||||
ok = op == LIR_ldp &&
|
ok = op == LIR_ldp &&
|
||||||
dispWithin(FrameRegs) &&
|
dispWithin(FrameRegs) &&
|
||||||
match(base, LIR_ldp, ACCSET_CX, offsetof(JSContext, stack) + ContextStack::offsetOfRegs());
|
match(base, LIR_ldp, ACCSET_SEG, StackSegment::offsetOfRegs());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACCSET_STACKFRAME:
|
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)));
|
isConstPrivatePtr(base->oprnd1(), ArgumentsObject::DATA_SLOT)));
|
||||||
break;
|
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:
|
default:
|
||||||
// This assertion will fail if any single-region AccSets aren't covered
|
// This assertion will fail if any single-region AccSets aren't covered
|
||||||
// by the switch -- only multi-region AccSets should be handled here.
|
// 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_TYPEMAP: All typemaps form a single region.
|
||||||
* - ACCSET_FCSLOTS: All fcslots arrays 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_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_STATE = (1 << 0);
|
||||||
static const nanojit::AccSet ACCSET_STACK = (1 << 1);
|
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_TYPEMAP = (1 << 25);
|
||||||
static const nanojit::AccSet ACCSET_FCSLOTS = (1 << 26);
|
static const nanojit::AccSet ACCSET_FCSLOTS = (1 << 26);
|
||||||
static const nanojit::AccSet ACCSET_ARGS_DATA = (1 << 27);
|
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
|
* 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), \
|
name(w.ldpContextFieldHelper(cx_ins, offsetof(JSContext, fieldname), LOAD_CONST), \
|
||||||
#fieldname)
|
#fieldname)
|
||||||
nj::LIns *ldpContextRegs(nj::LIns *cx) const {
|
nj::LIns *ldpContextRegs(nj::LIns *cx) const {
|
||||||
int32 offset = offsetof(JSContext, stack) + ContextStack::offsetOfRegs();
|
int32 segOff = offsetof(JSContext, stack) + ContextStack::offsetOfSeg();
|
||||||
return name(ldpContextFieldHelper(cx, offset, nj::LOAD_NORMAL),"regs");
|
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 {
|
nj::LIns *stContextField(nj::LIns *value, nj::LIns *cx, int32 offset) const {
|
||||||
|
@ -50,215 +50,6 @@
|
|||||||
|
|
||||||
namespace js {
|
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
|
inline void
|
||||||
StackFrame::initPrev(JSContext *cx)
|
StackFrame::initPrev(JSContext *cx)
|
||||||
{
|
{
|
||||||
@ -387,101 +178,6 @@ StackFrame::initJitFrameLatePrologue()
|
|||||||
SetValueRangeToUndefined(slots(), script()->nfixed);
|
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 &
|
inline Value &
|
||||||
StackFrame::canonicalActualArg(uintN i) const
|
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
|
#ifdef JS_TRACER
|
||||||
JS_ALWAYS_INLINE bool
|
JS_ALWAYS_INLINE bool
|
||||||
StackSpace::ensureEnoughSpaceToEnterTrace()
|
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
|
JS_ALWAYS_INLINE bool
|
||||||
OOMCheck::operator()(JSContext *cx, StackSpace &space, Value *from, uintN nvals)
|
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 *dst = firstUnused;
|
||||||
Value *src = args.base();
|
Value *src = args.base();
|
||||||
PodCopy(dst, src, ncopy);
|
PodCopy(dst, src, ncopy);
|
||||||
Debug_SetValueRangeToCrashOnTouch(src, ncopy);
|
|
||||||
return reinterpret_cast<StackFrame *>(firstUnused + 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,
|
JSObject &callee, JSFunction *fun, JSScript *script,
|
||||||
MaybeConstruct construct, Check check)
|
MaybeConstruct construct, Check check)
|
||||||
{
|
{
|
||||||
JS_ASSERT(isCurrentAndActive());
|
JS_ASSERT(onTop());
|
||||||
JS_ASSERT(®s == &cx->regs());
|
JS_ASSERT(®s == &seg_->regs());
|
||||||
JS_ASSERT(regs.sp == args.end());
|
JS_ASSERT(regs.sp == args.end());
|
||||||
/* Cannot assert callee == args.callee() since this is called from LeaveTree. */
|
/* Cannot assert callee == args.callee() since this is called from LeaveTree. */
|
||||||
JS_ASSERT(callee.getFunctionPrivate() == fun);
|
JS_ASSERT(callee.getFunctionPrivate() == fun);
|
||||||
@ -853,7 +505,7 @@ ContextStack::pushInlineFrame(JSContext *cx, FrameRegs ®s, const CallArgs &ar
|
|||||||
|
|
||||||
/* Initialize frame, locals, regs. */
|
/* Initialize frame, locals, regs. */
|
||||||
fp->initCallFrame(cx, callee, fun, script, args.argc(), flags);
|
fp->initCallFrame(cx, callee, fun, script, args.argc(), flags);
|
||||||
regs.prepareToRun(fp, script);
|
regs.prepareToRun(*fp, script);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -862,7 +514,7 @@ ContextStack::getFixupFrame(JSContext *cx, FrameRegs ®s, const CallArgs &args
|
|||||||
JSFunction *fun, JSScript *script, void *ncode,
|
JSFunction *fun, JSScript *script, void *ncode,
|
||||||
MaybeConstruct construct, LimitCheck check)
|
MaybeConstruct construct, LimitCheck check)
|
||||||
{
|
{
|
||||||
JS_ASSERT(isCurrentAndActive());
|
JS_ASSERT(onTop());
|
||||||
JS_ASSERT(®s == &cx->regs());
|
JS_ASSERT(®s == &cx->regs());
|
||||||
JS_ASSERT(regs.sp == args.end());
|
JS_ASSERT(regs.sp == args.end());
|
||||||
JS_ASSERT(args.callee().getFunctionPrivate() == fun);
|
JS_ASSERT(args.callee().getFunctionPrivate() == fun);
|
||||||
@ -880,143 +532,30 @@ ContextStack::getFixupFrame(JSContext *cx, FrameRegs ®s, const CallArgs &args
|
|||||||
}
|
}
|
||||||
|
|
||||||
JS_ALWAYS_INLINE void
|
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();
|
fp->putActivationObjects();
|
||||||
|
|
||||||
Value *newsp = fp->actualArgs() - 1;
|
Value *newsp = fp->actualArgs() - 1;
|
||||||
JS_ASSERT(newsp >= fp->prev()->base());
|
JS_ASSERT(newsp >= fp->prev()->base());
|
||||||
|
|
||||||
newsp[-1] = fp->returnValue();
|
newsp[-1] = fp->returnValue();
|
||||||
regs_->popFrame(newsp);
|
regs.popFrame(newsp);
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_ALWAYS_INLINE bool
|
inline void
|
||||||
ContextStack::pushInvokeArgs(JSContext *cx, uintN argc, InvokeArgsGuard *argsGuard)
|
ContextStack::popFrameAfterOverflow()
|
||||||
{
|
{
|
||||||
if (!isCurrentAndActive())
|
/* Restore the regs to what they were on entry to JSOP_CALL. */
|
||||||
return pushInvokeArgsSlow(cx, argc, argsGuard);
|
FrameRegs ®s = seg_->regs();
|
||||||
|
StackFrame *fp = regs.fp();
|
||||||
Value *start = space().activeFirstUnused();
|
regs.popFrame(fp->actualArgsEnd());
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
namespace detail {
|
||||||
|
|
||||||
struct STATIC_SKIP_INFERENCE CopyNonHoleArgsTo
|
struct STATIC_SKIP_INFERENCE CopyNonHoleArgsTo
|
||||||
@ -1101,5 +640,4 @@ ArgumentsObject::getElements(uint32 start, uint32 count, Value *vp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
} /* namespace js */
|
} /* namespace js */
|
||||||
|
|
||||||
#endif /* Stack_inl_h__ */
|
#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 DummyFrameGuard;
|
||||||
class GeneratorFrameGuard;
|
class GeneratorFrameGuard;
|
||||||
|
|
||||||
|
class CallIter;
|
||||||
|
class FrameRegsIter;
|
||||||
|
class AllFramesIter;
|
||||||
|
|
||||||
class ArgumentsObject;
|
class ArgumentsObject;
|
||||||
|
|
||||||
namespace mjit { struct JITScript; }
|
namespace mjit { struct JITScript; }
|
||||||
@ -74,20 +78,18 @@ namespace mjit { struct JITScript; }
|
|||||||
*
|
*
|
||||||
* The per-thread stack is subdivided into contiguous segments of memory which
|
* 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
|
* 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
|
* 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:
|
* encapsulated by a set of types that describe different regions of memory.
|
||||||
* StackSegment, StackFrame, FrameRegs and CallArgs. To avoid calling into C++,
|
* This encapsulation has holes: to avoid calling into C++ from generated code,
|
||||||
* the JIT compiler generates code that simulates C++ stack operations.
|
* 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
|
* regs
|
||||||
* .------------------------------------------------------.
|
* .---------------------------------------------.
|
||||||
* | current frame |
|
* | V
|
||||||
* | .-------------------------------------. V
|
* | fp .--FrameRegs--. sp
|
||||||
* | | initial frame | FrameRegs
|
* | V V
|
||||||
* | | .------------. | |
|
|
||||||
* | | | V V V
|
|
||||||
* |StackSegment| slots |StackFrame| slots |StackFrame| slots |
|
* |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
|
* 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
|
* 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
|
* Each script activation (global or function code) is given a fixed-size header
|
||||||
* (js::StackFrame) which is associated with the values (called "slots") before
|
* (js::StackFrame) which is associated with the values (called "slots") before
|
||||||
* and after it. The frame contains bookkeeping information about the activation
|
* and after it. The frame contains bookkeeping information about the activation
|
||||||
* and links to the previous frame.
|
* and links to the previous frame.
|
||||||
*
|
*
|
||||||
* The slots preceeding a (function) StackFrame in memory are the arguments of
|
* 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
|
* the call. The slots after a StackFrame in memory are its locals followed by
|
||||||
* by its expression stack. There is no clean line between the arguments of a
|
* 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
|
* 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
|
* the expression become the arguments of a call. There are also layout
|
||||||
* invariants concerning the arguments and StackFrame; see "Arguments" comment
|
* invariants concerning the arguments and StackFrame; see "Arguments" comment
|
||||||
@ -119,24 +121,45 @@ namespace mjit { struct JITScript; }
|
|||||||
* dependence on FrameRegs outside the interpreter.
|
* dependence on FrameRegs outside the interpreter.
|
||||||
*
|
*
|
||||||
* A call to a native (C++) function does not push a frame. Instead, an array
|
* 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
|
* of values is passed to the native. The layout of this array is abstracted by
|
||||||
* passed to the native. The layout of this array is abstracted by js::CallArgs.
|
* js::CallArgs. With respect to the StackSegment layout above, the args to a
|
||||||
* Note that, between any two StackFrames there may be any number of native
|
* native call are inserted anywhere there can be slots. A sample memory layout
|
||||||
* calls, so the meaning of 'prev' is not 'directly called by'.
|
* 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
|
* An additional feature (perhaps not for much longer: bug 650361) is that
|
||||||
* multiple independent "contexts" can interleave (LIFO) on a single contiguous
|
* multiple independent "contexts" can interleave (LIFO) on a single contiguous
|
||||||
* stack. "Independent" here means that neither context sees the other's frames.
|
* stack. "Independent" here means that neither context sees the other's
|
||||||
* Concretely, an embedding may enter the JS engine on cx1 and then, from a
|
* frames. Concretely, an embedding may enter the JS engine on cx1 and then,
|
||||||
* native called by the JS engine, reenter the VM on cx2. Changing from cx1 to
|
* from a native called by the JS engine, reenter the VM on cx2. Changing from
|
||||||
* cx2 causes cx1's segment to be "suspended" and a new segment started to be
|
* cx1 to cx2 causes a new segment to be started for cx2's stack on top of
|
||||||
* started for cx2. These two segments are linked from the perspective 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
|
* 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:
|
* perspective of cx1 and cx2. Thus, each segment has two links: prevInMemory
|
||||||
* previousInMemory and previousInContext. A context's apparent stack is
|
* and prevInContext. Each independent stack is encapsulated and managed by
|
||||||
* encapsulated and managed by the js::ContextStack object stored in JSContext.
|
* the js::ContextStack object stored in JSContext. ContextStack is the primary
|
||||||
* ContextStack is the primary interface to the rest of the engine for pushing
|
* interface to the rest of the engine for pushing and popping the stack.
|
||||||
* and popping args (for js::Invoke calls) and frames.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@ -195,6 +218,7 @@ CallReceiverFromVp(Value *vp)
|
|||||||
|
|
||||||
class CallArgs : public CallReceiver
|
class CallArgs : public CallReceiver
|
||||||
{
|
{
|
||||||
|
protected:
|
||||||
uintN argc_;
|
uintN argc_;
|
||||||
public:
|
public:
|
||||||
friend CallArgs CallArgsFromVp(uintN, Value *);
|
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 {
|
enum MaybeConstruct {
|
||||||
NO_CONSTRUCT = 0, /* == false */
|
NO_CONSTRUCT = 0, /* == false */
|
||||||
CONSTRUCT = 0x80 /* == StackFrame::CONSTRUCTING, asserted below */
|
CONSTRUCT = 0x80 /* == StackFrame::CONSTRUCTING, asserted below */
|
||||||
@ -332,8 +392,8 @@ class StackFrame
|
|||||||
void initJitFrameLatePrologue();
|
void initJitFrameLatePrologue();
|
||||||
|
|
||||||
/* Used for eval. */
|
/* Used for eval. */
|
||||||
void initExecuteFrame(JSScript *script, StackFrame *prev, const Value &thisv,
|
void initExecuteFrame(JSScript *script, StackFrame *prev, FrameRegs *regs,
|
||||||
JSObject &scopeChain, ExecuteType type);
|
const Value &thisv, JSObject &scopeChain, ExecuteType type);
|
||||||
|
|
||||||
/* Used when activating generators. */
|
/* Used when activating generators. */
|
||||||
void stealFrameAndSlots(Value *vp, StackFrame *otherfp, Value *othervp, Value *othersp);
|
void stealFrameAndSlots(Value *vp, StackFrame *otherfp, Value *othervp, Value *othersp);
|
||||||
@ -387,6 +447,10 @@ class StackFrame
|
|||||||
return flags_ & EVAL;
|
return flags_ & EVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isEvalInFunction() const {
|
||||||
|
return (flags_ & (EVAL | FUNCTION)) == (EVAL | FUNCTION);
|
||||||
|
}
|
||||||
|
|
||||||
bool isNonEvalFunctionFrame() const {
|
bool isNonEvalFunctionFrame() const {
|
||||||
return (flags_ & (FUNCTION | EVAL)) == FUNCTION;
|
return (flags_ & (FUNCTION | EVAL)) == FUNCTION;
|
||||||
}
|
}
|
||||||
@ -462,7 +526,7 @@ class StackFrame
|
|||||||
* Where I stop and I turn and I go right back
|
* Where I stop and I turn and I go right back
|
||||||
* Till I get to the bottom and I see you again...
|
* Till I get to the bottom and I see you again...
|
||||||
*/
|
*/
|
||||||
jsbytecode *pcQuadratic(JSContext *cx);
|
jsbytecode *pcQuadratic(JSContext *cx) const;
|
||||||
|
|
||||||
jsbytecode *prevpc() {
|
jsbytecode *prevpc() {
|
||||||
if (flags_ & HAS_PREVPC)
|
if (flags_ & HAS_PREVPC)
|
||||||
@ -926,10 +990,6 @@ class StackFrame
|
|||||||
return !!(flags_ & DEBUGGER);
|
return !!(flags_ & DEBUGGER);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isDirectEvalOrDebuggerFrame() const {
|
|
||||||
return (flags_ & (EVAL | DEBUGGER)) && !(flags_ & GLOBAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasOverriddenArgs() const {
|
bool hasOverriddenArgs() const {
|
||||||
return !!(flags_ & OVERRIDE_ARGS);
|
return !!(flags_ & OVERRIDE_ARGS);
|
||||||
}
|
}
|
||||||
@ -1076,10 +1136,11 @@ class FrameRegs
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* For generator: */
|
/* For generator: */
|
||||||
void rebaseFromTo(const FrameRegs &from, StackFrame *to) {
|
void rebaseFromTo(const FrameRegs &from, StackFrame &to) {
|
||||||
fp_ = to;
|
fp_ = &to;
|
||||||
sp = to->slots() + (from.sp - from.fp_->slots());
|
sp = to.slots() + (from.sp - from.fp_->slots());
|
||||||
pc = from.pc;
|
pc = from.pc;
|
||||||
|
JS_ASSERT(fp_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For ContextStack: */
|
/* For ContextStack: */
|
||||||
@ -1087,40 +1148,140 @@ class FrameRegs
|
|||||||
pc = fp_->prevpc();
|
pc = fp_->prevpc();
|
||||||
sp = newsp;
|
sp = newsp;
|
||||||
fp_ = fp_->prev();
|
fp_ = fp_->prev();
|
||||||
|
JS_ASSERT(fp_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For FixupArity: */
|
/* For FixupArity: */
|
||||||
void popPartialFrame(Value *newsp) {
|
void popPartialFrame(Value *newsp) {
|
||||||
sp = newsp;
|
sp = newsp;
|
||||||
fp_ = fp_->prev();
|
fp_ = fp_->prev();
|
||||||
|
JS_ASSERT(fp_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For stubs::CompileFunction, ContextStack: */
|
/* For stubs::CompileFunction, ContextStack: */
|
||||||
void prepareToRun(StackFrame *fp, JSScript *script) {
|
void prepareToRun(StackFrame &fp, JSScript *script) {
|
||||||
pc = script->code;
|
pc = script->code;
|
||||||
sp = fp->slots() + script->nfixed;
|
sp = fp.slots() + script->nfixed;
|
||||||
fp_ = fp;
|
fp_ = &fp;
|
||||||
|
JS_ASSERT(fp_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For pushDummyFrame: */
|
/* For pushDummyFrame: */
|
||||||
void initDummyFrame(StackFrame *fp) {
|
void initDummyFrame(StackFrame &fp) {
|
||||||
pc = NULL;
|
pc = NULL;
|
||||||
sp = fp->slots();
|
sp = fp.slots();
|
||||||
fp_ = fp;
|
fp_ = &fp;
|
||||||
|
JS_ASSERT(fp_);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
struct StackOverride
|
class StackSegment
|
||||||
{
|
{
|
||||||
Value *top;
|
/* Previous segment within same context stack. */
|
||||||
#ifdef DEBUG
|
StackSegment *const prevInContext_;
|
||||||
StackSegment *seg;
|
|
||||||
StackFrame *frame;
|
/* Previous segment sequentially in memory. */
|
||||||
#endif
|
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
|
class StackSpace
|
||||||
@ -1129,7 +1290,6 @@ class StackSpace
|
|||||||
mutable Value *commitEnd_;
|
mutable Value *commitEnd_;
|
||||||
Value *end_;
|
Value *end_;
|
||||||
StackSegment *seg_;
|
StackSegment *seg_;
|
||||||
StackOverride override_;
|
|
||||||
|
|
||||||
static const size_t CAPACITY_VALS = 512 * 1024;
|
static const size_t CAPACITY_VALS = 512 * 1024;
|
||||||
static const size_t CAPACITY_BYTES = CAPACITY_VALS * sizeof(Value);
|
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;
|
JS_FRIEND_API(bool) bumpCommit(JSContext *maybecx, Value *from, ptrdiff_t nvals) const;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
friend class AllFramesIter;
|
||||||
friend class ContextStack;
|
friend class ContextStack;
|
||||||
|
friend class StackFrame;
|
||||||
friend class OOMCheck;
|
friend class OOMCheck;
|
||||||
inline bool ensureSpace(JSContext *maybecx, Value *from, ptrdiff_t nvals) const;
|
inline bool ensureSpace(JSContext *maybecx, Value *from, ptrdiff_t nvals) const;
|
||||||
void pushSegment(StackSegment &seg);
|
StackSegment &findContainingSegment(const StackFrame *target) const;
|
||||||
void popSegment();
|
|
||||||
inline void pushOverride(Value *top, StackOverride *prev);
|
|
||||||
inline void popOverride(const StackOverride &prev);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StackSpace();
|
StackSpace();
|
||||||
@ -1158,14 +1317,8 @@ class StackSpace
|
|||||||
~StackSpace();
|
~StackSpace();
|
||||||
|
|
||||||
/* See stack layout comment above. */
|
/* See stack layout comment above. */
|
||||||
StackSegment *currentSegment() const { return seg_; }
|
Value *firstUnused() const { return seg_ ? seg_->end() : base_; }
|
||||||
Value *firstUnused() const;
|
Value *endOfSpace() const { return end_; }
|
||||||
|
|
||||||
/* 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;
|
|
||||||
|
|
||||||
#ifdef JS_TRACER
|
#ifdef JS_TRACER
|
||||||
/*
|
/*
|
||||||
@ -1235,70 +1388,59 @@ class LimitCheck
|
|||||||
|
|
||||||
class ContextStack
|
class ContextStack
|
||||||
{
|
{
|
||||||
FrameRegs *regs_;
|
|
||||||
StackSegment *seg_;
|
StackSegment *seg_;
|
||||||
StackSpace *space_;
|
StackSpace *space_;
|
||||||
JSContext *cx_;
|
JSContext *cx_;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is the collecting-point for code that wants to know when there is
|
* Return whether this ContextStack is at the top of the contiguous stack.
|
||||||
* no JS active. Note that "no JS active" does not mean the stack is empty
|
* This is a precondition for extending the current segment by pushing
|
||||||
* because of JS_(Save|Restore)FrameChain. If code really wants to know
|
* stack frames or overrides etc.
|
||||||
* when the stack is empty, test |cx->stack.empty()|.
|
*
|
||||||
|
* 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();
|
bool onTop() const;
|
||||||
|
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
void assertSegmentsInSync() const;
|
|
||||||
void assertSpaceInSync() const;
|
void assertSpaceInSync() const;
|
||||||
#else
|
#else
|
||||||
void assertSegmentsInSync() const {}
|
|
||||||
void assertSpaceInSync() const {}
|
void assertSpaceInSync() const {}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
friend class FrameGuard;
|
/* Implementation details of push* public interface. */
|
||||||
StackFrame *getSegmentAndFrame(JSContext *cx, uintN vplen, uintN nslots,
|
StackSegment *pushSegment(JSContext *cx);
|
||||||
FrameGuard *frameGuard) const;
|
enum MaybeExtend { CAN_EXTEND = true, CANT_EXTEND = false };
|
||||||
void pushSegmentAndFrame(FrameGuard *frameGuard);
|
Value *ensureOnTop(JSContext *cx, uintN nvars, MaybeExtend extend, bool *pushedSeg);
|
||||||
void pushSegmentAndFrameImpl(FrameRegs ®s, StackSegment &seg);
|
|
||||||
void popSegmentAndFrame();
|
|
||||||
void popSegmentAndFrameImpl();
|
|
||||||
|
|
||||||
friend class GeneratorFrameGuard;
|
|
||||||
void popGeneratorFrame(GeneratorFrameGuard *gfg);
|
|
||||||
|
|
||||||
|
/* Check = { OOMCheck, LimitCheck } */
|
||||||
template <class Check>
|
template <class Check>
|
||||||
inline StackFrame *getCallFrame(JSContext *cx, const CallArgs &args,
|
inline StackFrame *
|
||||||
JSFunction *fun, JSScript *script,
|
getCallFrame(JSContext *cx, const CallArgs &args, JSFunction *fun, JSScript *script,
|
||||||
StackFrame::Flags *pflags,
|
StackFrame::Flags *pflags, Check check) const;
|
||||||
Check check) const;
|
|
||||||
|
|
||||||
|
/* Make pop* functions private since only called by guard classes. */
|
||||||
|
void popSegment();
|
||||||
friend class InvokeArgsGuard;
|
friend class InvokeArgsGuard;
|
||||||
bool pushInvokeArgsSlow(JSContext *cx, uintN argc, InvokeArgsGuard *argsGuard);
|
void popInvokeArgs(const InvokeArgsGuard &iag);
|
||||||
void popInvokeArgsSlow(const InvokeArgsGuard &argsGuard);
|
friend class FrameGuard;
|
||||||
inline void popInvokeArgs(const InvokeArgsGuard &argsGuard);
|
void popFrame(const FrameGuard &fg);
|
||||||
|
friend class GeneratorFrameGuard;
|
||||||
|
void popGeneratorFrame(const GeneratorFrameGuard &gfg);
|
||||||
|
|
||||||
friend class InvokeFrameGuard;
|
friend class StackIter;
|
||||||
void pushInvokeFrameSlow(InvokeFrameGuard *frameGuard);
|
|
||||||
void popInvokeFrameSlow(const InvokeFrameGuard &frameGuard);
|
|
||||||
inline void popInvokeFrame(const InvokeFrameGuard &frameGuard);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ContextStack(JSContext *cx);
|
ContextStack(JSContext *cx);
|
||||||
~ContextStack();
|
~ContextStack();
|
||||||
|
|
||||||
|
/*** Stack accessors ***/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A context's stack is "empty" if there are no scripts or natives
|
* A context's stack is "empty" if there are no scripts or natives
|
||||||
* executing. Note that JS_SaveFrameChain does factor into this definition.
|
* 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
|
* 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
|
* and dummy frames are frames that do not represent script execution hence
|
||||||
* this query has little semantic meaning past "you can call fp()".
|
* 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. */
|
/* Faster alternatives to maybe* functions. */
|
||||||
FrameRegs *maybeRegs() const { return regs_; }
|
FrameRegs ®s() const { JS_ASSERT(hasfp()); return seg_->regs(); }
|
||||||
StackFrame *fp() const { return regs_->fp(); }
|
StackFrame *fp() const { JS_ASSERT(hasfp()); return seg_->fp(); }
|
||||||
StackFrame *maybefp() const { return regs_ ? regs_->fp() : NULL; }
|
|
||||||
|
|
||||||
/* The StackSpace currently hosting this ContextStack. */
|
/* The StackSpace currently hosting this ContextStack. */
|
||||||
StackSpace &space() const { assertSpaceInSync(); return *space_; }
|
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. */
|
/* Return whether the given frame is in this context's stack. */
|
||||||
bool contains(const StackFrame *fp) const;
|
bool containsSlow(const StackFrame *target) const;
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Mark the top segment as suspended, without pushing a new one. */
|
/*** Stack manipulation ***/
|
||||||
void saveActiveSegment();
|
|
||||||
|
|
||||||
/* Undoes calls to suspendActiveSegment. */
|
|
||||||
void restoreSegment();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* pushInvokeArgs allocates |argc + 2| rooted values that will be passed as
|
* 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);
|
bool pushInvokeArgs(JSContext *cx, uintN argc, InvokeArgsGuard *ag);
|
||||||
|
|
||||||
/* Called by Invoke for a scripted function call. */
|
/* Called by Invoke for a scripted function call. */
|
||||||
bool pushInvokeFrame(JSContext *cx, const CallArgs &args, MaybeConstruct,
|
bool pushInvokeFrame(JSContext *cx, const CallArgs &args,
|
||||||
JSObject &callee, JSFunction *fun, JSScript *script,
|
MaybeConstruct construct, InvokeFrameGuard *ifg);
|
||||||
InvokeFrameGuard *ifg);
|
|
||||||
|
|
||||||
/* Called by Execute for execution of eval or global code. */
|
/* Called by Execute for execution of eval or global code. */
|
||||||
bool pushExecuteFrame(JSContext *cx, JSScript *script, const Value &thisv,
|
bool pushExecuteFrame(JSContext *cx, JSScript *script, const Value &thisv,
|
||||||
JSObject &scopeChain, ExecuteType type,
|
JSObject &scopeChain, ExecuteType type,
|
||||||
StackFrame *evalInFrame, ExecuteFrameGuard *efg);
|
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);
|
bool pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg);
|
||||||
|
|
||||||
/* Pushes a "dummy" frame; should be removed one day. */
|
/* Pushes a "dummy" frame; should be removed one day. */
|
||||||
@ -1388,7 +1506,10 @@ class ContextStack
|
|||||||
bool pushInlineFrame(JSContext *cx, FrameRegs ®s, const CallArgs &args,
|
bool pushInlineFrame(JSContext *cx, FrameRegs ®s, const CallArgs &args,
|
||||||
JSObject &callee, JSFunction *fun, JSScript *script,
|
JSObject &callee, JSFunction *fun, JSScript *script,
|
||||||
MaybeConstruct construct, Check check);
|
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
|
* Called by the methodjit for an arity mismatch. Arity mismatch can be
|
||||||
@ -1402,54 +1523,65 @@ class ContextStack
|
|||||||
JSFunction *fun, JSScript *script, void *ncode,
|
JSFunction *fun, JSScript *script, void *ncode,
|
||||||
MaybeConstruct construct, LimitCheck check);
|
MaybeConstruct construct, LimitCheck check);
|
||||||
|
|
||||||
/* For jit use: */
|
bool saveFrameChain();
|
||||||
static size_t offsetOfRegs() { return offsetof(ContextStack, regs_); }
|
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;
|
friend class ContextStack;
|
||||||
ContextStack *stack_; /* null implies nothing pushed */
|
ContextStack *stack_;
|
||||||
StackSegment *seg_; /* null implies no segment pushed */
|
bool pushedSeg_;
|
||||||
StackOverride prevOverride_;
|
void setPushed(ContextStack &stack) { JS_ASSERT(!pushed()); stack_ = &stack; }
|
||||||
public:
|
public:
|
||||||
InvokeArgsGuard() : stack_(NULL), seg_(NULL) {}
|
InvokeArgsGuard() : CallArgsList(), stack_(NULL), pushedSeg_(false) {}
|
||||||
~InvokeArgsGuard();
|
~InvokeArgsGuard() { if (pushed()) stack_->popInvokeArgs(*this); }
|
||||||
bool pushed() const { return stack_ != NULL; }
|
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
|
class FrameGuard
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
friend class ContextStack;
|
friend class ContextStack;
|
||||||
ContextStack *stack_; /* null implies nothing pushed */
|
ContextStack *stack_;
|
||||||
StackSegment *seg_;
|
bool pushedSeg_;
|
||||||
FrameRegs regs_;
|
FrameRegs regs_;
|
||||||
|
FrameRegs *prevRegs_;
|
||||||
|
void setPushed(ContextStack &stack) { stack_ = &stack; }
|
||||||
public:
|
public:
|
||||||
FrameGuard() : stack_(NULL) {}
|
FrameGuard() : stack_(NULL), pushedSeg_(false) {}
|
||||||
~FrameGuard();
|
~FrameGuard() { if (pushed()) stack_->popFrame(*this); }
|
||||||
bool pushed() const { return stack_ != NULL; }
|
bool pushed() const { return !!stack_; }
|
||||||
|
void pop() { stack_->popFrame(*this); stack_ = NULL; }
|
||||||
|
|
||||||
StackFrame *fp() const { return regs_.fp(); }
|
StackFrame *fp() const { return regs_.fp(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class InvokeFrameGuard : public FrameGuard
|
||||||
|
{};
|
||||||
|
|
||||||
class ExecuteFrameGuard : public FrameGuard
|
class ExecuteFrameGuard : public FrameGuard
|
||||||
{};
|
{};
|
||||||
|
|
||||||
@ -1462,59 +1594,114 @@ class GeneratorFrameGuard : public FrameGuard
|
|||||||
JSGenerator *gen_;
|
JSGenerator *gen_;
|
||||||
Value *stackvp_;
|
Value *stackvp_;
|
||||||
public:
|
public:
|
||||||
~GeneratorFrameGuard();
|
~GeneratorFrameGuard() { if (pushed()) stack_->popGeneratorFrame(*this); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* While |cx->fp|'s pc/sp are available in |cx->regs|, to compute the saved
|
* Iterate through the callstack of the given context. Each element of said
|
||||||
* value of pc/sp for any other frame, it is necessary to know about that
|
* callstack can either be the execution of a script (scripted function call,
|
||||||
* frame's next-frame. This iterator maintains this information when walking
|
* global code, eval code, debugger code) or the invocation of a (C++) native.
|
||||||
* a chain of stack frames starting at |cx->fp|.
|
* Example usage:
|
||||||
*
|
*
|
||||||
* Usage:
|
* for (Stackiter i(cx); !i.done(); ++i) {
|
||||||
* for (FrameRegsIter i(cx); !i.done(); ++i)
|
* if (i.isScript()) {
|
||||||
* ... i.fp() ... i.sp() ... i.pc()
|
* ... 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_;
|
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_;
|
StackFrame *fp_;
|
||||||
|
CallArgsList *calls_;
|
||||||
|
|
||||||
|
StackSegment *seg_;
|
||||||
Value *sp_;
|
Value *sp_;
|
||||||
jsbytecode *pc_;
|
jsbytecode *pc_;
|
||||||
|
CallArgs args_;
|
||||||
|
|
||||||
void initSlow();
|
void poisonRegs();
|
||||||
void incSlow(StackFrame *oldfp);
|
void popFrame();
|
||||||
|
void popCall();
|
||||||
|
void settleOnNewSegment();
|
||||||
|
void settleOnNewState();
|
||||||
|
void startOnSegment(StackSegment *seg);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FrameRegsIter(JSContext *cx);
|
StackIter(JSContext *cx, SavedOption = STOP_AT_SAVED);
|
||||||
|
|
||||||
bool done() const { return fp_ == NULL; }
|
bool done() const { return state_ == DONE; }
|
||||||
FrameRegsIter &operator++();
|
StackIter &operator++();
|
||||||
bool operator==(const FrameRegsIter &rhs) const;
|
|
||||||
bool operator!=(const FrameRegsIter &rhs) const { return !(*this == rhs); }
|
|
||||||
|
|
||||||
StackFrame *fp() const { return fp_; }
|
bool operator==(const StackIter &rhs) const;
|
||||||
Value *sp() const { return sp_; }
|
bool operator!=(const StackIter &rhs) const { return !(*this == rhs); }
|
||||||
jsbytecode *pc() const { return pc_; }
|
|
||||||
|
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
|
class AllFramesIter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AllFramesIter(JSContext *cx);
|
AllFramesIter(StackSpace &space);
|
||||||
|
|
||||||
bool done() const { return fp_ == NULL; }
|
bool done() const { return fp_ == NULL; }
|
||||||
AllFramesIter& operator++();
|
AllFramesIter& operator++();
|
||||||
|
|
||||||
StackFrame *fp() const { return fp_; }
|
StackFrame *fp() const { return fp_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
StackSegment *seg_;
|
StackSegment *seg_;
|
||||||
StackFrame *fp_;
|
StackFrame *fp_;
|
||||||
};
|
};
|
||||||
|
@ -259,9 +259,9 @@ class JSCLContextHelper
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
JSCLContextHelper(mozJSComponentLoader* loader);
|
JSCLContextHelper(mozJSComponentLoader* loader);
|
||||||
~JSCLContextHelper() { Pop(); }
|
~JSCLContextHelper();
|
||||||
|
|
||||||
JSContext* Pop();
|
void reportErrorAfterPop(char *buf);
|
||||||
|
|
||||||
operator JSContext*() const {return mContext;}
|
operator JSContext*() const {return mContext;}
|
||||||
|
|
||||||
@ -269,6 +269,7 @@ private:
|
|||||||
JSContext* mContext;
|
JSContext* mContext;
|
||||||
intN mContextThread;
|
intN mContextThread;
|
||||||
nsIThreadJSContextStack* mContextStack;
|
nsIThreadJSContextStack* mContextStack;
|
||||||
|
char* mBuf;
|
||||||
|
|
||||||
// prevent copying and assignment
|
// prevent copying and assignment
|
||||||
JSCLContextHelper(const JSCLContextHelper &); // not implemented
|
JSCLContextHelper(const JSCLContextHelper &); // not implemented
|
||||||
@ -291,22 +292,6 @@ private:
|
|||||||
const JSCLAutoErrorReporterSetter& operator=(const JSCLAutoErrorReporterSetter &); // not implemented
|
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
|
static nsresult
|
||||||
ReportOnCaller(nsAXPCNativeCallContext *cc,
|
ReportOnCaller(nsAXPCNativeCallContext *cc,
|
||||||
const char *format, ...) {
|
const char *format, ...) {
|
||||||
@ -322,7 +307,15 @@ ReportOnCaller(nsAXPCNativeCallContext *cc,
|
|||||||
rv = cc->GetJSContext(&callerContext);
|
rv = cc->GetJSContext(&callerContext);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
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
|
static nsresult
|
||||||
@ -332,12 +325,14 @@ ReportOnCaller(JSCLContextHelper &helper,
|
|||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, format);
|
va_start(ap, format);
|
||||||
|
|
||||||
JSContext *cx = helper.Pop();
|
char *buf = JS_vsmprintf(format, ap);
|
||||||
if (!cx) {
|
if (!buf) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
return OutputError(cx, format, ap);
|
helper.reportErrorAfterPop(buf);
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static nsresult
|
static nsresult
|
||||||
@ -1639,7 +1634,8 @@ mozJSComponentLoader::ModuleEntry::GetFactory(const mozilla::Module& module,
|
|||||||
|
|
||||||
JSCLContextHelper::JSCLContextHelper(mozJSComponentLoader *loader)
|
JSCLContextHelper::JSCLContextHelper(mozJSComponentLoader *loader)
|
||||||
: mContext(loader->mContext), mContextThread(0),
|
: mContext(loader->mContext), mContextThread(0),
|
||||||
mContextStack(loader->mContextStack)
|
mContextStack(loader->mContextStack),
|
||||||
|
mBuf(nsnull)
|
||||||
{
|
{
|
||||||
mContextStack->Push(mContext);
|
mContextStack->Push(mContext);
|
||||||
mContextThread = JS_GetContextThread(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
|
JSCLContextHelper::~JSCLContextHelper()
|
||||||
// the top of the stack.
|
|
||||||
JSContext*
|
|
||||||
JSCLContextHelper::Pop()
|
|
||||||
{
|
{
|
||||||
JSContext* cx = nsnull;
|
|
||||||
if (mContextStack) {
|
if (mContextStack) {
|
||||||
if (mContextThread) {
|
if (mContextThread) {
|
||||||
JS_EndRequest(mContext);
|
JS_EndRequest(mContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
mContextStack->Pop(nsnull);
|
mContextStack->Pop(nsnull);
|
||||||
|
|
||||||
|
JSContext* cx = nsnull;
|
||||||
mContextStack->Peek(&cx);
|
mContextStack->Peek(&cx);
|
||||||
|
|
||||||
mContextStack = nsnull;
|
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;
|
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); */
|
/* void initClasses (in JSContextPtr aJSContext, in JSObjectPtr aGlobalJSObj); */
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsXPConnect::InitClasses(JSContext * aJSContext, JSObject * aGlobalJSObj)
|
nsXPConnect::InitClasses(JSContext * aJSContext, JSObject * aGlobalJSObj)
|
||||||
|
@ -3559,14 +3559,13 @@ private:
|
|||||||
struct XPCJSContextInfo {
|
struct XPCJSContextInfo {
|
||||||
XPCJSContextInfo(JSContext* aCx) :
|
XPCJSContextInfo(JSContext* aCx) :
|
||||||
cx(aCx),
|
cx(aCx),
|
||||||
frame(nsnull),
|
savedFrameChain(false),
|
||||||
suspendDepth(0)
|
suspendDepth(0)
|
||||||
{}
|
{}
|
||||||
JSContext* cx;
|
JSContext* cx;
|
||||||
|
|
||||||
// Frame to be restored when this JSContext becomes the topmost
|
// Whether the frame chain was saved
|
||||||
// one.
|
bool savedFrameChain;
|
||||||
JSStackFrame* frame;
|
|
||||||
|
|
||||||
// Greater than 0 if a request was suspended.
|
// Greater than 0 if a request was suspended.
|
||||||
jsrefcount suspendDepth;
|
jsrefcount suspendDepth;
|
||||||
|
@ -93,9 +93,6 @@ XPCJSContextStack::Pop(JSContext * *_retval)
|
|||||||
NS_ASSERTION(!mStack.IsEmpty(), "ThreadJSContextStack underflow");
|
NS_ASSERTION(!mStack.IsEmpty(), "ThreadJSContextStack underflow");
|
||||||
|
|
||||||
PRUint32 idx = mStack.Length() - 1; // The thing we're popping
|
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)
|
if(_retval)
|
||||||
*_retval = mStack[idx].cx;
|
*_retval = mStack[idx].cx;
|
||||||
@ -106,7 +103,6 @@ XPCJSContextStack::Pop(JSContext * *_retval)
|
|||||||
--idx; // Advance to new top of the stack
|
--idx; // Advance to new top of the stack
|
||||||
|
|
||||||
XPCJSContextInfo & e = mStack[idx];
|
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!");
|
NS_ASSERTION(!e.suspendDepth || e.cx, "Shouldn't have suspendDepth without a cx!");
|
||||||
if(e.cx)
|
if(e.cx)
|
||||||
{
|
{
|
||||||
@ -116,12 +112,12 @@ XPCJSContextStack::Pop(JSContext * *_retval)
|
|||||||
e.suspendDepth = 0;
|
e.suspendDepth = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(e.frame)
|
if(e.savedFrameChain)
|
||||||
{
|
{
|
||||||
// Pop() can be called outside any request for e.cx.
|
// Pop() can be called outside any request for e.cx.
|
||||||
JSAutoRequest ar(e.cx);
|
JSAutoRequest ar(e.cx);
|
||||||
JS_RestoreFrameChain(e.cx, e.frame);
|
JS_RestoreFrameChain(e.cx);
|
||||||
e.frame = nsnull;
|
e.savedFrameChain = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -146,11 +142,9 @@ NS_IMETHODIMP
|
|||||||
XPCJSContextStack::Push(JSContext * cx)
|
XPCJSContextStack::Push(JSContext * cx)
|
||||||
{
|
{
|
||||||
JS_ASSERT_IF(cx, JS_GetContextThread(cx));
|
JS_ASSERT_IF(cx, JS_GetContextThread(cx));
|
||||||
if(!mStack.AppendElement(cx))
|
if(mStack.Length() > 0)
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
|
||||||
if(mStack.Length() > 1)
|
|
||||||
{
|
{
|
||||||
XPCJSContextInfo & e = mStack[mStack.Length() - 2];
|
XPCJSContextInfo & e = mStack[mStack.Length() - 1];
|
||||||
if(e.cx)
|
if(e.cx)
|
||||||
{
|
{
|
||||||
if(e.cx == cx)
|
if(e.cx == cx)
|
||||||
@ -158,16 +152,14 @@ XPCJSContextStack::Push(JSContext * cx)
|
|||||||
nsIScriptSecurityManager* ssm = XPCWrapper::GetSecurityManager();
|
nsIScriptSecurityManager* ssm = XPCWrapper::GetSecurityManager();
|
||||||
if(ssm)
|
if(ssm)
|
||||||
{
|
{
|
||||||
nsIPrincipal* globalObjectPrincipal =
|
if(nsIPrincipal* globalObjectPrincipal = GetPrincipalFromCx(cx))
|
||||||
GetPrincipalFromCx(cx);
|
|
||||||
if(globalObjectPrincipal)
|
|
||||||
{
|
{
|
||||||
nsIPrincipal* subjectPrincipal = ssm->GetCxSubjectPrincipal(cx);
|
nsIPrincipal* subjectPrincipal = ssm->GetCxSubjectPrincipal(cx);
|
||||||
PRBool equals = PR_FALSE;
|
PRBool equals = PR_FALSE;
|
||||||
globalObjectPrincipal->Equals(subjectPrincipal, &equals);
|
globalObjectPrincipal->Equals(subjectPrincipal, &equals);
|
||||||
if(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.
|
// Push() can be called outside any request for e.cx.
|
||||||
JSAutoRequest ar(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)
|
if(!cx)
|
||||||
e.suspendDepth = JS_SuspendRequest(e.cx);
|
e.suspendDepth = JS_SuspendRequest(e.cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
append:
|
||||||
|
if(!mStack.AppendElement(cx))
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user