mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge tracemonkey into mozilla-central. (a=blockers)
This commit is contained in:
commit
306a55837a
45
js/src/jit-test/tests/basic/bug576837-regexp.js
Normal file
45
js/src/jit-test/tests/basic/bug576837-regexp.js
Normal 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);
|
16
js/src/jit-test/tests/basic/bug623859.js
Normal file
16
js/src/jit-test/tests/basic/bug623859.js
Normal 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() {}
|
||||||
|
}
|
||||||
|
})):
|
||||||
|
}
|
||||||
|
})())
|
||||||
|
})()
|
@ -1,19 +1,6 @@
|
|||||||
/* Touch/init early so global shape doesn't change in loop */
|
X = { value: "" }
|
||||||
var SetOnIter = HOTLOOP - 1;
|
z = 0;
|
||||||
var x = 3;
|
for (var i = 0; i < 20; i++) {
|
||||||
var i = 0;
|
Object.defineProperty(this, "z", X);
|
||||||
assertEq(true, true);
|
z = 1;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
17
js/src/jit-test/tests/basic/testInnerTreeMutatingUpvars.js
Normal file
17
js/src/jit-test/tests/basic/testInnerTreeMutatingUpvars.js
Normal 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();
|
10
js/src/jit-test/tests/jaeger/bug623474.js
Normal file
10
js/src/jit-test/tests/jaeger/bug623474.js
Normal 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. */
|
||||||
|
|
@ -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_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_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_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")
|
||||||
|
@ -2996,6 +2996,8 @@ NewArray(JSContext *cx, jsuint length, JSObject *proto)
|
|||||||
|
|
||||||
gc::FinalizeKind kind = GuessObjectGCKind(length, true);
|
gc::FinalizeKind kind = GuessObjectGCKind(length, true);
|
||||||
JSObject *obj = detail::NewObject<WithProto::Class, false>(cx, &js_ArrayClass, proto, NULL, kind);
|
JSObject *obj = detail::NewObject<WithProto::Class, false>(cx, &js_ArrayClass, proto, NULL, kind);
|
||||||
|
if (!obj)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
obj->setArrayLength(length);
|
obj->setArrayLength(length);
|
||||||
|
|
||||||
@ -3027,6 +3029,9 @@ JSObject *
|
|||||||
NewDenseCopiedArray(JSContext *cx, uintN length, Value *vp, JSObject *proto)
|
NewDenseCopiedArray(JSContext *cx, uintN length, Value *vp, JSObject *proto)
|
||||||
{
|
{
|
||||||
JSObject* obj = NewArray<true>(cx, length, proto);
|
JSObject* obj = NewArray<true>(cx, length, proto);
|
||||||
|
if (!obj)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
JS_ASSERT(obj->getDenseArrayCapacity() >= length);
|
JS_ASSERT(obj->getDenseArrayCapacity() >= length);
|
||||||
|
|
||||||
if (vp)
|
if (vp)
|
||||||
|
@ -2019,14 +2019,8 @@ JSContext::resetCompartment()
|
|||||||
|
|
||||||
compartment = scopeobj->compartment();
|
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())
|
if (isExceptionPending())
|
||||||
(void) compartment->wrapException(this);
|
wrapPendingException();
|
||||||
return;
|
return;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
@ -2038,6 +2032,21 @@ error:
|
|||||||
compartment = NULL;
|
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
|
void
|
||||||
JSContext::pushSegmentAndFrame(js::StackSegment *newseg, JSFrameRegs &newregs)
|
JSContext::pushSegmentAndFrame(js::StackSegment *newseg, JSFrameRegs &newregs)
|
||||||
{
|
{
|
||||||
|
@ -896,7 +896,7 @@ private:
|
|||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
# define FUNCTION_KIND_METER_LIST(_) \
|
# define FUNCTION_KIND_METER_LIST(_) \
|
||||||
_(allfun), _(heavy), _(nofreeupvar), _(onlyfreevar), \
|
_(allfun), _(heavy), _(nofreeupvar), _(onlyfreevar), \
|
||||||
_(display), _(flat), _(setupvar), _(badfunarg), \
|
_(flat), _(badfunarg), \
|
||||||
_(joinedsetmethod), _(joinedinitmethod), \
|
_(joinedsetmethod), _(joinedinitmethod), \
|
||||||
_(joinedreplace), _(joinedsort), _(joinedmodulepat), \
|
_(joinedreplace), _(joinedsort), _(joinedmodulepat), \
|
||||||
_(mreadbarrier), _(mwritebarrier), _(mwslotbarrier), \
|
_(mreadbarrier), _(mwritebarrier), _(mwslotbarrier), \
|
||||||
@ -1768,6 +1768,7 @@ struct JSContext
|
|||||||
friend bool js::Interpret(JSContext *, JSStackFrame *, uintN, JSInterpMode);
|
friend bool js::Interpret(JSContext *, JSStackFrame *, uintN, JSInterpMode);
|
||||||
|
|
||||||
void resetCompartment();
|
void resetCompartment();
|
||||||
|
void wrapPendingException();
|
||||||
|
|
||||||
/* 'regs' must only be changed by calling this function. */
|
/* 'regs' must only be changed by calling this function. */
|
||||||
void setCurrentRegs(JSFrameRegs *regs) {
|
void setCurrentRegs(JSFrameRegs *regs) {
|
||||||
|
@ -208,17 +208,21 @@ JSCompartment::wrap(JSContext *cx, Value *vp)
|
|||||||
if (obj->getCompartment() == this)
|
if (obj->getCompartment() == this)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (cx->runtime->preWrapObjectCallback)
|
if (cx->runtime->preWrapObjectCallback) {
|
||||||
obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags);
|
obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags);
|
||||||
if (!obj)
|
if (!obj)
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
vp->setObject(*obj);
|
vp->setObject(*obj);
|
||||||
if (obj->getCompartment() == this)
|
if (obj->getCompartment() == this)
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
if (cx->runtime->preWrapObjectCallback)
|
if (cx->runtime->preWrapObjectCallback) {
|
||||||
obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags);
|
obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags);
|
||||||
|
if (!obj)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
JS_ASSERT(!obj->isWrapper() || obj->getClass()->ext.innerObject);
|
JS_ASSERT(!obj->isWrapper() || obj->getClass()->ext.innerObject);
|
||||||
vp->setObject(*obj);
|
vp->setObject(*obj);
|
||||||
@ -353,21 +357,6 @@ JSCompartment::wrap(JSContext *cx, AutoIdVector &props)
|
|||||||
return true;
|
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
|
#if defined JS_METHODJIT && defined JS_MONOIC
|
||||||
/*
|
/*
|
||||||
* Check if the pool containing the code for jit should be destroyed, per the
|
* Check if the pool containing the code for jit should be destroyed, per the
|
||||||
|
@ -327,7 +327,6 @@ struct JS_FRIEND_API(JSCompartment) {
|
|||||||
bool wrap(JSContext *cx, js::PropertyOp *op);
|
bool wrap(JSContext *cx, js::PropertyOp *op);
|
||||||
bool wrap(JSContext *cx, js::PropertyDescriptor *desc);
|
bool wrap(JSContext *cx, js::PropertyDescriptor *desc);
|
||||||
bool wrap(JSContext *cx, js::AutoIdVector &props);
|
bool wrap(JSContext *cx, js::AutoIdVector &props);
|
||||||
bool wrapException(JSContext *cx);
|
|
||||||
|
|
||||||
void sweep(JSContext *cx, uint32 releaseInterval);
|
void sweep(JSContext *cx, uint32 releaseInterval);
|
||||||
void purge(JSContext *cx);
|
void purge(JSContext *cx);
|
||||||
|
@ -1974,99 +1974,6 @@ EmitLeaveBlock(JSContext *cx, JSCodeGenerator *cg, JSOp op,
|
|||||||
return bigSuffix == JSOP_NOP || js_Emit1(cx, cg, bigSuffix) >= 0;
|
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
|
* Try to convert a *NAME op to a *GNAME op, which optimizes access to
|
||||||
* undeclared globals. Return true if a conversion was made.
|
* undeclared globals. Return true if a conversion was made.
|
||||||
@ -2232,63 +2139,11 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!caller->isFunctionFrame())
|
|
||||||
return JS_TRUE;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure the variable object used by the compiler to initialize
|
* Out of tricks, so we must rely on PICs to optimize named
|
||||||
* parent links matches the caller's varobj. Compile-n-go compiler-
|
* accesses from direct eval called from function code.
|
||||||
* created function objects have the top-level cg's scopeChain set
|
|
||||||
* as their parent by Parser::newFunction.
|
|
||||||
*/
|
*/
|
||||||
JSObject *scopeobj = cg->inFunction()
|
return JS_TRUE;
|
||||||
? 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Optimize accesses to undeclared globals. */
|
/* Optimize accesses to undeclared globals. */
|
||||||
@ -2348,50 +2203,6 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||||||
uint16 level = cookie.level();
|
uint16 level = cookie.level();
|
||||||
JS_ASSERT(cg->staticLevel >= 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;
|
const uintN skip = cg->staticLevel - level;
|
||||||
if (skip != 0) {
|
if (skip != 0) {
|
||||||
JS_ASSERT(cg->inFunction());
|
JS_ASSERT(cg->inFunction());
|
||||||
@ -2410,29 +2221,8 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||||||
if (cg->flags & TCF_FUN_HEAVYWEIGHT)
|
if (cg->flags & TCF_FUN_HEAVYWEIGHT)
|
||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
|
|
||||||
if (cg->fun()->isFlatClosure()) {
|
if (!cg->fun()->isFlatClosure())
|
||||||
op = JSOP_GETFCSLOT;
|
return JS_TRUE;
|
||||||
} 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
ale = cg->upvarList.lookup(atom);
|
ale = cg->upvarList.lookup(atom);
|
||||||
if (ale) {
|
if (ale) {
|
||||||
@ -2473,7 +2263,7 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||||||
vector[index].set(skip, slot);
|
vector[index].set(skip, slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
pn->pn_op = op;
|
pn->pn_op = JSOP_GETFCSLOT;
|
||||||
JS_ASSERT((index & JS_BITMASK(16)) == index);
|
JS_ASSERT((index & JS_BITMASK(16)) == index);
|
||||||
pn->pn_cookie.set(0, index);
|
pn->pn_cookie.set(0, index);
|
||||||
pn->pn_dflags |= PND_BOUND;
|
pn->pn_dflags |= PND_BOUND;
|
||||||
@ -2852,9 +2642,6 @@ EmitNameOp(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
|
|||||||
case JSOP_GETLOCAL:
|
case JSOP_GETLOCAL:
|
||||||
op = JSOP_CALLLOCAL;
|
op = JSOP_CALLLOCAL;
|
||||||
break;
|
break;
|
||||||
case JSOP_GETUPVAR:
|
|
||||||
op = JSOP_CALLUPVAR;
|
|
||||||
break;
|
|
||||||
case JSOP_GETFCSLOT:
|
case JSOP_GETFCSLOT:
|
||||||
op = JSOP_CALLFCSLOT;
|
op = JSOP_CALLFCSLOT;
|
||||||
break;
|
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
|
* API users may also set the JSOPTION_NO_SCRIPT_RVAL option when
|
||||||
* calling JS_Compile* to suppress JSOP_POPV.
|
* 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 (!useful) {
|
||||||
if (!CheckSideEffects(cx, cg, pn2, &useful))
|
if (!CheckSideEffects(cx, cg, pn2, &useful))
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
@ -6266,7 +6054,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
EMIT_INDEX_OP(JSOP_GETXPROP, atomIndex);
|
EMIT_INDEX_OP(JSOP_GETXPROP, atomIndex);
|
||||||
} else {
|
} else {
|
||||||
JS_ASSERT(PN_OP(pn2) != JSOP_GETUPVAR);
|
|
||||||
EMIT_UINT16_IMM_OP((PN_OP(pn2) == JSOP_SETGNAME)
|
EMIT_UINT16_IMM_OP((PN_OP(pn2) == JSOP_SETGNAME)
|
||||||
? JSOP_GETGNAME
|
? JSOP_GETGNAME
|
||||||
: (PN_OP(pn2) == JSOP_SETGLOBAL)
|
: (PN_OP(pn2) == JSOP_SETGLOBAL)
|
||||||
|
@ -234,9 +234,9 @@ struct JSStmtInfo {
|
|||||||
/*
|
/*
|
||||||
* Flag to prevent a non-escaping function from being optimized into a null
|
* 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
|
* 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
|
* resolution), because this function contains a closure that needs one or more
|
||||||
* a closure that needs one or more scope objects surrounding it (i.e., Call
|
* scope objects surrounding it (i.e., a Call object for an outer heavyweight
|
||||||
* object for a heavyweight outer function). See bug 560234.
|
* function). See bug 560234.
|
||||||
*/
|
*/
|
||||||
#define TCF_FUN_ENTRAINS_SCOPES 0x400000
|
#define TCF_FUN_ENTRAINS_SCOPES 0x400000
|
||||||
|
|
||||||
|
127
js/src/jsfun.cpp
127
js/src/jsfun.cpp
@ -455,8 +455,6 @@ WrapEscapingClosure(JSContext *cx, JSStackFrame *fp, JSFunction *fun)
|
|||||||
* immediate operand.
|
* immediate operand.
|
||||||
*/
|
*/
|
||||||
switch (op) {
|
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_GETFCSLOT: *pc = JSOP_GETUPVAR_DBG; break;
|
||||||
case JSOP_CALLFCSLOT: *pc = JSOP_CALLUPVAR_DBG; break;
|
case JSOP_CALLFCSLOT: *pc = JSOP_CALLUPVAR_DBG; break;
|
||||||
case JSOP_DEFFUN_FC: *pc = JSOP_DEFFUN_DBGFC; break;
|
case JSOP_DEFFUN_FC: *pc = JSOP_DEFFUN_DBGFC; break;
|
||||||
@ -1077,6 +1075,12 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp)
|
|||||||
{
|
{
|
||||||
JSObject &callobj = fp->callObj();
|
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. */
|
/* Get the arguments object to snapshot fp's actual argument values. */
|
||||||
if (fp->hasArgsObj()) {
|
if (fp->hasArgsObj()) {
|
||||||
if (!fp->hasOverriddenArgs())
|
if (!fp->hasOverriddenArgs())
|
||||||
@ -1084,53 +1088,62 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp)
|
|||||||
js_PutArgsObject(cx, fp);
|
js_PutArgsObject(cx, fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSFunction *fun = fp->fun();
|
JSScript *script = fp->script();
|
||||||
JS_ASSERT(fun == callobj.getCallObjCalleeFunction());
|
Bindings &bindings = script->bindings;
|
||||||
|
|
||||||
Bindings &bindings = fun->script()->bindings;
|
if (callobj.callIsForEval()) {
|
||||||
uintN n = bindings.countArgsAndVars();
|
JS_ASSERT(script->strictModeCode);
|
||||||
|
JS_ASSERT(bindings.countArgs() == 0);
|
||||||
|
|
||||||
if (n != 0) {
|
/* This could be optimized as below, but keep it simple for now. */
|
||||||
JS_ASSERT(JSFunction::CLASS_RESERVED_SLOTS + n <= callobj.numSlots());
|
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();
|
if (uintN n = bindings.countArgsAndVars()) {
|
||||||
uint32 nargs = bindings.countArgs();
|
JS_ASSERT(JSObject::CALL_RESERVED_SLOTS + n <= callobj.numSlots());
|
||||||
JS_ASSERT(fun->nargs == nargs);
|
|
||||||
JS_ASSERT(nvars + nargs == n);
|
|
||||||
|
|
||||||
JSScript *script = fun->script();
|
uint32 nvars = bindings.countVars();
|
||||||
if (script->usesEval
|
uint32 nargs = bindings.countArgs();
|
||||||
|
JS_ASSERT(fun->nargs == nargs);
|
||||||
|
JS_ASSERT(nvars + nargs == n);
|
||||||
|
|
||||||
|
JSScript *script = fun->script();
|
||||||
|
if (script->usesEval
|
||||||
#ifdef JS_METHODJIT
|
#ifdef JS_METHODJIT
|
||||||
|| script->debugMode
|
|| script->debugMode
|
||||||
#endif
|
#endif
|
||||||
) {
|
) {
|
||||||
CopyValuesToCallObject(callobj, nargs, fp->formalArgs(), nvars, fp->slots());
|
CopyValuesToCallObject(callobj, nargs, fp->formalArgs(), nvars, fp->slots());
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* For each arg & var that is closed over, copy it from the stack
|
* For each arg & var that is closed over, copy it from the stack
|
||||||
* into the call object.
|
* into the call object.
|
||||||
*/
|
*/
|
||||||
uint32 nclosed = script->nClosedArgs;
|
uint32 nclosed = script->nClosedArgs;
|
||||||
for (uint32 i = 0; i < nclosed; i++) {
|
for (uint32 i = 0; i < nclosed; i++) {
|
||||||
uint32 e = script->getClosedArg(i);
|
uint32 e = script->getClosedArg(i);
|
||||||
callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + e, fp->formalArg(e));
|
callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + e, fp->formalArg(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
nclosed = script->nClosedVars;
|
nclosed = script->nClosedVars;
|
||||||
for (uint32 i = 0; i < nclosed; i++) {
|
for (uint32 i = 0; i < nclosed; i++) {
|
||||||
uint32 e = script->getClosedVar(i);
|
uint32 e = script->getClosedVar(i);
|
||||||
callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + nargs + e, fp->slots()[e]);
|
callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + nargs + e, fp->slots()[e]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* Clear private pointers to fp, which is about to go away (js_Invoke). */
|
/* Clear private pointers to fp, which is about to go away (js_Invoke). */
|
||||||
if (js_IsNamedLambda(fun)) {
|
if (js_IsNamedLambda(fun)) {
|
||||||
JSObject *env = callobj.getParent();
|
JSObject *env = callobj.getParent();
|
||||||
|
|
||||||
JS_ASSERT(env->getClass() == &js_DeclEnvClass);
|
JS_ASSERT(env->getClass() == &js_DeclEnvClass);
|
||||||
JS_ASSERT(env->getPrivate() == fp);
|
JS_ASSERT(env->getPrivate() == fp);
|
||||||
env->setPrivate(NULL);
|
env->setPrivate(NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
callobj.setPrivate(NULL);
|
callobj.setPrivate(NULL);
|
||||||
@ -1211,30 +1224,25 @@ SetCallArg(JSContext *cx, JSObject *obj, jsid id, Value *vp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
JSBool
|
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));
|
JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
|
||||||
uintN i = (uint16) JSID_TO_INT(id);
|
uintN i = (uint16) JSID_TO_INT(id);
|
||||||
|
|
||||||
JSObject *callee = obj->getCallObjCallee();
|
*vp = obj->getCallObjCallee()->getFlatClosureUpvar(i);
|
||||||
JS_ASSERT(callee);
|
|
||||||
|
|
||||||
*vp = callee->getFlatClosureUpvar(i);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSBool
|
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));
|
JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
|
||||||
uintN i = (uint16) JSID_TO_INT(id);
|
uintN i = (uint16) JSID_TO_INT(id);
|
||||||
|
|
||||||
JSObject *callee = obj->getCallObjCallee();
|
Value *up = &obj->getCallObjCallee()->getFlatClosureUpvar(i);
|
||||||
JS_ASSERT(callee);
|
|
||||||
|
|
||||||
Value *upvarp = &callee->getFlatClosureUpvar(i);
|
GC_POKE(cx, *up);
|
||||||
GC_POKE(cx, *upvarp);
|
*up = *vp;
|
||||||
*upvarp = *vp;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1360,8 +1368,7 @@ static void
|
|||||||
call_trace(JSTracer *trc, JSObject *obj)
|
call_trace(JSTracer *trc, JSObject *obj)
|
||||||
{
|
{
|
||||||
JS_ASSERT(obj->isCall());
|
JS_ASSERT(obj->isCall());
|
||||||
JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
|
if (JSStackFrame *fp = obj->maybeCallObjStackFrame()) {
|
||||||
if (fp) {
|
|
||||||
/*
|
/*
|
||||||
* FIXME: Hide copies of stack values rooted by fp from the Cycle
|
* FIXME: Hide copies of stack values rooted by fp from the Cycle
|
||||||
* Collector, which currently lacks a non-stub Unlink implementation
|
* Collector, which currently lacks a non-stub Unlink implementation
|
||||||
@ -1370,7 +1377,7 @@ call_trace(JSTracer *trc, JSObject *obj)
|
|||||||
* hiding hack.
|
* hiding hack.
|
||||||
*/
|
*/
|
||||||
uintN first = JSObject::CALL_RESERVED_SLOTS;
|
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);
|
JS_ASSERT(obj->numSlots() >= first + count);
|
||||||
SetValueRangeToUndefined(obj->getSlots() + 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,
|
JS_DEFINE_CALLINFO_3(extern, OBJECT, js_AllocFlatClosure,
|
||||||
CONTEXT, FUNCTION, OBJECT, 0, nanojit::ACCSET_STORE_ANY)
|
CONTEXT, FUNCTION, OBJECT, 0, nanojit::ACCSET_STORE_ANY)
|
||||||
|
|
||||||
JS_REQUIRES_STACK JSObject *
|
JSObject *
|
||||||
js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen)
|
js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Flat closures can be partial, they may need to search enclosing scope
|
* Flat closures cannot yet be partial, that is, all upvars must be copied,
|
||||||
* objects via JSOP_NAME, etc.
|
* 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);
|
VOUCH_DOES_NOT_REQUIRE_STACK();
|
||||||
if (!scopeChain)
|
JSObject *scopeChain = &cx->fp()->scopeChain();
|
||||||
return NULL;
|
|
||||||
|
|
||||||
JSObject *closure = js_AllocFlatClosure(cx, fun, scopeChain);
|
JSObject *closure = js_AllocFlatClosure(cx, fun, scopeChain);
|
||||||
if (!closure || !fun->script()->bindings.hasUpvars())
|
if (!closure || !fun->script()->bindings.hasUpvars())
|
||||||
|
@ -467,7 +467,7 @@ CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent)
|
|||||||
extern JSObject * JS_FASTCALL
|
extern JSObject * JS_FASTCALL
|
||||||
js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain);
|
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);
|
js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen);
|
||||||
|
|
||||||
extern JS_REQUIRES_STACK JSObject *
|
extern JS_REQUIRES_STACK JSObject *
|
||||||
@ -526,7 +526,7 @@ extern JSBool
|
|||||||
GetCallVarChecked(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
|
GetCallVarChecked(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
|
||||||
|
|
||||||
extern JSBool
|
extern JSBool
|
||||||
GetFlatUpvar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
|
GetCallUpvar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
|
||||||
|
|
||||||
extern JSBool
|
extern JSBool
|
||||||
SetCallArg(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
|
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);
|
SetCallVar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
|
||||||
|
|
||||||
extern JSBool
|
extern JSBool
|
||||||
SetFlatUpvar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
|
SetCallUpvar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
|
||||||
|
|
||||||
} // namespace js
|
} // namespace js
|
||||||
|
|
||||||
|
@ -966,12 +966,12 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script,
|
|||||||
initialVarObj = (cx->options & JSOPTION_VAROBJFIX) ? chain->getGlobal() : chain;
|
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 code receives its own, fresh lexical environment; thus
|
||||||
* strict mode eval can't mutate its calling frame's binding set.
|
* 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);
|
initialVarObj = NewCallObject(cx, &script->bindings, *initialVarObj, NULL);
|
||||||
if (!initialVarObj)
|
if (!initialVarObj)
|
||||||
return false;
|
return false;
|
||||||
@ -982,7 +982,6 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script,
|
|||||||
frame.fp()->clearCallObj();
|
frame.fp()->clearCallObj();
|
||||||
frame.fp()->setScopeChainAndCallObj(*initialVarObj);
|
frame.fp()->setScopeChainAndCallObj(*initialVarObj);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
JS_ASSERT(!initialVarObj->getOps()->defineProperty);
|
JS_ASSERT(!initialVarObj->getOps()->defineProperty);
|
||||||
|
|
||||||
#if JS_HAS_SHARP_VARS
|
#if JS_HAS_SHARP_VARS
|
||||||
@ -2132,9 +2131,8 @@ AssertValidPropertyCacheHit(JSContext *cx, JSScript *script, JSFrameRegs& regs,
|
|||||||
* same way as non-call bytecodes.
|
* same way as non-call bytecodes.
|
||||||
*/
|
*/
|
||||||
JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH);
|
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_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_GETFCSLOT_LENGTH == JSOP_CALLFCSLOT_LENGTH);
|
||||||
JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH);
|
JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH);
|
||||||
JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH);
|
JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH);
|
||||||
@ -5290,22 +5288,6 @@ BEGIN_CASE(JSOP_SETLOCAL)
|
|||||||
}
|
}
|
||||||
END_SET_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_GETUPVAR_DBG)
|
||||||
BEGIN_CASE(JSOP_CALLUPVAR_DBG)
|
BEGIN_CASE(JSOP_CALLUPVAR_DBG)
|
||||||
{
|
{
|
||||||
|
@ -722,13 +722,31 @@ ScriptEpilogue(JSContext *cx, JSStackFrame *fp, JSBool ok)
|
|||||||
if (JS_UNLIKELY(hook != NULL) && (hookData = fp->maybeHookData()))
|
if (JS_UNLIKELY(hook != NULL) && (hookData = fp->maybeHookData()))
|
||||||
hook(cx, fp, JS_FALSE, &ok, hookData);
|
hook(cx, fp, JS_FALSE, &ok, hookData);
|
||||||
|
|
||||||
/*
|
if (fp->isEvalFrame()) {
|
||||||
* An eval frame's parent owns its activation objects. A yielding frame's
|
/*
|
||||||
* activation objects are transferred to the floating frame, stored in the
|
* The parent (ancestor for nested eval) of a non-strict eval frame
|
||||||
* generator.
|
* owns its activation objects. Strict mode eval frames own their own
|
||||||
*/
|
* Call objects but never have an arguments object (the first non-eval
|
||||||
if (fp->isFunctionFrame() && !fp->isEvalFrame() && !fp->isYielding())
|
* parent frame has it).
|
||||||
PutActivationObjects(cx, fp);
|
*/
|
||||||
|
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
|
* If inline-constructing, replace primitive rval with the new object
|
||||||
|
@ -897,6 +897,9 @@ struct JSObject : js::gc::Cell {
|
|||||||
/* Number of reserved slots. */
|
/* Number of reserved slots. */
|
||||||
static const uint32 CALL_RESERVED_SLOTS = 2;
|
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. */
|
/* The stack frame for this Call object, if the frame is still active. */
|
||||||
inline JSStackFrame *maybeCallObjStackFrame() const;
|
inline JSStackFrame *maybeCallObjStackFrame() const;
|
||||||
|
|
||||||
|
@ -432,6 +432,16 @@ JSObject::setArgsElement(uint32 i, const js::Value &v)
|
|||||||
getArgsData()->slots[i] = 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 *
|
inline JSStackFrame *
|
||||||
JSObject::maybeCallObjStackFrame() const
|
JSObject::maybeCallObjStackFrame() const
|
||||||
{
|
{
|
||||||
@ -465,7 +475,7 @@ inline const js::Value &
|
|||||||
JSObject::getCallObjArguments() const
|
JSObject::getCallObjArguments() const
|
||||||
{
|
{
|
||||||
JS_ASSERT(isCall());
|
JS_ASSERT(isCall());
|
||||||
JS_ASSERT(getCallObjCallee() != NULL);
|
JS_ASSERT(!callIsForEval());
|
||||||
return getSlot(JSSLOT_CALL_ARGUMENTS);
|
return getSlot(JSSLOT_CALL_ARGUMENTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -473,7 +483,7 @@ inline void
|
|||||||
JSObject::setCallObjArguments(const js::Value &v)
|
JSObject::setCallObjArguments(const js::Value &v)
|
||||||
{
|
{
|
||||||
JS_ASSERT(isCall());
|
JS_ASSERT(isCall());
|
||||||
JS_ASSERT(getCallObjCallee() != NULL);
|
JS_ASSERT(!callIsForEval());
|
||||||
setSlot(JSSLOT_CALL_ARGUMENTS, v);
|
setSlot(JSSLOT_CALL_ARGUMENTS, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2883,8 +2883,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case JSOP_GETUPVAR:
|
|
||||||
case JSOP_CALLUPVAR:
|
|
||||||
case JSOP_GETUPVAR_DBG:
|
case JSOP_GETUPVAR_DBG:
|
||||||
case JSOP_CALLUPVAR_DBG:
|
case JSOP_CALLUPVAR_DBG:
|
||||||
case JSOP_GETFCSLOT:
|
case JSOP_GETFCSLOT:
|
||||||
|
@ -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_XMLCDATA, 183,"xmlcdata", NULL, 3, 0, 1, 19, JOF_ATOM)
|
||||||
OPDEF(JSOP_XMLCOMMENT, 184,"xmlcomment", 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_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_BLOCKCHAIN, 188,"blockchain", NULL, 3, 0, 0, 0, JOF_OBJECT)
|
||||||
OPDEF(JSOP_CALLUPVAR, 188,"callupvar", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_CALLOP)
|
OPDEF(JSOP_NULLBLOCKCHAIN,189,"nullblockchain",NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||||
|
|
||||||
OPDEF(JSOP_DELDESC, 189,"deldesc", NULL, 1, 2, 1, 15, JOF_BYTE|JOF_ELEM|JOF_DEL)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Opcode to hold 24-bit immediate integer operands.
|
* 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)
|
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_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)
|
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_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)
|
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. */
|
/* 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)
|
||||||
|
@ -1909,8 +1909,8 @@ Parser::analyzeFunctions(JSFunctionBox *funbox, uint32& tcflags)
|
|||||||
* but without this extra marking phase, function g will not be marked as a
|
* 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
|
* 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
|
* 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
|
* cannot assume that p's stack slot is still alive. In contast function h
|
||||||
* nor uses an upvar "above" o_m's level.
|
* neither escapes nor uses an upvar "above" o_m's level.
|
||||||
*
|
*
|
||||||
* If function g itself contained lambdas that contained non-lambdas that reach
|
* 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
|
* 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)) {
|
PN_OP(lexdep) == JSOP_CALLEE)) {
|
||||||
/*
|
/*
|
||||||
* Mark this formerly-Algol-like function as an escaping
|
* Mark this formerly-Algol-like function as an escaping
|
||||||
* function (i.e., as a funarg), because it is used from a
|
* function (i.e., as a funarg), because it is used from
|
||||||
* funarg and therefore can not use JSOP_{GET,CALL}UPVAR to
|
* another funarg.
|
||||||
* access upvars.
|
|
||||||
*
|
*
|
||||||
* Progress is guaranteed because we set the funarg flag
|
* Progress is guaranteed because we set the funarg flag
|
||||||
* here, which suppresses revisiting this function (thanks
|
* here, which suppresses revisiting this function (thanks
|
||||||
@ -2372,26 +2371,8 @@ Parser::setFunctionKinds(JSFunctionBox *funbox, uint32& tcflags)
|
|||||||
|
|
||||||
if (!fn->isFunArg()) {
|
if (!fn->isFunArg()) {
|
||||||
/*
|
/*
|
||||||
* This function is Algol-like, it never escapes. So long as it
|
* This function is Algol-like, it never escapes.
|
||||||
* 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.
|
|
||||||
*
|
*
|
||||||
* 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
|
* Check that at least one outer lexical binding was assigned
|
||||||
* to (global variables don't count). This is conservative: we
|
* to (global variables don't count). This is conservative: we
|
||||||
* could limit assignments to those in the current function,
|
* could limit assignments to those in the current function,
|
||||||
@ -2404,30 +2385,13 @@ Parser::setFunctionKinds(JSFunctionBox *funbox, uint32& tcflags)
|
|||||||
|
|
||||||
if (!lexdep->isFreeVar()) {
|
if (!lexdep->isFreeVar()) {
|
||||||
JS_ASSERT(lexdep->frameLevel() <= funbox->level);
|
JS_ASSERT(lexdep->frameLevel() <= funbox->level);
|
||||||
++nupvars;
|
break;
|
||||||
if (lexdep->isAssigned())
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!ale)
|
|
||||||
mutation = false;
|
|
||||||
|
|
||||||
if (nupvars == 0) {
|
if (!ale) {
|
||||||
FUN_METER(onlyfreevar);
|
FUN_METER(onlyfreevar);
|
||||||
FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
|
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 {
|
} else {
|
||||||
uintN nupvars = 0, nflattened = 0;
|
uintN nupvars = 0, nflattened = 0;
|
||||||
@ -2902,8 +2866,8 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
|
|||||||
pn->pn_cookie.makeFree();
|
pn->pn_cookie.makeFree();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If a lambda, give up on JSOP_{GET,CALL}UPVAR usage unless this function
|
* If a lambda, mark this function as escaping (as a "funarg") unless it is
|
||||||
* is immediately applied (we clear PND_FUNARG if so -- see memberExpr).
|
* immediately applied (we clear PND_FUNARG if so -- see memberExpr).
|
||||||
*
|
*
|
||||||
* Treat function sub-statements (non-lambda, non-body-level functions) as
|
* Treat function sub-statements (non-lambda, non-body-level functions) as
|
||||||
* escaping funargs, since we can't statically analyze their definitions
|
* escaping funargs, since we can't statically analyze their definitions
|
||||||
@ -3244,6 +3208,13 @@ Parser::functionStmt()
|
|||||||
}
|
}
|
||||||
tokenStream.ungetToken();
|
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);
|
return functionDef(name, GENERAL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -832,12 +832,8 @@ struct LexicalScopeNode : public JSParseNode {
|
|||||||
* and because all uses are contained in the same block as the definition.
|
* and because all uses are contained in the same block as the definition.
|
||||||
*
|
*
|
||||||
* We also analyze function uses to flag upward/downward funargs, optimizing
|
* We also analyze function uses to flag upward/downward funargs, optimizing
|
||||||
* Algol-like (not passed as funargs, only ever called) lightweight functions
|
* those lambdas that post-dominate their upvars inevitable only assignments or
|
||||||
* using cx->display. See JSOP_{GET,CALL}UPVAR.
|
* initializations as flat closures (after Chez Scheme's display closures).
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
#define dn_uses pn_link
|
#define dn_uses pn_link
|
||||||
|
|
||||||
|
@ -58,6 +58,8 @@
|
|||||||
#include "jsobjinlines.h"
|
#include "jsobjinlines.h"
|
||||||
#include "jsregexpinlines.h"
|
#include "jsregexpinlines.h"
|
||||||
|
|
||||||
|
#include "yarr/RegexParser.h"
|
||||||
|
|
||||||
#ifdef JS_TRACER
|
#ifdef JS_TRACER
|
||||||
#include "jstracer.h"
|
#include "jstracer.h"
|
||||||
using namespace avmplus;
|
using namespace avmplus;
|
||||||
@ -184,26 +186,12 @@ js_ObjectIsRegExp(JSObject *obj)
|
|||||||
void
|
void
|
||||||
RegExp::handleYarrError(JSContext *cx, int error)
|
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) {
|
switch (error) {
|
||||||
case NoError:
|
case JSC::Yarr::NoError:
|
||||||
JS_NOT_REACHED("Precondition violation: an error must have occurred.");
|
JS_NOT_REACHED("Precondition violation: an error must have occurred.");
|
||||||
return;
|
return;
|
||||||
#define COMPILE_EMSG(__code, __msg) \
|
#define COMPILE_EMSG(__code, __msg) \
|
||||||
case __code: \
|
case JSC::Yarr::__code: \
|
||||||
JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, __msg); \
|
JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, __msg); \
|
||||||
return
|
return
|
||||||
COMPILE_EMSG(PatternTooLarge, JSMSG_REGEXP_TOO_COMPLEX);
|
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(QuantifierWithoutAtom, JSMSG_BAD_QUANTIFIER);
|
||||||
COMPILE_EMSG(MissingParentheses, JSMSG_MISSING_PAREN);
|
COMPILE_EMSG(MissingParentheses, JSMSG_MISSING_PAREN);
|
||||||
COMPILE_EMSG(ParenthesesUnmatched, JSMSG_UNMATCHED_RIGHT_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(CharacterClassUnmatched, JSMSG_BAD_CLASS_RANGE);
|
||||||
COMPILE_EMSG(CharacterClassOutOfOrder, 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(EscapeUnterminated, JSMSG_TRAILING_SLASH);
|
||||||
COMPILE_EMSG(QuantifierTooLarge, JSMSG_BAD_QUANTIFIER);
|
COMPILE_EMSG(QuantifierTooLarge, JSMSG_BAD_QUANTIFIER);
|
||||||
#undef COMPILE_EMSG
|
#undef COMPILE_EMSG
|
||||||
|
@ -485,10 +485,10 @@ Shape::getChild(JSContext *cx, const js::Shape &child, Shape **listp)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Beware duplicate formal parameters, allowed by ECMA-262 in
|
* Beware duplicate formal parameters, allowed by ECMA-262 in
|
||||||
* non-strict mode. Otherwise we know that JSFunction::addLocal
|
* non-strict mode. Otherwise we know that Bindings::add (our
|
||||||
* (our caller) won't pass an id already in the table to us. In
|
* caller) won't pass an id already in the table to us. In the
|
||||||
* the case of duplicate formals, the last one wins, so while
|
* case of duplicate formals, the last one wins, so while we
|
||||||
* we must not overcount entries, we must store newShape.
|
* must not overcount entries, we must store newShape.
|
||||||
*/
|
*/
|
||||||
if (!SHAPE_FETCH(spp))
|
if (!SHAPE_FETCH(spp))
|
||||||
++table->entryCount;
|
++table->entryCount;
|
||||||
|
@ -91,7 +91,7 @@ Bindings::lookup(JSContext *cx, JSAtom *name, uintN *indexp) const
|
|||||||
|
|
||||||
if (shape->getter() == GetCallArg)
|
if (shape->getter() == GetCallArg)
|
||||||
return ARGUMENT;
|
return ARGUMENT;
|
||||||
if (shape->getter() == GetFlatUpvar)
|
if (shape->getter() == GetCallUpvar)
|
||||||
return UPVAR;
|
return UPVAR;
|
||||||
|
|
||||||
return shape->writable() ? VARIABLE : CONSTANT;
|
return shape->writable() ? VARIABLE : CONSTANT;
|
||||||
@ -122,8 +122,8 @@ Bindings::add(JSContext *cx, JSAtom *name, BindingKind kind)
|
|||||||
slot += nargs;
|
slot += nargs;
|
||||||
} else if (kind == UPVAR) {
|
} else if (kind == UPVAR) {
|
||||||
indexp = &nupvars;
|
indexp = &nupvars;
|
||||||
getter = GetFlatUpvar;
|
getter = GetCallUpvar;
|
||||||
setter = SetFlatUpvar;
|
setter = SetCallUpvar;
|
||||||
slot = SHAPE_INVALID_SLOT;
|
slot = SHAPE_INVALID_SLOT;
|
||||||
} else {
|
} else {
|
||||||
JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
|
JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
|
||||||
@ -218,7 +218,7 @@ Bindings::getLocalNameArray(JSContext *cx, JSArenaPool *pool)
|
|||||||
|
|
||||||
if (shape.getter() == GetCallArg) {
|
if (shape.getter() == GetCallArg) {
|
||||||
JS_ASSERT(index < nargs);
|
JS_ASSERT(index < nargs);
|
||||||
} else if (shape.getter() == GetFlatUpvar) {
|
} else if (shape.getter() == GetCallUpvar) {
|
||||||
JS_ASSERT(index < nupvars);
|
JS_ASSERT(index < nupvars);
|
||||||
index += nargs + nvars;
|
index += nargs + nvars;
|
||||||
} else {
|
} else {
|
||||||
@ -267,7 +267,7 @@ Bindings::lastVariable() const
|
|||||||
|
|
||||||
const js::Shape *shape = lastUpvar();
|
const js::Shape *shape = lastUpvar();
|
||||||
if (nupvars > 0) {
|
if (nupvars > 0) {
|
||||||
while (shape->getter() == GetFlatUpvar)
|
while (shape->getter() == GetCallUpvar)
|
||||||
shape = shape->previous();
|
shape = shape->previous();
|
||||||
}
|
}
|
||||||
return shape;
|
return shape;
|
||||||
|
@ -73,7 +73,7 @@ namespace js {
|
|||||||
*
|
*
|
||||||
* TODO: consider giving more bits to the slot value and takings ome from the level.
|
* TODO: consider giving more bits to the slot value and takings ome from the level.
|
||||||
*/
|
*/
|
||||||
class UpvarCookie
|
class UpvarCookie
|
||||||
{
|
{
|
||||||
uint32 value;
|
uint32 value;
|
||||||
|
|
||||||
@ -206,11 +206,11 @@ class Bindings {
|
|||||||
inline const js::Shape *lastShape() const;
|
inline const js::Shape *lastShape() const;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
/*
|
/*
|
||||||
* A script may have no more than this many arguments, variables, or
|
* A script may have no more than this many arguments, variables, or
|
||||||
* upvars.
|
* upvars.
|
||||||
*/
|
*/
|
||||||
BINDING_COUNT_LIMIT = 0xFFFF
|
BINDING_COUNT_LIMIT = 0xFFFF
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -224,11 +224,10 @@ class Bindings {
|
|||||||
* which must deal with such cases.) Pass null for name when indicating a
|
* which must deal with such cases.) Pass null for name when indicating a
|
||||||
* destructuring argument. Return true on success.
|
* destructuring argument. Return true on success.
|
||||||
*
|
*
|
||||||
*
|
|
||||||
* The parser builds shape paths for functions, usable by Call objects at
|
* The parser builds shape paths for functions, usable by Call objects at
|
||||||
* runtime, by calling addLocal. All ARGUMENT bindings must be added before
|
* runtime, by calling an "add" method. All ARGUMENT bindings must be added
|
||||||
* before any VARIABLE or CONSTANT bindings, which themselves must be added
|
* before before any VARIABLE or CONSTANT bindings, which themselves must
|
||||||
* before all UPVAR bindings.
|
* be added before all UPVAR bindings.
|
||||||
*/
|
*/
|
||||||
bool add(JSContext *cx, JSAtom *name, BindingKind kind);
|
bool add(JSContext *cx, JSAtom *name, BindingKind kind);
|
||||||
|
|
||||||
@ -431,7 +430,7 @@ struct JSScript {
|
|||||||
* JS_CompileFile, etc.) have these objects.
|
* JS_CompileFile, etc.) have these objects.
|
||||||
* - Function scripts never have script objects; such scripts are owned
|
* - Function scripts never have script objects; such scripts are owned
|
||||||
* by their function objects.
|
* 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
|
* similar functions never have these objects; such scripts are
|
||||||
* explicitly destroyed by the code that created them.
|
* explicitly destroyed by the code that created them.
|
||||||
* Debugging API functions (JSDebugHooks::newScriptHook;
|
* Debugging API functions (JSDebugHooks::newScriptHook;
|
||||||
|
@ -300,7 +300,6 @@ TypeToChar(JSValueType type)
|
|||||||
case JSVAL_TYPE_BOXED: return '#';
|
case JSVAL_TYPE_BOXED: return '#';
|
||||||
case JSVAL_TYPE_STRORNULL: return 's';
|
case JSVAL_TYPE_STRORNULL: return 's';
|
||||||
case JSVAL_TYPE_OBJORNULL: return 'o';
|
case JSVAL_TYPE_OBJORNULL: return 'o';
|
||||||
case JSVAL_TYPE_UNINITIALIZED: return '*';
|
|
||||||
}
|
}
|
||||||
return '?';
|
return '?';
|
||||||
}
|
}
|
||||||
@ -3660,7 +3659,6 @@ TraceRecorder::importGlobalSlot(unsigned slot)
|
|||||||
JS_ASSERT(tree->nGlobalTypes() == tree->globalSlots->length());
|
JS_ASSERT(tree->nGlobalTypes() == tree->globalSlots->length());
|
||||||
} else {
|
} else {
|
||||||
type = importTypeMap[importStackSlots + index];
|
type = importTypeMap[importStackSlots + index];
|
||||||
JS_ASSERT(type != JSVAL_TYPE_UNINITIALIZED);
|
|
||||||
}
|
}
|
||||||
import(EosAddress(eos_ins, slot * sizeof(double)), vp, type, "global", index, NULL);
|
import(EosAddress(eos_ins, slot * sizeof(double)), vp, type, "global", index, NULL);
|
||||||
}
|
}
|
||||||
@ -3809,7 +3807,6 @@ TraceRecorder::getImpl(const void *p)
|
|||||||
} else {
|
} else {
|
||||||
unsigned slot = nativeStackSlotImpl(p);
|
unsigned slot = nativeStackSlotImpl(p);
|
||||||
JSValueType type = importTypeMap[slot];
|
JSValueType type = importTypeMap[slot];
|
||||||
JS_ASSERT(type != JSVAL_TYPE_UNINITIALIZED);
|
|
||||||
importImpl(StackAddress(lirbuf->sp, -tree->nativeStackBase + slot * sizeof(jsdouble)),
|
importImpl(StackAddress(lirbuf->sp, -tree->nativeStackBase + slot * sizeof(jsdouble)),
|
||||||
p, type, "stack", slot, cx->fp());
|
p, type, "stack", slot, cx->fp());
|
||||||
}
|
}
|
||||||
@ -4033,7 +4030,6 @@ TraceRecorder::determineSlotType(Value* vp)
|
|||||||
} else {
|
} else {
|
||||||
t = importTypeMap[nativeStackSlot(vp)];
|
t = importTypeMap[nativeStackSlot(vp)];
|
||||||
}
|
}
|
||||||
JS_ASSERT(t != JSVAL_TYPE_UNINITIALIZED);
|
|
||||||
JS_ASSERT_IF(t == JSVAL_TYPE_INT32, hasInt32Repr(*vp));
|
JS_ASSERT_IF(t == JSVAL_TYPE_INT32, hasInt32Repr(*vp));
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
@ -5194,6 +5190,28 @@ TraceRecorder::prepareTreeCall(TreeFragment* inner)
|
|||||||
w.xbarrier(createGuardRecord(exit));
|
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
|
static unsigned
|
||||||
BuildGlobalTypeMapFromInnerTree(Queue<JSValueType>& typeMap, VMSideExit* inner)
|
BuildGlobalTypeMapFromInnerTree(Queue<JSValueType>& typeMap, VMSideExit* inner)
|
||||||
{
|
{
|
||||||
@ -5275,32 +5293,9 @@ TraceRecorder::emitTreeCall(TreeFragment* inner, VMSideExit* exit)
|
|||||||
JS_ASSERT(map[i] != JSVAL_TYPE_BOXED);
|
JS_ASSERT(map[i] != JSVAL_TYPE_BOXED);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/* The inner tree may modify currently-tracked upvars, so flush everything. */
|
||||||
* Clear anything from the tracker that the inner tree could have written so
|
ClearSlotsVisitor visitor(tracker);
|
||||||
* that it will be lazily reloaded from the native stack. The only portion
|
VisitStackSlots(visitor, cx, callDepth);
|
||||||
* 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);
|
|
||||||
SlotList& gslots = *tree->globalSlots;
|
SlotList& gslots = *tree->globalSlots;
|
||||||
for (unsigned i = 0; i < gslots.length(); i++) {
|
for (unsigned i = 0; i < gslots.length(); i++) {
|
||||||
unsigned slot = gslots[i];
|
unsigned slot = gslots[i];
|
||||||
@ -5310,10 +5305,6 @@ TraceRecorder::emitTreeCall(TreeFragment* inner, VMSideExit* exit)
|
|||||||
|
|
||||||
/* Set stack slots from the innermost frame. */
|
/* Set stack slots from the innermost frame. */
|
||||||
importTypeMap.setLength(NativeStackSlots(cx, callDepth));
|
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;
|
unsigned startOfInnerFrame = importTypeMap.length() - exit->numStackSlots;
|
||||||
for (unsigned i = 0; i < exit->numStackSlots; i++)
|
for (unsigned i = 0; i < exit->numStackSlots; i++)
|
||||||
importTypeMap[startOfInnerFrame + i] = exit->stackTypeMap()[i];
|
importTypeMap[startOfInnerFrame + i] = exit->stackTypeMap()[i];
|
||||||
@ -10171,45 +10162,21 @@ TraceRecorder::guardNativeConversion(Value& v)
|
|||||||
return RECORD_CONTINUE;
|
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
|
JS_REQUIRES_STACK void
|
||||||
TraceRecorder::clearCurrentFrameSlotsFromTracker(Tracker& which)
|
TraceRecorder::clearReturningFrameFromNativeveTracker()
|
||||||
{
|
{
|
||||||
JSStackFrame *const fp = cx->fp();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Duplicate native stack layout computation: see VisitFrameSlots header comment.
|
* Clear all tracker entries associated with the frame for the same reason
|
||||||
* This doesn't do layout arithmetic, but it must clear out all the slots defined as
|
* described in record_EnterFrame. Reuse the generic visitor to avoid
|
||||||
* imported by VisitFrameSlots.
|
* 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()) {
|
ClearSlotsVisitor visitor(nativeFrameTracker);
|
||||||
Value *vp = fp->slots() + fp->globalScript()->nfixed;
|
VisitStackSlots(visitor, cx, 0);
|
||||||
Value *vpend = fp->slots() + fp->globalScript()->nslots;
|
Value *vp = cx->regs->sp;
|
||||||
for (; vp < vpend; ++vp)
|
Value *vpend = cx->fp()->slots() + cx->fp()->script()->nslots;
|
||||||
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;
|
|
||||||
for (; vp < vpend; ++vp)
|
for (; vp < vpend; ++vp)
|
||||||
which.set(vp, (LIns*)0);
|
nativeFrameTracker.set(vp, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
class BoxArg
|
class BoxArg
|
||||||
@ -10502,7 +10469,7 @@ TraceRecorder::record_JSOP_RETURN()
|
|||||||
fp->fun()->atom ?
|
fp->fun()->atom ?
|
||||||
js_AtomToPrintableString(cx, fp->fun()->atom, &funBytes) :
|
js_AtomToPrintableString(cx, fp->fun()->atom, &funBytes) :
|
||||||
"<anonymous>");
|
"<anonymous>");
|
||||||
clearCurrentFrameSlotsFromTracker(nativeFrameTracker);
|
clearReturningFrameFromNativeveTracker();
|
||||||
|
|
||||||
return ARECORD_CONTINUE;
|
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
|
JS_REQUIRES_STACK AbortableRecordingStatus
|
||||||
TraceRecorder::record_JSOP_GETFCSLOT()
|
TraceRecorder::record_JSOP_GETFCSLOT()
|
||||||
{
|
{
|
||||||
@ -15885,7 +15828,7 @@ TraceRecorder::record_JSOP_STOP()
|
|||||||
} else {
|
} else {
|
||||||
rval_ins = w.immiUndefined();
|
rval_ins = w.immiUndefined();
|
||||||
}
|
}
|
||||||
clearCurrentFrameSlotsFromTracker(nativeFrameTracker);
|
clearReturningFrameFromNativeveTracker();
|
||||||
return ARECORD_CONTINUE;
|
return ARECORD_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1472,7 +1472,7 @@ class TraceRecorder
|
|||||||
nanojit::LIns* obj_ins,
|
nanojit::LIns* obj_ins,
|
||||||
VMSideExit *exit);
|
VMSideExit *exit);
|
||||||
JS_REQUIRES_STACK RecordingStatus guardNativeConversion(Value& v);
|
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 void putActivationObjects();
|
||||||
JS_REQUIRES_STACK RecordingStatus guardCallee(Value& callee);
|
JS_REQUIRES_STACK RecordingStatus guardCallee(Value& callee);
|
||||||
JS_REQUIRES_STACK JSStackFrame *guardArguments(JSObject *obj, nanojit::LIns* obj_ins,
|
JS_REQUIRES_STACK JSStackFrame *guardArguments(JSObject *obj, nanojit::LIns* obj_ins,
|
||||||
@ -1594,6 +1594,7 @@ class TraceRecorder
|
|||||||
TreeFragment* getTree() const { return tree; }
|
TreeFragment* getTree() const { return tree; }
|
||||||
bool outOfMemory() const { return traceMonitor->outOfMemory(); }
|
bool outOfMemory() const { return traceMonitor->outOfMemory(); }
|
||||||
Oracle* getOracle() const { return oracle; }
|
Oracle* getOracle() const { return oracle; }
|
||||||
|
JSObject* getGlobal() const { return globalObj; }
|
||||||
|
|
||||||
/* Entry points / callbacks from the interpreter. */
|
/* Entry points / callbacks from the interpreter. */
|
||||||
JS_REQUIRES_STACK AbortableRecordingStatus monitorRecording(JSOp op);
|
JS_REQUIRES_STACK AbortableRecordingStatus monitorRecording(JSOp op);
|
||||||
@ -1612,7 +1613,20 @@ class TraceRecorder
|
|||||||
* Do slot arithmetic manually to avoid getSlotRef assertions which
|
* Do slot arithmetic manually to avoid getSlotRef assertions which
|
||||||
* do not need to be satisfied for this purpose.
|
* 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;
|
pendingGlobalSlotToSet = -1;
|
||||||
return true;
|
return true;
|
||||||
@ -1868,7 +1882,7 @@ AbortRecordingIfUnexpectedGlobalWrite(JSContext *cx, JSObject *obj, unsigned slo
|
|||||||
{
|
{
|
||||||
#ifdef JS_TRACER
|
#ifdef JS_TRACER
|
||||||
if (TraceRecorder *tr = TRACE_RECORDER(cx)) {
|
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");
|
AbortRecording(cx, "Global slot written outside tracer supervision");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -108,8 +108,7 @@ JS_ENUM_HEADER(JSValueType, uint8)
|
|||||||
JSVAL_TYPE_STRORNULL = 0x97,
|
JSVAL_TYPE_STRORNULL = 0x97,
|
||||||
JSVAL_TYPE_OBJORNULL = 0x98,
|
JSVAL_TYPE_OBJORNULL = 0x98,
|
||||||
|
|
||||||
JSVAL_TYPE_BOXED = 0x99,
|
JSVAL_TYPE_BOXED = 0x99
|
||||||
JSVAL_TYPE_UNINITIALIZED = 0xcd
|
|
||||||
} JS_ENUM_FOOTER(JSValueType);
|
} JS_ENUM_FOOTER(JSValueType);
|
||||||
|
|
||||||
JS_STATIC_ASSERT(sizeof(JSValueType) == 1);
|
JS_STATIC_ASSERT(sizeof(JSValueType) == 1);
|
||||||
|
@ -348,19 +348,15 @@ AutoCompartment::enter()
|
|||||||
if (origin != destination) {
|
if (origin != destination) {
|
||||||
LeaveTrace(context);
|
LeaveTrace(context);
|
||||||
|
|
||||||
#ifdef DEBUG
|
if (context->isExceptionPending())
|
||||||
JSCompartment *oldCompartment = context->compartment;
|
return false;
|
||||||
context->resetCompartment();
|
|
||||||
wasSane = (context->compartment == oldCompartment);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
context->compartment = destination;
|
context->compartment = destination;
|
||||||
JSObject *scopeChain = target->getGlobal();
|
JSObject *scopeChain = target->getGlobal();
|
||||||
JS_ASSERT(scopeChain->isNative());
|
JS_ASSERT(scopeChain->isNative());
|
||||||
|
|
||||||
frame.construct();
|
frame.construct();
|
||||||
if (!context->stack().pushDummyFrame(context, *scopeChain, &frame.ref()) ||
|
if (!context->stack().pushDummyFrame(context, *scopeChain, &frame.ref())) {
|
||||||
!destination->wrapException(context)) {
|
|
||||||
frame.destroy();
|
|
||||||
context->compartment = origin;
|
context->compartment = origin;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -376,8 +372,6 @@ AutoCompartment::leave()
|
|||||||
if (origin != destination) {
|
if (origin != destination) {
|
||||||
frame.destroy();
|
frame.destroy();
|
||||||
context->resetCompartment();
|
context->resetCompartment();
|
||||||
JS_ASSERT_IF(wasSane && context->hasfp(), context->compartment == origin);
|
|
||||||
context->compartment->wrapException(context);
|
|
||||||
}
|
}
|
||||||
entered = false;
|
entered = false;
|
||||||
}
|
}
|
||||||
@ -642,8 +636,7 @@ JSCrossCompartmentWrapper::construct(JSContext *cx, JSObject *wrapper, uintN arg
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
call.leave();
|
call.leave();
|
||||||
return call.origin->wrap(cx, rval) &&
|
return call.origin->wrap(cx, rval);
|
||||||
call.origin->wrapException(cx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -168,9 +168,6 @@ class AutoCompartment
|
|||||||
JSFrameRegs regs;
|
JSFrameRegs regs;
|
||||||
AutoStringRooter input;
|
AutoStringRooter input;
|
||||||
bool entered;
|
bool entered;
|
||||||
#ifdef DEBUG
|
|
||||||
bool wasSane;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AutoCompartment(JSContext *cx, JSObject *target);
|
AutoCompartment(JSContext *cx, JSObject *target);
|
||||||
|
@ -206,7 +206,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id);
|
|||||||
* before deserialization of bytecode. If the saved version does not match
|
* before deserialization of bytecode. If the saved version does not match
|
||||||
* the current version, abort deserialization and invalidate the file.
|
* the current version, abort deserialization and invalidate the file.
|
||||||
*/
|
*/
|
||||||
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 80)
|
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 81)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Library-private functions.
|
* Library-private functions.
|
||||||
|
@ -1954,22 +1954,6 @@ mjit::Compiler::generateMethod()
|
|||||||
return Compile_Error;
|
return Compile_Error;
|
||||||
END_CASE(JSOP_CALLPROP)
|
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)
|
BEGIN_CASE(JSOP_UINT24)
|
||||||
frame.push(Value(Int32Value((int32_t) GET_UINT24(PC))));
|
frame.push(Value(Int32Value((int32_t) GET_UINT24(PC))));
|
||||||
END_CASE(JSOP_UINT24)
|
END_CASE(JSOP_UINT24)
|
||||||
@ -2336,6 +2320,12 @@ mjit::Compiler::emitReturn(FrameEntry *fe)
|
|||||||
emitReturnValue(&stubcc.masm, fe);
|
emitReturnValue(&stubcc.masm, fe);
|
||||||
emitFinalReturn(stubcc.masm);
|
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);
|
emitReturnValue(&masm, fe);
|
||||||
@ -3294,9 +3284,14 @@ mjit::Compiler::jsop_callprop_str(JSAtom *atom)
|
|||||||
return true;
|
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;
|
JSObject *obj;
|
||||||
if (!js_GetClassPrototype(cx, NULL, JSProto_String, &obj))
|
if (!js_GetClassPrototype(cx, &fp->scopeChain(), JSProto_String, &obj))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* Force into a register because getprop won't expect a constant. */
|
/* Force into a register because getprop won't expect a constant. */
|
||||||
|
@ -923,7 +923,7 @@ mjit::Compiler::jsop_typeof()
|
|||||||
if (op == JSOP_STRICTEQ || op == JSOP_EQ || op == JSOP_STRICTNE || op == JSOP_NE) {
|
if (op == JSOP_STRICTEQ || op == JSOP_EQ || op == JSOP_STRICTNE || op == JSOP_NE) {
|
||||||
JSAtom *atom = script->getAtom(fullAtomIndex(PC + JSOP_TYPEOF_LENGTH));
|
JSAtom *atom = script->getAtom(fullAtomIndex(PC + JSOP_TYPEOF_LENGTH));
|
||||||
JSRuntime *rt = cx->runtime;
|
JSRuntime *rt = cx->runtime;
|
||||||
JSValueType type = JSVAL_TYPE_UNINITIALIZED;
|
JSValueType type = JSVAL_TYPE_BOXED;
|
||||||
Assembler::Condition cond = (op == JSOP_STRICTEQ || op == JSOP_EQ)
|
Assembler::Condition cond = (op == JSOP_STRICTEQ || op == JSOP_EQ)
|
||||||
? Assembler::Equal
|
? Assembler::Equal
|
||||||
: Assembler::NotEqual;
|
: Assembler::NotEqual;
|
||||||
@ -941,7 +941,7 @@ mjit::Compiler::jsop_typeof()
|
|||||||
cond = (cond == Assembler::Equal) ? Assembler::BelowOrEqual : Assembler::Above;
|
cond = (cond == Assembler::Equal) ? Assembler::BelowOrEqual : Assembler::Above;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type != JSVAL_TYPE_UNINITIALIZED) {
|
if (type != JSVAL_TYPE_BOXED) {
|
||||||
PC += JSOP_STRING_LENGTH;;
|
PC += JSOP_STRING_LENGTH;;
|
||||||
PC += JSOP_EQ_LENGTH;
|
PC += JSOP_EQ_LENGTH;
|
||||||
|
|
||||||
|
@ -480,8 +480,10 @@ stubs::UncachedCallHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void JS_FASTCALL
|
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_ASSERT(f.fp()->hasCallObj());
|
||||||
js_PutCallObject(f.cx, f.fp());
|
js_PutCallObject(f.cx, f.fp());
|
||||||
}
|
}
|
||||||
|
@ -905,8 +905,9 @@ class GetPropCompiler : public PICStubCompiler
|
|||||||
Assembler masm;
|
Assembler masm;
|
||||||
|
|
||||||
Jump notStringObj = masm.testObjClass(Assembler::NotEqual, pic.objReg, obj->getClass());
|
Jump notStringObj = masm.testObjClass(Assembler::NotEqual, pic.objReg, obj->getClass());
|
||||||
masm.loadPayload(Address(pic.objReg, JSObject::getFixedSlotOffset(
|
masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg);
|
||||||
JSObject::JSSLOT_PRIMITIVE_THIS)), 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.loadPtr(Address(pic.objReg, JSString::offsetOfLengthAndFlags()), pic.objReg);
|
||||||
masm.urshift32(Imm32(JSString::LENGTH_SHIFT), pic.objReg);
|
masm.urshift32(Imm32(JSString::LENGTH_SHIFT), pic.objReg);
|
||||||
masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg);
|
masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg);
|
||||||
|
@ -107,7 +107,7 @@ void UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr);
|
|||||||
|
|
||||||
void JS_FASTCALL CreateThis(VMFrame &f, JSObject *proto);
|
void JS_FASTCALL CreateThis(VMFrame &f, JSObject *proto);
|
||||||
void JS_FASTCALL Throw(VMFrame &f);
|
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 PutActivationObjects(VMFrame &f);
|
||||||
void JS_FASTCALL GetCallObject(VMFrame &f);
|
void JS_FASTCALL GetCallObject(VMFrame &f);
|
||||||
#if JS_MONOIC
|
#if JS_MONOIC
|
||||||
|
@ -4246,59 +4246,6 @@ Deserialize(JSContext *cx, uintN argc, jsval *vp)
|
|||||||
return true;
|
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
|
JSBool
|
||||||
MJitStats(JSContext *cx, uintN argc, jsval *vp)
|
MJitStats(JSContext *cx, uintN argc, jsval *vp)
|
||||||
{
|
{
|
||||||
@ -4410,8 +4357,6 @@ static JSFunctionSpec shell_functions[] = {
|
|||||||
JS_FN("wrap", Wrap, 1,0),
|
JS_FN("wrap", Wrap, 1,0),
|
||||||
JS_FN("serialize", Serialize, 1,0),
|
JS_FN("serialize", Serialize, 1,0),
|
||||||
JS_FN("deserialize", Deserialize, 1,0),
|
JS_FN("deserialize", Deserialize, 1,0),
|
||||||
JS_FN("setGlobalPropIf",SetGlobalPropIf,3,0),
|
|
||||||
JS_FN("defGlobalPropIf",DefGlobalPropIf,3,0),
|
|
||||||
#ifdef JS_METHODJIT
|
#ifdef JS_METHODJIT
|
||||||
JS_FN("mjitstats", MJitStats, 0,0),
|
JS_FN("mjitstats", MJitStats, 0,0),
|
||||||
#endif
|
#endif
|
||||||
@ -4545,9 +4490,6 @@ static const char *const shell_help_messages[] = {
|
|||||||
"wrap(obj) Wrap an object into a noop wrapper.\n",
|
"wrap(obj) Wrap an object into a noop wrapper.\n",
|
||||||
"serialize(sd) Serialize sd using JS_WriteStructuredClone. Returns a TypedArray.\n",
|
"serialize(sd) Serialize sd using JS_WriteStructuredClone. Returns a TypedArray.\n",
|
||||||
"deserialize(a) Deserialize data generated by serialize.\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
|
#ifdef JS_METHODJIT
|
||||||
"mjitstats() Return stats on mjit memory usage.\n",
|
"mjitstats() Return stats on mjit memory usage.\n",
|
||||||
#endif
|
#endif
|
||||||
|
@ -16,22 +16,22 @@ url-prefix ../../jsreftest.html?test=ecma_5/eval/
|
|||||||
# optimizations we might perform -- add new tests for such changes as needed.
|
# optimizations we might perform -- add new tests for such changes as needed.
|
||||||
#
|
#
|
||||||
|
|
||||||
# script exhaustive-fun-normalcaller-direct-normalcode.js
|
script exhaustive-fun-normalcaller-direct-normalcode.js
|
||||||
# script exhaustive-fun-normalcaller-direct-strictcode.js
|
script exhaustive-fun-normalcaller-direct-strictcode.js
|
||||||
# script exhaustive-fun-normalcaller-indirect-normalcode.js
|
script exhaustive-fun-normalcaller-indirect-normalcode.js
|
||||||
# script exhaustive-fun-normalcaller-indirect-strictcode.js
|
script exhaustive-fun-normalcaller-indirect-strictcode.js
|
||||||
# script exhaustive-fun-strictcaller-direct-normalcode.js
|
script exhaustive-fun-strictcaller-direct-normalcode.js
|
||||||
# script exhaustive-fun-strictcaller-direct-strictcode.js
|
script exhaustive-fun-strictcaller-direct-strictcode.js
|
||||||
# script exhaustive-fun-strictcaller-indirect-normalcode.js
|
script exhaustive-fun-strictcaller-indirect-normalcode.js
|
||||||
# script exhaustive-fun-strictcaller-indirect-strictcode.js
|
script exhaustive-fun-strictcaller-indirect-strictcode.js
|
||||||
# script exhaustive-global-normalcaller-direct-normalcode.js
|
script exhaustive-global-normalcaller-direct-normalcode.js
|
||||||
# script exhaustive-global-normalcaller-direct-strictcode.js
|
script exhaustive-global-normalcaller-direct-strictcode.js
|
||||||
# script exhaustive-global-normalcaller-indirect-normalcode.js
|
script exhaustive-global-normalcaller-indirect-normalcode.js
|
||||||
# script exhaustive-global-normalcaller-indirect-strictcode.js
|
script exhaustive-global-normalcaller-indirect-strictcode.js
|
||||||
# script exhaustive-global-strictcaller-direct-normalcode.js
|
script exhaustive-global-strictcaller-direct-normalcode.js
|
||||||
# script exhaustive-global-strictcaller-direct-strictcode.js
|
script exhaustive-global-strictcaller-direct-strictcode.js
|
||||||
# script exhaustive-global-strictcaller-indirect-normalcode.js
|
script exhaustive-global-strictcaller-indirect-normalcode.js
|
||||||
# script exhaustive-global-strictcaller-indirect-strictcode.js
|
script exhaustive-global-strictcaller-indirect-strictcode.js
|
||||||
|
|
||||||
# Not written yet! These require a new shell primitive to work there, though
|
# Not written yet! These require a new shell primitive to work there, though
|
||||||
# browser could probably use setTimeout for this. Moreover, we haven't
|
# browser could probably use setTimeout for this. Moreover, we haven't
|
||||||
|
@ -21,3 +21,4 @@ script bug472534.js
|
|||||||
script bug496985.js
|
script bug496985.js
|
||||||
script bug566661.js
|
script bug566661.js
|
||||||
script iterator-in-catch.js
|
script iterator-in-catch.js
|
||||||
|
script strict-function-statements.js
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
100
js/src/tests/ecma_5/extensions/strict-function-statements.js
Normal file
100
js/src/tests/ecma_5/extensions/strict-function-statements.js
Normal 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);
|
@ -39,4 +39,4 @@ script unbrand-this.js
|
|||||||
script this-for-function-expression-recursion.js
|
script this-for-function-expression-recursion.js
|
||||||
script assign-to-callee-name.js
|
script assign-to-callee-name.js
|
||||||
script directive-prologue-01.js
|
script directive-prologue-01.js
|
||||||
# script eval-variable-environment.js
|
script eval-variable-environment.js
|
||||||
|
@ -65,7 +65,7 @@ function test()
|
|||||||
|
|
||||||
(function (){
|
(function (){
|
||||||
var x;
|
var x;
|
||||||
eval("const x; (function ()x)");
|
eval("var x; (function ()x)");
|
||||||
}
|
}
|
||||||
)();
|
)();
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ function test()
|
|||||||
function f() {
|
function f() {
|
||||||
var x;
|
var x;
|
||||||
(function(){})();
|
(function(){})();
|
||||||
eval("if(x|=[]) {const x; }");
|
eval("if(x|=[]) {var x; }");
|
||||||
}
|
}
|
||||||
f();
|
f();
|
||||||
|
|
||||||
|
@ -15,6 +15,8 @@ script regress-552432.js
|
|||||||
script regress-553778.js
|
script regress-553778.js
|
||||||
script regress-555246-0.js
|
script regress-555246-0.js
|
||||||
script regress-555246-1.js
|
script regress-555246-1.js
|
||||||
|
script regress-559402-1.js
|
||||||
|
script regress-559402-2.js
|
||||||
script regress-559438.js
|
script regress-559438.js
|
||||||
script regress-560101.js
|
script regress-560101.js
|
||||||
script regress-560998-1.js
|
script regress-560998-1.js
|
||||||
@ -38,6 +40,10 @@ script regress-586482-4.js
|
|||||||
script regress-586482-5.js
|
script regress-586482-5.js
|
||||||
script regress-588339.js
|
script regress-588339.js
|
||||||
script regress-yarr-regexp.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-592217.js
|
||||||
script regress-592556-c35.js
|
script regress-592556-c35.js
|
||||||
script regress-593256.js
|
script regress-593256.js
|
||||||
|
22
js/src/tests/js1_8_5/regress/regress-559402-1.js
Normal file
22
js/src/tests/js1_8_5/regress/regress-559402-1.js
Normal 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");
|
8
js/src/tests/js1_8_5/regress/regress-559402-2.js
Normal file
8
js/src/tests/js1_8_5/regress/regress-559402-2.js
Normal 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");
|
7
js/src/tests/js1_8_5/regress/regress-592202-1.js
Normal file
7
js/src/tests/js1_8_5/regress/regress-592202-1.js
Normal 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");
|
15
js/src/tests/js1_8_5/regress/regress-592202-2.js
Normal file
15
js/src/tests/js1_8_5/regress/regress-592202-2.js
Normal 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");
|
28
js/src/tests/js1_8_5/regress/regress-592202-3.js
Normal file
28
js/src/tests/js1_8_5/regress/regress-592202-3.js
Normal 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");
|
31
js/src/tests/js1_8_5/regress/regress-592202-4.js
Normal file
31
js/src/tests/js1_8_5/regress/regress-592202-4.js
Normal 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");
|
@ -144,6 +144,8 @@ GetExpandoObject(JSContext *cx, JSObject *holder)
|
|||||||
JSObject *expando = holder->getSlot(JSSLOT_EXPANDO).toObjectOrNull();
|
JSObject *expando = holder->getSlot(JSSLOT_EXPANDO).toObjectOrNull();
|
||||||
if (!expando) {
|
if (!expando) {
|
||||||
expando = JS_NewObjectWithGivenProto(cx, nsnull, nsnull, holder->getParent());
|
expando = JS_NewObjectWithGivenProto(cx, nsnull, nsnull, holder->getParent());
|
||||||
|
if (!expando)
|
||||||
|
return NULL;
|
||||||
holder->setSlot(JSSLOT_EXPANDO, ObjectValue(*expando));
|
holder->setSlot(JSSLOT_EXPANDO, ObjectValue(*expando));
|
||||||
}
|
}
|
||||||
return expando;
|
return expando;
|
||||||
|
@ -39,6 +39,22 @@ enum BuiltInCharacterClassID {
|
|||||||
NewlineClassID
|
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.
|
// The Parser class should not be used directly - only via the Yarr::parse() method.
|
||||||
template<class Delegate>
|
template<class Delegate>
|
||||||
class Parser {
|
class Parser {
|
||||||
@ -46,21 +62,6 @@ private:
|
|||||||
template<class FriendDelegate>
|
template<class FriendDelegate>
|
||||||
friend int parse(FriendDelegate& delegate, const UString& pattern, unsigned backReferenceLimit);
|
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:
|
* CharacterClassParserDelegate:
|
||||||
*
|
*
|
||||||
@ -147,6 +148,15 @@ private:
|
|||||||
*/
|
*/
|
||||||
void atomBuiltInCharacterClass(BuiltInCharacterClassID classID, bool invert)
|
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();
|
flush();
|
||||||
m_delegate.atomCharacterClassBuiltIn(classID, invert);
|
m_delegate.atomCharacterClassBuiltIn(classID, invert);
|
||||||
}
|
}
|
||||||
@ -404,7 +414,7 @@ private:
|
|||||||
/*
|
/*
|
||||||
* parseCharacterClass():
|
* 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
|
* to an instance of CharacterClassParserDelegate, to describe the character class to the
|
||||||
* delegate.
|
* delegate.
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user