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:
Luke Wagner 2011-05-13 08:56:26 -07:00
Родитель 749dfad05a
Коммит 2afe24d969
31 изменённых файлов: 1552 добавлений и 1234 удалений

Просмотреть файл

@ -342,9 +342,10 @@ nsXBLProtoImplAnonymousMethod::Execute(nsIContent* aBoundElement)
// anything else. We just report it. Note that we need to set aside the
// frame chain here, since the constructor invocation is not related to
// whatever is on the stack right now, really.
JSStackFrame* frame = JS_SaveFrameChain(cx);
::JS_ReportPendingException(cx);
JS_RestoreFrameChain(cx, frame);
JSBool saved = JS_SaveFrameChain(cx);
JS_ReportPendingException(cx);
if (saved)
JS_RestoreFrameChain(cx);
return NS_ERROR_FAILURE;
}

Просмотреть файл

@ -3520,9 +3520,10 @@ nsJSContext::ReportPendingException()
// set aside the frame chain, since it has nothing to do with the
// exception we're reporting.
if (mIsInitialized && ::JS_IsExceptionPending(mContext)) {
JSStackFrame* frame = JS_SaveFrameChain(mContext);
PRBool saved = ::JS_SaveFrameChain(mContext);
::JS_ReportPendingException(mContext);
JS_RestoreFrameChain(mContext, frame);
if (saved)
::JS_RestoreFrameChain(mContext);
}
}

Просмотреть файл

@ -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]);
}

Просмотреть файл

@ -0,0 +1,52 @@
// |jit-test| mjitalways;debug
function stackToString(stack) {
var str = "|";
for (var i = 0; i < stack.length; ++i) {
if (typeof stack[i] === "string")
str += stack[i];
else
str += stack[i].name;
str += "|";
}
return str;
}
function assertStackIs(s1) {
var s2 = dumpStack();
var me = s2.shift();
assertEq(me, assertStackIs);
try {
if (s1.length != s2.length)
throw "Length " + s1.length + " not equal " + s2.length;
for (var i = 0; i < s1.length; ++i) {
var match;
if (typeof s1[i] === "string" && (m = s1[i].match(/bound\((.*)\)/))) {
if (s2[i].name != m[1] || s2[i].toSource().indexOf("[native code]") < 0)
throw "Element " + i + " not bound function";
} else if (s1[i] != s2[i]) {
throw "Element " + i + " not equal";
}
}
}
catch (e) {
print("Given = " + stackToString(s1));
print("dumpStack = " + stackToString(s2));
throw e;
}
}
/*********************************************/
(function f() { evalInFrame(0, "assertStackIs(['eval-code', evalInFrame, f, 'global-code'])"); })();
(function f() { (function g() { evalInFrame(1, "assertStackIs(['eval-code', f, 'global-code'])"); })() })();
(function f() { (function g() { evalInFrame(1, "assertStackIs(['eval-code', f, 'bound(f)', 'global-code'])"); })() }).bind()();
(function f() { evalInFrame(0, "assertStackIs(['eval-code', evalInFrame, f, 'global-code'])", true); })();
(function f() { (function g() { evalInFrame(1, "assertStackIs(['eval-code', f, 'global-code'])", true); })() })();
(function f() { (function g() { evalInFrame(1, "assertStackIs(['eval-code', f, 'bound(f)', 'global-code'])", true); })() }).bind()();
(function f() { (function g() { evalInFrame(1, "assertStackIs(['eval-code', f, 'bound(f)', 'global-code'])", true); }).bind()() }).bind()();
(function f() { (function g() { evalInFrame(1, "assertStackIs(['eval-code', f, 'bound(f)', 'global-code'])", true); }).bind().bind()() }).bind()();
(function f() { var o = { toString:function() { evalInFrame(1, "assertStackIs(['eval-code', f, 'global-code'])"); }}; [o,o].sort() })();
(function f() { var o = { toString:function() { evalInFrame(1, "assertStackIs(['eval-code', f, 'global-code'])", true); }}; [o,o].sort() })();

Просмотреть файл

@ -5201,26 +5201,20 @@ JS_IsRunning(JSContext *cx)
return fp != NULL;
}
JS_PUBLIC_API(JSStackFrame *)
JS_PUBLIC_API(JSBool)
JS_SaveFrameChain(JSContext *cx)
{
CHECK_REQUEST(cx);
StackFrame *fp = js_GetTopStackFrame(cx);
if (!fp)
return NULL;
cx->stack.saveActiveSegment();
return Jsvalify(fp);
LeaveTrace(cx);
return cx->stack.saveFrameChain();
}
JS_PUBLIC_API(void)
JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp)
JS_RestoreFrameChain(JSContext *cx)
{
CHECK_REQUEST(cx);
JS_ASSERT_NOT_ON_TRACE(cx);
JS_ASSERT(!cx->hasfp());
if (!fp)
return;
cx->stack.restoreSegment();
cx->stack.restoreFrameChain();
}
/************************************************************************/

Просмотреть файл

@ -2943,15 +2943,13 @@ JS_IsRunning(JSContext *cx);
* must be balanced and all nested calls to JS_SaveFrameChain must have had
* matching JS_RestoreFrameChain calls.
*
* JS_SaveFrameChain deals with cx not having any code running on it. A null
* return does not signify an error, and JS_RestoreFrameChain handles a null
* frame pointer argument safely.
* JS_SaveFrameChain deals with cx not having any code running on it.
*/
extern JS_PUBLIC_API(JSStackFrame *)
extern JS_PUBLIC_API(JSBool)
JS_SaveFrameChain(JSContext *cx);
extern JS_PUBLIC_API(void)
JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp);
JS_RestoreFrameChain(JSContext *cx);
/************************************************************************/

Просмотреть файл

@ -1540,7 +1540,8 @@ JSContext::wrapPendingException()
JSGenerator *
JSContext::generatorFor(StackFrame *fp) const
{
JS_ASSERT(stack.contains(fp) && fp->isGeneratorFrame());
JS_ASSERT(stack.containsSlow(fp));
JS_ASSERT(fp->isGeneratorFrame());
JS_ASSERT(!fp->isFloatingGenerator());
JS_ASSERT(!genStack.empty());

Просмотреть файл

@ -1186,10 +1186,11 @@ struct JSContext
* default version.
*/
void maybeMigrateVersionOverride() {
if (JS_LIKELY(!isVersionOverridden() && stack.empty()))
return;
defaultVersion = versionOverride;
clearVersionOverride();
JS_ASSERT(stack.empty());
if (JS_UNLIKELY(isVersionOverridden())) {
defaultVersion = versionOverride;
clearVersionOverride();
}
}
/*

Просмотреть файл

@ -170,9 +170,9 @@ CompartmentHasLiveScripts(JSCompartment *comp)
if (JS_GetContextThread(icx) != currentThreadId)
continue;
#endif
for (AllFramesIter i(icx); !i.done(); ++i) {
JSScript *script = i.fp()->maybeScript();
if (script && script->compartment == comp)
for (FrameRegsIter i(icx); !i.done(); ++i) {
JSScript *script = i.fp()->script();
if (script->compartment == comp)
return JS_TRUE;
}
}
@ -1491,7 +1491,7 @@ JS_PUBLIC_API(JSObject *)
JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fpArg)
{
StackFrame *fp = Valueify(fpArg);
JS_ASSERT(cx->stack.contains(fp));
JS_ASSERT(cx->stack.containsSlow(fp));
js::AutoCompartment ac(cx, &fp->scopeChain());
if (!ac.enter())
@ -1506,7 +1506,7 @@ JS_PUBLIC_API(JSObject *)
JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fpArg)
{
StackFrame *fp = Valueify(fpArg);
JS_ASSERT(cx->stack.contains(fp));
JS_ASSERT(cx->stack.containsSlow(fp));
if (!fp->isFunctionFrame())
return NULL;

Просмотреть файл

@ -356,8 +356,7 @@ InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message,
elem->filename = NULL;
if (fp->isScriptFrame()) {
elem->filename = fp->script()->filename;
if (fp->isScriptFrame())
elem->ulineno = js_FramePCToLineNumber(cx, fp, iter.pc());
elem->ulineno = js_FramePCToLineNumber(cx, fp, iter.pc());
}
++elem;
}

Просмотреть файл

@ -247,7 +247,7 @@ js_GetArgsObject(JSContext *cx, StackFrame *fp)
*/
JS_ASSERT_IF(fp->fun()->isHeavyweight(), fp->hasCallObj());
while (fp->isDirectEvalOrDebuggerFrame())
while (fp->isEvalInFunction())
fp = fp->prev();
/* Create an arguments object for fp only if it lacks one. */
@ -1588,12 +1588,9 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
JSFunction *fun = obj->getFunctionPrivate();
/* Find fun's top-most activation record. */
StackFrame *fp;
for (fp = js_GetTopStackFrame(cx);
fp && (fp->maybeFun() != fun || fp->isDirectEvalOrDebuggerFrame());
fp = fp->prev()) {
continue;
}
StackFrame *fp = js_GetTopStackFrame(cx);
while (fp && (fp->maybeFun() != fun || fp->isEvalInFunction()))
fp = fp->prev();
switch (slot) {
case FUN_ARGUMENTS:
@ -3016,9 +3013,6 @@ js_ReportIsNotFunction(JSContext *cx, const Value *vp, uintN flags)
ptrdiff_t spindex = 0;
FrameRegsIter i(cx);
while (!i.done() && !i.pc())
++i;
if (!i.done()) {
uintN depth = js_ReconstructStackDepth(cx, i.fp()->script(), i.pc());
Value *simsp = i.fp()->base() + depth;

Просмотреть файл

@ -217,6 +217,11 @@ struct JSFunction : public JSObject_Slots2
return isInterpreted() ? NULL : u.n.native;
}
js::Native native() const {
JS_ASSERT(isNative());
return u.n.native;
}
JSScript *script() const {
JS_ASSERT(isInterpreted());
return u.i.script;
@ -300,6 +305,19 @@ IsFunctionObject(const js::Value &v, JSFunction **fun)
return b;
}
static JS_ALWAYS_INLINE bool
IsNativeFunction(const js::Value &v)
{
JSFunction *fun;
return IsFunctionObject(v, &fun) && fun->isNative();
}
static JS_ALWAYS_INLINE bool
IsNativeFunction(const js::Value &v, JSFunction **fun)
{
return IsFunctionObject(v, fun) && (*fun)->isNative();
}
extern JS_ALWAYS_INLINE bool
SameTraceType(const Value &lhs, const Value &rhs)
{

Просмотреть файл

@ -671,7 +671,7 @@ Invoke(JSContext *cx, const CallArgs &argsRef, MaybeConstruct construct)
/* Get pointer to new frame/slots, prepare arguments. */
InvokeFrameGuard ifg;
if (!cx->stack.pushInvokeFrame(cx, args, construct, callee, fun, script, &ifg))
if (!cx->stack.pushInvokeFrame(cx, args, construct, &ifg))
return false;
/* Now that the new frame is rooted, maybe create a call object. */
@ -731,7 +731,7 @@ InvokeSessionGuard::start(JSContext *cx, const Value &calleev, const Value &this
break;
/* Push the stack frame once for the session. */
if (!stack.pushInvokeFrame(cx, args_, NO_CONSTRUCT, callee, fun, script_, &ifg_))
if (!stack.pushInvokeFrame(cx, args_, NO_CONSTRUCT, &ifg_))
return false;
StackFrame *fp = ifg_.fp();
@ -858,7 +858,7 @@ InitSharpSlots(JSContext *cx, StackFrame *fp)
Value *sharps = &fp->slots()[script->nfixed - SHARP_NSLOTS];
if (!fp->isGlobalFrame() && prev->script()->hasSharps) {
JS_ASSERT(prev->numFixed() >= SHARP_NSLOTS);
int base = (prev->isFunctionFrame() && !prev->isDirectEvalOrDebuggerFrame())
int base = prev->isNonEvalFunctionFrame()
? prev->fun()->script()->bindings.sharpSlotBase(cx)
: prev->numFixed() - SHARP_NSLOTS;
if (base < 0)
@ -1408,7 +1408,7 @@ js::GetUpvar(JSContext *cx, uintN closureLevel, UpvarCookie cookie)
const uintN targetLevel = closureLevel - cookie.level();
JS_ASSERT(targetLevel < UpvarCookie::UPVAR_LEVEL_LIMIT);
StackFrame *fp = cx->stack.findFrameAtLevel(targetLevel);
StackFrame *fp = FindUpvarFrame(cx, targetLevel);
uintN slot = cookie.slot();
const Value *vp;
@ -1428,6 +1428,19 @@ js::GetUpvar(JSContext *cx, uintN closureLevel, UpvarCookie cookie)
return vp[slot];
}
extern StackFrame *
js::FindUpvarFrame(JSContext *cx, uintN targetLevel)
{
StackFrame *fp = cx->fp();
while (true) {
JS_ASSERT(fp && fp->isScriptFrame());
if (fp->script()->staticLevel == targetLevel)
break;
fp = fp->prev();
}
return fp;
}
#ifdef DEBUG
JS_STATIC_INTERPRET JS_REQUIRES_STACK void
@ -2810,7 +2823,7 @@ BEGIN_CASE(JSOP_STOP)
#ifdef JS_METHODJIT
jit_return:
#endif
cx->stack.popInlineFrame();
cx->stack.popInlineFrame(regs);
/* Sync interpreter locals. */
script = regs.fp()->script();

Просмотреть файл

@ -152,9 +152,25 @@ ComputeThis(JSContext *cx, StackFrame *fp);
* and the range [args.getvp() + 2, args.getvp() + 2 + args.getArgc()) should
* be initialized actual arguments.
*/
extern JS_REQUIRES_STACK bool
extern bool
Invoke(JSContext *cx, const CallArgs &args, MaybeConstruct construct = NO_CONSTRUCT);
/*
* For calls to natives, the InvokeArgsGuard object provides a record of the
* call for the debugger's callstack. For this to work, the InvokeArgsGuard
* record needs to know when the call is actually active (because the
* InvokeArgsGuard can be pushed long before and popped long after the actual
* call, during which time many stack-observing things can happen).
*/
inline bool
Invoke(JSContext *cx, InvokeArgsGuard &args, MaybeConstruct construct = NO_CONSTRUCT)
{
args.setActive();
bool ok = Invoke(cx, ImplicitCast<CallArgs>(args), construct);
args.setInactive();
return ok;
}
/*
* Natives like sort/forEach/replace call Invoke repeatedly with the same
* callee, this, and number of arguments. To optimize this, such natives can
@ -274,8 +290,12 @@ ValueToId(JSContext *cx, const Value &v, jsid *idp);
* closure level.
* @return The value of the upvar.
*/
extern const js::Value &
GetUpvar(JSContext *cx, uintN level, js::UpvarCookie cookie);
extern const Value &
GetUpvar(JSContext *cx, uintN level, UpvarCookie cookie);
/* Search the call stack for the nearest frame with static level targetLevel. */
extern StackFrame *
FindUpvarFrame(JSContext *cx, uintN targetLevel);
} /* namespace js */

Просмотреть файл

@ -89,7 +89,7 @@ class InvokeSessionGuard
~InvokeSessionGuard() {}
bool start(JSContext *cx, const Value &callee, const Value &thisv, uintN argc);
bool invoke(JSContext *cx) const;
bool invoke(JSContext *cx);
bool started() const {
return args_.pushed();
@ -113,7 +113,7 @@ class InvokeSessionGuard
};
inline bool
InvokeSessionGuard::invoke(JSContext *cx) const
InvokeSessionGuard::invoke(JSContext *cx)
{
/* N.B. Must be kept in sync with Invoke */
@ -139,6 +139,7 @@ InvokeSessionGuard::invoke(JSContext *cx) const
JSBool ok;
{
AutoPreserveEnumerators preserve(cx);
args_.setActive(); /* From js::Invoke(InvokeArgsGuard) overload. */
Probes::enterJSFun(cx, fp->fun(), script_);
#ifdef JS_METHODJIT
ok = mjit::EnterMethodJIT(cx, fp, code, stackLimit_);
@ -148,6 +149,7 @@ InvokeSessionGuard::invoke(JSContext *cx) const
ok = Interpret(cx, cx->fp());
#endif
Probes::exitJSFun(cx, fp->fun(), script_);
args_.setInactive();
}
/* Don't clobber callee with rval; rval gets read from fp->rval. */

Просмотреть файл

@ -1181,7 +1181,7 @@ js_NewGenerator(JSContext *cx)
gen->floating = genfp;
/* Copy from the stack to the generator's floating frame. */
gen->regs.rebaseFromTo(stackRegs, genfp);
gen->regs.rebaseFromTo(stackRegs, *genfp);
genfp->stealFrameAndSlots(genvp, stackfp, stackvp, stackRegs.sp);
genfp->initFloatingGenerator();

Просмотреть файл

@ -214,7 +214,6 @@ js_NewGenerator(JSContext *cx);
inline js::StackFrame *
js_FloatingFrameIfGenerator(JSContext *cx, js::StackFrame *fp)
{
JS_ASSERT(cx->stack.contains(fp));
if (JS_UNLIKELY(fp->isGeneratorFrame()))
return cx->generatorFor(fp)->floatingFrame();
return fp;

Просмотреть файл

@ -1741,8 +1741,8 @@ enum MatchControlFlags {
/* Factor out looping and matching logic. */
static bool
DoMatch(JSContext *cx, RegExpStatics *res, Value *vp, JSString *str, const RegExpPair &rep,
DoMatchCallback callback, void *data, MatchControlFlags flags)
DoMatch(JSContext *cx, RegExpStatics *res, JSString *str, const RegExpPair &rep,
DoMatchCallback callback, void *data, MatchControlFlags flags, Value *rval)
{
RegExp &re = rep.re();
if (re.global()) {
@ -1751,9 +1751,9 @@ DoMatch(JSContext *cx, RegExpStatics *res, Value *vp, JSString *str, const RegEx
if (rep.reobj())
rep.reobj()->zeroRegExpLastIndex();
for (size_t count = 0, i = 0, length = str->length(); i <= length; ++count) {
if (!re.execute(cx, res, str, &i, testGlobal, vp))
if (!re.execute(cx, res, str, &i, testGlobal, rval))
return false;
if (!Matched(testGlobal, *vp))
if (!Matched(testGlobal, *rval))
break;
if (!callback(cx, res, count, data))
return false;
@ -1765,9 +1765,9 @@ DoMatch(JSContext *cx, RegExpStatics *res, Value *vp, JSString *str, const RegEx
bool testSingle = !!(flags & TEST_SINGLE_BIT),
callbackOnSingle = !!(flags & CALLBACK_ON_SINGLE_BIT);
size_t i = 0;
if (!re.execute(cx, res, str, &i, testSingle, vp))
if (!re.execute(cx, res, str, &i, testSingle, rval))
return false;
if (callbackOnSingle && Matched(testSingle, *vp) && !callback(cx, res, 0, data))
if (callbackOnSingle && Matched(testSingle, *rval) && !callback(cx, res, 0, data))
return false;
}
return true;
@ -1842,12 +1842,14 @@ str_match(JSContext *cx, uintN argc, Value *vp)
AutoObjectRooter array(cx);
MatchArgType arg = array.addr();
RegExpStatics *res = cx->regExpStatics();
if (!DoMatch(cx, res, vp, str, *rep, MatchCallback, arg, MATCH_ARGS))
Value rval;
if (!DoMatch(cx, res, str, *rep, MatchCallback, arg, MATCH_ARGS, &rval))
return false;
/* When not global, DoMatch will leave |RegExp.exec()| in *vp. */
if (rep->re().global())
vp->setObjectOrNull(array.object());
else
*vp = rval;
return true;
}
@ -2323,7 +2325,8 @@ str_replace_regexp(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata)
rdata.calledBack = false;
RegExpStatics *res = cx->regExpStatics();
if (!DoMatch(cx, res, vp, rdata.str, *rep, ReplaceRegExpCallback, &rdata, REPLACE_ARGS))
Value tmp;
if (!DoMatch(cx, res, rdata.str, *rep, ReplaceRegExpCallback, &rdata, REPLACE_ARGS, &tmp))
return false;
if (!rdata.calledBack) {
@ -2849,23 +2852,18 @@ str_concat(JSContext *cx, uintN argc, Value *vp)
if (!str)
return false;
/* Set vp (aka rval) early to handle the argc == 0 case. */
vp->setString(str);
Value *argv;
uintN i;
for (i = 0, argv = vp + 2; i < argc; i++) {
Value *argv = JS_ARGV(cx, vp);
for (uintN i = 0; i < argc; i++) {
JSString *str2 = js_ValueToString(cx, argv[i]);
if (!str2)
return false;
argv[i].setString(str2);
str = js_ConcatStrings(cx, str, str2);
if (!str)
return false;
vp->setString(str);
}
JS_SET_RVAL(cx, vp, StringValue(str));
return true;
}

Просмотреть файл

@ -315,6 +315,7 @@ nanojit::LInsPrinter::accNames[] = {
"typemap", // (1 << 25) == ACCSET_TYPEMAP
"fcslots", // (1 << 26) == ACCSET_FCSLOTS
"argsdata", // (1 << 27) == ACCSET_ARGS_DATA
"seg", // (1 << 28) == ACCSET_SEG
"?!" // this entry should never be used, have it just in case
};
@ -382,6 +383,13 @@ ValueToTypeChar(const Value &v)
if (v.isMagic()) return 'M';
return '?';
}
static inline uintN
FramePCOffset(JSContext *cx, js::StackFrame* fp)
{
jsbytecode *pc = fp->pcQuadratic(cx);
return uintN(pc - fp->script()->code);
}
#endif
static inline uintN
@ -3309,7 +3317,7 @@ GetUpvarOnTrace(JSContext* cx, uint32 upvarLevel, int32 slot, uint32 callDepth,
* then we simply get the value from the interpreter state.
*/
JS_ASSERT(upvarLevel < UpvarCookie::UPVAR_LEVEL_LIMIT);
StackFrame* fp = cx->stack.findFrameAtLevel(upvarLevel);
StackFrame* fp = FindUpvarFrame(cx, upvarLevel);
Value v = T::interp_get(fp, slot);
JSValueType type = getCoercedType(v);
ValueToNative(v, type, result);
@ -13448,7 +13456,7 @@ TraceRecorder::upvar(JSScript* script, JSUpvarArray* uva, uintN index, Value& v)
*/
uint32 level = script->staticLevel - cookie.level();
uint32 cookieSlot = cookie.slot();
StackFrame* fp = cx->stack.findFrameAtLevel(level);
StackFrame* fp = FindUpvarFrame(cx, level);
const CallInfo* ci;
int32 slot;
if (!fp->isFunctionFrame() || fp->isEvalFrame()) {
@ -15086,25 +15094,13 @@ TraceRecorder::record_JSOP_BINDNAME()
if (!fp->isFunctionFrame()) {
obj = &fp->scopeChain();
#ifdef DEBUG
StackFrame *fp2 = fp;
#endif
/*
* In global code, fp->scopeChain can only contain blocks whose values
* are still on the stack. We never use BINDNAME to refer to these.
*/
while (obj->isBlock()) {
// The block's values are still on the stack.
#ifdef DEBUG
// NB: fp2 can't be a generator frame, because !fp->hasFunction.
while (obj->getPrivate() != fp2) {
JS_ASSERT(fp2->isDirectEvalOrDebuggerFrame());
fp2 = fp2->prev();
if (!fp2)
JS_NOT_REACHED("bad stack frame");
}
#endif
JS_ASSERT(obj->getPrivate() == fp);
obj = obj->getParent();
// Blocks always have parents.
JS_ASSERT(obj);

Просмотреть файл

@ -1736,11 +1736,9 @@ ParseXMLSource(JSContext *cx, JSString *src)
LeaveTrace(cx);
xml = NULL;
FrameRegsIter i(cx);
for (; !i.done() && !i.pc(); ++i)
JS_ASSERT(!i.fp()->isScriptFrame());
filename = NULL;
lineno = 1;
FrameRegsIter i(cx);
if (!i.done()) {
op = (JSOp) *i.pc();
if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) {

Просмотреть файл

@ -178,7 +178,7 @@ InlineReturn(VMFrame &f)
{
JS_ASSERT(f.fp() != f.entryfp);
JS_ASSERT(!js_IsActiveWithOrBlock(f.cx, &f.fp()->scopeChain(), 0));
f.cx->stack.popInlineFrame();
f.cx->stack.popInlineFrame(f.regs);
}
void JS_FASTCALL
@ -195,16 +195,6 @@ stubs::SlowNew(VMFrame &f, uint32 argc)
THROW();
}
/*
* This function must only be called after the early prologue, since it depends
* on fp->exec.fun.
*/
static inline void
RemovePartialFrame(JSContext *cx, StackFrame *fp)
{
cx->stack.popInlineFrame();
}
/*
* HitStackQuota is called after the early prologue pushing the new frame would
* overflow f.stackLimit.
@ -218,8 +208,7 @@ stubs::HitStackQuota(VMFrame &f)
if (f.cx->stack.space().tryBumpLimit(NULL, f.regs.sp, nvals, &f.stackLimit))
return;
/* Remove the current partially-constructed frame before throwing. */
RemovePartialFrame(f.cx, f.fp());
f.cx->stack.popFrameAfterOverflow();
js_ReportOverRecursed(f.cx);
THROW();
}
@ -285,10 +274,7 @@ stubs::CompileFunction(VMFrame &f, uint32 nactual)
JSFunction *fun = callee.getFunctionPrivate();
JSScript *script = fun->script();
/*
* FixupArity/RemovePartialFrame expect to be called after the early
* prologue.
*/
/* FixupArity expect to be called after the early prologue. */
fp->initJitFrameEarlyPrologue(fun, nactual);
if (nactual != fp->numFormalArgs()) {
@ -301,7 +287,7 @@ stubs::CompileFunction(VMFrame &f, uint32 nactual)
fp->initJitFrameLatePrologue();
/* These would have been initialized by the prologue. */
f.regs.prepareToRun(fp, script);
f.regs.prepareToRun(*fp, script);
if (fun->isHeavyweight() && !js::CreateFunCallObject(cx, fp))
THROWV(NULL);

Просмотреть файл

@ -2830,6 +2830,52 @@ DumpObject(JSContext *cx, uintN argc, jsval *vp)
#endif /* DEBUG */
/*
* This shell function is temporary (used by testStackIter.js) and should be
* removed once JSD2 lands wholly subsumes the functionality here.
*/
JSBool
DumpStack(JSContext *cx, uintN argc, Value *vp)
{
JSObject *arr = JS_NewArrayObject(cx, 0, NULL);
if (!arr)
return false;
JSString *evalStr = JS_NewStringCopyZ(cx, "eval-code");
if (!evalStr)
return false;
JSString *globalStr = JS_NewStringCopyZ(cx, "global-code");
if (!globalStr)
return false;
StackIter iter(cx);
JS_ASSERT(iter.nativeArgs().callee().getFunctionPrivate()->native() == DumpStack);
++iter;
jsint index = 0;
for (; !iter.done(); ++index, ++iter) {
Value v;
if (iter.isScript()) {
if (iter.fp()->isNonEvalFunctionFrame()) {
if (!iter.fp()->getValidCalleeObject(cx, &v))
return false;
} else if (iter.fp()->isEvalFrame()) {
v = StringValue(evalStr);
} else {
v = StringValue(globalStr);
}
} else {
v = iter.nativeArgs().calleev();
}
if (!JS_SetElement(cx, arr, index, Jsvalify(&v)))
return false;
}
JS_SET_RVAL(cx, vp, ObjectValue(*arr));
return true;
}
#ifdef TEST_CVTARGS
#include <ctype.h>
@ -3736,9 +3782,9 @@ EvalInFrame(JSContext *cx, uintN argc, jsval *vp)
return JS_FALSE;
}
JSStackFrame *oldfp = NULL;
JSBool saved = JS_FALSE;;
if (saveCurrent)
oldfp = JS_SaveFrameChain(cx);
saved = JS_SaveFrameChain(cx);
size_t length;
const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
@ -3751,8 +3797,8 @@ EvalInFrame(JSContext *cx, uintN argc, jsval *vp)
fi.pc()),
vp);
if (saveCurrent)
JS_RestoreFrameChain(cx, oldfp);
if (saved)
JS_RestoreFrameChain(cx);
return ok;
}
@ -4847,6 +4893,7 @@ static JSFunctionSpec shell_functions[] = {
JS_FN("tracing", Tracing, 0,0),
JS_FN("stats", DumpStats, 1,0),
#endif
JS_FN("dumpStack", DumpStack, 1,0),
#ifdef TEST_CVTARGS
JS_FN("cvtargs", ConvertArgs, 0,0),
#endif
@ -4980,6 +5027,7 @@ static const char *const shell_help_messages[] = {
" With filename, send to file.",
"stats([string ...]) Dump 'arena', 'atom', 'global' stats",
#endif
"dumpStack() Dump the stack as an array of callees (youngest first)",
#ifdef TEST_CVTARGS
"cvtargs(arg1..., arg12) Test argument formatter",
#endif

Просмотреть файл

@ -376,7 +376,7 @@ void ValidateWriter::checkAccSet(LOpcode op, LIns *base, int32_t disp, AccSet ac
// ins = ldp.regs base[<disp within FrameRegs>]
ok = op == LIR_ldp &&
dispWithin(FrameRegs) &&
match(base, LIR_ldp, ACCSET_CX, offsetof(JSContext, stack) + ContextStack::offsetOfRegs());
match(base, LIR_ldp, ACCSET_SEG, StackSegment::offsetOfRegs());
break;
case ACCSET_STACKFRAME:
@ -552,6 +552,12 @@ void ValidateWriter::checkAccSet(LOpcode op, LIns *base, int32_t disp, AccSet ac
isConstPrivatePtr(base->oprnd1(), ArgumentsObject::DATA_SLOT)));
break;
case ACCSET_SEG:
// Match the ACCSET_SEG load that comes out of ldpContextRegs
ok = dispWithin(StackSegment) &&
match(base, LIR_ldp, ACCSET_CX, offsetof(JSContext, stack) + ContextStack::offsetOfSeg());
break;
default:
// This assertion will fail if any single-region AccSets aren't covered
// by the switch -- only multi-region AccSets should be handled here.

Просмотреть файл

@ -126,6 +126,7 @@ enum LC_TMBits {
* - ACCSET_TYPEMAP: All typemaps form a single region.
* - ACCSET_FCSLOTS: All fcslots arrays form a single region.
* - ACCSET_ARGS_DATA: All Arguments data arrays form a single region.
* - ACCSET_SEG: All StackSegment structs.
*/
static const nanojit::AccSet ACCSET_STATE = (1 << 0);
static const nanojit::AccSet ACCSET_STACK = (1 << 1);
@ -158,8 +159,9 @@ static const nanojit::AccSet ACCSET_STRING_MCHARS = (1 << 24);
static const nanojit::AccSet ACCSET_TYPEMAP = (1 << 25);
static const nanojit::AccSet ACCSET_FCSLOTS = (1 << 26);
static const nanojit::AccSet ACCSET_ARGS_DATA = (1 << 27);
static const nanojit::AccSet ACCSET_SEG = (1 << 28);
static const uint8_t TM_NUM_USED_ACCS = 28; // number of access regions used by TraceMonkey
static const uint8_t TM_NUM_USED_ACCS = 29; // number of access regions used by TraceMonkey
/*
* An Address describes everything about a loaded/stored memory location. One
@ -427,8 +429,11 @@ class Writer
name(w.ldpContextFieldHelper(cx_ins, offsetof(JSContext, fieldname), LOAD_CONST), \
#fieldname)
nj::LIns *ldpContextRegs(nj::LIns *cx) const {
int32 offset = offsetof(JSContext, stack) + ContextStack::offsetOfRegs();
return name(ldpContextFieldHelper(cx, offset, nj::LOAD_NORMAL),"regs");
int32 segOff = offsetof(JSContext, stack) + ContextStack::offsetOfSeg();
nj::LIns *seg = ldpContextFieldHelper(cx, segOff, nj::LOAD_CONST);
int32 regsOff = StackSegment::offsetOfRegs();
return name(lir->insLoad(nj::LIR_ldp, seg, regsOff, ACCSET_SEG, nj::LOAD_CONST), "cx->regs()");
}
nj::LIns *stContextField(nj::LIns *value, nj::LIns *cx, int32 offset) const {

Просмотреть файл

@ -50,215 +50,6 @@
namespace js {
/*****************************************************************************/
/* See VM stack layout comment in Stack.h. */
class StackSegment
{
/* The context to which this segment belongs. */
ContextStack *stack_;
/* Link for JSContext segment stack mentioned in big comment above. */
StackSegment *previousInContext_;
/* Link for StackSpace segment stack mentioned in StackSpace comment. */
StackSegment *previousInMemory_;
/* The first frame executed in this segment. null iff cx is null */
StackFrame *initialFrame_;
/* If this segment is suspended, |cx->regs| when it was suspended. */
FrameRegs *suspendedRegs_;
/* Whether this segment was suspended by JS_SaveFrameChain. */
bool saved_;
/*
* To make isActive a single null-ness check, this non-null constant is
* assigned to suspendedRegs when empty.
*/
#define NON_NULL_SUSPENDED_REGS ((FrameRegs *)0x1)
public:
StackSegment()
: stack_(NULL), previousInContext_(NULL), previousInMemory_(NULL),
initialFrame_(NULL), suspendedRegs_(NON_NULL_SUSPENDED_REGS),
saved_(false)
{
JS_ASSERT(empty());
}
/* Safe casts guaranteed by the contiguous-stack layout. */
Value *valueRangeBegin() const {
return (Value *)(this + 1);
}
/*
* The set of fields provided by a segment depend on its state. In addition
* to the "active" and "suspended" states described in Stack.h, segments
* have a third state: empty. An empty segment contains no frames and is
* pushed for the purpose of preparing the args to Invoke. Invoke args
* requires special handling because anything can happen between pushing
* Invoke args and calling Invoke. Since an empty segment contains no
* frames, it cannot become the "current segment" of a ContextStack (for
* various arcane and hopefully temporary reasons). Thus, an empty segment
* is pushed onto the StackSpace but only pushed onto a ContextStack when it
* gets its first frame pushed from js::Invoke.
*
* Finally, (to support JS_SaveFrameChain/JS_RestoreFrameChain) a suspended
* segment may or may not be "saved". Normally, when the active segment is
* popped, the previous segment (which is necessarily suspended) becomes
* active. If the previous segment was saved, however, then it stays
* suspended until it is made active by a call to JS_RestoreFrameChain. This
* is why a context may have a current segment, but not an active segment.
* Hopefully, this feature will be removed.
*/
bool empty() const {
JS_ASSERT(!!stack_ == !!initialFrame_);
JS_ASSERT_IF(!stack_, suspendedRegs_ == NON_NULL_SUSPENDED_REGS && !saved_);
return !stack_;
}
bool isActive() const {
JS_ASSERT_IF(!suspendedRegs_, stack_ && !saved_);
JS_ASSERT_IF(!stack_, suspendedRegs_ == NON_NULL_SUSPENDED_REGS);
return !suspendedRegs_;
}
bool isSuspended() const {
JS_ASSERT_IF(!stack_ || !suspendedRegs_, !saved_);
JS_ASSERT_IF(!stack_, suspendedRegs_ == NON_NULL_SUSPENDED_REGS);
return stack_ && suspendedRegs_;
}
/* Substate of suspended, queryable in any state. */
bool isSaved() const {
JS_ASSERT_IF(saved_, isSuspended());
return saved_;
}
/* Transitioning between empty <--> isActive */
void joinContext(ContextStack &stack, StackFrame &frame) {
JS_ASSERT(empty());
stack_ = &stack;
initialFrame_ = &frame;
suspendedRegs_ = NULL;
JS_ASSERT(isActive());
}
void leaveContext() {
JS_ASSERT(isActive());
stack_ = NULL;
initialFrame_ = NULL;
suspendedRegs_ = NON_NULL_SUSPENDED_REGS;
JS_ASSERT(empty());
}
ContextStack &stack() const {
JS_ASSERT(!empty());
return *stack_;
}
ContextStack *maybeStack() const {
return stack_;
}
#undef NON_NULL_SUSPENDED_REGS
/* Transitioning between isActive <--> isSuspended */
void suspend(FrameRegs &regs) {
JS_ASSERT(isActive());
JS_ASSERT(contains(regs.fp()));
suspendedRegs_ = &regs;
JS_ASSERT(isSuspended());
}
void resume() {
JS_ASSERT(isSuspended());
suspendedRegs_ = NULL;
JS_ASSERT(isActive());
}
/* When isSuspended, transitioning isSaved <--> !isSaved */
void save(FrameRegs &regs) {
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 &currentRegs() const {
JS_ASSERT(!empty());
return isActive() ? stack_->regs() : suspendedRegs();
}
StackFrame *currentFrame() const {
return currentRegs().fp();
}
StackFrame *currentFrameOrNull() const {
return empty() ? NULL : currentFrame();
}
/* Data available when isSuspended. */
FrameRegs &suspendedRegs() const {
JS_ASSERT(isSuspended());
return *suspendedRegs_;
}
StackFrame *suspendedFrame() const {
return suspendedRegs_->fp();
}
/* JSContext / js::StackSpace bookkeeping. */
void setPreviousInContext(StackSegment *seg) {
previousInContext_ = seg;
}
StackSegment *previousInContext() const {
return previousInContext_;
}
void setPreviousInMemory(StackSegment *seg) {
previousInMemory_ = seg;
}
StackSegment *previousInMemory() const {
return previousInMemory_;
}
bool contains(const StackFrame *fp) const;
StackFrame *computeNextFrame(StackFrame *fp) const;
};
static const size_t VALUES_PER_STACK_SEGMENT = sizeof(StackSegment) / sizeof(Value);
JS_STATIC_ASSERT(sizeof(StackSegment) % sizeof(Value) == 0);
/*****************************************************************************/
inline void
StackFrame::initPrev(JSContext *cx)
{
@ -387,101 +178,6 @@ StackFrame::initJitFrameLatePrologue()
SetValueRangeToUndefined(slots(), script()->nfixed);
}
inline void
StackFrame::initExecuteFrame(JSScript *script, StackFrame *prev, const Value &thisv,
JSObject &scopeChain, ExecuteType type)
{
/*
* See encoding of ExecuteType. When GLOBAL isn't set, we are executing a
* script in the context of another frame and the frame type is determined
* by the context.
*/
flags_ = type | HAS_SCOPECHAIN;
if (!(flags_ & GLOBAL))
flags_ |= (prev->flags_ & (FUNCTION | GLOBAL));
Value *dstvp = (Value *)this - 2;
dstvp[1] = thisv;
if (isFunctionFrame()) {
dstvp[0] = prev->calleev();
exec = prev->exec;
args.script = script;
} else {
JS_ASSERT(isGlobalFrame());
dstvp[0] = NullValue();
exec.script = script;
#ifdef DEBUG
args.script = (JSScript *)0xbad;
#endif
}
scopeChain_ = &scopeChain;
prev_ = prev;
#ifdef DEBUG
ncode_ = (void *)0xbad;
Debug_SetValueRangeToCrashOnTouch(&rval_, 1);
prevpc_ = (jsbytecode *)0xbad;
hookData_ = (void *)0xbad;
annotation_ = (void *)0xbad;
#endif
if (flags_ & HAS_ANNOTATION)
annotation_ = prev->annotation_;
}
inline void
StackFrame::initDummyFrame(JSContext *cx, JSObject &chain)
{
PodZero(this);
flags_ = DUMMY | HAS_PREVPC | HAS_SCOPECHAIN;
initPrev(cx);
chain.isGlobal();
setScopeChainNoCallObj(chain);
}
inline void
StackFrame::stealFrameAndSlots(Value *vp, StackFrame *otherfp,
Value *othervp, Value *othersp)
{
JS_ASSERT(vp == (Value *)this - ((Value *)otherfp - othervp));
JS_ASSERT(othervp == otherfp->actualArgs() - 2);
JS_ASSERT(othersp >= otherfp->slots());
JS_ASSERT(othersp <= otherfp->base() + otherfp->numSlots());
PodCopy(vp, othervp, othersp - othervp);
JS_ASSERT(vp == this->actualArgs() - 2);
/* Catch bad-touching of non-canonical args (e.g., generator_trace). */
if (otherfp->hasOverflowArgs())
Debug_SetValueRangeToCrashOnTouch(othervp, othervp + 2 + otherfp->numFormalArgs());
/*
* Repoint Call, Arguments, Block and With objects to the new live frame.
* Call and Arguments are done directly because we have pointers to them.
* Block and With objects are done indirectly through 'liveFrame'. See
* js_LiveFrameToFloating comment in jsiter.h.
*/
if (hasCallObj()) {
JSObject &obj = callObj();
obj.setPrivate(this);
otherfp->flags_ &= ~HAS_CALL_OBJ;
if (js_IsNamedLambda(fun())) {
JSObject *env = obj.getParent();
JS_ASSERT(env->getClass() == &js_DeclEnvClass);
env->setPrivate(this);
}
}
if (hasArgsObj()) {
ArgumentsObject &argsobj = argsObj();
if (argsobj.isNormalArguments())
argsobj.setPrivate(this);
else
JS_ASSERT(!argsobj.getPrivate());
otherfp->flags_ &= ~HAS_ARGS_OBJ;
}
}
inline Value &
StackFrame::canonicalActualArg(uintN i) const
{
@ -672,42 +368,6 @@ StackFrame::markActivationObjectsAsPut()
/*****************************************************************************/
JS_ALWAYS_INLINE void
StackSpace::pushOverride(Value *top, StackOverride *prev)
{
*prev = override_;
override_.top = top;
#ifdef DEBUG
override_.seg = seg_;
override_.frame = seg_->currentFrameOrNull();
#endif
JS_ASSERT(prev->top < override_.top);
}
JS_ALWAYS_INLINE void
StackSpace::popOverride(const StackOverride &prev)
{
JS_ASSERT(prev.top < override_.top);
JS_ASSERT_IF(seg_->empty(), override_.frame == NULL);
JS_ASSERT_IF(!seg_->empty(), override_.frame == seg_->currentFrame());
JS_ASSERT(override_.seg == seg_);
override_ = prev;
}
JS_ALWAYS_INLINE Value *
StackSpace::activeFirstUnused() const
{
JS_ASSERT(seg_->isActive());
Value *max = Max(seg_->stack().regs().sp, override_.top);
JS_ASSERT(max == firstUnused());
return max;
}
#ifdef JS_TRACER
JS_ALWAYS_INLINE bool
StackSpace::ensureEnoughSpaceToEnterTrace()
@ -764,13 +424,6 @@ StackSpace::getStackLimit(JSContext *cx)
/*****************************************************************************/
JS_ALWAYS_INLINE bool
ContextStack::isCurrentAndActive() const
{
assertSegmentsInSync();
return seg_ && seg_->isActive() && seg_ == space().currentSegment();
}
JS_ALWAYS_INLINE bool
OOMCheck::operator()(JSContext *cx, StackSpace &space, Value *from, uintN nvals)
{
@ -829,7 +482,6 @@ ContextStack::getCallFrame(JSContext *cx, const CallArgs &args,
Value *dst = firstUnused;
Value *src = args.base();
PodCopy(dst, src, ncopy);
Debug_SetValueRangeToCrashOnTouch(src, ncopy);
return reinterpret_cast<StackFrame *>(firstUnused + ncopy);
}
@ -839,8 +491,8 @@ ContextStack::pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &ar
JSObject &callee, JSFunction *fun, JSScript *script,
MaybeConstruct construct, Check check)
{
JS_ASSERT(isCurrentAndActive());
JS_ASSERT(&regs == &cx->regs());
JS_ASSERT(onTop());
JS_ASSERT(&regs == &seg_->regs());
JS_ASSERT(regs.sp == args.end());
/* Cannot assert callee == args.callee() since this is called from LeaveTree. */
JS_ASSERT(callee.getFunctionPrivate() == fun);
@ -853,7 +505,7 @@ ContextStack::pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &ar
/* Initialize frame, locals, regs. */
fp->initCallFrame(cx, callee, fun, script, args.argc(), flags);
regs.prepareToRun(fp, script);
regs.prepareToRun(*fp, script);
return true;
}
@ -862,7 +514,7 @@ ContextStack::getFixupFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args
JSFunction *fun, JSScript *script, void *ncode,
MaybeConstruct construct, LimitCheck check)
{
JS_ASSERT(isCurrentAndActive());
JS_ASSERT(onTop());
JS_ASSERT(&regs == &cx->regs());
JS_ASSERT(regs.sp == args.end());
JS_ASSERT(args.callee().getFunctionPrivate() == fun);
@ -880,143 +532,30 @@ ContextStack::getFixupFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args
}
JS_ALWAYS_INLINE void
ContextStack::popInlineFrame()
ContextStack::popInlineFrame(FrameRegs &regs)
{
JS_ASSERT(isCurrentAndActive());
JS_ASSERT(onTop());
JS_ASSERT(&regs == &seg_->regs());
StackFrame *fp = regs_->fp();
StackFrame *fp = regs.fp();
fp->putActivationObjects();
Value *newsp = fp->actualArgs() - 1;
JS_ASSERT(newsp >= fp->prev()->base());
newsp[-1] = fp->returnValue();
regs_->popFrame(newsp);
regs.popFrame(newsp);
}
JS_ALWAYS_INLINE bool
ContextStack::pushInvokeArgs(JSContext *cx, uintN argc, InvokeArgsGuard *argsGuard)
inline void
ContextStack::popFrameAfterOverflow()
{
if (!isCurrentAndActive())
return pushInvokeArgsSlow(cx, argc, argsGuard);
Value *start = space().activeFirstUnused();
uintN vplen = 2 + argc;
if (!space().ensureSpace(cx, start, vplen))
return false;
Value *vp = start;
ImplicitCast<CallArgs>(*argsGuard) = CallArgsFromVp(argc, vp);
/*
* Use stack override to root vp until the frame is pushed. Don't need to
* MakeRangeGCSafe: the VM stack is conservatively marked.
*/
space().pushOverride(vp + vplen, &argsGuard->prevOverride_);
argsGuard->stack_ = this;
return true;
/* Restore the regs to what they were on entry to JSOP_CALL. */
FrameRegs &regs = seg_->regs();
StackFrame *fp = regs.fp();
regs.popFrame(fp->actualArgsEnd());
}
JS_ALWAYS_INLINE void
ContextStack::popInvokeArgs(const InvokeArgsGuard &argsGuard)
{
if (argsGuard.seg_) {
popInvokeArgsSlow(argsGuard);
return;
}
JS_ASSERT(isCurrentAndActive());
space().popOverride(argsGuard.prevOverride_);
}
JS_ALWAYS_INLINE
InvokeArgsGuard::~InvokeArgsGuard()
{
if (JS_UNLIKELY(!pushed()))
return;
stack_->popInvokeArgs(*this);
}
JS_ALWAYS_INLINE bool
ContextStack::pushInvokeFrame(JSContext *cx, const CallArgs &args, MaybeConstruct construct,
JSObject &callee, JSFunction *fun, JSScript *script,
InvokeFrameGuard *ifg)
{
JS_ASSERT(callee == args.callee());
JS_ASSERT(callee.getFunctionPrivate() == fun);
JS_ASSERT(fun->script() == script);
JS_ASSERT(args.end() == space().firstUnused());
/* Get pointers into the stack; check for overflow. */
StackFrame::Flags flags = ToFrameFlags(construct);
StackFrame *fp = getCallFrame(cx, args, fun, script, &flags, OOMCheck());
if (!fp)
return false;
/* Initialize regs, frame, locals. */
ifg->regs_.prepareToRun(fp, script);
fp->initCallFrame(cx, callee, fun, script, args.argc(), flags);
if (JS_UNLIKELY(space().seg_->empty())) {
pushInvokeFrameSlow(ifg);
return true;
}
/* Offically push onto the stack. */
ifg->prevRegs_ = regs_;
regs_ = &ifg->regs_;
/* Mark as pushed. */
ifg->stack_ = this;
return true;
}
JS_ALWAYS_INLINE void
ContextStack::popInvokeFrame(const InvokeFrameGuard &frameGuard)
{
JS_ASSERT(isCurrentAndActive());
JS_ASSERT(&frameGuard.regs_ == regs_);
if (JS_UNLIKELY(seg_->initialFrame() == regs_->fp())) {
popInvokeFrameSlow(frameGuard);
return;
}
regs_->fp()->putActivationObjects();
regs_ = frameGuard.prevRegs_;
}
JS_ALWAYS_INLINE void
InvokeFrameGuard::pop()
{
JS_ASSERT(pushed());
stack_->popInvokeFrame(*this);
stack_ = NULL;
}
JS_ALWAYS_INLINE
InvokeFrameGuard::~InvokeFrameGuard()
{
if (pushed())
pop();
}
inline StackFrame *
ContextStack::findFrameAtLevel(uintN targetLevel) const
{
StackFrame *fp = regs_->fp();
while (true) {
JS_ASSERT(fp && fp->isScriptFrame());
if (fp->script()->staticLevel == targetLevel)
break;
fp = fp->prev();
}
return fp;
}
/*****************************************************************************/
namespace detail {
struct STATIC_SKIP_INFERENCE CopyNonHoleArgsTo
@ -1101,5 +640,4 @@ ArgumentsObject::getElements(uint32 start, uint32 count, Value *vp)
}
} /* namespace js */
#endif /* Stack_inl_h__ */

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -60,6 +60,10 @@ class ExecuteFrameGuard;
class DummyFrameGuard;
class GeneratorFrameGuard;
class CallIter;
class FrameRegsIter;
class AllFramesIter;
class ArgumentsObject;
namespace mjit { struct JITScript; }
@ -74,20 +78,18 @@ namespace mjit { struct JITScript; }
*
* The per-thread stack is subdivided into contiguous segments of memory which
* have a memory layout invariant that allows fixed offsets to be used for stack
* access (by the JIT) as well as fast call/return. This memory layout is
* encapsulated by a set of types that describe different regions of memory:
* StackSegment, StackFrame, FrameRegs and CallArgs. To avoid calling into C++,
* the JIT compiler generates code that simulates C++ stack operations.
* access (by jit code) as well as fast call/return. This memory layout is
* encapsulated by a set of types that describe different regions of memory.
* This encapsulation has holes: to avoid calling into C++ from generated code,
* JIT compilers generate code that simulates analogous operations in C++.
*
* The memory layout of a segment looks like:
* A sample memory layout of a segment looks like:
*
* current regs
* .------------------------------------------------------.
* | current frame |
* | .-------------------------------------. V
* | | initial frame | FrameRegs
* | | .------------. | |
* | | | V V V
* regs
* .---------------------------------------------.
* | V
* | fp .--FrameRegs--. sp
* | V V
* |StackSegment| slots |StackFrame| slots |StackFrame| slots |
* | ^ |
* ? <----------' `-----------'
@ -95,16 +97,16 @@ namespace mjit { struct JITScript; }
*
* A segment starts with a fixed-size header (js::StackSegment) which logically
* describes the segment, links it to the rest of the stack, and points to the
* first and last frames in the segment.
* end of the stack.
*
* Each script activation (global or function code) is given a fixed-size header
* (js::StackFrame) which is associated with the values (called "slots") before
* and after it. The frame contains bookkeeping information about the activation
* and links to the previous frame.
*
* The slots preceeding a (function) StackFrame in memory are the arguments of
* the call. The slots after a StackFrame in memory are its locals followed
* by its expression stack. There is no clean line between the arguments of a
* The slots preceding a (function) StackFrame in memory are the arguments of
* the call. The slots after a StackFrame in memory are its locals followed by
* its expression stack. There is no clean line between the arguments of a
* frame and the expression stack of the previous frame since the top slots of
* the expression become the arguments of a call. There are also layout
* invariants concerning the arguments and StackFrame; see "Arguments" comment
@ -119,24 +121,45 @@ namespace mjit { struct JITScript; }
* dependence on FrameRegs outside the interpreter.
*
* A call to a native (C++) function does not push a frame. Instead, an array
* of values (possibly from the top of a calling frame's expression stack) is
* passed to the native. The layout of this array is abstracted by js::CallArgs.
* Note that, between any two StackFrames there may be any number of native
* calls, so the meaning of 'prev' is not 'directly called by'.
* of values is passed to the native. The layout of this array is abstracted by
* js::CallArgs. With respect to the StackSegment layout above, the args to a
* native call are inserted anywhere there can be slots. A sample memory layout
* looks like:
*
* regs
* .----------------------------------------.
* | V
* | fp .--FrameRegs--. sp
* | V V
* |StackSegment| native call | slots |StackFrame| slots | native call |
* | vp <--argc--> end vp <--argc--> end
* | CallArgs <------------------------------ CallArgs
* | prev ^
* `-----------------------------------------------------'
* calls
*
* Here there are two native calls on the stack. The start of each native arg
* range is recorded by a CallArgs element which is prev-linked like stack
* frames. Note that, in full generality, native and scripted calls can
* interleave arbitrarily. Thus, the end of a segment is the maximum of its
* current frame and its current native call. Similarly, the top of the entire
* thread stack is the end of its current segment.
*
* Note that, between any two StackFrames there may be any number
* of native calls, so the meaning of 'prev' is not 'directly called by'.
*
* An additional feature (perhaps not for much longer: bug 650361) is that
* multiple independent "contexts" can interleave (LIFO) on a single contiguous
* stack. "Independent" here means that neither context sees the other's frames.
* Concretely, an embedding may enter the JS engine on cx1 and then, from a
* native called by the JS engine, reenter the VM on cx2. Changing from cx1 to
* cx2 causes cx1's segment to be "suspended" and a new segment started to be
* started for cx2. These two segments are linked from the perspective of
* stack. "Independent" here means that neither context sees the other's
* frames. Concretely, an embedding may enter the JS engine on cx1 and then,
* from a native called by the JS engine, reenter the VM on cx2. Changing from
* cx1 to cx2 causes a new segment to be started for cx2's stack on top of
* cx1's current segment. These two segments are linked from the perspective of
* StackSpace, since they are adjacent on the thread's stack, but not from the
* perspective of cx1 and cx2. Thus, each segment has two prev-links:
* previousInMemory and previousInContext. A context's apparent stack is
* encapsulated and managed by the js::ContextStack object stored in JSContext.
* ContextStack is the primary interface to the rest of the engine for pushing
* and popping args (for js::Invoke calls) and frames.
* perspective of cx1 and cx2. Thus, each segment has two links: prevInMemory
* and prevInContext. Each independent stack is encapsulated and managed by
* the js::ContextStack object stored in JSContext. ContextStack is the primary
* interface to the rest of the engine for pushing and popping the stack.
*/
/*****************************************************************************/
@ -195,6 +218,7 @@ CallReceiverFromVp(Value *vp)
class CallArgs : public CallReceiver
{
protected:
uintN argc_;
public:
friend CallArgs CallArgsFromVp(uintN, Value *);
@ -230,6 +254,42 @@ CallArgsFromSp(uintN argc, Value *sp)
/*****************************************************************************/
class CallArgsList : public CallArgs
{
friend class StackSegment;
CallArgsList *prev_;
bool active_;
public:
friend CallArgsList CallArgsListFromVp(uintN, Value *, CallArgsList *);
friend CallArgsList CallArgsListFromArgv(uintN, Value *, CallArgsList *);
CallArgsList *prev() const { return prev_; }
bool active() const { return active_; }
void setActive() { active_ = true; }
void setInactive() { active_ = false; }
};
JS_ALWAYS_INLINE CallArgsList
CallArgsListFromArgv(uintN argc, Value *argv, CallArgsList *prev)
{
CallArgsList args;
#ifdef DEBUG
args.usedRval_ = false;
#endif
args.argv_ = argv;
args.argc_ = argc;
args.prev_ = prev;
args.active_ = false;
return args;
}
JS_ALWAYS_INLINE CallArgsList
CallArgsListFromVp(uintN argc, Value *vp, CallArgsList *prev)
{
return CallArgsListFromArgv(argc, vp + 2, prev);
}
/*****************************************************************************/
enum MaybeConstruct {
NO_CONSTRUCT = 0, /* == false */
CONSTRUCT = 0x80 /* == StackFrame::CONSTRUCTING, asserted below */
@ -332,8 +392,8 @@ class StackFrame
void initJitFrameLatePrologue();
/* Used for eval. */
void initExecuteFrame(JSScript *script, StackFrame *prev, const Value &thisv,
JSObject &scopeChain, ExecuteType type);
void initExecuteFrame(JSScript *script, StackFrame *prev, FrameRegs *regs,
const Value &thisv, JSObject &scopeChain, ExecuteType type);
/* Used when activating generators. */
void stealFrameAndSlots(Value *vp, StackFrame *otherfp, Value *othervp, Value *othersp);
@ -387,6 +447,10 @@ class StackFrame
return flags_ & EVAL;
}
bool isEvalInFunction() const {
return (flags_ & (EVAL | FUNCTION)) == (EVAL | FUNCTION);
}
bool isNonEvalFunctionFrame() const {
return (flags_ & (FUNCTION | EVAL)) == FUNCTION;
}
@ -462,7 +526,7 @@ class StackFrame
* Where I stop and I turn and I go right back
* Till I get to the bottom and I see you again...
*/
jsbytecode *pcQuadratic(JSContext *cx);
jsbytecode *pcQuadratic(JSContext *cx) const;
jsbytecode *prevpc() {
if (flags_ & HAS_PREVPC)
@ -926,10 +990,6 @@ class StackFrame
return !!(flags_ & DEBUGGER);
}
bool isDirectEvalOrDebuggerFrame() const {
return (flags_ & (EVAL | DEBUGGER)) && !(flags_ & GLOBAL);
}
bool hasOverriddenArgs() const {
return !!(flags_ & OVERRIDE_ARGS);
}
@ -1076,10 +1136,11 @@ class FrameRegs
}
/* For generator: */
void rebaseFromTo(const FrameRegs &from, StackFrame *to) {
fp_ = to;
sp = to->slots() + (from.sp - from.fp_->slots());
void rebaseFromTo(const FrameRegs &from, StackFrame &to) {
fp_ = &to;
sp = to.slots() + (from.sp - from.fp_->slots());
pc = from.pc;
JS_ASSERT(fp_);
}
/* For ContextStack: */
@ -1087,40 +1148,140 @@ class FrameRegs
pc = fp_->prevpc();
sp = newsp;
fp_ = fp_->prev();
JS_ASSERT(fp_);
}
/* For FixupArity: */
void popPartialFrame(Value *newsp) {
sp = newsp;
fp_ = fp_->prev();
JS_ASSERT(fp_);
}
/* For stubs::CompileFunction, ContextStack: */
void prepareToRun(StackFrame *fp, JSScript *script) {
void prepareToRun(StackFrame &fp, JSScript *script) {
pc = script->code;
sp = fp->slots() + script->nfixed;
fp_ = fp;
sp = fp.slots() + script->nfixed;
fp_ = &fp;
JS_ASSERT(fp_);
}
/* For pushDummyFrame: */
void initDummyFrame(StackFrame *fp) {
void initDummyFrame(StackFrame &fp) {
pc = NULL;
sp = fp->slots();
fp_ = fp;
sp = fp.slots();
fp_ = &fp;
JS_ASSERT(fp_);
}
};
/*****************************************************************************/
struct StackOverride
class StackSegment
{
Value *top;
#ifdef DEBUG
StackSegment *seg;
StackFrame *frame;
#endif
/* Previous segment within same context stack. */
StackSegment *const prevInContext_;
/* Previous segment sequentially in memory. */
StackSegment *const prevInMemory_;
/* Execution registers for most recent script in this segment (or null). */
FrameRegs *regs_;
/* Call args for most recent native call in this segment (or null). */
CallArgsList *calls_;
public:
StackSegment(StackSegment *prevInContext,
StackSegment *prevInMemory,
FrameRegs *regs,
CallArgsList *calls)
: prevInContext_(prevInContext),
prevInMemory_(prevInMemory),
regs_(regs),
calls_(calls)
{}
/* A segment is followed in memory by the arguments of the first call. */
Value *slotsBegin() const {
return (Value *)(this + 1);
}
/* Accessors. */
FrameRegs &regs() 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 &regs);
void popRegs(FrameRegs *regs);
void pushCall(CallArgsList &callList);
void popCall();
/* For jit access: */
static const size_t offsetOfRegs() { return offsetof(StackSegment, regs_); }
};
static const size_t VALUES_PER_STACK_SEGMENT = sizeof(StackSegment) / sizeof(Value);
JS_STATIC_ASSERT(sizeof(StackSegment) % sizeof(Value) == 0);
/*****************************************************************************/
class StackSpace
@ -1129,7 +1290,6 @@ class StackSpace
mutable Value *commitEnd_;
Value *end_;
StackSegment *seg_;
StackOverride override_;
static const size_t CAPACITY_VALS = 512 * 1024;
static const size_t CAPACITY_BYTES = CAPACITY_VALS * sizeof(Value);
@ -1144,13 +1304,12 @@ class StackSpace
JS_FRIEND_API(bool) bumpCommit(JSContext *maybecx, Value *from, ptrdiff_t nvals) const;
#endif
friend class AllFramesIter;
friend class ContextStack;
friend class StackFrame;
friend class OOMCheck;
inline bool ensureSpace(JSContext *maybecx, Value *from, ptrdiff_t nvals) const;
void pushSegment(StackSegment &seg);
void popSegment();
inline void pushOverride(Value *top, StackOverride *prev);
inline void popOverride(const StackOverride &prev);
StackSegment &findContainingSegment(const StackFrame *target) const;
public:
StackSpace();
@ -1158,14 +1317,8 @@ class StackSpace
~StackSpace();
/* See stack layout comment above. */
StackSegment *currentSegment() const { return seg_; }
Value *firstUnused() const;
/* Optimization of firstUnused when currentSegment() is known active. */
inline Value *activeFirstUnused() const;
/* Get the segment containing the target frame. */
StackSegment &containingSegment(const StackFrame *target) const;
Value *firstUnused() const { return seg_ ? seg_->end() : base_; }
Value *endOfSpace() const { return end_; }
#ifdef JS_TRACER
/*
@ -1235,70 +1388,59 @@ class LimitCheck
class ContextStack
{
FrameRegs *regs_;
StackSegment *seg_;
StackSpace *space_;
JSContext *cx_;
/*
* This is the collecting-point for code that wants to know when there is
* no JS active. Note that "no JS active" does not mean the stack is empty
* because of JS_(Save|Restore)FrameChain. If code really wants to know
* when the stack is empty, test |cx->stack.empty()|.
* Return whether this ContextStack is at the top of the contiguous stack.
* This is a precondition for extending the current segment by pushing
* stack frames or overrides etc.
*
* NB: Just because a stack is onTop() doesn't mean there is necessarily
* a frame pushed on the stack. For this, use hasfp().
*/
void notifyIfNoCodeRunning();
/*
* Return whether this ContextStack is running code at the top of the
* contiguous stack. This is a precondition for extending the current
* segment by pushing stack frames or overrides etc.
*/
inline bool isCurrentAndActive() const;
bool onTop() const;
#ifdef DEBUG
void assertSegmentsInSync() const;
void assertSpaceInSync() const;
#else
void assertSegmentsInSync() const {}
void assertSpaceInSync() const {}
#endif
friend class FrameGuard;
StackFrame *getSegmentAndFrame(JSContext *cx, uintN vplen, uintN nslots,
FrameGuard *frameGuard) const;
void pushSegmentAndFrame(FrameGuard *frameGuard);
void pushSegmentAndFrameImpl(FrameRegs &regs, StackSegment &seg);
void popSegmentAndFrame();
void popSegmentAndFrameImpl();
friend class GeneratorFrameGuard;
void popGeneratorFrame(GeneratorFrameGuard *gfg);
/* Implementation details of push* public interface. */
StackSegment *pushSegment(JSContext *cx);
enum MaybeExtend { CAN_EXTEND = true, CANT_EXTEND = false };
Value *ensureOnTop(JSContext *cx, uintN nvars, MaybeExtend extend, bool *pushedSeg);
/* Check = { OOMCheck, LimitCheck } */
template <class Check>
inline StackFrame *getCallFrame(JSContext *cx, const CallArgs &args,
JSFunction *fun, JSScript *script,
StackFrame::Flags *pflags,
Check check) const;
inline StackFrame *
getCallFrame(JSContext *cx, const CallArgs &args, JSFunction *fun, JSScript *script,
StackFrame::Flags *pflags, Check check) const;
/* Make pop* functions private since only called by guard classes. */
void popSegment();
friend class InvokeArgsGuard;
bool pushInvokeArgsSlow(JSContext *cx, uintN argc, InvokeArgsGuard *argsGuard);
void popInvokeArgsSlow(const InvokeArgsGuard &argsGuard);
inline void popInvokeArgs(const InvokeArgsGuard &argsGuard);
void popInvokeArgs(const InvokeArgsGuard &iag);
friend class FrameGuard;
void popFrame(const FrameGuard &fg);
friend class GeneratorFrameGuard;
void popGeneratorFrame(const GeneratorFrameGuard &gfg);
friend class InvokeFrameGuard;
void pushInvokeFrameSlow(InvokeFrameGuard *frameGuard);
void popInvokeFrameSlow(const InvokeFrameGuard &frameGuard);
inline void popInvokeFrame(const InvokeFrameGuard &frameGuard);
friend class StackIter;
public:
ContextStack(JSContext *cx);
~ContextStack();
/*** Stack accessors ***/
/*
* A context's stack is "empty" if there are no scripts or natives
* executing. Note that JS_SaveFrameChain does factor into this definition.
*/
bool empty() const { JS_ASSERT_IF(regs_, seg_); return !seg_; }
bool empty() const { return !seg_; }
/*
* Return whether there has been at least one frame pushed since the most
@ -1306,54 +1448,26 @@ class ContextStack
* and dummy frames are frames that do not represent script execution hence
* this query has little semantic meaning past "you can call fp()".
*/
bool hasfp() const { JS_ASSERT_IF(regs_, regs_->fp()); return !!regs_; }
bool hasfp() const { return seg_ && seg_->maybeRegs(); }
/* Current regs of the current segment (see VM stack layout comment). */
FrameRegs &regs() const { JS_ASSERT(regs_); return *regs_; }
/*
* Return the most recent script activation's registers with the same
* caveat as hasfp regarding JS_SaveFrameChain.
*/
FrameRegs *maybeRegs() const { return seg_ ? seg_->maybeRegs() : NULL; }
StackFrame *maybefp() const { return seg_ ? seg_->maybefp() : NULL; }
/* Convenience helpers. */
FrameRegs *maybeRegs() const { return regs_; }
StackFrame *fp() const { return regs_->fp(); }
StackFrame *maybefp() const { return regs_ ? regs_->fp() : NULL; }
/* Faster alternatives to maybe* functions. */
FrameRegs &regs() const { JS_ASSERT(hasfp()); return seg_->regs(); }
StackFrame *fp() const { JS_ASSERT(hasfp()); return seg_->fp(); }
/* The StackSpace currently hosting this ContextStack. */
StackSpace &space() const { assertSpaceInSync(); return *space_; }
/*
* To avoid indirection, ContextSpace caches a pointers to the StackSpace.
* This must be kept coherent with cx->thread->data.space by calling
* 'threadReset' whenver cx->thread changes.
*/
void threadReset();
/*
* As an optimization, the interpreter/mjit can operate on a local
* FrameRegs instance repoint the ContextStack to this local instance.
*/
void repointRegs(FrameRegs *regs) {
JS_ASSERT_IF(regs, regs->fp());
regs_ = regs;
}
/* Return the current segment, which may or may not be active. */
StackSegment *currentSegment() const {
assertSegmentsInSync();
return seg_;
}
/* Search the call stack for the nearest frame with static level targetLevel. */
inline StackFrame *findFrameAtLevel(uintN targetLevel) const;
#ifdef DEBUG
/* Return whether the given frame is in this context's stack. */
bool contains(const StackFrame *fp) const;
#endif
bool containsSlow(const StackFrame *target) const;
/* Mark the top segment as suspended, without pushing a new one. */
void saveActiveSegment();
/* Undoes calls to suspendActiveSegment. */
void restoreSegment();
/*** Stack manipulation ***/
/*
* pushInvokeArgs allocates |argc + 2| rooted values that will be passed as
@ -1364,16 +1478,20 @@ class ContextStack
bool pushInvokeArgs(JSContext *cx, uintN argc, InvokeArgsGuard *ag);
/* Called by Invoke for a scripted function call. */
bool pushInvokeFrame(JSContext *cx, const CallArgs &args, MaybeConstruct,
JSObject &callee, JSFunction *fun, JSScript *script,
InvokeFrameGuard *ifg);
bool pushInvokeFrame(JSContext *cx, const CallArgs &args,
MaybeConstruct construct, InvokeFrameGuard *ifg);
/* Called by Execute for execution of eval or global code. */
bool pushExecuteFrame(JSContext *cx, JSScript *script, const Value &thisv,
JSObject &scopeChain, ExecuteType type,
StackFrame *evalInFrame, ExecuteFrameGuard *efg);
/* Called by SendToGenerator to resume a yielded generator. */
/*
* Called by SendToGenerator to resume a yielded generator. In addition to
* pushing a frame onto the VM stack, this function copies over the
* floating frame stored in 'gen'. When 'gfg' is destroyed, the destructor
* will copy the frame back to the floating frame.
*/
bool pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg);
/* Pushes a "dummy" frame; should be removed one day. */
@ -1388,7 +1506,10 @@ class ContextStack
bool pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
JSObject &callee, JSFunction *fun, JSScript *script,
MaybeConstruct construct, Check check);
void popInlineFrame();
void popInlineFrame(FrameRegs &regs);
/* Pop a partially-pushed frame after hitting the limit before throwing. */
void popFrameAfterOverflow();
/*
* Called by the methodjit for an arity mismatch. Arity mismatch can be
@ -1402,54 +1523,65 @@ class ContextStack
JSFunction *fun, JSScript *script, void *ncode,
MaybeConstruct construct, LimitCheck check);
/* For jit use: */
static size_t offsetOfRegs() { return offsetof(ContextStack, regs_); }
bool saveFrameChain();
void restoreFrameChain();
/*
* As an optimization, the interpreter/mjit can operate on a local
* FrameRegs instance repoint the ContextStack to this local instance.
*/
void repointRegs(FrameRegs *regs) { JS_ASSERT(hasfp()); seg_->repointRegs(regs); }
/*** For JSContext: ***/
/*
* To avoid indirection, ContextSpace caches a pointer to the StackSpace.
* This must be kept coherent with cx->thread->data.space by calling
* 'threadReset' whenver cx->thread changes.
*/
void threadReset();
/*** For jit compiler: ***/
static size_t offsetOfSeg() { return offsetof(ContextStack, seg_); }
};
/*****************************************************************************/
class InvokeArgsGuard : public CallArgs
class InvokeArgsGuard : public CallArgsList
{
friend class ContextStack;
ContextStack *stack_; /* null implies nothing pushed */
StackSegment *seg_; /* null implies no segment pushed */
StackOverride prevOverride_;
ContextStack *stack_;
bool pushedSeg_;
void setPushed(ContextStack &stack) { JS_ASSERT(!pushed()); stack_ = &stack; }
public:
InvokeArgsGuard() : stack_(NULL), seg_(NULL) {}
~InvokeArgsGuard();
bool pushed() const { return stack_ != NULL; }
InvokeArgsGuard() : CallArgsList(), stack_(NULL), pushedSeg_(false) {}
~InvokeArgsGuard() { if (pushed()) stack_->popInvokeArgs(*this); }
bool pushed() const { return !!stack_; }
void pop() { stack_->popInvokeArgs(*this); stack_ = NULL; }
};
class InvokeFrameGuard
{
friend class ContextStack;
ContextStack *stack_; /* null implies nothing pushed */
FrameRegs regs_;
FrameRegs *prevRegs_;
public:
InvokeFrameGuard() : stack_(NULL) {}
~InvokeFrameGuard();
bool pushed() const { return stack_ != NULL; }
void pop();
StackFrame *fp() const { return regs_.fp(); }
};
/* Reusable base; not for direct use. */
class FrameGuard
{
protected:
friend class ContextStack;
ContextStack *stack_; /* null implies nothing pushed */
StackSegment *seg_;
ContextStack *stack_;
bool pushedSeg_;
FrameRegs regs_;
FrameRegs *prevRegs_;
void setPushed(ContextStack &stack) { stack_ = &stack; }
public:
FrameGuard() : stack_(NULL) {}
~FrameGuard();
bool pushed() const { return stack_ != NULL; }
FrameGuard() : stack_(NULL), pushedSeg_(false) {}
~FrameGuard() { if (pushed()) stack_->popFrame(*this); }
bool pushed() const { return !!stack_; }
void pop() { stack_->popFrame(*this); stack_ = NULL; }
StackFrame *fp() const { return regs_.fp(); }
};
class InvokeFrameGuard : public FrameGuard
{};
class ExecuteFrameGuard : public FrameGuard
{};
@ -1462,59 +1594,114 @@ class GeneratorFrameGuard : public FrameGuard
JSGenerator *gen_;
Value *stackvp_;
public:
~GeneratorFrameGuard();
~GeneratorFrameGuard() { if (pushed()) stack_->popGeneratorFrame(*this); }
};
/*****************************************************************************/
/*
* While |cx->fp|'s pc/sp are available in |cx->regs|, to compute the saved
* value of pc/sp for any other frame, it is necessary to know about that
* frame's next-frame. This iterator maintains this information when walking
* a chain of stack frames starting at |cx->fp|.
* Iterate through the callstack of the given context. Each element of said
* callstack can either be the execution of a script (scripted function call,
* global code, eval code, debugger code) or the invocation of a (C++) native.
* Example usage:
*
* Usage:
* for (FrameRegsIter i(cx); !i.done(); ++i)
* ... i.fp() ... i.sp() ... i.pc()
* for (Stackiter i(cx); !i.done(); ++i) {
* if (i.isScript()) {
* ... i.fp() ... i.sp() ... i.pc()
* } else {
* JS_ASSERT(i.isNativeCall());
* ... i.args();
* }
*
* The SavedOption parameter additionally lets the iterator continue through
* breaks in the callstack (from JS_SaveFrameChain). The default is to stop.
*/
class FrameRegsIter
class StackIter
{
friend class ContextStack;
JSContext *cx_;
StackSegment *seg_;
public:
enum SavedOption { STOP_AT_SAVED, GO_THROUGH_SAVED };
private:
SavedOption savedOption_;
enum State { DONE, SCRIPTED, NATIVE, IMPLICIT_NATIVE };
State state_;
StackFrame *fp_;
CallArgsList *calls_;
StackSegment *seg_;
Value *sp_;
jsbytecode *pc_;
CallArgs args_;
void initSlow();
void incSlow(StackFrame *oldfp);
void poisonRegs();
void popFrame();
void popCall();
void settleOnNewSegment();
void settleOnNewState();
void startOnSegment(StackSegment *seg);
public:
FrameRegsIter(JSContext *cx);
StackIter(JSContext *cx, SavedOption = STOP_AT_SAVED);
bool done() const { return fp_ == NULL; }
FrameRegsIter &operator++();
bool operator==(const FrameRegsIter &rhs) const;
bool operator!=(const FrameRegsIter &rhs) const { return !(*this == rhs); }
bool done() const { return state_ == DONE; }
StackIter &operator++();
StackFrame *fp() const { return fp_; }
Value *sp() const { return sp_; }
jsbytecode *pc() const { return pc_; }
bool operator==(const StackIter &rhs) const;
bool operator!=(const StackIter &rhs) const { return !(*this == rhs); }
bool isScript() const { JS_ASSERT(!done()); return state_ == SCRIPTED; }
StackFrame *fp() const { JS_ASSERT(!done() && isScript()); return fp_; }
Value *sp() const { JS_ASSERT(!done() && isScript()); return sp_; }
jsbytecode *pc() const { JS_ASSERT(!done() && isScript()); return pc_; }
bool isNativeCall() const { JS_ASSERT(!done()); return state_ != SCRIPTED; }
CallArgs nativeArgs() const { JS_ASSERT(!done() && isNativeCall()); return args_; }
};
/* A filtering of the StackIter to only stop at scripts. */
class FrameRegsIter
{
StackIter iter_;
void settle() {
while (!iter_.done() && !iter_.isScript())
++iter_;
}
public:
FrameRegsIter(JSContext *cx) : iter_(cx) { settle(); }
bool done() const { return iter_.done(); }
FrameRegsIter &operator++() { ++iter_; settle(); return *this; }
bool operator==(const FrameRegsIter &rhs) const { return iter_ == rhs.iter_; }
bool operator!=(const FrameRegsIter &rhs) const { return iter_ != rhs.iter_; }
StackFrame *fp() const { return iter_.fp(); }
Value *sp() const { return iter_.sp(); }
jsbytecode *pc() const { return iter_.pc(); }
};
/*****************************************************************************/
/*
* Utility class for iteration over all active stack frames.
* Blindly iterate over all frames in the current thread's stack. These frames
* can be from different contexts and compartments, so beware.
*/
class AllFramesIter
{
public:
AllFramesIter(JSContext *cx);
public:
AllFramesIter(StackSpace &space);
bool done() const { return fp_ == NULL; }
AllFramesIter& operator++();
StackFrame *fp() const { return fp_; }
private:
private:
StackSegment *seg_;
StackFrame *fp_;
};

Просмотреть файл

@ -259,9 +259,9 @@ class JSCLContextHelper
{
public:
JSCLContextHelper(mozJSComponentLoader* loader);
~JSCLContextHelper() { Pop(); }
~JSCLContextHelper();
JSContext* Pop();
void reportErrorAfterPop(char *buf);
operator JSContext*() const {return mContext;}
@ -269,6 +269,7 @@ private:
JSContext* mContext;
intN mContextThread;
nsIThreadJSContextStack* mContextStack;
char* mBuf;
// prevent copying and assignment
JSCLContextHelper(const JSCLContextHelper &); // not implemented
@ -291,22 +292,6 @@ private:
const JSCLAutoErrorReporterSetter& operator=(const JSCLAutoErrorReporterSetter &); // not implemented
};
static nsresult
OutputError(JSContext *cx,
const char *format,
va_list ap)
{
char *buf = JS_vsmprintf(format, ap);
if (!buf) {
return NS_ERROR_OUT_OF_MEMORY;
}
JS_ReportError(cx, buf);
JS_smprintf_free(buf);
return NS_OK;
}
static nsresult
ReportOnCaller(nsAXPCNativeCallContext *cc,
const char *format, ...) {
@ -322,7 +307,15 @@ ReportOnCaller(nsAXPCNativeCallContext *cc,
rv = cc->GetJSContext(&callerContext);
NS_ENSURE_SUCCESS(rv, rv);
return OutputError(callerContext, format, ap);
char *buf = JS_vsmprintf(format, ap);
if (!buf) {
return NS_ERROR_OUT_OF_MEMORY;
}
JS_ReportError(callerContext, buf);
JS_smprintf_free(buf);
return NS_OK;
}
static nsresult
@ -332,12 +325,14 @@ ReportOnCaller(JSCLContextHelper &helper,
va_list ap;
va_start(ap, format);
JSContext *cx = helper.Pop();
if (!cx) {
return NS_ERROR_FAILURE;
char *buf = JS_vsmprintf(format, ap);
if (!buf) {
return NS_ERROR_OUT_OF_MEMORY;
}
return OutputError(cx, format, ap);
helper.reportErrorAfterPop(buf);
return NS_OK;
}
static nsresult
@ -1639,7 +1634,8 @@ mozJSComponentLoader::ModuleEntry::GetFactory(const mozilla::Module& module,
JSCLContextHelper::JSCLContextHelper(mozJSComponentLoader *loader)
: mContext(loader->mContext), mContextThread(0),
mContextStack(loader->mContextStack)
mContextStack(loader->mContextStack),
mBuf(nsnull)
{
mContextStack->Push(mContext);
mContextThread = JS_GetContextThread(mContext);
@ -1648,20 +1644,33 @@ JSCLContextHelper::JSCLContextHelper(mozJSComponentLoader *loader)
}
}
// Pops the context that was pushed and then returns the context that is now at
// the top of the stack.
JSContext*
JSCLContextHelper::Pop()
JSCLContextHelper::~JSCLContextHelper()
{
JSContext* cx = nsnull;
if (mContextStack) {
if (mContextThread) {
JS_EndRequest(mContext);
}
mContextStack->Pop(nsnull);
JSContext* cx = nsnull;
mContextStack->Peek(&cx);
mContextStack = nsnull;
if (cx && mBuf) {
JS_ReportError(cx, mBuf);
}
}
if (mBuf) {
JS_smprintf_free(mBuf);
}
return cx;
}
void
JSCLContextHelper::reportErrorAfterPop(char *buf)
{
NS_ASSERTION(!mBuf, "Already called reportErrorAfterPop");
mBuf = buf;
}

Просмотреть файл

@ -967,23 +967,6 @@ inline nsresult UnexpectedFailure(nsresult rv)
return rv;
}
class SaveFrame
{
public:
SaveFrame(JSContext *cx)
: mJSContext(cx) {
mFrame = JS_SaveFrameChain(mJSContext);
}
~SaveFrame() {
JS_RestoreFrameChain(mJSContext, mFrame);
}
private:
JSContext *mJSContext;
JSStackFrame *mFrame;
};
/* void initClasses (in JSContextPtr aJSContext, in JSObjectPtr aGlobalJSObj); */
NS_IMETHODIMP
nsXPConnect::InitClasses(JSContext * aJSContext, JSObject * aGlobalJSObj)

Просмотреть файл

@ -3559,14 +3559,13 @@ private:
struct XPCJSContextInfo {
XPCJSContextInfo(JSContext* aCx) :
cx(aCx),
frame(nsnull),
savedFrameChain(false),
suspendDepth(0)
{}
JSContext* cx;
// Frame to be restored when this JSContext becomes the topmost
// one.
JSStackFrame* frame;
// Whether the frame chain was saved
bool savedFrameChain;
// Greater than 0 if a request was suspended.
jsrefcount suspendDepth;

Просмотреть файл

@ -93,9 +93,6 @@ XPCJSContextStack::Pop(JSContext * *_retval)
NS_ASSERTION(!mStack.IsEmpty(), "ThreadJSContextStack underflow");
PRUint32 idx = mStack.Length() - 1; // The thing we're popping
NS_ASSERTION(!mStack[idx].frame,
"Shouldn't have a pending frame to restore on the context "
"we're popping!");
if(_retval)
*_retval = mStack[idx].cx;
@ -106,7 +103,6 @@ XPCJSContextStack::Pop(JSContext * *_retval)
--idx; // Advance to new top of the stack
XPCJSContextInfo & e = mStack[idx];
NS_ASSERTION(!e.frame || e.cx, "Shouldn't have frame without a cx!");
NS_ASSERTION(!e.suspendDepth || e.cx, "Shouldn't have suspendDepth without a cx!");
if(e.cx)
{
@ -116,12 +112,12 @@ XPCJSContextStack::Pop(JSContext * *_retval)
e.suspendDepth = 0;
}
if(e.frame)
if(e.savedFrameChain)
{
// Pop() can be called outside any request for e.cx.
JSAutoRequest ar(e.cx);
JS_RestoreFrameChain(e.cx, e.frame);
e.frame = nsnull;
JS_RestoreFrameChain(e.cx);
e.savedFrameChain = false;
}
}
}
@ -146,11 +142,9 @@ NS_IMETHODIMP
XPCJSContextStack::Push(JSContext * cx)
{
JS_ASSERT_IF(cx, JS_GetContextThread(cx));
if(!mStack.AppendElement(cx))
return NS_ERROR_OUT_OF_MEMORY;
if(mStack.Length() > 1)
if(mStack.Length() > 0)
{
XPCJSContextInfo & e = mStack[mStack.Length() - 2];
XPCJSContextInfo & e = mStack[mStack.Length() - 1];
if(e.cx)
{
if(e.cx == cx)
@ -158,16 +152,14 @@ XPCJSContextStack::Push(JSContext * cx)
nsIScriptSecurityManager* ssm = XPCWrapper::GetSecurityManager();
if(ssm)
{
nsIPrincipal* globalObjectPrincipal =
GetPrincipalFromCx(cx);
if(globalObjectPrincipal)
if(nsIPrincipal* globalObjectPrincipal = GetPrincipalFromCx(cx))
{
nsIPrincipal* subjectPrincipal = ssm->GetCxSubjectPrincipal(cx);
PRBool equals = PR_FALSE;
globalObjectPrincipal->Equals(subjectPrincipal, &equals);
if(equals)
{
return NS_OK;
goto append;
}
}
}
@ -176,13 +168,19 @@ XPCJSContextStack::Push(JSContext * cx)
{
// Push() can be called outside any request for e.cx.
JSAutoRequest ar(e.cx);
e.frame = JS_SaveFrameChain(e.cx);
if(!JS_SaveFrameChain(e.cx))
return NS_ERROR_OUT_OF_MEMORY;
e.savedFrameChain = true;
}
if(!cx)
e.suspendDepth = JS_SuspendRequest(e.cx);
}
}
append:
if(!mStack.AppendElement(cx))
return NS_ERROR_OUT_OF_MEMORY;
return NS_OK;
}