Merge tracemonkey into mozilla-central. (a=blockers)

This commit is contained in:
Chris Leary 2011-01-08 00:48:44 -08:00
commit 306a55837a
55 changed files with 654 additions and 738 deletions

View File

@ -0,0 +1,45 @@
/*
* Check that builtin character classes within ranges produce syntax
* errors.
*
* Note that, per the extension in bug 351463, SpiderMonkey permits hyphens
* adjacent to character class escapes in character classes, treating them as a
* hyphen pattern character. Therefore /[\d-\s]/ is okay
*
* Note: /\b/ is the backspace escape, which is a single pattern character,
* though it looks deceptively like a character class.
*/
function isRegExpSyntaxError(pattern) {
try {
var re = new RegExp(pattern);
} catch (e) {
if (e instanceof SyntaxError)
return true;
}
return false;
}
assertEq(isRegExpSyntaxError('[C-\\s]'), true);
assertEq(isRegExpSyntaxError('[C-\\d]'), true);
assertEq(isRegExpSyntaxError('[C-\\W]'), true);
assertEq(isRegExpSyntaxError('[C-]'), false);
assertEq(isRegExpSyntaxError('[-C]'), false);
assertEq(isRegExpSyntaxError('[C-C]'), false);
assertEq(isRegExpSyntaxError('[C-C]'), false);
assertEq(isRegExpSyntaxError('[\\b-\\b]'), false);
assertEq(isRegExpSyntaxError('[\\B-\\B]'), false);
assertEq(isRegExpSyntaxError('[\\b-\\B]'), false);
assertEq(isRegExpSyntaxError('[\\B-\\b]'), true);
assertEq(isRegExpSyntaxError('[\\b-\\w]'), true);
assertEq(isRegExpSyntaxError('[\\B-\\w]'), true);
/* Extension. */
assertEq(isRegExpSyntaxError('[\\s-\\s]'), false);
assertEq(isRegExpSyntaxError('[\\W-\\s]'), false);
assertEq(isRegExpSyntaxError('[\\s-\\W]'), false);
assertEq(isRegExpSyntaxError('[\\W-C]'), false);
assertEq(isRegExpSyntaxError('[\\d-C]'), false);
assertEq(isRegExpSyntaxError('[\\s-C]'), false);
assertEq(isRegExpSyntaxError('[\\w-\\b]'), false);
assertEq(isRegExpSyntaxError('[\\w-\\B]'), false);

View File

@ -0,0 +1,16 @@
// |jit-test| allow-oom
(function() {
Iterator((function() {
switch ((7)) {
default:
return (Float32Array).call([], 4300018)
case Proxy.create((function() {
return {
e:
function() {}
}
})):
}
})())
})()

View File

@ -1,19 +1,6 @@
/* Touch/init early so global shape doesn't change in loop */
var SetOnIter = HOTLOOP - 1;
var x = 3;
var i = 0;
assertEq(true, true);
for (i = 0; i < SetOnIter + 10; ++i) {
x = 3;
setGlobalPropIf(i == SetOnIter, 'x', 'pretty');
assertEq(x == 'pretty', i == SetOnIter);
x = 3;
}
for (i = 0; i < SetOnIter + 10; ++i) {
x = 3;
defGlobalPropIf(i == SetOnIter, 'x', { value:'pretty' });
assertEq(x == 'pretty', i == SetOnIter);
x = 3;
X = { value: "" }
z = 0;
for (var i = 0; i < 20; i++) {
Object.defineProperty(this, "z", X);
z = 1;
}

View File

@ -0,0 +1,17 @@
function f() {
for (var a = 0; a < HOTLOOP; ++a) {
(function () {
var x;
for (var b = 0; b < HOTLOOP; ++b) {
x = 0;
(function () {
for (var i = 0; i < 1; ++i) {
x = 1;
}
})();
x++;
}
})();
}
}
f();

View File

@ -0,0 +1,10 @@
for (var j=0;j<2;++j) { (function(o){o.length})(String.prototype); }
for each(let y in [Number, Number]) {
try {
"".length()
} catch(e) {}
}
/* Don't crash. */

View File

@ -346,3 +346,4 @@ MSG_DEF(JSMSG_CANT_WRAP_XML_OBJECT, 263, 0, JSEXN_TYPEERR, "can't wrap XML obj
MSG_DEF(JSMSG_BAD_CLONE_VERSION, 264, 0, JSEXN_ERR, "unsupported structured clone version")
MSG_DEF(JSMSG_CANT_CLONE_OBJECT, 265, 0, JSEXN_TYPEERR, "can't clone object")
MSG_DEF(JSMSG_NON_NATIVE_SCOPE, 266, 0, JSEXN_TYPEERR, "non-native scope object")
MSG_DEF(JSMSG_STRICT_FUNCTION_STATEMENT, 267, 0, JSEXN_SYNTAXERR, "in strict mode code, functions may be declared only at top level or immediately within another function")

View File

@ -2996,6 +2996,8 @@ NewArray(JSContext *cx, jsuint length, JSObject *proto)
gc::FinalizeKind kind = GuessObjectGCKind(length, true);
JSObject *obj = detail::NewObject<WithProto::Class, false>(cx, &js_ArrayClass, proto, NULL, kind);
if (!obj)
return NULL;
obj->setArrayLength(length);
@ -3027,6 +3029,9 @@ JSObject *
NewDenseCopiedArray(JSContext *cx, uintN length, Value *vp, JSObject *proto)
{
JSObject* obj = NewArray<true>(cx, length, proto);
if (!obj)
return NULL;
JS_ASSERT(obj->getDenseArrayCapacity() >= length);
if (vp)

View File

@ -2019,14 +2019,8 @@ JSContext::resetCompartment()
compartment = scopeobj->compartment();
/*
* If wrapException fails, it overrides this->exception and
* reports OOM. The upshot is that we silently turn the exception
* into an uncatchable OOM error. A bit surprising, but the
* caller is just going to return false either way.
*/
if (isExceptionPending())
(void) compartment->wrapException(this);
wrapPendingException();
return;
error:
@ -2038,6 +2032,21 @@ error:
compartment = NULL;
}
/*
* Since this function is only called in the context of a pending exception,
* the caller must subsequently take an error path. If wrapping fails, we leave
* the exception cleared, which, in the context of an error path, will be
* interpreted as an uncatchable exception.
*/
void
JSContext::wrapPendingException()
{
Value v = getPendingException();
clearPendingException();
if (compartment->wrap(this, &v))
setPendingException(v);
}
void
JSContext::pushSegmentAndFrame(js::StackSegment *newseg, JSFrameRegs &newregs)
{

View File

@ -896,7 +896,7 @@ private:
#ifdef DEBUG
# define FUNCTION_KIND_METER_LIST(_) \
_(allfun), _(heavy), _(nofreeupvar), _(onlyfreevar), \
_(display), _(flat), _(setupvar), _(badfunarg), \
_(flat), _(badfunarg), \
_(joinedsetmethod), _(joinedinitmethod), \
_(joinedreplace), _(joinedsort), _(joinedmodulepat), \
_(mreadbarrier), _(mwritebarrier), _(mwslotbarrier), \
@ -1768,6 +1768,7 @@ struct JSContext
friend bool js::Interpret(JSContext *, JSStackFrame *, uintN, JSInterpMode);
void resetCompartment();
void wrapPendingException();
/* 'regs' must only be changed by calling this function. */
void setCurrentRegs(JSFrameRegs *regs) {

View File

@ -208,17 +208,21 @@ JSCompartment::wrap(JSContext *cx, Value *vp)
if (obj->getCompartment() == this)
return true;
if (cx->runtime->preWrapObjectCallback)
if (cx->runtime->preWrapObjectCallback) {
obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags);
if (!obj)
return false;
if (!obj)
return false;
}
vp->setObject(*obj);
if (obj->getCompartment() == this)
return true;
} else {
if (cx->runtime->preWrapObjectCallback)
if (cx->runtime->preWrapObjectCallback) {
obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags);
if (!obj)
return false;
}
JS_ASSERT(!obj->isWrapper() || obj->getClass()->ext.innerObject);
vp->setObject(*obj);
@ -353,21 +357,6 @@ JSCompartment::wrap(JSContext *cx, AutoIdVector &props)
return true;
}
bool
JSCompartment::wrapException(JSContext *cx)
{
JS_ASSERT(cx->compartment == this);
if (cx->isExceptionPending()) {
Value v = cx->getPendingException();
cx->clearPendingException();
if (wrap(cx, &v))
cx->setPendingException(v);
return false;
}
return true;
}
#if defined JS_METHODJIT && defined JS_MONOIC
/*
* Check if the pool containing the code for jit should be destroyed, per the

View File

@ -327,7 +327,6 @@ struct JS_FRIEND_API(JSCompartment) {
bool wrap(JSContext *cx, js::PropertyOp *op);
bool wrap(JSContext *cx, js::PropertyDescriptor *desc);
bool wrap(JSContext *cx, js::AutoIdVector &props);
bool wrapException(JSContext *cx);
void sweep(JSContext *cx, uint32 releaseInterval);
void purge(JSContext *cx);

View File

@ -1974,99 +1974,6 @@ EmitLeaveBlock(JSContext *cx, JSCodeGenerator *cg, JSOp op,
return bigSuffix == JSOP_NOP || js_Emit1(cx, cg, bigSuffix) >= 0;
}
/*
* When eval is called from a function, the eval code or function code it
* compiles may reference upvars that live in the eval-calling function. The
* eval-invoked compiler does not have explicit definitions for these upvars
* and we do not attempt to create them a-priori (by inspecting the function's
* args and vars) -- we could, but we'd take an avoidable penalty for each
* function local not referenced by any upvar. Instead, we map such upvars
* lazily, growing upvarMap.vector by powers of two.
*
* This function knows that it is called with pn pointing to a PN_NAME-arity
* node, and cg->parser->callerFrame having a non-null fun member, and the
* static level of cg at least one greater than the eval-calling function's
* static level.
*/
static bool
MakeUpvarForEval(JSParseNode *pn, JSCodeGenerator *cg)
{
JSContext *cx = cg->parser->context;
JSFunction *fun = cg->parser->callerFrame->fun();
uintN upvarLevel = fun->u.i.script->staticLevel;
JSFunctionBox *funbox = cg->funbox;
if (funbox) {
/*
* Treat top-level function definitions as escaping (i.e., as funargs),
* required since we compile each such top level function or statement
* and throw away the AST, so we can't yet see all funarg uses of this
* function being compiled (cg->funbox->object). See bug 493177.
*/
if (funbox->level == fun->u.i.script->staticLevel + 1U &&
!(((JSFunction *) funbox->object)->flags & JSFUN_LAMBDA)) {
JS_ASSERT_IF(cx->options & JSOPTION_ANONFUNFIX,
((JSFunction *) funbox->object)->atom);
return true;
}
while (funbox->level >= upvarLevel) {
if (funbox->node->pn_dflags & PND_FUNARG)
return true;
funbox = funbox->parent;
if (!funbox)
break;
}
}
JSAtom *atom = pn->pn_atom;
uintN index;
BindingKind kind = fun->script()->bindings.lookup(cx, atom, &index);
if (kind == NONE)
return true;
JS_ASSERT(cg->staticLevel > upvarLevel);
if (cg->staticLevel >= UpvarCookie::UPVAR_LEVEL_LIMIT)
return true;
JSAtomListElement *ale = cg->upvarList.lookup(atom);
if (!ale) {
if (cg->inFunction() && !cg->bindings.addUpvar(cx, atom))
return false;
ale = cg->upvarList.add(cg->parser, atom);
if (!ale)
return false;
JS_ASSERT(ALE_INDEX(ale) == cg->upvarList.count - 1);
UpvarCookie *vector = cg->upvarMap.vector;
uint32 length = cg->upvarMap.length;
JS_ASSERT(ALE_INDEX(ale) <= length);
if (ALE_INDEX(ale) == length) {
length = 2 * JS_MAX(2, length);
vector = reinterpret_cast<UpvarCookie *>(cx->realloc(vector, length * sizeof *vector));
if (!vector)
return false;
cg->upvarMap.vector = vector;
cg->upvarMap.length = length;
}
if (kind != ARGUMENT)
index += fun->nargs;
JS_ASSERT(index < JS_BIT(16));
uintN skip = cg->staticLevel - upvarLevel;
vector[ALE_INDEX(ale)].set(skip, index);
}
pn->pn_op = JSOP_GETUPVAR;
pn->pn_cookie.set(cg->staticLevel, uint16(ALE_INDEX(ale)));
pn->pn_dflags |= PND_BOUND;
return true;
}
/*
* Try to convert a *NAME op to a *GNAME op, which optimizes access to
* undeclared globals. Return true if a conversion was made.
@ -2232,63 +2139,11 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
return JS_TRUE;
}
if (!caller->isFunctionFrame())
return JS_TRUE;
/*
* Make sure the variable object used by the compiler to initialize
* parent links matches the caller's varobj. Compile-n-go compiler-
* created function objects have the top-level cg's scopeChain set
* as their parent by Parser::newFunction.
* Out of tricks, so we must rely on PICs to optimize named
* accesses from direct eval called from function code.
*/
JSObject *scopeobj = cg->inFunction()
? FUN_OBJECT(cg->fun())->getParent()
: cg->scopeChain();
if (scopeobj != cg->parser->callerVarObj)
return JS_TRUE;
/*
* We are compiling eval or debug script inside a function frame
* and the scope chain matches the function's variable object.
* Optimize access to function's arguments and variable and the
* arguments object.
*/
if (op != JSOP_NAME)
return JS_TRUE;
/*
* It is illegal to add upvars to heavyweight functions (and
* unnecessary, since the optimization avoids creating call
* objects). Take the following code as an eval string:
*
* (function () {
* $(init);
* function init() {
* $();
* }
* })();
*
* The first instance of "$" cannot be an upvar, because the
* outermost lambda is on "init"'s scope chain, which escapes.
*
* A similar restriction exists for upvars which do not cross
* eval (see the end of BindNameToSlot and bug 616762).
*/
if (cg->flags & TCF_FUN_HEAVYWEIGHT)
return JS_TRUE;
/*
* Generator functions may be resumed from any call stack, which
* defeats the display optimization to static link searching used
* by JSOP_{GET,CALL}UPVAR.
*/
JSFunction *fun = cg->parser->callerFrame->fun();
JS_ASSERT(cg->staticLevel >= fun->u.i.script->staticLevel);
unsigned skip = cg->staticLevel - fun->u.i.script->staticLevel;
if (cg->skipSpansGenerator(skip))
return JS_TRUE;
return MakeUpvarForEval(pn, cg);
return JS_TRUE;
}
/* Optimize accesses to undeclared globals. */
@ -2348,50 +2203,6 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
uint16 level = cookie.level();
JS_ASSERT(cg->staticLevel >= level);
/*
* A JSDefinition witnessed as a declaration by the parser cannot be an
* upvar, unless it is the degenerate kind of upvar selected above (in the
* code before the PND_GVAR test) for the special case of compile-and-go
* code generated from eval called from a function, where the eval code
* uses local vars defined in the function. We detect this upvar-for-eval
* case by checking dn's op.
*/
if (PN_OP(dn) == JSOP_GETUPVAR) {
JS_ASSERT(cg->staticLevel >= level);
if (op != JSOP_NAME)
return JS_TRUE;
#ifdef DEBUG
JSStackFrame *caller = cg->parser->callerFrame;
#endif
JS_ASSERT(caller->isScriptFrame());
JSTreeContext *tc = cg;
while (tc->staticLevel != level)
tc = tc->parent;
JSCodeGenerator *evalcg = tc->asCodeGenerator();
JS_ASSERT(evalcg->compileAndGo());
JS_ASSERT(caller->isFunctionFrame());
JS_ASSERT(cg->parser->callerVarObj == evalcg->scopeChain());
/*
* Don't generate upvars on the left side of a for loop. See
* bug 470758 and bug 520513.
*/
if (evalcg->flags & TCF_IN_FOR_INIT)
return JS_TRUE;
if (cg->staticLevel == level) {
pn->pn_op = JSOP_GETUPVAR;
pn->pn_cookie = cookie;
pn->pn_dflags |= PND_BOUND;
return JS_TRUE;
}
return MakeUpvarForEval(pn, cg);
}
const uintN skip = cg->staticLevel - level;
if (skip != 0) {
JS_ASSERT(cg->inFunction());
@ -2410,29 +2221,8 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
if (cg->flags & TCF_FUN_HEAVYWEIGHT)
return JS_TRUE;
if (cg->fun()->isFlatClosure()) {
op = JSOP_GETFCSLOT;
} else {
/*
* The function we're compiling may not be heavyweight, but if it
* escapes as a funarg, we can't use JSOP_GETUPVAR/JSOP_CALLUPVAR.
* Parser::analyzeFunctions has arranged for this function's
* enclosing functions to be heavyweight, so we can safely stick
* with JSOP_NAME/JSOP_CALLNAME.
*/
if (cg->funbox->node->pn_dflags & PND_FUNARG)
return JS_TRUE;
/*
* Generator functions may be resumed from any call stack, which
* defeats the display optimization to static link searching used
* by JSOP_{GET,CALL}UPVAR.
*/
if (cg->skipSpansGenerator(skip))
return JS_TRUE;
op = JSOP_GETUPVAR;
}
if (!cg->fun()->isFlatClosure())
return JS_TRUE;
ale = cg->upvarList.lookup(atom);
if (ale) {
@ -2473,7 +2263,7 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
vector[index].set(skip, slot);
}
pn->pn_op = op;
pn->pn_op = JSOP_GETFCSLOT;
JS_ASSERT((index & JS_BITMASK(16)) == index);
pn->pn_cookie.set(0, index);
pn->pn_dflags |= PND_BOUND;
@ -2852,9 +2642,6 @@ EmitNameOp(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
case JSOP_GETLOCAL:
op = JSOP_CALLLOCAL;
break;
case JSOP_GETUPVAR:
op = JSOP_CALLUPVAR;
break;
case JSOP_GETFCSLOT:
op = JSOP_CALLFCSLOT;
break;
@ -6070,7 +5857,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
* API users may also set the JSOPTION_NO_SCRIPT_RVAL option when
* calling JS_Compile* to suppress JSOP_POPV.
*/
useful = wantval = !(cg->flags & (TCF_IN_FUNCTION | TCF_NO_SCRIPT_RVAL));
wantval = !(cg->flags & (TCF_IN_FUNCTION | TCF_NO_SCRIPT_RVAL));
useful = wantval || pn->isDirectivePrologueMember();
if (!useful) {
if (!CheckSideEffects(cx, cg, pn2, &useful))
return JS_FALSE;
@ -6266,7 +6054,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
return JS_FALSE;
EMIT_INDEX_OP(JSOP_GETXPROP, atomIndex);
} else {
JS_ASSERT(PN_OP(pn2) != JSOP_GETUPVAR);
EMIT_UINT16_IMM_OP((PN_OP(pn2) == JSOP_SETGNAME)
? JSOP_GETGNAME
: (PN_OP(pn2) == JSOP_SETGLOBAL)

View File

@ -234,9 +234,9 @@ struct JSStmtInfo {
/*
* Flag to prevent a non-escaping function from being optimized into a null
* closure (i.e., a closure that needs only its global object for free variable
* resolution, thanks to JSOP_{GET,CALL}UPVAR), because this function contains
* a closure that needs one or more scope objects surrounding it (i.e., Call
* object for a heavyweight outer function). See bug 560234.
* resolution), because this function contains a closure that needs one or more
* scope objects surrounding it (i.e., a Call object for an outer heavyweight
* function). See bug 560234.
*/
#define TCF_FUN_ENTRAINS_SCOPES 0x400000

View File

@ -455,8 +455,6 @@ WrapEscapingClosure(JSContext *cx, JSStackFrame *fp, JSFunction *fun)
* immediate operand.
*/
switch (op) {
case JSOP_GETUPVAR: *pc = JSOP_GETUPVAR_DBG; break;
case JSOP_CALLUPVAR: *pc = JSOP_CALLUPVAR_DBG; break;
case JSOP_GETFCSLOT: *pc = JSOP_GETUPVAR_DBG; break;
case JSOP_CALLFCSLOT: *pc = JSOP_CALLUPVAR_DBG; break;
case JSOP_DEFFUN_FC: *pc = JSOP_DEFFUN_DBGFC; break;
@ -1077,6 +1075,12 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp)
{
JSObject &callobj = fp->callObj();
/*
* Strict mode eval frames have Call objects to put. Normal eval frames
* never put a Call object.
*/
JS_ASSERT(fp->isEvalFrame() == callobj.callIsForEval());
/* Get the arguments object to snapshot fp's actual argument values. */
if (fp->hasArgsObj()) {
if (!fp->hasOverriddenArgs())
@ -1084,53 +1088,62 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp)
js_PutArgsObject(cx, fp);
}
JSFunction *fun = fp->fun();
JS_ASSERT(fun == callobj.getCallObjCalleeFunction());
JSScript *script = fp->script();
Bindings &bindings = script->bindings;
Bindings &bindings = fun->script()->bindings;
uintN n = bindings.countArgsAndVars();
if (callobj.callIsForEval()) {
JS_ASSERT(script->strictModeCode);
JS_ASSERT(bindings.countArgs() == 0);
if (n != 0) {
JS_ASSERT(JSFunction::CLASS_RESERVED_SLOTS + n <= callobj.numSlots());
/* This could be optimized as below, but keep it simple for now. */
CopyValuesToCallObject(callobj, 0, NULL, bindings.countVars(), fp->slots());
} else {
JSFunction *fun = fp->fun();
JS_ASSERT(fun == callobj.getCallObjCalleeFunction());
JS_ASSERT(script == fun->script());
uint32 nvars = bindings.countVars();
uint32 nargs = bindings.countArgs();
JS_ASSERT(fun->nargs == nargs);
JS_ASSERT(nvars + nargs == n);
if (uintN n = bindings.countArgsAndVars()) {
JS_ASSERT(JSObject::CALL_RESERVED_SLOTS + n <= callobj.numSlots());
JSScript *script = fun->script();
if (script->usesEval
uint32 nvars = bindings.countVars();
uint32 nargs = bindings.countArgs();
JS_ASSERT(fun->nargs == nargs);
JS_ASSERT(nvars + nargs == n);
JSScript *script = fun->script();
if (script->usesEval
#ifdef JS_METHODJIT
|| script->debugMode
|| script->debugMode
#endif
) {
CopyValuesToCallObject(callobj, nargs, fp->formalArgs(), nvars, fp->slots());
} else {
/*
* For each arg & var that is closed over, copy it from the stack
* into the call object.
*/
uint32 nclosed = script->nClosedArgs;
for (uint32 i = 0; i < nclosed; i++) {
uint32 e = script->getClosedArg(i);
callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + e, fp->formalArg(e));
}
) {
CopyValuesToCallObject(callobj, nargs, fp->formalArgs(), nvars, fp->slots());
} else {
/*
* For each arg & var that is closed over, copy it from the stack
* into the call object.
*/
uint32 nclosed = script->nClosedArgs;
for (uint32 i = 0; i < nclosed; i++) {
uint32 e = script->getClosedArg(i);
callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + e, fp->formalArg(e));
}
nclosed = script->nClosedVars;
for (uint32 i = 0; i < nclosed; i++) {
uint32 e = script->getClosedVar(i);
callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + nargs + e, fp->slots()[e]);
nclosed = script->nClosedVars;
for (uint32 i = 0; i < nclosed; i++) {
uint32 e = script->getClosedVar(i);
callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + nargs + e, fp->slots()[e]);
}
}
}
}
/* Clear private pointers to fp, which is about to go away (js_Invoke). */
if (js_IsNamedLambda(fun)) {
JSObject *env = callobj.getParent();
/* Clear private pointers to fp, which is about to go away (js_Invoke). */
if (js_IsNamedLambda(fun)) {
JSObject *env = callobj.getParent();
JS_ASSERT(env->getClass() == &js_DeclEnvClass);
JS_ASSERT(env->getPrivate() == fp);
env->setPrivate(NULL);
JS_ASSERT(env->getClass() == &js_DeclEnvClass);
JS_ASSERT(env->getPrivate() == fp);
env->setPrivate(NULL);
}
}
callobj.setPrivate(NULL);
@ -1211,30 +1224,25 @@ SetCallArg(JSContext *cx, JSObject *obj, jsid id, Value *vp)
}
JSBool
GetFlatUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
GetCallUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
uintN i = (uint16) JSID_TO_INT(id);
JSObject *callee = obj->getCallObjCallee();
JS_ASSERT(callee);
*vp = callee->getFlatClosureUpvar(i);
*vp = obj->getCallObjCallee()->getFlatClosureUpvar(i);
return true;
}
JSBool
SetFlatUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
SetCallUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
uintN i = (uint16) JSID_TO_INT(id);
JSObject *callee = obj->getCallObjCallee();
JS_ASSERT(callee);
Value *up = &obj->getCallObjCallee()->getFlatClosureUpvar(i);
Value *upvarp = &callee->getFlatClosureUpvar(i);
GC_POKE(cx, *upvarp);
*upvarp = *vp;
GC_POKE(cx, *up);
*up = *vp;
return true;
}
@ -1360,8 +1368,7 @@ static void
call_trace(JSTracer *trc, JSObject *obj)
{
JS_ASSERT(obj->isCall());
JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
if (fp) {
if (JSStackFrame *fp = obj->maybeCallObjStackFrame()) {
/*
* FIXME: Hide copies of stack values rooted by fp from the Cycle
* Collector, which currently lacks a non-stub Unlink implementation
@ -1370,7 +1377,7 @@ call_trace(JSTracer *trc, JSObject *obj)
* hiding hack.
*/
uintN first = JSObject::CALL_RESERVED_SLOTS;
uintN count = fp->fun()->script()->bindings.countArgsAndVars();
uintN count = fp->script()->bindings.countArgsAndVars();
JS_ASSERT(obj->numSlots() >= first + count);
SetValueRangeToUndefined(obj->getSlots() + first, count);
@ -2780,16 +2787,20 @@ js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain)
JS_DEFINE_CALLINFO_3(extern, OBJECT, js_AllocFlatClosure,
CONTEXT, FUNCTION, OBJECT, 0, nanojit::ACCSET_STORE_ANY)
JS_REQUIRES_STACK JSObject *
JSObject *
js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen)
{
/*
* Flat closures can be partial, they may need to search enclosing scope
* objects via JSOP_NAME, etc.
* Flat closures cannot yet be partial, that is, all upvars must be copied,
* or the closure won't be flattened. Therefore they do not need to search
* enclosing scope objects via JSOP_NAME, etc.
*
* FIXME: bug 545759 proposes to enable partial flat closures. Fixing this
* bug requires a GetScopeChainFast call here, along with JS_REQUIRES_STACK
* annotations on this function's prototype and definition.
*/
JSObject *scopeChain = GetScopeChainFast(cx, cx->fp(), op, oplen);
if (!scopeChain)
return NULL;
VOUCH_DOES_NOT_REQUIRE_STACK();
JSObject *scopeChain = &cx->fp()->scopeChain();
JSObject *closure = js_AllocFlatClosure(cx, fun, scopeChain);
if (!closure || !fun->script()->bindings.hasUpvars())

View File

@ -467,7 +467,7 @@ CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent)
extern JSObject * JS_FASTCALL
js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain);
extern JS_REQUIRES_STACK JSObject *
extern JSObject *
js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen);
extern JS_REQUIRES_STACK JSObject *
@ -526,7 +526,7 @@ extern JSBool
GetCallVarChecked(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
extern JSBool
GetFlatUpvar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
GetCallUpvar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
extern JSBool
SetCallArg(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
@ -535,7 +535,7 @@ extern JSBool
SetCallVar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
extern JSBool
SetFlatUpvar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
SetCallUpvar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
} // namespace js

View File

@ -966,12 +966,12 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script,
initialVarObj = (cx->options & JSOPTION_VAROBJFIX) ? chain->getGlobal() : chain;
}
#if 0 /* to be reenabled shortly when this works */
/*
* Strict mode eval code receives its own, fresh lexical environment; thus
* strict mode eval can't mutate its calling frame's binding set.
*/
if (script->strictModeCode) {
if ((flags & JSFRAME_EVAL) && script->strictModeCode) {
AutoScriptRooter root(cx, script);
initialVarObj = NewCallObject(cx, &script->bindings, *initialVarObj, NULL);
if (!initialVarObj)
return false;
@ -982,7 +982,6 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script,
frame.fp()->clearCallObj();
frame.fp()->setScopeChainAndCallObj(*initialVarObj);
}
#endif
JS_ASSERT(!initialVarObj->getOps()->defineProperty);
#if JS_HAS_SHARP_VARS
@ -2132,9 +2131,8 @@ AssertValidPropertyCacheHit(JSContext *cx, JSScript *script, JSFrameRegs& regs,
* same way as non-call bytecodes.
*/
JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH);
JS_STATIC_ASSERT(JSOP_GETUPVAR_LENGTH == JSOP_CALLUPVAR_LENGTH);
JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_GETFCSLOT_LENGTH);
JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_CALLUPVAR_DBG_LENGTH);
JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_GETUPVAR_LENGTH);
JS_STATIC_ASSERT(JSOP_GETFCSLOT_LENGTH == JSOP_CALLFCSLOT_LENGTH);
JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH);
JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH);
@ -5290,22 +5288,6 @@ BEGIN_CASE(JSOP_SETLOCAL)
}
END_SET_CASE(JSOP_SETLOCAL)
BEGIN_CASE(JSOP_GETUPVAR)
BEGIN_CASE(JSOP_CALLUPVAR)
{
JSUpvarArray *uva = script->upvars();
uintN index = GET_UINT16(regs.pc);
JS_ASSERT(index < uva->length);
const Value &rval = GetUpvar(cx, script->staticLevel, uva->vector[index]);
PUSH_COPY(rval);
if (op == JSOP_CALLUPVAR)
PUSH_UNDEFINED();
}
END_CASE(JSOP_GETUPVAR)
BEGIN_CASE(JSOP_GETUPVAR_DBG)
BEGIN_CASE(JSOP_CALLUPVAR_DBG)
{

View File

@ -722,13 +722,31 @@ ScriptEpilogue(JSContext *cx, JSStackFrame *fp, JSBool ok)
if (JS_UNLIKELY(hook != NULL) && (hookData = fp->maybeHookData()))
hook(cx, fp, JS_FALSE, &ok, hookData);
/*
* An eval frame's parent owns its activation objects. A yielding frame's
* activation objects are transferred to the floating frame, stored in the
* generator.
*/
if (fp->isFunctionFrame() && !fp->isEvalFrame() && !fp->isYielding())
PutActivationObjects(cx, fp);
if (fp->isEvalFrame()) {
/*
* The parent (ancestor for nested eval) of a non-strict eval frame
* owns its activation objects. Strict mode eval frames own their own
* Call objects but never have an arguments object (the first non-eval
* parent frame has it).
*/
if (fp->script()->strictModeCode) {
JS_ASSERT(!fp->isYielding());
JS_ASSERT(!fp->hasArgsObj());
JS_ASSERT(fp->hasCallObj());
JS_ASSERT(fp->callObj().callIsForEval());
js_PutCallObject(cx, fp);
}
} else {
/*
* Otherwise only function frames have activation objects. A yielding
* frame's activation objects are transferred to the floating frame,
* stored in the generator, and thus need not be synced.
*/
if (fp->isFunctionFrame() && !fp->isYielding()) {
JS_ASSERT_IF(fp->hasCallObj(), !fp->callObj().callIsForEval());
PutActivationObjects(cx, fp);
}
}
/*
* If inline-constructing, replace primitive rval with the new object

View File

@ -897,6 +897,9 @@ struct JSObject : js::gc::Cell {
/* Number of reserved slots. */
static const uint32 CALL_RESERVED_SLOTS = 2;
/* True if this is for a strict mode eval frame or for a function call. */
inline bool callIsForEval() const;
/* The stack frame for this Call object, if the frame is still active. */
inline JSStackFrame *maybeCallObjStackFrame() const;

View File

@ -432,6 +432,16 @@ JSObject::setArgsElement(uint32 i, const js::Value &v)
getArgsData()->slots[i] = v;
}
inline bool
JSObject::callIsForEval() const
{
JS_ASSERT(isCall());
JS_ASSERT(getSlot(JSSLOT_CALL_CALLEE).isObjectOrNull());
JS_ASSERT_IF(getSlot(JSSLOT_CALL_CALLEE).isObject(),
getSlot(JSSLOT_CALL_CALLEE).toObject().isFunction());
return getSlot(JSSLOT_CALL_CALLEE).isNull();
}
inline JSStackFrame *
JSObject::maybeCallObjStackFrame() const
{
@ -465,7 +475,7 @@ inline const js::Value &
JSObject::getCallObjArguments() const
{
JS_ASSERT(isCall());
JS_ASSERT(getCallObjCallee() != NULL);
JS_ASSERT(!callIsForEval());
return getSlot(JSSLOT_CALL_ARGUMENTS);
}
@ -473,7 +483,7 @@ inline void
JSObject::setCallObjArguments(const js::Value &v)
{
JS_ASSERT(isCall());
JS_ASSERT(getCallObjCallee() != NULL);
JS_ASSERT(!callIsForEval());
setSlot(JSSLOT_CALL_ARGUMENTS, v);
}

View File

@ -2883,8 +2883,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
break;
}
case JSOP_GETUPVAR:
case JSOP_CALLUPVAR:
case JSOP_GETUPVAR_DBG:
case JSOP_CALLUPVAR_DBG:
case JSOP_GETFCSLOT:

View File

@ -443,15 +443,19 @@ OPDEF(JSOP_NOTRACE, 182,"notrace", NULL, 3, 0, 0, 0, JOF_UINT16
OPDEF(JSOP_XMLCDATA, 183,"xmlcdata", NULL, 3, 0, 1, 19, JOF_ATOM)
OPDEF(JSOP_XMLCOMMENT, 184,"xmlcomment", NULL, 3, 0, 1, 19, JOF_ATOM)
OPDEF(JSOP_XMLPI, 185,"xmlpi", NULL, 3, 1, 1, 19, JOF_ATOM)
OPDEF(JSOP_CALLPROP, 186,"callprop", NULL, 3, 1, 2, 18, JOF_ATOM|JOF_PROP|JOF_CALLOP|JOF_TMPSLOT3)
OPDEF(JSOP_DELDESC, 186,"deldesc", NULL, 1, 2, 1, 15, JOF_BYTE|JOF_ELEM|JOF_DEL)
OPDEF(JSOP_CALLPROP, 187,"callprop", NULL, 3, 1, 2, 18, JOF_ATOM|JOF_PROP|JOF_CALLOP|JOF_TMPSLOT3)
/*
* Get a display (free) variable from the closure's reserved slots.
* These opcodes contain a reference to the current blockChain object.
* They are emitted directly after instructions, such as DEFFUN, that need fast access to
* the blockChain. The special NULLBLOCKCHAIN is needed because the JOF_OBJECT
* does not permit NULL object references, since it stores an index into a table of
* objects.
*/
OPDEF(JSOP_GETUPVAR, 187,"getupvar", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME)
OPDEF(JSOP_CALLUPVAR, 188,"callupvar", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_DELDESC, 189,"deldesc", NULL, 1, 2, 1, 15, JOF_BYTE|JOF_ELEM|JOF_DEL)
OPDEF(JSOP_BLOCKCHAIN, 188,"blockchain", NULL, 3, 0, 0, 0, JOF_OBJECT)
OPDEF(JSOP_NULLBLOCKCHAIN,189,"nullblockchain",NULL, 1, 0, 0, 0, JOF_BYTE)
/*
* Opcode to hold 24-bit immediate integer operands.
@ -586,7 +590,7 @@ OPDEF(JSOP_OBJTOP, 228,"objtop", NULL, 3, 0, 0, 0, JOF_UINT16
OPDEF(JSOP_TRACE, 229, "trace", NULL, 3, 0, 0, 0, JOF_UINT16)
/*
* Debugger versions of JSOP_{GET,CALL}UPVAR and the flat closure (_FC) ops.
* Debugger versions of the flat closure (_FC) ops.
*/
OPDEF(JSOP_GETUPVAR_DBG, 230,"getupvar_dbg", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME)
OPDEF(JSOP_CALLUPVAR_DBG, 231,"callupvar_dbg", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_CALLOP)
@ -614,15 +618,5 @@ OPDEF(JSOP_GLOBALDEC, 245,"globaldec", NULL, 3, 0, 1, 15, JOF_GLOBAL
OPDEF(JSOP_CALLGLOBAL, 246,"callglobal", NULL, 3, 0, 2, 19, JOF_GLOBAL|JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_FORGLOBAL, 247,"forglobal", NULL, 3, 1, 1, 19, JOF_GLOBAL|JOF_NAME|JOF_FOR|JOF_TMPSLOT)
/*
* These opcodes contain a reference to the current blockChain object.
* They are emitted directly after instructions, such as DEFFUN, that need fast access to
* the blockChain. The special NULLBLOCKCHAIN is needed because the JOF_OBJECT
* does not permit NULL object references, since it stores an index into a table of
* objects.
*/
OPDEF(JSOP_BLOCKCHAIN, 248,"blockchain", NULL, 3, 0, 0, 0, JOF_OBJECT)
OPDEF(JSOP_NULLBLOCKCHAIN,249,"nullblockchain",NULL, 1, 0, 0, 0, JOF_BYTE)
/* Like JSOP_FUNAPPLY but for f.call instead of f.apply. */
OPDEF(JSOP_FUNCALL, 250,"funcall", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE)
OPDEF(JSOP_FUNCALL, 248,"funcall", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE)

View File

@ -1909,8 +1909,8 @@ Parser::analyzeFunctions(JSFunctionBox *funbox, uint32& tcflags)
* but without this extra marking phase, function g will not be marked as a
* funarg since it is called from within its parent scope. But g reaches up to
* f's parameter p, so if o_m escapes f's activation scope, g does too and
* cannot use JSOP_GETUPVAR to reach p. In contast function h neither escapes
* nor uses an upvar "above" o_m's level.
* cannot assume that p's stack slot is still alive. In contast function h
* neither escapes nor uses an upvar "above" o_m's level.
*
* If function g itself contained lambdas that contained non-lambdas that reach
* up above its level, then those non-lambdas would have to be marked too. This
@ -2050,9 +2050,8 @@ Parser::markFunArgs(JSFunctionBox *funbox, uintN tcflags)
PN_OP(lexdep) == JSOP_CALLEE)) {
/*
* Mark this formerly-Algol-like function as an escaping
* function (i.e., as a funarg), because it is used from a
* funarg and therefore can not use JSOP_{GET,CALL}UPVAR to
* access upvars.
* function (i.e., as a funarg), because it is used from
* another funarg.
*
* Progress is guaranteed because we set the funarg flag
* here, which suppresses revisiting this function (thanks
@ -2372,26 +2371,8 @@ Parser::setFunctionKinds(JSFunctionBox *funbox, uint32& tcflags)
if (!fn->isFunArg()) {
/*
* This function is Algol-like, it never escapes. So long as it
* does not assign to outer variables, it needs only an upvars
* array in its script and JSOP_{GET,CALL}UPVAR opcodes in its
* bytecode to reach up the frame stack at runtime based on
* those upvars' cookies.
* This function is Algol-like, it never escapes.
*
* Any assignments to upvars from functions called by this one
* will be coherent because of the JSOP_{GET,CALL}UPVAR ops,
* which load from stack homes when interpreting or from native
* stack slots when executing a trace.
*
* We could add JSOP_SETUPVAR, etc., but it is uncommon for a
* nested function to assign to an outer lexical variable, so
* we defer adding yet more code footprint in the absence of
* evidence motivating these opcodes.
*/
bool mutation = !!(funbox->tcflags & TCF_FUN_SETS_OUTER_NAME);
uintN nupvars = 0;
/*
* Check that at least one outer lexical binding was assigned
* to (global variables don't count). This is conservative: we
* could limit assignments to those in the current function,
@ -2404,30 +2385,13 @@ Parser::setFunctionKinds(JSFunctionBox *funbox, uint32& tcflags)
if (!lexdep->isFreeVar()) {
JS_ASSERT(lexdep->frameLevel() <= funbox->level);
++nupvars;
if (lexdep->isAssigned())
break;
break;
}
}
if (!ale)
mutation = false;
if (nupvars == 0) {
if (!ale) {
FUN_METER(onlyfreevar);
FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
} else if (!mutation &&
!(funbox->tcflags & (TCF_FUN_IS_GENERATOR | TCF_FUN_ENTRAINS_SCOPES))) {
/*
* Algol-like functions can read upvars using the dynamic
* link (cx->fp/fp->down), optimized using the cx->display
* lookup table indexed by static level. They do not need
* to entrain and search their environment objects.
*/
FUN_METER(display);
FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
} else {
if (!(funbox->tcflags & TCF_FUN_IS_GENERATOR))
FUN_METER(setupvar);
}
} else {
uintN nupvars = 0, nflattened = 0;
@ -2902,8 +2866,8 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
pn->pn_cookie.makeFree();
/*
* If a lambda, give up on JSOP_{GET,CALL}UPVAR usage unless this function
* is immediately applied (we clear PND_FUNARG if so -- see memberExpr).
* If a lambda, mark this function as escaping (as a "funarg") unless it is
* immediately applied (we clear PND_FUNARG if so -- see memberExpr).
*
* Treat function sub-statements (non-lambda, non-body-level functions) as
* escaping funargs, since we can't statically analyze their definitions
@ -3244,6 +3208,13 @@ Parser::functionStmt()
}
tokenStream.ungetToken();
}
/* We forbid function statements in strict mode code. */
if (!tc->atBodyLevel() && tc->inStrictMode()) {
reportErrorNumber(NULL, JSREPORT_STRICT_MODE_ERROR, JSMSG_STRICT_FUNCTION_STATEMENT);
return NULL;
}
return functionDef(name, GENERAL, 0);
}

View File

@ -832,12 +832,8 @@ struct LexicalScopeNode : public JSParseNode {
* and because all uses are contained in the same block as the definition.
*
* We also analyze function uses to flag upward/downward funargs, optimizing
* Algol-like (not passed as funargs, only ever called) lightweight functions
* using cx->display. See JSOP_{GET,CALL}UPVAR.
*
* This means that closure optimizations may be frustrated by with, eval, or
* assignment to an outer var. Such hard cases require heavyweight functions
* and JSOP_NAME, etc.
* those lambdas that post-dominate their upvars inevitable only assignments or
* initializations as flat closures (after Chez Scheme's display closures).
*/
#define dn_uses pn_link

View File

@ -58,6 +58,8 @@
#include "jsobjinlines.h"
#include "jsregexpinlines.h"
#include "yarr/RegexParser.h"
#ifdef JS_TRACER
#include "jstracer.h"
using namespace avmplus;
@ -184,26 +186,12 @@ js_ObjectIsRegExp(JSObject *obj)
void
RegExp::handleYarrError(JSContext *cx, int error)
{
/* Hack: duplicated from yarr/yarr/RegexParser.h */
enum ErrorCode {
NoError,
PatternTooLarge,
QuantifierOutOfOrder,
QuantifierWithoutAtom,
MissingParentheses,
ParenthesesUnmatched,
ParenthesesTypeInvalid, /* "(?" with bad next char or end of pattern. */
CharacterClassUnmatched,
CharacterClassOutOfOrder,
QuantifierTooLarge,
EscapeUnterminated
};
switch (error) {
case NoError:
case JSC::Yarr::NoError:
JS_NOT_REACHED("Precondition violation: an error must have occurred.");
return;
#define COMPILE_EMSG(__code, __msg) \
case __code: \
case JSC::Yarr::__code: \
JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, __msg); \
return
COMPILE_EMSG(PatternTooLarge, JSMSG_REGEXP_TOO_COMPLEX);
@ -211,9 +199,10 @@ RegExp::handleYarrError(JSContext *cx, int error)
COMPILE_EMSG(QuantifierWithoutAtom, JSMSG_BAD_QUANTIFIER);
COMPILE_EMSG(MissingParentheses, JSMSG_MISSING_PAREN);
COMPILE_EMSG(ParenthesesUnmatched, JSMSG_UNMATCHED_RIGHT_PAREN);
COMPILE_EMSG(ParenthesesTypeInvalid, JSMSG_BAD_QUANTIFIER);
COMPILE_EMSG(ParenthesesTypeInvalid, JSMSG_BAD_QUANTIFIER); /* "(?" with bad next char */
COMPILE_EMSG(CharacterClassUnmatched, JSMSG_BAD_CLASS_RANGE);
COMPILE_EMSG(CharacterClassOutOfOrder, JSMSG_BAD_CLASS_RANGE);
COMPILE_EMSG(CharacterClassRangeSingleChar, JSMSG_BAD_CLASS_RANGE);
COMPILE_EMSG(EscapeUnterminated, JSMSG_TRAILING_SLASH);
COMPILE_EMSG(QuantifierTooLarge, JSMSG_BAD_QUANTIFIER);
#undef COMPILE_EMSG

View File

@ -485,10 +485,10 @@ Shape::getChild(JSContext *cx, const js::Shape &child, Shape **listp)
/*
* Beware duplicate formal parameters, allowed by ECMA-262 in
* non-strict mode. Otherwise we know that JSFunction::addLocal
* (our caller) won't pass an id already in the table to us. In
* the case of duplicate formals, the last one wins, so while
* we must not overcount entries, we must store newShape.
* non-strict mode. Otherwise we know that Bindings::add (our
* caller) won't pass an id already in the table to us. In the
* case of duplicate formals, the last one wins, so while we
* must not overcount entries, we must store newShape.
*/
if (!SHAPE_FETCH(spp))
++table->entryCount;

View File

@ -91,7 +91,7 @@ Bindings::lookup(JSContext *cx, JSAtom *name, uintN *indexp) const
if (shape->getter() == GetCallArg)
return ARGUMENT;
if (shape->getter() == GetFlatUpvar)
if (shape->getter() == GetCallUpvar)
return UPVAR;
return shape->writable() ? VARIABLE : CONSTANT;
@ -122,8 +122,8 @@ Bindings::add(JSContext *cx, JSAtom *name, BindingKind kind)
slot += nargs;
} else if (kind == UPVAR) {
indexp = &nupvars;
getter = GetFlatUpvar;
setter = SetFlatUpvar;
getter = GetCallUpvar;
setter = SetCallUpvar;
slot = SHAPE_INVALID_SLOT;
} else {
JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
@ -218,7 +218,7 @@ Bindings::getLocalNameArray(JSContext *cx, JSArenaPool *pool)
if (shape.getter() == GetCallArg) {
JS_ASSERT(index < nargs);
} else if (shape.getter() == GetFlatUpvar) {
} else if (shape.getter() == GetCallUpvar) {
JS_ASSERT(index < nupvars);
index += nargs + nvars;
} else {
@ -267,7 +267,7 @@ Bindings::lastVariable() const
const js::Shape *shape = lastUpvar();
if (nupvars > 0) {
while (shape->getter() == GetFlatUpvar)
while (shape->getter() == GetCallUpvar)
shape = shape->previous();
}
return shape;

View File

@ -73,7 +73,7 @@ namespace js {
*
* TODO: consider giving more bits to the slot value and takings ome from the level.
*/
class UpvarCookie
class UpvarCookie
{
uint32 value;
@ -206,11 +206,11 @@ class Bindings {
inline const js::Shape *lastShape() const;
enum {
/*
* A script may have no more than this many arguments, variables, or
* upvars.
*/
BINDING_COUNT_LIMIT = 0xFFFF
/*
* A script may have no more than this many arguments, variables, or
* upvars.
*/
BINDING_COUNT_LIMIT = 0xFFFF
};
/*
@ -224,11 +224,10 @@ class Bindings {
* which must deal with such cases.) Pass null for name when indicating a
* destructuring argument. Return true on success.
*
*
* The parser builds shape paths for functions, usable by Call objects at
* runtime, by calling addLocal. All ARGUMENT bindings must be added before
* before any VARIABLE or CONSTANT bindings, which themselves must be added
* before all UPVAR bindings.
* runtime, by calling an "add" method. All ARGUMENT bindings must be added
* before before any VARIABLE or CONSTANT bindings, which themselves must
* be added before all UPVAR bindings.
*/
bool add(JSContext *cx, JSAtom *name, BindingKind kind);
@ -431,7 +430,7 @@ struct JSScript {
* JS_CompileFile, etc.) have these objects.
* - Function scripts never have script objects; such scripts are owned
* by their function objects.
* - Temporary scripts created by obj_eval, JS_EvaluateScript, and
* - Temporary scripts created by obj_eval, JS_EvaluateScript, and
* similar functions never have these objects; such scripts are
* explicitly destroyed by the code that created them.
* Debugging API functions (JSDebugHooks::newScriptHook;

View File

@ -300,7 +300,6 @@ TypeToChar(JSValueType type)
case JSVAL_TYPE_BOXED: return '#';
case JSVAL_TYPE_STRORNULL: return 's';
case JSVAL_TYPE_OBJORNULL: return 'o';
case JSVAL_TYPE_UNINITIALIZED: return '*';
}
return '?';
}
@ -3660,7 +3659,6 @@ TraceRecorder::importGlobalSlot(unsigned slot)
JS_ASSERT(tree->nGlobalTypes() == tree->globalSlots->length());
} else {
type = importTypeMap[importStackSlots + index];
JS_ASSERT(type != JSVAL_TYPE_UNINITIALIZED);
}
import(EosAddress(eos_ins, slot * sizeof(double)), vp, type, "global", index, NULL);
}
@ -3809,7 +3807,6 @@ TraceRecorder::getImpl(const void *p)
} else {
unsigned slot = nativeStackSlotImpl(p);
JSValueType type = importTypeMap[slot];
JS_ASSERT(type != JSVAL_TYPE_UNINITIALIZED);
importImpl(StackAddress(lirbuf->sp, -tree->nativeStackBase + slot * sizeof(jsdouble)),
p, type, "stack", slot, cx->fp());
}
@ -4033,7 +4030,6 @@ TraceRecorder::determineSlotType(Value* vp)
} else {
t = importTypeMap[nativeStackSlot(vp)];
}
JS_ASSERT(t != JSVAL_TYPE_UNINITIALIZED);
JS_ASSERT_IF(t == JSVAL_TYPE_INT32, hasInt32Repr(*vp));
return t;
}
@ -5194,6 +5190,28 @@ TraceRecorder::prepareTreeCall(TreeFragment* inner)
w.xbarrier(createGuardRecord(exit));
}
class ClearSlotsVisitor : public SlotVisitorBase
{
Tracker &tracker;
public:
ClearSlotsVisitor(Tracker &tracker)
: tracker(tracker)
{}
JS_ALWAYS_INLINE bool
visitStackSlots(Value *vp, size_t count, JSStackFrame *) {
for (Value *vpend = vp + count; vp != vpend; ++vp)
tracker.set(vp, NULL);
return true;
}
JS_ALWAYS_INLINE bool
visitFrameObjPtr(void *p, JSStackFrame *) {
tracker.set(p, NULL);
return true;
}
};
static unsigned
BuildGlobalTypeMapFromInnerTree(Queue<JSValueType>& typeMap, VMSideExit* inner)
{
@ -5275,32 +5293,9 @@ TraceRecorder::emitTreeCall(TreeFragment* inner, VMSideExit* exit)
JS_ASSERT(map[i] != JSVAL_TYPE_BOXED);
#endif
/*
* Clear anything from the tracker that the inner tree could have written so
* that it will be lazily reloaded from the native stack. The only portion
* of the native stack that may be written to by both the inner and outer
* tree are the slots associated with the inner tree's entry frame. We may
* be certain of this by the following argument:
*
* 0. The only way for the inner tree to mutate the outer tree's upvars
* is by writing to the call object associated with cx->fp or the
* callDepth frames below it.
* 1. To write to a given call object, the innerTree must be executing with
* that call object on its scope chain.
* 2. Only function frames' scope chains may contain call objects.
* 3. An interpreted function frame's scope chain is set to be the parent
* of the callee when the callee is called.
* 4. On trace, there is a guard on the identity of the callee's parent.
* 5. From 1, 2, 3 and 4, any call object written to on trace will be the
* exact same call object as was observed at record time.
* 6. Inner trees are recorded before outer trees.
* 7. From 5 and 6, the only call object created by an outer tree that can
* be written to by an inner tree before the call object's associated
* stack frame is popped is cx->fp->callObj.
* 8. setCallProp has an explicit guard against writing to cx->fp->callObj.
* 9. From 0, 7 and 8, an inner tree never mutates its outer tree's upvars.
*/
clearCurrentFrameSlotsFromTracker(tracker);
/* The inner tree may modify currently-tracked upvars, so flush everything. */
ClearSlotsVisitor visitor(tracker);
VisitStackSlots(visitor, cx, callDepth);
SlotList& gslots = *tree->globalSlots;
for (unsigned i = 0; i < gslots.length(); i++) {
unsigned slot = gslots[i];
@ -5310,10 +5305,6 @@ TraceRecorder::emitTreeCall(TreeFragment* inner, VMSideExit* exit)
/* Set stack slots from the innermost frame. */
importTypeMap.setLength(NativeStackSlots(cx, callDepth));
#ifdef DEBUG
for (unsigned i = importStackSlots; i < importTypeMap.length(); i++)
importTypeMap[i] = JSVAL_TYPE_UNINITIALIZED;
#endif
unsigned startOfInnerFrame = importTypeMap.length() - exit->numStackSlots;
for (unsigned i = 0; i < exit->numStackSlots; i++)
importTypeMap[startOfInnerFrame + i] = exit->stackTypeMap()[i];
@ -10171,45 +10162,21 @@ TraceRecorder::guardNativeConversion(Value& v)
return RECORD_CONTINUE;
}
/*
* Clear out all slots of this frame in the nativeFrameTracker. Different
* locations on the VM stack might map to different locations on the native
* stack depending on the number of arguments (i.e.) of the next call, so we
* have to make sure we map those in to the cache with the right offsets.
*/
JS_REQUIRES_STACK void
TraceRecorder::clearCurrentFrameSlotsFromTracker(Tracker& which)
TraceRecorder::clearReturningFrameFromNativeveTracker()
{
JSStackFrame *const fp = cx->fp();
/*
* Duplicate native stack layout computation: see VisitFrameSlots header comment.
* This doesn't do layout arithmetic, but it must clear out all the slots defined as
* imported by VisitFrameSlots.
* Clear all tracker entries associated with the frame for the same reason
* described in record_EnterFrame. Reuse the generic visitor to avoid
* duplicating logic. The generic visitor stops at 'sp', whereas we need to
* clear up to script->nslots, so finish the job manually.
*/
if (fp->isGlobalFrame()) {
Value *vp = fp->slots() + fp->globalScript()->nfixed;
Value *vpend = fp->slots() + fp->globalScript()->nslots;
for (; vp < vpend; ++vp)
which.set(vp, (LIns*)0);
return;
}
if (!fp->isEvalFrame()) {
/* For simplicitly, flush 'em all, even non-canonical arg slots. */
Value *vp = fp->actualArgs() - 2 /* callee, this */;
Value *vpend = fp->formalArgsEnd();
for (; vp < vpend; ++vp)
which.set(vp, (LIns*)0);
}
which.set(fp->addressOfArgs(), (LIns*)0);
which.set(fp->addressOfScopeChain(), (LIns*)0);
Value *vp = fp->slots();
Value *vpend = fp->slots() + fp->functionScript()->nslots;
ClearSlotsVisitor visitor(nativeFrameTracker);
VisitStackSlots(visitor, cx, 0);
Value *vp = cx->regs->sp;
Value *vpend = cx->fp()->slots() + cx->fp()->script()->nslots;
for (; vp < vpend; ++vp)
which.set(vp, (LIns*)0);
nativeFrameTracker.set(vp, NULL);
}
class BoxArg
@ -10502,7 +10469,7 @@ TraceRecorder::record_JSOP_RETURN()
fp->fun()->atom ?
js_AtomToPrintableString(cx, fp->fun()->atom, &funBytes) :
"<anonymous>");
clearCurrentFrameSlotsFromTracker(nativeFrameTracker);
clearReturningFrameFromNativeveTracker();
return ARECORD_CONTINUE;
}
@ -13289,30 +13256,6 @@ TraceRecorder::stackLoad(Address addr, uint8 type)
}
}
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_GETUPVAR()
{
uintN index = GET_UINT16(cx->regs->pc);
JSScript *script = cx->fp()->script();
JSUpvarArray* uva = script->upvars();
JS_ASSERT(index < uva->length);
Value v;
LIns* upvar_ins = upvar(script, uva, index, v);
if (!upvar_ins)
return ARECORD_STOP;
stack(0, upvar_ins);
return ARECORD_CONTINUE;
}
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_CALLUPVAR()
{
CHECK_STATUS_A(record_JSOP_GETUPVAR());
stack(1, w.immiUndefined());
return ARECORD_CONTINUE;
}
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_GETFCSLOT()
{
@ -15885,7 +15828,7 @@ TraceRecorder::record_JSOP_STOP()
} else {
rval_ins = w.immiUndefined();
}
clearCurrentFrameSlotsFromTracker(nativeFrameTracker);
clearReturningFrameFromNativeveTracker();
return ARECORD_CONTINUE;
}

View File

@ -1472,7 +1472,7 @@ class TraceRecorder
nanojit::LIns* obj_ins,
VMSideExit *exit);
JS_REQUIRES_STACK RecordingStatus guardNativeConversion(Value& v);
JS_REQUIRES_STACK void clearCurrentFrameSlotsFromTracker(Tracker& which);
JS_REQUIRES_STACK void clearReturningFrameFromNativeveTracker();
JS_REQUIRES_STACK void putActivationObjects();
JS_REQUIRES_STACK RecordingStatus guardCallee(Value& callee);
JS_REQUIRES_STACK JSStackFrame *guardArguments(JSObject *obj, nanojit::LIns* obj_ins,
@ -1594,6 +1594,7 @@ class TraceRecorder
TreeFragment* getTree() const { return tree; }
bool outOfMemory() const { return traceMonitor->outOfMemory(); }
Oracle* getOracle() const { return oracle; }
JSObject* getGlobal() const { return globalObj; }
/* Entry points / callbacks from the interpreter. */
JS_REQUIRES_STACK AbortableRecordingStatus monitorRecording(JSOp op);
@ -1612,7 +1613,20 @@ class TraceRecorder
* Do slot arithmetic manually to avoid getSlotRef assertions which
* do not need to be satisfied for this purpose.
*/
return !tracker.has(globalObj->getSlots() + slot);
Value *vp = globalObj->getSlots() + slot;
/* If this global is definitely being tracked, then the write is unexpected. */
if (tracker.has(vp))
return false;
/*
* Otherwise, only abort if the global is not present in the
* import typemap. Just deep aborting false here is not acceptable,
* because the recorder does not guard on every operation that
* could lazily resolve. Since resolving adds properties to
* reserved slots, the tracer will never have imported them.
*/
return tree->globalSlots->offsetOf(nativeGlobalSlot(vp)) == -1;
}
pendingGlobalSlotToSet = -1;
return true;
@ -1868,7 +1882,7 @@ AbortRecordingIfUnexpectedGlobalWrite(JSContext *cx, JSObject *obj, unsigned slo
{
#ifdef JS_TRACER
if (TraceRecorder *tr = TRACE_RECORDER(cx)) {
if (!obj->parent && !tr->globalSetExpected(slot))
if (obj == tr->getGlobal() && !tr->globalSetExpected(slot))
AbortRecording(cx, "Global slot written outside tracer supervision");
}
#endif

View File

@ -108,8 +108,7 @@ JS_ENUM_HEADER(JSValueType, uint8)
JSVAL_TYPE_STRORNULL = 0x97,
JSVAL_TYPE_OBJORNULL = 0x98,
JSVAL_TYPE_BOXED = 0x99,
JSVAL_TYPE_UNINITIALIZED = 0xcd
JSVAL_TYPE_BOXED = 0x99
} JS_ENUM_FOOTER(JSValueType);
JS_STATIC_ASSERT(sizeof(JSValueType) == 1);

View File

@ -348,19 +348,15 @@ AutoCompartment::enter()
if (origin != destination) {
LeaveTrace(context);
#ifdef DEBUG
JSCompartment *oldCompartment = context->compartment;
context->resetCompartment();
wasSane = (context->compartment == oldCompartment);
#endif
if (context->isExceptionPending())
return false;
context->compartment = destination;
JSObject *scopeChain = target->getGlobal();
JS_ASSERT(scopeChain->isNative());
frame.construct();
if (!context->stack().pushDummyFrame(context, *scopeChain, &frame.ref()) ||
!destination->wrapException(context)) {
frame.destroy();
if (!context->stack().pushDummyFrame(context, *scopeChain, &frame.ref())) {
context->compartment = origin;
return false;
}
@ -376,8 +372,6 @@ AutoCompartment::leave()
if (origin != destination) {
frame.destroy();
context->resetCompartment();
JS_ASSERT_IF(wasSane && context->hasfp(), context->compartment == origin);
context->compartment->wrapException(context);
}
entered = false;
}
@ -642,8 +636,7 @@ JSCrossCompartmentWrapper::construct(JSContext *cx, JSObject *wrapper, uintN arg
return false;
call.leave();
return call.origin->wrap(cx, rval) &&
call.origin->wrapException(cx);
return call.origin->wrap(cx, rval);
}
bool

View File

@ -168,9 +168,6 @@ class AutoCompartment
JSFrameRegs regs;
AutoStringRooter input;
bool entered;
#ifdef DEBUG
bool wasSane;
#endif
public:
AutoCompartment(JSContext *cx, JSObject *target);

View File

@ -206,7 +206,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id);
* before deserialization of bytecode. If the saved version does not match
* the current version, abort deserialization and invalidate the file.
*/
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 80)
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 81)
/*
* Library-private functions.

View File

@ -1954,22 +1954,6 @@ mjit::Compiler::generateMethod()
return Compile_Error;
END_CASE(JSOP_CALLPROP)
BEGIN_CASE(JSOP_GETUPVAR)
BEGIN_CASE(JSOP_CALLUPVAR)
{
uint32 index = GET_UINT16(PC);
JSUpvarArray *uva = script->upvars();
JS_ASSERT(index < uva->length);
prepareStubCall(Uses(0));
masm.move(Imm32(uva->vector[index].asInteger()), Registers::ArgReg1);
INLINE_STUBCALL(stubs::GetUpvar);
frame.pushSynced();
if (op == JSOP_CALLUPVAR)
frame.push(UndefinedValue());
}
END_CASE(JSOP_CALLUPVAR)
BEGIN_CASE(JSOP_UINT24)
frame.push(Value(Int32Value((int32_t) GET_UINT24(PC))));
END_CASE(JSOP_UINT24)
@ -2336,6 +2320,12 @@ mjit::Compiler::emitReturn(FrameEntry *fe)
emitReturnValue(&stubcc.masm, fe);
emitFinalReturn(stubcc.masm);
}
} else {
if (fp->isEvalFrame() && script->strictModeCode) {
/* There will always be a call object. */
prepareStubCall(Uses(fe ? 1 : 0));
INLINE_STUBCALL(stubs::PutStrictEvalCallObject);
}
}
emitReturnValue(&masm, fe);
@ -3294,9 +3284,14 @@ mjit::Compiler::jsop_callprop_str(JSAtom *atom)
return true;
}
/* Bake in String.prototype. Is this safe? */
/*
* Bake in String.prototype. This is safe because of compileAndGo.
* We must pass an explicit scope chain only because JSD calls into
* here via the recompiler with a dummy context, and we need to use
* the global object for the script we are now compiling.
*/
JSObject *obj;
if (!js_GetClassPrototype(cx, NULL, JSProto_String, &obj))
if (!js_GetClassPrototype(cx, &fp->scopeChain(), JSProto_String, &obj))
return false;
/* Force into a register because getprop won't expect a constant. */

View File

@ -923,7 +923,7 @@ mjit::Compiler::jsop_typeof()
if (op == JSOP_STRICTEQ || op == JSOP_EQ || op == JSOP_STRICTNE || op == JSOP_NE) {
JSAtom *atom = script->getAtom(fullAtomIndex(PC + JSOP_TYPEOF_LENGTH));
JSRuntime *rt = cx->runtime;
JSValueType type = JSVAL_TYPE_UNINITIALIZED;
JSValueType type = JSVAL_TYPE_BOXED;
Assembler::Condition cond = (op == JSOP_STRICTEQ || op == JSOP_EQ)
? Assembler::Equal
: Assembler::NotEqual;
@ -941,7 +941,7 @@ mjit::Compiler::jsop_typeof()
cond = (cond == Assembler::Equal) ? Assembler::BelowOrEqual : Assembler::Above;
}
if (type != JSVAL_TYPE_UNINITIALIZED) {
if (type != JSVAL_TYPE_BOXED) {
PC += JSOP_STRING_LENGTH;;
PC += JSOP_EQ_LENGTH;

View File

@ -480,8 +480,10 @@ stubs::UncachedCallHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr)
}
void JS_FASTCALL
stubs::PutCallObject(VMFrame &f)
stubs::PutStrictEvalCallObject(VMFrame &f)
{
JS_ASSERT(f.fp()->isEvalFrame());
JS_ASSERT(f.fp()->script()->strictModeCode);
JS_ASSERT(f.fp()->hasCallObj());
js_PutCallObject(f.cx, f.fp());
}

View File

@ -905,8 +905,9 @@ class GetPropCompiler : public PICStubCompiler
Assembler masm;
Jump notStringObj = masm.testObjClass(Assembler::NotEqual, pic.objReg, obj->getClass());
masm.loadPayload(Address(pic.objReg, JSObject::getFixedSlotOffset(
JSObject::JSSLOT_PRIMITIVE_THIS)), pic.objReg);
masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg);
masm.loadPayload(Address(pic.objReg, JSObject::JSSLOT_PRIMITIVE_THIS * sizeof(Value)),
pic.objReg);
masm.loadPtr(Address(pic.objReg, JSString::offsetOfLengthAndFlags()), pic.objReg);
masm.urshift32(Imm32(JSString::LENGTH_SHIFT), pic.objReg);
masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg);

View File

@ -107,7 +107,7 @@ void UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr);
void JS_FASTCALL CreateThis(VMFrame &f, JSObject *proto);
void JS_FASTCALL Throw(VMFrame &f);
void JS_FASTCALL PutCallObject(VMFrame &f);
void JS_FASTCALL PutStrictEvalCallObject(VMFrame &f);
void JS_FASTCALL PutActivationObjects(VMFrame &f);
void JS_FASTCALL GetCallObject(VMFrame &f);
#if JS_MONOIC

View File

@ -4246,59 +4246,6 @@ Deserialize(JSContext *cx, uintN argc, jsval *vp)
return true;
}
JSBool
SetGlobalPropIf(JSContext *cx, uintN argc, jsval *vp)
{
if (argc != 3) {
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "setGlobalPropIf");
return false;
}
jsval *argv = JS_ARGV(cx, vp);
JSBool doSet;
if (!JS_ValueToBoolean(cx, argv[0], &doSet))
return false;
JS_SET_RVAL(cx, vp, JSVAL_VOID);
if (!doSet)
return true;
jsid id;
if (!JS_ValueToId(cx, argv[1], &id))
return false;
JSObject *global = JS_GetGlobalForScopeChain(cx);
return global && JS_SetPropertyById(cx, global, id, &argv[2]);
}
JSBool
DefGlobalPropIf(JSContext *cx, uintN argc, jsval *vp)
{
if (argc != 3) {
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "setGlobalPropIf");
return false;
}
jsval *argv = JS_ARGV(cx, vp);
JSBool doSet;
if (!JS_ValueToBoolean(cx, argv[0], &doSet))
return false;
JS_SET_RVAL(cx, vp, JSVAL_VOID);
if (!doSet)
return true;
jsid id;
if (!JS_ValueToId(cx, argv[1], &id))
return false;
JSObject *global = JS_GetGlobalForScopeChain(cx);
JSBool ignore;
return global && JS_DefineOwnProperty(cx, global, id, argv[2], &ignore);
}
JSBool
MJitStats(JSContext *cx, uintN argc, jsval *vp)
{
@ -4410,8 +4357,6 @@ static JSFunctionSpec shell_functions[] = {
JS_FN("wrap", Wrap, 1,0),
JS_FN("serialize", Serialize, 1,0),
JS_FN("deserialize", Deserialize, 1,0),
JS_FN("setGlobalPropIf",SetGlobalPropIf,3,0),
JS_FN("defGlobalPropIf",DefGlobalPropIf,3,0),
#ifdef JS_METHODJIT
JS_FN("mjitstats", MJitStats, 0,0),
#endif
@ -4545,9 +4490,6 @@ static const char *const shell_help_messages[] = {
"wrap(obj) Wrap an object into a noop wrapper.\n",
"serialize(sd) Serialize sd using JS_WriteStructuredClone. Returns a TypedArray.\n",
"deserialize(a) Deserialize data generated by serialize.\n",
"setGlobalPropIf(b,id,v) If b, get the global object o and perform o[id] = v.\n",
"defGlobalPropIf(b,id,dsc)If b, get the global object o and perform\n"
" Object.defineProperty(o, id, dsc).\n",
#ifdef JS_METHODJIT
"mjitstats() Return stats on mjit memory usage.\n",
#endif

View File

@ -16,22 +16,22 @@ url-prefix ../../jsreftest.html?test=ecma_5/eval/
# optimizations we might perform -- add new tests for such changes as needed.
#
# script exhaustive-fun-normalcaller-direct-normalcode.js
# script exhaustive-fun-normalcaller-direct-strictcode.js
# script exhaustive-fun-normalcaller-indirect-normalcode.js
# script exhaustive-fun-normalcaller-indirect-strictcode.js
# script exhaustive-fun-strictcaller-direct-normalcode.js
# script exhaustive-fun-strictcaller-direct-strictcode.js
# script exhaustive-fun-strictcaller-indirect-normalcode.js
# script exhaustive-fun-strictcaller-indirect-strictcode.js
# script exhaustive-global-normalcaller-direct-normalcode.js
# script exhaustive-global-normalcaller-direct-strictcode.js
# script exhaustive-global-normalcaller-indirect-normalcode.js
# script exhaustive-global-normalcaller-indirect-strictcode.js
# script exhaustive-global-strictcaller-direct-normalcode.js
# script exhaustive-global-strictcaller-direct-strictcode.js
# script exhaustive-global-strictcaller-indirect-normalcode.js
# script exhaustive-global-strictcaller-indirect-strictcode.js
script exhaustive-fun-normalcaller-direct-normalcode.js
script exhaustive-fun-normalcaller-direct-strictcode.js
script exhaustive-fun-normalcaller-indirect-normalcode.js
script exhaustive-fun-normalcaller-indirect-strictcode.js
script exhaustive-fun-strictcaller-direct-normalcode.js
script exhaustive-fun-strictcaller-direct-strictcode.js
script exhaustive-fun-strictcaller-indirect-normalcode.js
script exhaustive-fun-strictcaller-indirect-strictcode.js
script exhaustive-global-normalcaller-direct-normalcode.js
script exhaustive-global-normalcaller-direct-strictcode.js
script exhaustive-global-normalcaller-indirect-normalcode.js
script exhaustive-global-normalcaller-indirect-strictcode.js
script exhaustive-global-strictcaller-direct-normalcode.js
script exhaustive-global-strictcaller-direct-strictcode.js
script exhaustive-global-strictcaller-indirect-normalcode.js
script exhaustive-global-strictcaller-indirect-strictcode.js
# Not written yet! These require a new shell primitive to work there, though
# browser could probably use setTimeout for this. Moreover, we haven't

View File

@ -21,3 +21,4 @@ script bug472534.js
script bug496985.js
script bug566661.js
script iterator-in-catch.js
script strict-function-statements.js

View File

@ -1,37 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
/*
* Return true if both of these return true:
* - LENIENT_PRED applied to CODE
* - STRICT_PRED applied to CODE with a use strict directive added to the front
*
* Run STRICT_PRED first, for testing code that affects the global environment
* in loose mode, but fails in strict mode.
*/
function testLenientAndStrict(code, lenient_pred, strict_pred) {
return (strict_pred("'use strict'; " + code) &&
lenient_pred(code));
}
/*
* raisesException(EXCEPTION)(CODE) returns true if evaluating CODE (as eval
* code) throws an exception object whose prototype is
* EXCEPTION.prototype, and returns false if it throws any other error
* or evaluates successfully. For example: raises(TypeError)("0()") ==
* true.
*/
function raisesException(exception) {
return function (code) {
try {
eval(code);
return false;
} catch (actual) {
return exception.prototype.isPrototypeOf(actual);
}
};
};

View File

@ -0,0 +1,100 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
// Ordinary function definitions should be unaffected.
assertEq(testLenientAndStrict("function f() { }",
parsesSuccessfully,
parsesSuccessfully),
true);
// Function statements within blocks are forbidden in strict mode code.
assertEq(testLenientAndStrict("{ function f() { } }",
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
// Lambdas are always permitted within blocks.
assertEq(testLenientAndStrict("{ (function f() { }) }",
parsesSuccessfully,
parsesSuccessfully),
true);
// Function statements within any sort of statement are forbidden in strict mode code.
assertEq(testLenientAndStrict("if (true) function f() { }",
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict("while (true) function f() { }",
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict("do function f() { } while (true);",
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict("for(;;) function f() { }",
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict("for(x in []) function f() { }",
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict("with(o) function f() { }",
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict("switch(1) { case 1: function f() { } }",
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict("x: function f() { }",
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict("try { function f() { } } catch (x) { }",
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
// Lambdas are always permitted within any sort of statement.
assertEq(testLenientAndStrict("if (true) (function f() { })",
parsesSuccessfully,
parsesSuccessfully),
true);
// Function statements are permitted in blocks within lenient functions.
assertEq(parsesSuccessfully("function f() { function g() { } }"),
true);
// Function statements are permitted in any statement within lenient functions.
assertEq(parsesSuccessfully("function f() { if (true) function g() { } }"),
true);
assertEq(parseRaisesException(SyntaxError)
("function f() { 'use strict'; if (true) function g() { } }"),
true);
assertEq(parseRaisesException(SyntaxError)
("function f() { 'use strict'; { function g() { } } }"),
true);
assertEq(parsesSuccessfully("function f() { 'use strict'; if (true) (function g() { }) }"),
true);
assertEq(parsesSuccessfully("function f() { 'use strict'; { (function g() { }) } }"),
true);
// Eval should behave the same way. (The parse-only tests use the Function constructor.)
assertEq(testLenientAndStrict("function f() { }",
completesNormally,
completesNormally),
true);
assertEq(testLenientAndStrict("{ function f() { } }",
completesNormally,
raisesException(SyntaxError)),
true);
reportCompare(true, true);

View File

@ -39,4 +39,4 @@ script unbrand-this.js
script this-for-function-expression-recursion.js
script assign-to-callee-name.js
script directive-prologue-01.js
# script eval-variable-environment.js
script eval-variable-environment.js

View File

@ -65,7 +65,7 @@ function test()
(function (){
var x;
eval("const x; (function ()x)");
eval("var x; (function ()x)");
}
)();

View File

@ -78,7 +78,7 @@ function test()
function f() {
var x;
(function(){})();
eval("if(x|=[]) {const x; }");
eval("if(x|=[]) {var x; }");
}
f();

View File

@ -15,6 +15,8 @@ script regress-552432.js
script regress-553778.js
script regress-555246-0.js
script regress-555246-1.js
script regress-559402-1.js
script regress-559402-2.js
script regress-559438.js
script regress-560101.js
script regress-560998-1.js
@ -38,6 +40,10 @@ script regress-586482-4.js
script regress-586482-5.js
script regress-588339.js
script regress-yarr-regexp.js
script regress-592202-1.js
script regress-592202-2.js
script regress-592202-3.js
script regress-592202-4.js
script regress-592217.js
script regress-592556-c35.js
script regress-593256.js

View File

@ -0,0 +1,22 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
var expect = "No error";
var actual = expect;
if (typeof options == "function") {
var opts = options();
if (!/\bstrict\b/.test(opts))
options("strict");
if (!/\bwerror\b/.test(opts))
options("werror");
}
try {
eval('function foo() { "use strict"; }');
} catch (e) {
actual = '' + e;
}
reportCompare(expect, actual, "ok");

View File

@ -0,0 +1,8 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
var expect = undefined;
var actual = (function foo() { "bogus"; })();
reportCompare(expect, actual, "ok");

View File

@ -0,0 +1,7 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
i = 42
eval("let(y){(function(){let({}=y){(function(){let({}=y=[])(i)})()}})()}")
reportCompare(0, 0, "ok");

View File

@ -0,0 +1,15 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
eval("\
let(b)((\
function(){\
let(d=b)\
((function(){\
b=b\
})())\
}\
)())\
")
reportCompare(0, 0, "ok");

View File

@ -0,0 +1,28 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
test();
function test()
{
var counter = 0;
function f(x,y) {
try
{
throw 42;
}
catch(e2)
{
foo(function(){ return x; }| "9.2" && 5 || counter && e);
++counter;
}
}
f(2, 1);
}
function foo(bar) { return ""+bar; }
reportCompare(0, 0, "ok");

View File

@ -0,0 +1,31 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
function p() { }
function test()
{
var counter = 0;
function f(x) {
try
{
throw 42;
}
catch(e)
{
assertEq(counter, 0);
p(function(){x;});
counter = 1;
}
}
f(2);
assertEq(counter, 1);
}
test();
reportCompare(0, 0, "ok");

View File

@ -144,6 +144,8 @@ GetExpandoObject(JSContext *cx, JSObject *holder)
JSObject *expando = holder->getSlot(JSSLOT_EXPANDO).toObjectOrNull();
if (!expando) {
expando = JS_NewObjectWithGivenProto(cx, nsnull, nsnull, holder->getParent());
if (!expando)
return NULL;
holder->setSlot(JSSLOT_EXPANDO, ObjectValue(*expando));
}
return expando;

View File

@ -39,6 +39,22 @@ enum BuiltInCharacterClassID {
NewlineClassID
};
enum ErrorCode {
NoError,
PatternTooLarge,
QuantifierOutOfOrder,
QuantifierWithoutAtom,
MissingParentheses,
ParenthesesUnmatched,
ParenthesesTypeInvalid,
CharacterClassUnmatched,
CharacterClassOutOfOrder,
CharacterClassRangeSingleChar,
EscapeUnterminated,
QuantifierTooLarge,
NumberOfErrorCodes
};
// The Parser class should not be used directly - only via the Yarr::parse() method.
template<class Delegate>
class Parser {
@ -46,21 +62,6 @@ private:
template<class FriendDelegate>
friend int parse(FriendDelegate& delegate, const UString& pattern, unsigned backReferenceLimit);
enum ErrorCode {
NoError,
PatternTooLarge,
QuantifierOutOfOrder,
QuantifierWithoutAtom,
MissingParentheses,
ParenthesesUnmatched,
ParenthesesTypeInvalid,
CharacterClassUnmatched,
CharacterClassOutOfOrder,
EscapeUnterminated,
QuantifierTooLarge,
NumberOfErrorCodes
};
/*
* CharacterClassParserDelegate:
*
@ -147,6 +148,15 @@ private:
*/
void atomBuiltInCharacterClass(BuiltInCharacterClassID classID, bool invert)
{
if (m_state == cachedCharacterHyphen) {
// If the RHS of a range does not contain exacly one character then a SyntaxError
// must be thrown. SpiderMonkey only errors out in the [c-\s] case as an extension.
// (This assumes none of the built in character classes contain a single
// character.)
m_err = CharacterClassRangeSingleChar;
m_state = empty;
return;
}
flush();
m_delegate.atomCharacterClassBuiltIn(classID, invert);
}
@ -404,7 +414,7 @@ private:
/*
* parseCharacterClass():
*
* Helper for parseTokens(); calls dirctly and indirectly (via parseCharacterClassEscape)
* Helper for parseTokens(); calls directly and indirectly (via parseCharacterClassEscape)
* to an instance of CharacterClassParserDelegate, to describe the character class to the
* delegate.
*/