зеркало из https://github.com/mozilla/pjs.git
Bug 667915 - Don't let content JS consume all the stack and cause chrome JS to OOM (r=waldo,mrbkap)
This commit is contained in:
Родитель
d96139acde
Коммит
5a5ed0ab29
|
@ -59,7 +59,7 @@ public:
|
|||
NS_DECL_NSIPRINCIPAL
|
||||
NS_DECL_NSISERIALIZABLE
|
||||
|
||||
nsresult Init();
|
||||
nsresult Init(JSPrincipals **jsprin);
|
||||
|
||||
nsSystemPrincipal();
|
||||
|
||||
|
|
|
@ -3379,7 +3379,8 @@ nsresult nsScriptSecurityManager::Init()
|
|||
nsRefPtr<nsSystemPrincipal> system = new nsSystemPrincipal();
|
||||
NS_ENSURE_TRUE(system, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
rv = system->Init();
|
||||
JSPrincipals *jsprin;
|
||||
rv = system->Init(&jsprin);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mSystemPrincipal = system;
|
||||
|
@ -3406,6 +3407,8 @@ nsresult nsScriptSecurityManager::Init()
|
|||
JS_SetRuntimeSecurityCallbacks(sRuntime, &securityCallbacks);
|
||||
NS_ASSERTION(!oldcallbacks, "Someone else set security callbacks!");
|
||||
|
||||
JS_SetTrustedPrincipals(sRuntime, jsprin);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -3429,6 +3432,7 @@ nsScriptSecurityManager::Shutdown()
|
|||
{
|
||||
if (sRuntime) {
|
||||
JS_SetRuntimeSecurityCallbacks(sRuntime, NULL);
|
||||
JS_SetTrustedPrincipals(sRuntime, NULL);
|
||||
sRuntime = nsnull;
|
||||
}
|
||||
sEnabledID = JSID_VOID;
|
||||
|
|
|
@ -314,7 +314,7 @@ nsSystemPrincipal::nsSystemPrincipal()
|
|||
#define SYSTEM_PRINCIPAL_SPEC "[System Principal]"
|
||||
|
||||
nsresult
|
||||
nsSystemPrincipal::Init()
|
||||
nsSystemPrincipal::Init(JSPrincipals **jsprin)
|
||||
{
|
||||
// Use an nsCString so we only do the allocation once here and then
|
||||
// share with nsJSPrincipals
|
||||
|
@ -324,7 +324,11 @@ nsSystemPrincipal::Init()
|
|||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
return mJSPrincipals.Init(this, str);
|
||||
nsresult rv = mJSPrincipals.Init(this, str);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
*jsprin = &mJSPrincipals;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsSystemPrincipal::~nsSystemPrincipal(void)
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
foo = evalcx("(function foo() { foo.bar() })");
|
||||
foo.bar = evalcx("(function bar() {})");
|
||||
|
||||
function fatty() {
|
||||
try {
|
||||
fatty();
|
||||
} catch (e) {
|
||||
foo();
|
||||
}
|
||||
}
|
||||
|
||||
fatty();
|
|
@ -0,0 +1,15 @@
|
|||
function foo() {
|
||||
bar(1,2,3,4,5,6,7,8,9);
|
||||
}
|
||||
|
||||
function bar() {
|
||||
foo(1,2,3,4,5,6,7,8,9);
|
||||
}
|
||||
|
||||
var caught = false;
|
||||
try {
|
||||
foo();
|
||||
} catch (e) {
|
||||
caught = true;
|
||||
}
|
||||
assertEq(caught, true);
|
|
@ -86,6 +86,7 @@ CPPSRCS = \
|
|||
testXDR.cpp \
|
||||
testCustomIterator.cpp \
|
||||
testExternalStrings.cpp \
|
||||
testChromeBuffer.cpp \
|
||||
$(NULL)
|
||||
|
||||
# Disabled: an entirely unrelated test seems to cause this to fail. Moreover,
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
#include "tests.h"
|
||||
|
||||
JSPrincipals system_principals = {
|
||||
NULL, NULL, NULL, 1, NULL, NULL
|
||||
};
|
||||
|
||||
JSClass global_class = {
|
||||
"global",
|
||||
JSCLASS_IS_GLOBAL | JSCLASS_GLOBAL_FLAGS,
|
||||
JS_PropertyStub,
|
||||
JS_PropertyStub,
|
||||
JS_PropertyStub,
|
||||
JS_StrictPropertyStub,
|
||||
JS_EnumerateStub,
|
||||
JS_ResolveStub,
|
||||
JS_ConvertStub,
|
||||
JS_FinalizeStub,
|
||||
JSCLASS_NO_OPTIONAL_MEMBERS
|
||||
};
|
||||
|
||||
JS::Anchor<JSObject *> trusted_glob, trusted_fun;
|
||||
|
||||
JSBool
|
||||
CallTrusted(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
if (!JS_SaveFrameChain(cx))
|
||||
return JS_FALSE;
|
||||
|
||||
JSBool ok = JS_FALSE;
|
||||
{
|
||||
JSAutoEnterCompartment ac;
|
||||
ok = ac.enter(cx, trusted_glob.get());
|
||||
if (!ok)
|
||||
goto out;
|
||||
ok = JS_CallFunctionValue(cx, NULL, OBJECT_TO_JSVAL(trusted_fun.get()),
|
||||
0, NULL, vp);
|
||||
}
|
||||
out:
|
||||
JS_RestoreFrameChain(cx);
|
||||
return ok;
|
||||
}
|
||||
|
||||
BEGIN_TEST(testChromeBuffer)
|
||||
{
|
||||
JS_SetTrustedPrincipals(rt, &system_principals);
|
||||
|
||||
JSFunction *fun;
|
||||
JSObject *o;
|
||||
|
||||
CHECK(o = JS_NewCompartmentAndGlobalObject(cx, &global_class, &system_principals));
|
||||
trusted_glob.set(o);
|
||||
|
||||
/*
|
||||
* Check that, even after untrusted content has exhausted the stack, code
|
||||
* compiled with "trusted principals" can run using reserved trusted-only
|
||||
* buffer space.
|
||||
*/
|
||||
{
|
||||
{
|
||||
JSAutoEnterCompartment ac;
|
||||
CHECK(ac.enter(cx, trusted_glob.get()));
|
||||
const char *paramName = "x";
|
||||
const char *bytes = "return x ? 1 + trusted(x-1) : 0";
|
||||
CHECK(fun = JS_CompileFunctionForPrincipals(cx, trusted_glob.get(), &system_principals,
|
||||
"trusted", 1, ¶mName, bytes, strlen(bytes),
|
||||
"", 0));
|
||||
trusted_fun.set(JS_GetFunctionObject(fun));
|
||||
}
|
||||
|
||||
jsval v = OBJECT_TO_JSVAL(trusted_fun.get());
|
||||
CHECK(JS_WrapValue(cx, &v));
|
||||
|
||||
const char *paramName = "trusted";
|
||||
const char *bytes = "try { "
|
||||
" return untrusted(trusted); "
|
||||
"} catch (e) { "
|
||||
" return trusted(100); "
|
||||
"} ";
|
||||
CHECK(fun = JS_CompileFunction(cx, global, "untrusted", 1, ¶mName,
|
||||
bytes, strlen(bytes), "", 0));
|
||||
|
||||
jsval rval;
|
||||
CHECK(JS_CallFunction(cx, NULL, fun, 1, &v, &rval));
|
||||
CHECK(JSVAL_TO_INT(rval) == 100);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that content called from chrome in the reserved-buffer space
|
||||
* immediately ooms.
|
||||
*/
|
||||
{
|
||||
{
|
||||
JSAutoEnterCompartment ac;
|
||||
CHECK(ac.enter(cx, trusted_glob.get()));
|
||||
const char *paramName = "untrusted";
|
||||
const char *bytes = "try { "
|
||||
" untrusted(); "
|
||||
"} catch (e) { "
|
||||
" return 'From trusted: ' + e; "
|
||||
"} ";
|
||||
CHECK(fun = JS_CompileFunctionForPrincipals(cx, trusted_glob.get(), &system_principals,
|
||||
"trusted", 1, ¶mName, bytes, strlen(bytes),
|
||||
"", 0));
|
||||
trusted_fun.set(JS_GetFunctionObject(fun));
|
||||
}
|
||||
|
||||
jsval v = OBJECT_TO_JSVAL(trusted_fun.get());
|
||||
CHECK(JS_WrapValue(cx, &v));
|
||||
|
||||
const char *paramName = "trusted";
|
||||
const char *bytes = "try { "
|
||||
" return untrusted(trusted); "
|
||||
"} catch (e) { "
|
||||
" return trusted(untrusted); "
|
||||
"} ";
|
||||
CHECK(fun = JS_CompileFunction(cx, global, "untrusted", 1, ¶mName,
|
||||
bytes, strlen(bytes), "", 0));
|
||||
|
||||
jsval rval;
|
||||
CHECK(JS_CallFunction(cx, NULL, fun, 1, &v, &rval));
|
||||
JSBool match;
|
||||
CHECK(JS_StringEqualsAscii(cx, JSVAL_TO_STRING(rval), "From trusted: InternalError: too much recursion", &match));
|
||||
CHECK(match);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that JS_SaveFrameChain called on the way from content to chrome
|
||||
* (say, as done by XPCJSContextSTack::Push) works.
|
||||
*/
|
||||
{
|
||||
{
|
||||
JSAutoEnterCompartment ac;
|
||||
CHECK(ac.enter(cx, trusted_glob.get()));
|
||||
const char *bytes = "return 42";
|
||||
CHECK(fun = JS_CompileFunctionForPrincipals(cx, trusted_glob.get(), &system_principals,
|
||||
"trusted", 0, NULL, bytes, strlen(bytes),
|
||||
"", 0));
|
||||
trusted_fun.set(JS_GetFunctionObject(fun));
|
||||
}
|
||||
|
||||
JSFunction *fun = JS_NewFunction(cx, CallTrusted, 0, 0, global, "callTrusted");
|
||||
JS::Anchor<JSObject *> callTrusted(JS_GetFunctionObject(fun));
|
||||
|
||||
const char *paramName = "f";
|
||||
const char *bytes = "try { "
|
||||
" return untrusted(trusted); "
|
||||
"} catch (e) { "
|
||||
" return f(); "
|
||||
"} ";
|
||||
CHECK(fun = JS_CompileFunction(cx, global, "untrusted", 1, ¶mName,
|
||||
bytes, strlen(bytes), "", 0));
|
||||
|
||||
jsval arg = OBJECT_TO_JSVAL(callTrusted.get());
|
||||
jsval rval;
|
||||
CHECK(JS_CallFunction(cx, NULL, fun, 1, &arg, &rval));
|
||||
CHECK(JSVAL_TO_INT(rval) == 42);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testChromeBuffer)
|
|
@ -637,7 +637,8 @@ static JSBool js_NewRuntimeWasCalled = JS_FALSE;
|
|||
#endif
|
||||
|
||||
JSRuntime::JSRuntime()
|
||||
: gcChunkAllocator(&defaultGCChunkAllocator)
|
||||
: gcChunkAllocator(&defaultGCChunkAllocator),
|
||||
trustedPrincipals_(NULL)
|
||||
{
|
||||
/* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */
|
||||
JS_INIT_CLIST(&contextList);
|
||||
|
@ -4152,6 +4153,12 @@ JS_GetSecurityCallbacks(JSContext *cx)
|
|||
: cx->runtime->securityCallbacks;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_SetTrustedPrincipals(JSRuntime *rt, JSPrincipals *prin)
|
||||
{
|
||||
rt->setTrustedPrincipals(prin);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSFunction *)
|
||||
JS_NewFunction(JSContext *cx, JSNative native, uintN nargs, uintN flags,
|
||||
JSObject *parent, const char *name)
|
||||
|
|
|
@ -2594,6 +2594,21 @@ JS_SetContextSecurityCallbacks(JSContext *cx, JSSecurityCallbacks *callbacks);
|
|||
extern JS_PUBLIC_API(JSSecurityCallbacks *)
|
||||
JS_GetSecurityCallbacks(JSContext *cx);
|
||||
|
||||
/*
|
||||
* Code running with "trusted" principals will be given a deeper stack
|
||||
* allocation than ordinary scripts. This allows trusted script to run after
|
||||
* untrusted script has exhausted the stack. This function sets the
|
||||
* runtime-wide trusted principal.
|
||||
*
|
||||
* This principals is not held (via JS_HoldPrincipals/JS_DropPrincipals) since
|
||||
* there is no available JSContext. Instead, the caller must ensure that the
|
||||
* given principals stays valid for as long as 'rt' may point to it. If the
|
||||
* principals would be destroyed before 'rt', JS_SetTrustedPrincipals must be
|
||||
* called again, passing NULL for 'prin'.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_SetTrustedPrincipals(JSRuntime *rt, JSPrincipals *prin);
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
/*
|
||||
|
|
|
@ -597,6 +597,12 @@ struct JSRuntime {
|
|||
#define JS_THREAD_DATA(cx) (&(cx)->runtime->threadData)
|
||||
#endif
|
||||
|
||||
private:
|
||||
JSPrincipals *trustedPrincipals_;
|
||||
public:
|
||||
void setTrustedPrincipals(JSPrincipals *p) { trustedPrincipals_ = p; }
|
||||
JSPrincipals *trustedPrincipals() const { return trustedPrincipals_; }
|
||||
|
||||
/*
|
||||
* Object shape (property cache structural type) identifier generator.
|
||||
*
|
||||
|
|
|
@ -186,7 +186,7 @@ js_GetArgsProperty(JSContext *cx, StackFrame *fp, jsid id, Value *vp)
|
|||
js::ArgumentsObject *
|
||||
ArgumentsObject::create(JSContext *cx, uint32 argc, JSObject &callee)
|
||||
{
|
||||
JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
|
||||
JS_ASSERT(argc <= StackSpace::ARGS_LENGTH_MAX);
|
||||
|
||||
JSObject *proto;
|
||||
if (!js_GetClassPrototype(cx, callee.getGlobal(), JSProto_Object, &proto))
|
||||
|
@ -2119,7 +2119,7 @@ js_fun_apply(JSContext *cx, uintN argc, Value *vp)
|
|||
LeaveTrace(cx);
|
||||
|
||||
/* Step 6. */
|
||||
uintN n = uintN(JS_MIN(length, JS_ARGS_LENGTH_MAX));
|
||||
uintN n = uintN(JS_MIN(length, StackSpace::ARGS_LENGTH_MAX));
|
||||
|
||||
InvokeArgsGuard args;
|
||||
if (!cx->stack.pushInvokeArgs(cx, n, &args))
|
||||
|
@ -2223,7 +2223,7 @@ CallOrConstructBoundFunction(JSContext *cx, uintN argc, Value *vp)
|
|||
uintN argslen;
|
||||
const Value *boundArgs = obj->getBoundFunctionArguments(argslen);
|
||||
|
||||
if (argc + argslen > JS_ARGS_LENGTH_MAX) {
|
||||
if (argc + argslen > StackSpace::ARGS_LENGTH_MAX) {
|
||||
js_ReportAllocationOverflow(cx);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -576,25 +576,6 @@ js_PutArgsObject(js::StackFrame *fp);
|
|||
inline bool
|
||||
js_IsNamedLambda(JSFunction *fun) { return (fun->flags & JSFUN_LAMBDA) && fun->atom; }
|
||||
|
||||
/*
|
||||
* Maximum supported value of arguments.length. It bounds the maximum number of
|
||||
* arguments that can be supplied via the second (so-called |argArray|) param
|
||||
* to Function.prototype.apply. This value also bounds the number of elements
|
||||
* parsed in an array initialiser.
|
||||
*
|
||||
* The thread's stack is the limiting factor for this number. It is currently
|
||||
* 2MB, which fits a little less than 2^19 arguments (once the stack frame,
|
||||
* callstack, etc. are included). Pick a max args length that is a little less.
|
||||
*/
|
||||
const uint32 JS_ARGS_LENGTH_MAX = JS_BIT(19) - 1024;
|
||||
|
||||
/*
|
||||
* JSSLOT_ARGS_LENGTH stores ((argc << 1) | overwritten_flag) as an Int32
|
||||
* Value. Thus (JS_ARGS_LENGTH_MAX << 1) | 1 must be less than JSVAL_INT_MAX.
|
||||
*/
|
||||
JS_STATIC_ASSERT(JS_ARGS_LENGTH_MAX <= JS_BIT(30));
|
||||
JS_STATIC_ASSERT(((JS_ARGS_LENGTH_MAX << 1) | 1) <= JSVAL_INT_MAX);
|
||||
|
||||
extern JSBool
|
||||
js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp);
|
||||
|
||||
|
|
|
@ -625,7 +625,7 @@ Invoke(JSContext *cx, const CallArgs &argsRef, MaybeConstruct construct)
|
|||
/* N.B. Must be kept in sync with InvokeSessionGuard::start/invoke */
|
||||
|
||||
CallArgs args = argsRef;
|
||||
JS_ASSERT(args.argc() <= JS_ARGS_LENGTH_MAX);
|
||||
JS_ASSERT(args.argc() <= StackSpace::ARGS_LENGTH_MAX);
|
||||
|
||||
if (args.calleev().isPrimitive()) {
|
||||
js_ReportIsNotFunction(cx, &args.calleev(), ToReportFlags(construct));
|
||||
|
@ -746,7 +746,7 @@ InvokeSessionGuard::start(JSContext *cx, const Value &calleev, const Value &this
|
|||
|
||||
/* Hoist dynamic checks from CheckStackAndEnterMethodJIT. */
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
stackLimit_ = stack.space().getStackLimit(cx);
|
||||
stackLimit_ = stack.space().getStackLimit(cx, REPORT_ERROR);
|
||||
if (!stackLimit_)
|
||||
return false;
|
||||
|
||||
|
@ -4095,7 +4095,7 @@ BEGIN_CASE(JSOP_FUNAPPLY)
|
|||
}
|
||||
|
||||
JSScript *newScript = fun->script();
|
||||
if (!cx->stack.pushInlineFrame(cx, regs, args, *callee, fun, newScript, construct, OOMCheck()))
|
||||
if (!cx->stack.pushInlineFrame(cx, regs, args, *callee, fun, newScript, construct))
|
||||
goto error;
|
||||
|
||||
/* Refresh local js::Interpret state. */
|
||||
|
@ -5310,7 +5310,7 @@ BEGIN_CASE(JSOP_INITELEM)
|
|||
if (rref.isMagic(JS_ARRAY_HOLE)) {
|
||||
JS_ASSERT(obj->isArray());
|
||||
JS_ASSERT(JSID_IS_INT(id));
|
||||
JS_ASSERT(jsuint(JSID_TO_INT(id)) < JS_ARGS_LENGTH_MAX);
|
||||
JS_ASSERT(jsuint(JSID_TO_INT(id)) < StackSpace::ARGS_LENGTH_MAX);
|
||||
if (js_GetOpcode(cx, script, regs.pc + JSOP_INITELEM_LENGTH) == JSOP_ENDINIT &&
|
||||
!js_SetLengthProperty(cx, obj, (jsuint) (JSID_TO_INT(id) + 1))) {
|
||||
goto error;
|
||||
|
|
|
@ -8302,7 +8302,7 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot)
|
|||
matched = tokenStream.matchToken(TOK_RB, TSF_OPERAND);
|
||||
if (!matched) {
|
||||
for (index = 0; ; index++) {
|
||||
if (index == JS_ARGS_LENGTH_MAX) {
|
||||
if (index == StackSpace::ARGS_LENGTH_MAX) {
|
||||
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_ARRAY_INIT_TOO_BIG);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -55,7 +55,6 @@
|
|||
#include "jsatom.h"
|
||||
#include "jscntxt.h"
|
||||
#include "jsdbgapi.h"
|
||||
#include "jsfun.h" /* for JS_ARGS_LENGTH_MAX */
|
||||
#include "jslock.h"
|
||||
#include "jsnum.h"
|
||||
#include "jsobj.h"
|
||||
|
|
|
@ -61,7 +61,6 @@
|
|||
#include "jsbool.h"
|
||||
#include "jsbuiltins.h"
|
||||
#include "jscntxt.h"
|
||||
#include "jsfun.h" /* for JS_ARGS_LENGTH_MAX */
|
||||
#include "jsgc.h"
|
||||
#include "jsinterp.h"
|
||||
#include "jslock.h"
|
||||
|
@ -3101,7 +3100,7 @@ static JSBool
|
|||
str_fromCharCode(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
Value *argv = JS_ARGV(cx, vp);
|
||||
JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
|
||||
JS_ASSERT(argc <= StackSpace::ARGS_LENGTH_MAX);
|
||||
if (argc == 1) {
|
||||
uint16_t code;
|
||||
if (!ValueToUint16(cx, argv[0], &code))
|
||||
|
|
|
@ -5739,8 +5739,7 @@ SynthesizeFrame(JSContext* cx, const FrameInfo& fi, JSObject* callee)
|
|||
/* Push a frame for the call. */
|
||||
CallArgs args = CallArgsFromSp(fi.get_argc(), regs.sp);
|
||||
cx->stack.pushInlineFrame(cx, regs, args, *callee, newfun, newscript,
|
||||
MaybeConstructFromBool(fi.is_constructing()),
|
||||
NoCheck());
|
||||
MaybeConstructFromBool(fi.is_constructing()));
|
||||
|
||||
#ifdef DEBUG
|
||||
/* These should be initialized by FlushNativeStackFrame. */
|
||||
|
@ -6635,7 +6634,8 @@ ExecuteTree(JSContext* cx, TraceMonitor* tm, TreeFragment* f,
|
|||
#endif
|
||||
JS_ASSERT(f->root == f && f->code());
|
||||
|
||||
if (!ScopeChainCheck(cx, f) || !cx->stack.space().ensureEnoughSpaceToEnterTrace()) {
|
||||
if (!ScopeChainCheck(cx, f) ||
|
||||
!cx->stack.space().ensureEnoughSpaceToEnterTrace(cx)) {
|
||||
*lrp = NULL;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -670,6 +670,16 @@ PodEqual(T *one, T *two, size_t len)
|
|||
return !memcmp(one, two, len * sizeof(T));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Ordinarily, a function taking a JSContext* 'cx' paremter reports errors on
|
||||
* the context. In some cases, functions optionally report and indicate this by
|
||||
* taking a nullable 'maybecx' parameter. In some cases, though, a function
|
||||
* always needs a 'cx', but optionally reports. This option is presented by the
|
||||
* MaybeReportError.
|
||||
*/
|
||||
enum MaybeReportError { REPORT_ERROR = true, DONT_REPORT_ERROR = false };
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* defined(__cplusplus) */
|
||||
|
|
|
@ -399,7 +399,7 @@ ForceFrame::enter()
|
|||
JSObject *scopeChain = target->getGlobal();
|
||||
JS_ASSERT(scopeChain->isNative());
|
||||
|
||||
return context->stack.pushDummyFrame(context, *scopeChain, frame);
|
||||
return context->stack.pushDummyFrame(context, REPORT_ERROR, *scopeChain, frame);
|
||||
}
|
||||
|
||||
AutoCompartment::AutoCompartment(JSContext *cx, JSObject *target)
|
||||
|
@ -424,13 +424,23 @@ AutoCompartment::enter()
|
|||
if (origin != destination) {
|
||||
LeaveTrace(context);
|
||||
|
||||
context->compartment = destination;
|
||||
JSObject *scopeChain = target->getGlobal();
|
||||
JS_ASSERT(scopeChain->isNative());
|
||||
|
||||
frame.construct();
|
||||
if (!context->stack.pushDummyFrame(context, *scopeChain, &frame.ref())) {
|
||||
|
||||
/*
|
||||
* Set the compartment eagerly so that pushDummyFrame associates the
|
||||
* resource allocation request with 'destination' instead of 'origin'.
|
||||
* (This is important when content has overflowed the stack and chrome
|
||||
* is preparing to run JS to throw up a slow script dialog.) However,
|
||||
* if an exception is thrown, we need it to be in origin's compartment
|
||||
* so be careful to only report after restoring.
|
||||
*/
|
||||
context->compartment = destination;
|
||||
if (!context->stack.pushDummyFrame(context, DONT_REPORT_ERROR, *scopeChain, &frame.ref())) {
|
||||
context->compartment = origin;
|
||||
js_ReportOverRecursed(context);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -202,9 +202,8 @@ void JS_FASTCALL
|
|||
stubs::HitStackQuota(VMFrame &f)
|
||||
{
|
||||
/* Include space to push another frame. */
|
||||
uintN nvals = f.fp()->script()->nslots + VALUES_PER_STACK_FRAME;
|
||||
JS_ASSERT(f.regs.sp == f.fp()->base());
|
||||
if (f.cx->stack.space().tryBumpLimit(NULL, f.regs.sp, nvals, &f.stackLimit))
|
||||
f.stackLimit = f.cx->stack.space().getStackLimit(f.cx, DONT_REPORT_ERROR);
|
||||
if (f.stackLimit)
|
||||
return;
|
||||
|
||||
f.cx->stack.popFrameAfterOverflow();
|
||||
|
@ -240,14 +239,15 @@ stubs::FixupArity(VMFrame &f, uint32 nactual)
|
|||
|
||||
/* Reserve enough space for a callee frame. */
|
||||
CallArgs args = CallArgsFromSp(nactual, f.regs.sp);
|
||||
StackFrame *fp = cx->stack.getFixupFrame(cx, f.regs, args, fun, script, ncode,
|
||||
construct, LimitCheck(&f.stackLimit));
|
||||
StackFrame *fp = cx->stack.getFixupFrame(cx, DONT_REPORT_ERROR, args, fun,
|
||||
script, ncode, construct, &f.stackLimit);
|
||||
if (!fp) {
|
||||
/*
|
||||
* The PC is not coherent with the current frame, so fix it up for
|
||||
* exception handling.
|
||||
*/
|
||||
f.regs.pc = f.jit()->nativeToPC(ncode);
|
||||
js_ReportOverRecursed(cx);
|
||||
THROWV(NULL);
|
||||
}
|
||||
|
||||
|
@ -316,8 +316,7 @@ UncachedInlineCall(VMFrame &f, MaybeConstruct construct, void **pret, bool *unji
|
|||
JSScript *newscript = newfun->script();
|
||||
|
||||
/* Get pointer to new frame/slots, prepare arguments. */
|
||||
LimitCheck check(&f.stackLimit);
|
||||
if (!cx->stack.pushInlineFrame(cx, f.regs, args, callee, newfun, newscript, construct, check))
|
||||
if (!cx->stack.pushInlineFrame(cx, f.regs, args, callee, newfun, newscript, construct, &f.stackLimit))
|
||||
return false;
|
||||
|
||||
/* Scope with a call object parented by callee's parent. */
|
||||
|
|
|
@ -709,7 +709,7 @@ CheckStackAndEnterMethodJIT(JSContext *cx, StackFrame *fp, void *code)
|
|||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
|
||||
Value *stackLimit = cx->stack.space().getStackLimit(cx);
|
||||
Value *stackLimit = cx->stack.space().getStackLimit(cx, REPORT_ERROR);
|
||||
if (!stackLimit)
|
||||
return false;
|
||||
|
||||
|
|
|
@ -1096,7 +1096,7 @@ ic::SplatApplyArgs(VMFrame &f)
|
|||
THROWV(false);
|
||||
|
||||
/* Step 6. */
|
||||
n = Min(length, JS_ARGS_LENGTH_MAX);
|
||||
n = Min(length, StackSpace::ARGS_LENGTH_MAX);
|
||||
|
||||
if (!BumpStack(f, n))
|
||||
THROWV(false);
|
||||
|
@ -1151,7 +1151,7 @@ ic::SplatApplyArgs(VMFrame &f)
|
|||
JS_ASSERT(!JS_ON_TRACE(cx));
|
||||
|
||||
/* Step 6. */
|
||||
uintN n = uintN(JS_MIN(length, JS_ARGS_LENGTH_MAX));
|
||||
uintN n = uintN(JS_MIN(length, StackSpace::ARGS_LENGTH_MAX));
|
||||
|
||||
intN delta = n - 1;
|
||||
if (delta > 0 && !BumpStack(f, delta))
|
||||
|
|
|
@ -1299,7 +1299,7 @@ stubs::InitElem(VMFrame &f, uint32 last)
|
|||
if (rref.isMagic(JS_ARRAY_HOLE)) {
|
||||
JS_ASSERT(obj->isArray());
|
||||
JS_ASSERT(JSID_IS_INT(id));
|
||||
JS_ASSERT(jsuint(JSID_TO_INT(id)) < JS_ARGS_LENGTH_MAX);
|
||||
JS_ASSERT(jsuint(JSID_TO_INT(id)) < StackSpace::ARGS_LENGTH_MAX);
|
||||
if (last && !js_SetLengthProperty(cx, obj, (jsuint) (JSID_TO_INT(id) + 1)))
|
||||
THROW();
|
||||
} else {
|
||||
|
|
|
@ -4797,6 +4797,13 @@ EnableStackWalkingAssertion(JSContext *cx, uintN argc, jsval *vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
GetMaxArgs(JSContext *cx, uintN arg, jsval *vp)
|
||||
{
|
||||
JS_SET_RVAL(cx, vp, INT_TO_JSVAL(StackSpace::ARGS_LENGTH_MAX));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSFunctionSpec shell_functions[] = {
|
||||
JS_FN("version", Version, 0,0),
|
||||
JS_FN("revertVersion", RevertVersion, 0,0),
|
||||
|
@ -4897,6 +4904,7 @@ static JSFunctionSpec shell_functions[] = {
|
|||
JS_FN("newGlobal", NewGlobal, 1,0),
|
||||
JS_FN("parseLegacyJSON",ParseLegacyJSON,1,0),
|
||||
JS_FN("enableStackWalkingAssertion",EnableStackWalkingAssertion,1,0),
|
||||
JS_FN("getMaxArgs", GetMaxArgs, 0,0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
|
@ -5042,6 +5050,7 @@ static const char *const shell_help_messages[] = {
|
|||
" code. If your test isn't ridiculously thorough, such that performing this\n"
|
||||
" assertion increases test duration by an order of magnitude, you shouldn't\n"
|
||||
" use this.",
|
||||
"getMaxArgs() Return the maximum number of supported args for a call."
|
||||
|
||||
/* Keep these last: see the static assertion below. */
|
||||
#ifdef MOZ_PROFILING
|
||||
|
@ -6046,6 +6055,21 @@ MaybeOverrideOutFileFromEnv(const char* const envVar,
|
|||
}
|
||||
}
|
||||
|
||||
JSBool
|
||||
ShellPrincipalsSubsume(JSPrincipals *, JSPrincipals *)
|
||||
{
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSPrincipals shellTrustedPrincipals = {
|
||||
(char *)"[shell trusted principals]",
|
||||
NULL,
|
||||
NULL,
|
||||
1,
|
||||
NULL, /* nobody should be destroying this */
|
||||
ShellPrincipalsSubsume
|
||||
};
|
||||
|
||||
int
|
||||
main(int argc, char **argv, char **envp)
|
||||
{
|
||||
|
@ -6132,6 +6156,8 @@ main(int argc, char **argv, char **envp)
|
|||
if (!rt)
|
||||
return 1;
|
||||
|
||||
JS_SetTrustedPrincipals(rt, &shellTrustedPrincipals);
|
||||
|
||||
if (!InitWatchdog(rt))
|
||||
return 1;
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ script regress-345961.js
|
|||
script regress-348810.js
|
||||
script regress-350256-01.js
|
||||
script regress-350256-02.js
|
||||
silentfail script regress-350256-03.js
|
||||
script regress-360681-01.js
|
||||
script regress-360681-02.js
|
||||
script regress-364104.js
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
|
||||
//-----------------------------------------------------------------------------
|
||||
var BUGNUMBER = 350256;
|
||||
var summary = 'Array.apply maximum arguments: 2^16';
|
||||
var summary = 'Array.apply maximum arguments';
|
||||
var actual = '';
|
||||
var expect = '';
|
||||
|
||||
|
|
|
@ -37,13 +37,14 @@
|
|||
|
||||
//-----------------------------------------------------------------------------
|
||||
var BUGNUMBER = 350256;
|
||||
var summary = 'Array.apply maximum arguments: 2^19 - 1024';
|
||||
var summary = 'Array.apply maximum arguments';
|
||||
var actual = '';
|
||||
var expect = '';
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
test(Math.pow(2, 19) - 1024);
|
||||
if (getMaxArgs)
|
||||
test(getMaxArgs());
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function test(length)
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is JavaScript Engine testing utilities.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2006
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s): Bertrand Le Roy
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
var BUGNUMBER = 350256;
|
||||
var summary = 'Array.apply maximum arguments: 2^19-1024';
|
||||
var actual = '';
|
||||
var expect = '';
|
||||
|
||||
expectExitCode(0);
|
||||
expectExitCode(5);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
test(Math.pow(2, 19)-1024);
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function test(length)
|
||||
{
|
||||
enterFunc ('test');
|
||||
printBugNumber(BUGNUMBER);
|
||||
printStatus (summary);
|
||||
|
||||
var a = new Array();
|
||||
a[length - 2] = 'length-2';
|
||||
a[length - 1] = 'length-1';
|
||||
|
||||
var b = Array.apply(null, a);
|
||||
|
||||
expect = length + ',length-2,length-1';
|
||||
actual = b.length + "," + b[length - 2] + "," + b[length - 1];
|
||||
reportCompare(expect, actual, summary);
|
||||
|
||||
function f() {
|
||||
return arguments.length + "," + arguments[length - 2] + "," +
|
||||
arguments[length - 1];
|
||||
}
|
||||
|
||||
expect = length + ',length-2,length-1';
|
||||
actual = f.apply(null, a);
|
||||
|
||||
reportCompare(expect, actual, summary);
|
||||
|
||||
exitFunc ('test');
|
||||
}
|
|
@ -58,7 +58,7 @@ inline uint32
|
|||
ArgumentsObject::initialLength() const
|
||||
{
|
||||
uint32 argc = uint32(getSlot(INITIAL_LENGTH_SLOT).toInt32()) >> PACKED_BITS_COUNT;
|
||||
JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
|
||||
JS_ASSERT(argc <= StackSpace::ARGS_LENGTH_MAX);
|
||||
return argc;
|
||||
}
|
||||
|
||||
|
|
|
@ -144,8 +144,7 @@ StackFrame::resetCallFrame(JSScript *script)
|
|||
}
|
||||
|
||||
inline void
|
||||
StackFrame::initJitFrameCallerHalf(JSContext *cx, StackFrame::Flags flags,
|
||||
void *ncode)
|
||||
StackFrame::initJitFrameCallerHalf(StackFrame *prev, StackFrame::Flags flags, void *ncode)
|
||||
{
|
||||
JS_ASSERT((flags & ~(CONSTRUCTING |
|
||||
FUNCTION |
|
||||
|
@ -153,7 +152,7 @@ StackFrame::initJitFrameCallerHalf(JSContext *cx, StackFrame::Flags flags,
|
|||
UNDERFLOW_ARGS)) == 0);
|
||||
|
||||
flags_ = FUNCTION | flags;
|
||||
prev_ = cx->fp();
|
||||
prev_ = prev;
|
||||
ncode_ = ncode;
|
||||
}
|
||||
|
||||
|
@ -176,15 +175,11 @@ StackFrame::initJitFrameEarlyPrologue(JSFunction *fun, uint32 nactual)
|
|||
inline bool
|
||||
StackFrame::initJitFrameLatePrologue(JSContext *cx, Value **limit)
|
||||
{
|
||||
uintN nvals = script()->nslots + VALUES_PER_STACK_FRAME;
|
||||
Value *required = (Value *)this + nvals;
|
||||
if (required >= *limit) {
|
||||
ContextStack &stack = cx->stack;
|
||||
if (!stack.space().tryBumpLimit(NULL, slots(), nvals, limit)) {
|
||||
stack.popFrameAfterOverflow();
|
||||
js_ReportOverRecursed(cx);
|
||||
return false;
|
||||
}
|
||||
*limit = cx->stack.space().getStackLimit(cx, DONT_REPORT_ERROR);
|
||||
if (!*limit) {
|
||||
cx->stack.popFrameAfterOverflow();
|
||||
js_ReportOverRecursed(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
SetValueRangeToUndefined(slots(), script()->nfixed);
|
||||
|
@ -383,97 +378,60 @@ StackFrame::markActivationObjectsAsPut()
|
|||
|
||||
#ifdef JS_TRACER
|
||||
JS_ALWAYS_INLINE bool
|
||||
StackSpace::ensureEnoughSpaceToEnterTrace()
|
||||
StackSpace::ensureEnoughSpaceToEnterTrace(JSContext *cx)
|
||||
{
|
||||
ptrdiff_t needed = TraceNativeStorage::MAX_NATIVE_STACK_SLOTS +
|
||||
TraceNativeStorage::MAX_CALL_STACK_ENTRIES * VALUES_PER_STACK_FRAME;
|
||||
#ifdef XP_WIN
|
||||
return ensureSpace(NULL, firstUnused(), needed);
|
||||
#else
|
||||
return end_ - firstUnused() > needed;
|
||||
#endif
|
||||
return ensureSpace(cx, DONT_REPORT_ERROR, firstUnused(), needed);
|
||||
}
|
||||
#endif
|
||||
|
||||
STATIC_POSTCONDITION(!return || ubound(from) >= nvals)
|
||||
JS_ALWAYS_INLINE bool
|
||||
StackSpace::ensureSpace(JSContext *maybecx, Value *from, ptrdiff_t nvals) const
|
||||
StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals) const
|
||||
{
|
||||
assertInvariants();
|
||||
JS_ASSERT(from >= firstUnused());
|
||||
#ifdef XP_WIN
|
||||
JS_ASSERT(from <= commitEnd_);
|
||||
if (commitEnd_ - from < nvals)
|
||||
return bumpCommit(maybecx, from, nvals);
|
||||
return true;
|
||||
#else
|
||||
if (end_ - from < nvals) {
|
||||
js_ReportOverRecursed(maybecx);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
if (JS_UNLIKELY(conservativeEnd_ - from < nvals))
|
||||
return ensureSpaceSlow(cx, report, from, nvals);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline Value *
|
||||
StackSpace::getStackLimit(JSContext *cx)
|
||||
StackSpace::getStackLimit(JSContext *cx, MaybeReportError report)
|
||||
{
|
||||
Value *limit;
|
||||
#ifdef XP_WIN
|
||||
limit = commitEnd_;
|
||||
#else
|
||||
limit = end_;
|
||||
#endif
|
||||
|
||||
/* See getStackLimit comment in Stack.h. */
|
||||
FrameRegs ®s = cx->regs();
|
||||
uintN minSpace = regs.fp()->numSlots() + VALUES_PER_STACK_FRAME;
|
||||
if (regs.sp + minSpace > limit) {
|
||||
js_ReportOverRecursed(cx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return limit;
|
||||
uintN nvals = regs.fp()->numSlots() + VALUES_PER_STACK_FRAME;
|
||||
return ensureSpace(cx, report, regs.sp, nvals)
|
||||
? conservativeEnd_
|
||||
: NULL;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
JS_ALWAYS_INLINE bool
|
||||
OOMCheck::operator()(JSContext *cx, StackSpace &space, Value *from, uintN nvals)
|
||||
JS_ALWAYS_INLINE StackFrame *
|
||||
ContextStack::getCallFrame(JSContext *cx, MaybeReportError report, const CallArgs &args,
|
||||
JSFunction *fun, JSScript *script, StackFrame::Flags *flags) const
|
||||
{
|
||||
return space.ensureSpace(cx, from, nvals);
|
||||
}
|
||||
JS_ASSERT(fun->script() == script);
|
||||
uintN nformal = fun->nargs;
|
||||
|
||||
Value *firstUnused = args.end();
|
||||
JS_ASSERT(firstUnused == space().firstUnused());
|
||||
|
||||
JS_ALWAYS_INLINE bool
|
||||
LimitCheck::operator()(JSContext *cx, StackSpace &space, Value *from, uintN nvals)
|
||||
{
|
||||
/*
|
||||
* Include an extra sizeof(StackFrame) to satisfy the method-jit
|
||||
* stackLimit invariant.
|
||||
*/
|
||||
nvals += VALUES_PER_STACK_FRAME;
|
||||
JS_ASSERT(from < *limit);
|
||||
if (*limit - from >= ptrdiff_t(nvals))
|
||||
return true;
|
||||
return space.tryBumpLimit(cx, from, nvals, limit);
|
||||
}
|
||||
|
||||
template <class Check>
|
||||
JS_ALWAYS_INLINE StackFrame *
|
||||
ContextStack::getCallFrame(JSContext *cx, const CallArgs &args,
|
||||
JSFunction *fun, JSScript *script,
|
||||
StackFrame::Flags *flags, Check check) const
|
||||
{
|
||||
JS_ASSERT(fun->script() == script);
|
||||
JS_ASSERT(space().firstUnused() == args.end());
|
||||
|
||||
Value *firstUnused = args.end();
|
||||
uintN nvals = VALUES_PER_STACK_FRAME + script->nslots;
|
||||
uintN nformal = fun->nargs;
|
||||
uintN nvals = 2 * VALUES_PER_STACK_FRAME + script->nslots;
|
||||
|
||||
/* Maintain layout invariant: &formalArgs[0] == ((Value *)fp) - nformal. */
|
||||
|
||||
if (args.argc() == nformal) {
|
||||
if (JS_UNLIKELY(!check(cx, space(), firstUnused, nvals)))
|
||||
if (!space().ensureSpace(cx, report, firstUnused, nvals))
|
||||
return NULL;
|
||||
return reinterpret_cast<StackFrame *>(firstUnused);
|
||||
}
|
||||
|
@ -481,7 +439,7 @@ ContextStack::getCallFrame(JSContext *cx, const CallArgs &args,
|
|||
if (args.argc() < nformal) {
|
||||
*flags = StackFrame::Flags(*flags | StackFrame::UNDERFLOW_ARGS);
|
||||
uintN nmissing = nformal - args.argc();
|
||||
if (JS_UNLIKELY(!check(cx, space(), firstUnused, nmissing + nvals)))
|
||||
if (!space().ensureSpace(cx, report, firstUnused, nmissing + nvals))
|
||||
return NULL;
|
||||
SetValueRangeToUndefined(firstUnused, nmissing);
|
||||
return reinterpret_cast<StackFrame *>(firstUnused + nmissing);
|
||||
|
@ -489,20 +447,18 @@ ContextStack::getCallFrame(JSContext *cx, const CallArgs &args,
|
|||
|
||||
*flags = StackFrame::Flags(*flags | StackFrame::OVERFLOW_ARGS);
|
||||
uintN ncopy = 2 + nformal;
|
||||
if (JS_UNLIKELY(!check(cx, space(), firstUnused, ncopy + nvals)))
|
||||
if (!space().ensureSpace(cx, report, firstUnused, ncopy + nvals))
|
||||
return NULL;
|
||||
|
||||
Value *dst = firstUnused;
|
||||
Value *src = args.base();
|
||||
PodCopy(dst, src, ncopy);
|
||||
return reinterpret_cast<StackFrame *>(firstUnused + ncopy);
|
||||
}
|
||||
|
||||
template <class Check>
|
||||
JS_ALWAYS_INLINE bool
|
||||
ContextStack::pushInlineFrame(JSContext *cx, FrameRegs ®s, const CallArgs &args,
|
||||
JSObject &callee, JSFunction *fun, JSScript *script,
|
||||
MaybeConstruct construct, Check check)
|
||||
MaybeConstruct construct)
|
||||
{
|
||||
JS_ASSERT(onTop());
|
||||
JS_ASSERT(®s == &seg_->regs());
|
||||
|
@ -512,7 +468,7 @@ ContextStack::pushInlineFrame(JSContext *cx, FrameRegs ®s, const CallArgs &ar
|
|||
JS_ASSERT(fun->script() == script);
|
||||
|
||||
StackFrame::Flags flags = ToFrameFlags(construct);
|
||||
StackFrame *fp = getCallFrame(cx, args, fun, script, &flags, check);
|
||||
StackFrame *fp = getCallFrame(cx, REPORT_ERROR, args, fun, script, &flags);
|
||||
if (!fp)
|
||||
return false;
|
||||
|
||||
|
@ -522,25 +478,36 @@ ContextStack::pushInlineFrame(JSContext *cx, FrameRegs ®s, const CallArgs &ar
|
|||
return true;
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE bool
|
||||
ContextStack::pushInlineFrame(JSContext *cx, FrameRegs ®s, const CallArgs &args,
|
||||
JSObject &callee, JSFunction *fun, JSScript *script,
|
||||
MaybeConstruct construct, Value **stackLimit)
|
||||
{
|
||||
if (!pushInlineFrame(cx, regs, args, callee, fun, script, construct))
|
||||
return false;
|
||||
*stackLimit = space().conservativeEnd_;
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE StackFrame *
|
||||
ContextStack::getFixupFrame(JSContext *cx, FrameRegs ®s, const CallArgs &args,
|
||||
JSFunction *fun, JSScript *script, void *ncode,
|
||||
MaybeConstruct construct, LimitCheck check)
|
||||
ContextStack::getFixupFrame(JSContext *cx, MaybeReportError report,
|
||||
const CallArgs &args, JSFunction *fun, JSScript *script,
|
||||
void *ncode, MaybeConstruct construct, Value **stackLimit)
|
||||
{
|
||||
JS_ASSERT(onTop());
|
||||
JS_ASSERT(®s == &cx->regs());
|
||||
JS_ASSERT(regs.sp == args.end());
|
||||
JS_ASSERT(args.callee().getFunctionPrivate() == fun);
|
||||
JS_ASSERT(fun->script() == script);
|
||||
|
||||
StackFrame::Flags flags = ToFrameFlags(construct);
|
||||
StackFrame *fp = getCallFrame(cx, args, fun, script, &flags, check);
|
||||
StackFrame *fp = getCallFrame(cx, report, args, fun, script, &flags);
|
||||
if (!fp)
|
||||
return NULL;
|
||||
|
||||
/* Do not init late prologue or regs; this is done by jit code. */
|
||||
fp->initJitFrameCallerHalf(cx, flags, ncode);
|
||||
fp->initJitFrameCallerHalf(cx->fp(), flags, ncode);
|
||||
fp->initJitFrameEarlyPrologue(fun, args.argc());
|
||||
|
||||
*stackLimit = space().conservativeEnd_;
|
||||
return fp;
|
||||
}
|
||||
|
||||
|
@ -648,7 +615,7 @@ ArgumentsObject::getElements(uint32 start, uint32 count, Value *vp)
|
|||
return false;
|
||||
|
||||
/* Otherwise, element values are on the stack. */
|
||||
JS_ASSERT(fp->numActualArgs() <= JS_ARGS_LENGTH_MAX);
|
||||
JS_ASSERT(fp->numActualArgs() <= StackSpace::ARGS_LENGTH_MAX);
|
||||
return fp->forEachCanonicalActualArg(detail::CopyNonHoleArgsTo(this, vp), start, count);
|
||||
}
|
||||
|
||||
|
|
|
@ -302,11 +302,17 @@ StackSegment::popCall()
|
|||
/*****************************************************************************/
|
||||
|
||||
StackSpace::StackSpace()
|
||||
: base_(NULL),
|
||||
: seg_(NULL),
|
||||
base_(NULL),
|
||||
conservativeEnd_(NULL),
|
||||
#ifdef XP_WIN
|
||||
commitEnd_(NULL),
|
||||
end_(NULL),
|
||||
seg_(NULL)
|
||||
{}
|
||||
#endif
|
||||
defaultEnd_(NULL),
|
||||
trustedEnd_(NULL)
|
||||
{
|
||||
assertInvariants();
|
||||
}
|
||||
|
||||
bool
|
||||
StackSpace::init()
|
||||
|
@ -320,27 +326,32 @@ StackSpace::init()
|
|||
if (p != check)
|
||||
return false;
|
||||
base_ = reinterpret_cast<Value *>(p);
|
||||
commitEnd_ = base_ + COMMIT_VALS;
|
||||
end_ = base_ + CAPACITY_VALS;
|
||||
conservativeEnd_ = commitEnd_ = base_ + COMMIT_VALS;
|
||||
trustedEnd_ = base_ + CAPACITY_VALS;
|
||||
defaultEnd_ = trustedEnd_ - BUFFER_VALS;
|
||||
#elif defined(XP_OS2)
|
||||
if (DosAllocMem(&p, CAPACITY_BYTES, PAG_COMMIT | PAG_READ | PAG_WRITE | OBJ_ANY) &&
|
||||
DosAllocMem(&p, CAPACITY_BYTES, PAG_COMMIT | PAG_READ | PAG_WRITE))
|
||||
return false;
|
||||
base_ = reinterpret_cast<Value *>(p);
|
||||
end_ = commitEnd_ = base_ + CAPACITY_VALS;
|
||||
trustedEnd_ = base_ + CAPACITY_VALS;
|
||||
conservativeEnd_ = defaultEnd_ = trustedEnd_ - BUFFER_VALS;
|
||||
#else
|
||||
JS_ASSERT(CAPACITY_BYTES % getpagesize() == 0);
|
||||
p = mmap(NULL, CAPACITY_BYTES, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
if (p == MAP_FAILED)
|
||||
return false;
|
||||
base_ = reinterpret_cast<Value *>(p);
|
||||
end_ = commitEnd_ = base_ + CAPACITY_VALS;
|
||||
trustedEnd_ = base_ + CAPACITY_VALS;
|
||||
conservativeEnd_ = defaultEnd_ = trustedEnd_ - BUFFER_VALS;
|
||||
#endif
|
||||
assertInvariants();
|
||||
return true;
|
||||
}
|
||||
|
||||
StackSpace::~StackSpace()
|
||||
{
|
||||
assertInvariants();
|
||||
JS_ASSERT(!seg_);
|
||||
if (!base_)
|
||||
return;
|
||||
|
@ -402,55 +413,75 @@ StackSpace::mark(JSTracer *trc)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
JS_FRIEND_API(bool)
|
||||
StackSpace::bumpCommit(JSContext *maybecx, Value *from, ptrdiff_t nvals) const
|
||||
StackSpace::ensureSpaceSlow(JSContext *cx, MaybeReportError report,
|
||||
Value *from, ptrdiff_t nvals) const
|
||||
{
|
||||
if (end_ - from < nvals) {
|
||||
js_ReportOverRecursed(maybecx);
|
||||
assertInvariants();
|
||||
|
||||
bool trusted = !cx->compartment ||
|
||||
cx->compartment->principals == cx->runtime->trustedPrincipals();
|
||||
Value *end = trusted ? trustedEnd_ : defaultEnd_;
|
||||
|
||||
/*
|
||||
* conservativeEnd_ must stay below defaultEnd_: if conservativeEnd_ were
|
||||
* to be bumped past defaultEnd_, untrusted JS would be able to consume the
|
||||
* buffer space at the end of the stack reserved for trusted JS.
|
||||
*/
|
||||
|
||||
if (end - from < nvals) {
|
||||
if (report)
|
||||
js_ReportOverRecursed(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
Value *newCommit = commitEnd_;
|
||||
Value *request = from + nvals;
|
||||
#ifdef XP_WIN
|
||||
if (commitEnd_ - from < nvals) {
|
||||
Value *newCommit = commitEnd_;
|
||||
Value *request = from + nvals;
|
||||
|
||||
/* Use a dumb loop; will probably execute once. */
|
||||
JS_ASSERT((end_ - newCommit) % COMMIT_VALS == 0);
|
||||
do {
|
||||
newCommit += COMMIT_VALS;
|
||||
JS_ASSERT((end_ - newCommit) >= 0);
|
||||
} while (newCommit < request);
|
||||
/* Use a dumb loop; will probably execute once. */
|
||||
JS_ASSERT((trustedEnd_ - newCommit) % COMMIT_VALS == 0);
|
||||
do {
|
||||
newCommit += COMMIT_VALS;
|
||||
JS_ASSERT((trustedEnd_ - newCommit) >= 0);
|
||||
} while (newCommit < request);
|
||||
|
||||
/* The cast is safe because CAPACITY_BYTES is small. */
|
||||
int32 size = static_cast<int32>(newCommit - commitEnd_) * sizeof(Value);
|
||||
/* The cast is safe because CAPACITY_BYTES is small. */
|
||||
int32 size = static_cast<int32>(newCommit - commitEnd_) * sizeof(Value);
|
||||
|
||||
if (!VirtualAlloc(commitEnd_, size, MEM_COMMIT, PAGE_READWRITE)) {
|
||||
js_ReportOverRecursed(maybecx);
|
||||
return false;
|
||||
if (!VirtualAlloc(commitEnd_, size, MEM_COMMIT, PAGE_READWRITE)) {
|
||||
if (report)
|
||||
js_ReportOverRecursed(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
commitEnd_ = newCommit;
|
||||
conservativeEnd_ = Min(commitEnd_, defaultEnd_);
|
||||
assertInvariants();
|
||||
}
|
||||
#endif
|
||||
|
||||
commitEnd_ = newCommit;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
StackSpace::tryBumpLimit(JSContext *maybecx, Value *from, uintN nvals, Value **limit)
|
||||
StackSpace::tryBumpLimit(JSContext *cx, Value *from, uintN nvals, Value **limit)
|
||||
{
|
||||
if (!ensureSpace(maybecx, from, nvals))
|
||||
if (!ensureSpace(cx, REPORT_ERROR, from, nvals))
|
||||
return false;
|
||||
#ifdef XP_WIN
|
||||
*limit = commitEnd_;
|
||||
#else
|
||||
*limit = end_;
|
||||
#endif
|
||||
*limit = conservativeEnd_;
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t
|
||||
StackSpace::committedSize()
|
||||
{
|
||||
#ifdef XP_WIN
|
||||
return (commitEnd_ - base_) * sizeof(Value);
|
||||
#else
|
||||
return (trustedEnd_ - base_) * sizeof(Value);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
@ -516,17 +547,18 @@ ContextStack::containsSlow(const StackFrame *target) const
|
|||
* there is space for nvars slots on top of the stack.
|
||||
*/
|
||||
Value *
|
||||
ContextStack::ensureOnTop(JSContext *cx, uintN nvars, MaybeExtend extend, bool *pushedSeg)
|
||||
ContextStack::ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars,
|
||||
MaybeExtend extend, bool *pushedSeg)
|
||||
{
|
||||
Value *firstUnused = space().firstUnused();
|
||||
|
||||
if (onTop() && extend) {
|
||||
if (!space().ensureSpace(cx, firstUnused, nvars))
|
||||
if (!space().ensureSpace(cx, report, firstUnused, nvars))
|
||||
return NULL;
|
||||
return firstUnused;
|
||||
}
|
||||
|
||||
if (!space().ensureSpace(cx, firstUnused, VALUES_PER_STACK_SEGMENT + nvars))
|
||||
if (!space().ensureSpace(cx, report, firstUnused, VALUES_PER_STACK_SEGMENT + nvars))
|
||||
return NULL;
|
||||
|
||||
FrameRegs *regs;
|
||||
|
@ -559,7 +591,7 @@ bool
|
|||
ContextStack::pushInvokeArgs(JSContext *cx, uintN argc, InvokeArgsGuard *iag)
|
||||
{
|
||||
uintN nvars = 2 + argc;
|
||||
Value *firstUnused = ensureOnTop(cx, nvars, CAN_EXTEND, &iag->pushedSeg_);
|
||||
Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, CAN_EXTEND, &iag->pushedSeg_);
|
||||
if (!firstUnused)
|
||||
return false;
|
||||
|
||||
|
@ -595,7 +627,7 @@ ContextStack::pushInvokeFrame(JSContext *cx, const CallArgs &args,
|
|||
JSScript *script = fun->script();
|
||||
|
||||
StackFrame::Flags flags = ToFrameFlags(construct);
|
||||
StackFrame *fp = getCallFrame(cx, args, fun, script, &flags, OOMCheck());
|
||||
StackFrame *fp = getCallFrame(cx, REPORT_ERROR, args, fun, script, &flags);
|
||||
if (!fp)
|
||||
return false;
|
||||
|
||||
|
@ -642,7 +674,7 @@ ContextStack::pushExecuteFrame(JSContext *cx, JSScript *script, const Value &thi
|
|||
}
|
||||
|
||||
uintN nvars = 2 /* callee, this */ + VALUES_PER_STACK_FRAME + script->nslots;
|
||||
Value *firstUnused = ensureOnTop(cx, nvars, extend, &efg->pushedSeg_);
|
||||
Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, extend, &efg->pushedSeg_);
|
||||
if (!firstUnused)
|
||||
return NULL;
|
||||
|
||||
|
@ -662,10 +694,11 @@ ContextStack::pushExecuteFrame(JSContext *cx, JSScript *script, const Value &thi
|
|||
}
|
||||
|
||||
bool
|
||||
ContextStack::pushDummyFrame(JSContext *cx, JSObject &scopeChain, DummyFrameGuard *dfg)
|
||||
ContextStack::pushDummyFrame(JSContext *cx, MaybeReportError report, JSObject &scopeChain,
|
||||
DummyFrameGuard *dfg)
|
||||
{
|
||||
uintN nvars = VALUES_PER_STACK_FRAME;
|
||||
Value *firstUnused = ensureOnTop(cx, nvars, CAN_EXTEND, &dfg->pushedSeg_);
|
||||
Value *firstUnused = ensureOnTop(cx, report, nvars, CAN_EXTEND, &dfg->pushedSeg_);
|
||||
if (!firstUnused)
|
||||
return NULL;
|
||||
|
||||
|
@ -709,7 +742,7 @@ ContextStack::pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrame
|
|||
uintN vplen = (Value *)genfp - genvp;
|
||||
|
||||
uintN nvars = vplen + VALUES_PER_STACK_FRAME + genfp->numSlots();
|
||||
Value *firstUnused = ensureOnTop(cx, nvars, CAN_EXTEND, &gfg->pushedSeg_);
|
||||
Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, CAN_EXTEND, &gfg->pushedSeg_);
|
||||
if (!firstUnused)
|
||||
return false;
|
||||
|
||||
|
@ -755,14 +788,30 @@ ContextStack::popGeneratorFrame(const GeneratorFrameGuard &gfg)
|
|||
bool
|
||||
ContextStack::saveFrameChain()
|
||||
{
|
||||
/*
|
||||
* The StackSpace uses the context's current compartment to determine
|
||||
* whether to allow access to the privileged end-of-stack buffer.
|
||||
* However, we always want saveFrameChain to have access to this privileged
|
||||
* buffer since it gets used to prepare calling trusted JS. To force this,
|
||||
* we clear the current compartment (which is interpreted by ensureSpace as
|
||||
* 'trusted') and either restore it on OOM or let resetCompartment()
|
||||
* clobber it.
|
||||
*/
|
||||
JSCompartment *original = cx_->compartment;
|
||||
cx_->compartment = NULL;
|
||||
|
||||
bool pushedSeg;
|
||||
if (!ensureOnTop(cx_, 0, CANT_EXTEND, &pushedSeg))
|
||||
if (!ensureOnTop(cx_, DONT_REPORT_ERROR, 0, CANT_EXTEND, &pushedSeg)) {
|
||||
cx_->compartment = original;
|
||||
js_ReportOverRecursed(cx_);
|
||||
return false;
|
||||
}
|
||||
|
||||
JS_ASSERT(pushedSeg);
|
||||
JS_ASSERT(!hasfp());
|
||||
cx_->resetCompartment();
|
||||
|
||||
JS_ASSERT(onTop() && seg_->isEmpty());
|
||||
|
||||
cx_->resetCompartment();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -387,7 +387,7 @@ class StackFrame
|
|||
void resetCallFrame(JSScript *script);
|
||||
|
||||
/* Called by jit stubs and serve as a specification for jit-code. */
|
||||
void initJitFrameCallerHalf(JSContext *cx, StackFrame::Flags flags, void *ncode);
|
||||
void initJitFrameCallerHalf(StackFrame *prev, StackFrame::Flags flags, void *ncode);
|
||||
void initJitFrameEarlyPrologue(JSFunction *fun, uint32 nactual);
|
||||
bool initJitFrameLatePrologue(JSContext *cx, Value **limit);
|
||||
|
||||
|
@ -1286,29 +1286,49 @@ JS_STATIC_ASSERT(sizeof(StackSegment) % sizeof(Value) == 0);
|
|||
|
||||
class StackSpace
|
||||
{
|
||||
Value *base_;
|
||||
mutable Value *commitEnd_;
|
||||
Value *end_;
|
||||
StackSegment *seg_;
|
||||
Value *base_;
|
||||
mutable Value *conservativeEnd_;
|
||||
#ifdef XP_WIN
|
||||
mutable Value *commitEnd_;
|
||||
#endif
|
||||
Value *defaultEnd_;
|
||||
Value *trustedEnd_;
|
||||
|
||||
void assertInvariants() const {
|
||||
JS_ASSERT(base_ <= conservativeEnd_);
|
||||
#ifdef XP_WIN
|
||||
JS_ASSERT(conservativeEnd_ <= commitEnd_);
|
||||
JS_ASSERT(commitEnd_ <= trustedEnd_);
|
||||
#endif
|
||||
JS_ASSERT(conservativeEnd_ <= defaultEnd_);
|
||||
JS_ASSERT(defaultEnd_ <= trustedEnd_);
|
||||
}
|
||||
|
||||
/* The total number of values/bytes reserved for the stack. */
|
||||
static const size_t CAPACITY_VALS = 512 * 1024;
|
||||
static const size_t CAPACITY_BYTES = CAPACITY_VALS * sizeof(Value);
|
||||
|
||||
/* How much of the stack is initially committed. */
|
||||
static const size_t COMMIT_VALS = 16 * 1024;
|
||||
static const size_t COMMIT_BYTES = COMMIT_VALS * sizeof(Value);
|
||||
|
||||
/* How much space is reserved at the top of the stack for trusted JS. */
|
||||
static const size_t BUFFER_VALS = 16 * 1024;
|
||||
static const size_t BUFFER_BYTES = BUFFER_VALS * sizeof(Value);
|
||||
|
||||
static void staticAsserts() {
|
||||
JS_STATIC_ASSERT(CAPACITY_VALS % COMMIT_VALS == 0);
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
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;
|
||||
|
||||
inline bool ensureSpace(JSContext *cx, MaybeReportError report,
|
||||
Value *from, ptrdiff_t nvals) const;
|
||||
JS_FRIEND_API(bool) ensureSpaceSlow(JSContext *cx, MaybeReportError report,
|
||||
Value *from, ptrdiff_t nvals) const;
|
||||
StackSegment &findContainingSegment(const StackFrame *target) const;
|
||||
|
||||
public:
|
||||
|
@ -1316,9 +1336,21 @@ class StackSpace
|
|||
bool init();
|
||||
~StackSpace();
|
||||
|
||||
/*
|
||||
* Maximum supported value of arguments.length. This bounds the maximum
|
||||
* number of arguments that can be supplied to Function.prototype.apply.
|
||||
* This value also bounds the number of elements parsed in an array
|
||||
* initialiser.
|
||||
*
|
||||
* Since arguments are copied onto the stack, the stack size is the
|
||||
* limiting factor for this constant. Use the max stack size (available to
|
||||
* untrusted code) with an extra buffer so that, after such an apply, the
|
||||
* callee can do a little work without OOMing.
|
||||
*/
|
||||
static const uintN ARGS_LENGTH_MAX = CAPACITY_VALS - (2 * BUFFER_VALS);
|
||||
|
||||
/* See stack layout comment above. */
|
||||
Value *firstUnused() const { return seg_ ? seg_->end() : base_; }
|
||||
Value *endOfSpace() const { return end_; }
|
||||
|
||||
#ifdef JS_TRACER
|
||||
/*
|
||||
|
@ -1326,8 +1358,11 @@ class StackSpace
|
|||
* good way to handle an OOM for these allocations, so this function checks
|
||||
* that OOM cannot occur using the size of the TraceNativeStorage as a
|
||||
* conservative upper bound.
|
||||
*
|
||||
* Despite taking a 'cx', this function does not report an error if it
|
||||
* returns 'false'.
|
||||
*/
|
||||
inline bool ensureEnoughSpaceToEnterTrace();
|
||||
inline bool ensureEnoughSpaceToEnterTrace(JSContext *cx);
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -1344,8 +1379,8 @@ class StackSpace
|
|||
* does indeed have this required space and reports an error and returns
|
||||
* NULL if this reserve space cannot be allocated.
|
||||
*/
|
||||
inline Value *getStackLimit(JSContext *cx);
|
||||
bool tryBumpLimit(JSContext *maybecx, Value *from, uintN nvals, Value **limit);
|
||||
inline Value *getStackLimit(JSContext *cx, MaybeReportError report);
|
||||
bool tryBumpLimit(JSContext *cx, Value *from, uintN nvals, Value **limit);
|
||||
|
||||
/* Called during GC: mark segments, frames, and slots under firstUnused. */
|
||||
void mark(JSTracer *trc);
|
||||
|
@ -1356,36 +1391,6 @@ class StackSpace
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
* For pushInlineFrame, there are three different ways the caller may want to
|
||||
* check for stack overflow:
|
||||
* - NoCheck: the caller has already ensured there is enough space
|
||||
* - OOMCheck: perform normal checking against the end of the stack
|
||||
* - LimitCheck: check against the given stack limit (see getStackLimit)
|
||||
*/
|
||||
|
||||
class NoCheck
|
||||
{
|
||||
public:
|
||||
bool operator()(JSContext *, StackSpace &, Value *, uintN) { return true; }
|
||||
};
|
||||
|
||||
class OOMCheck
|
||||
{
|
||||
public:
|
||||
bool operator()(JSContext *cx, StackSpace &space, Value *from, uintN nvals);
|
||||
};
|
||||
|
||||
class LimitCheck
|
||||
{
|
||||
Value **limit;
|
||||
public:
|
||||
LimitCheck(Value **limit) : limit(limit) {}
|
||||
bool operator()(JSContext *cx, StackSpace &space, Value *from, uintN nvals);
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
class ContextStack
|
||||
{
|
||||
StackSegment *seg_;
|
||||
|
@ -1411,13 +1416,12 @@ class ContextStack
|
|||
/* 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);
|
||||
Value *ensureOnTop(JSContext *cx, MaybeReportError report, 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;
|
||||
getCallFrame(JSContext *cx, MaybeReportError report, const CallArgs &args,
|
||||
JSFunction *fun, JSScript *script, StackFrame::Flags *pflags) const;
|
||||
|
||||
/* Make pop* functions private since only called by guard classes. */
|
||||
void popSegment();
|
||||
|
@ -1495,17 +1499,20 @@ class ContextStack
|
|||
bool pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg);
|
||||
|
||||
/* Pushes a "dummy" frame; should be removed one day. */
|
||||
bool pushDummyFrame(JSContext *cx, JSObject &scopeChain, DummyFrameGuard *dfg);
|
||||
bool pushDummyFrame(JSContext *cx, MaybeReportError report, JSObject &scopeChain,
|
||||
DummyFrameGuard *dfg);
|
||||
|
||||
/*
|
||||
* An "inline frame" may only be pushed from within the top, active
|
||||
* segment. This is the case for calls made inside mjit code and Interpret.
|
||||
* For the Check parameter, see OOMCheck et al above.
|
||||
* The 'stackLimit' overload updates 'stackLimit' if it changes.
|
||||
*/
|
||||
template <class Check>
|
||||
bool pushInlineFrame(JSContext *cx, FrameRegs ®s, const CallArgs &args,
|
||||
JSObject &callee, JSFunction *fun, JSScript *script,
|
||||
MaybeConstruct construct, Check check);
|
||||
MaybeConstruct construct);
|
||||
bool pushInlineFrame(JSContext *cx, FrameRegs ®s, const CallArgs &args,
|
||||
JSObject &callee, JSFunction *fun, JSScript *script,
|
||||
MaybeConstruct construct, Value **stackLimit);
|
||||
void popInlineFrame(FrameRegs ®s);
|
||||
|
||||
/* Pop a partially-pushed frame after hitting the limit before throwing. */
|
||||
|
@ -1519,9 +1526,9 @@ class ContextStack
|
|||
* getFixupFrame = pushInlineFrame -
|
||||
* (fp->initJitFrameLatePrologue + regs->prepareToRun)
|
||||
*/
|
||||
StackFrame *getFixupFrame(JSContext *cx, FrameRegs ®s, const CallArgs &args,
|
||||
JSFunction *fun, JSScript *script, void *ncode,
|
||||
MaybeConstruct construct, LimitCheck check);
|
||||
StackFrame *getFixupFrame(JSContext *cx, MaybeReportError report,
|
||||
const CallArgs &args, JSFunction *fun, JSScript *script,
|
||||
void *ncode, MaybeConstruct construct, Value **stackLimit);
|
||||
|
||||
bool saveFrameChain();
|
||||
void restoreFrameChain();
|
||||
|
|
Загрузка…
Ссылка в новой задаче