Merge tracemonkey to mozilla-central.
Merge tracemonkey to mozilla-central.
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -55,16 +55,17 @@
#include "jsgc.h"
#include "jsinterp.h"
#include "jslock.h"
#include "jsobj.h"
#include "jsopcode.h"
#include "jsparse.h"
#include "jsscope.h"
#include "jsscript.h"
+#include "jsstaticcheck.h"
#include "jsstr.h"
#include "jsautooplen.h"
typedef struct JSTrap {
JSCList links;
JSScript *script;
jsbytecode *pc;
@@ -1236,16 +1237,19 @@ JS_SetDestroyScriptHook(JSRuntime *rt, J
/***************************************************************************/
JS_PUBLIC_API(JSBool)
JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp,
const jschar *chars, uintN length,
const char *filename, uintN lineno,
jsval *rval)
{
+ JS_ASSERT_NOT_ON_TRACE(cx);
+ JS_ASSERT(cx->fp);
+
JSObject *scobj;
JSScript *script;
JSBool ok;
scobj = JS_GetFrameScopeChain(cx, fp);
if (!scobj)
return JS_FALSE;
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -1982,23 +1982,39 @@ BindNameToSlot(JSContext *cx, JSCodeGene
cookie = dn->pn_cookie;
dn_kind = dn->kind();
/*
* Turn attempts to mutate const-declared bindings into get ops (for
* pre-increment and pre-decrement ops, our caller will have to emit
* JSOP_POS, JSOP_ONE, and JSOP_ADD as well).
*
- * Leave JSOP_DELNAME as is so it can be turned into JSOP_FALSE as
- * appropriate, further below.
+ * Turn JSOP_DELNAME into JSOP_FALSE if dn is known, as all declared
+ * bindings visible to the compiler are permanent in JS unless the
+ * declaration originates in eval code. We detect eval code by testing
+ * cg->compiler->callerFrame, which is set only by eval or a debugger
+ * equivalent.
+ *
+ * Note that this callerFrame non-null test must be qualified by testing
+ * !cg->funbox to exclude function code nested in eval code, which is not
+ * subject to the deletable binding exception.
*/
switch (op) {
case JSOP_NAME:
case JSOP_SETCONST:
+ break;
case JSOP_DELNAME:
+ if (dn_kind != JSDefinition::UNKNOWN) {
+ if (cg->compiler->callerFrame && !cg->funbox)
+ JS_ASSERT(cg->flags & TCF_COMPILE_N_GO);
+ else
+ pn->pn_op = JSOP_FALSE;
+ pn->pn_dflags |= PND_BOUND;
+ return JS_TRUE;
+ }
break;
default:
if (pn->isConst())
pn->pn_op = op = JSOP_NAME;
}
if (cookie == FREE_UPVAR_COOKIE) {
JSStackFrame *caller = cg->compiler->callerFrame;
@@ -2223,58 +2239,53 @@ BindNameToSlot(JSContext *cx, JSCodeGene
switch (op) {
case JSOP_NAME: op = JSOP_GETLOCAL; break;
case JSOP_SETNAME: op = JSOP_SETLOCAL; break;
case JSOP_INCNAME: op = JSOP_INCLOCAL; break;
case JSOP_NAMEINC: op = JSOP_LOCALINC; break;
case JSOP_DECNAME: op = JSOP_DECLOCAL; break;
case JSOP_NAMEDEC: op = JSOP_LOCALDEC; break;
case JSOP_FORNAME: op = JSOP_FORLOCAL; break;
- case JSOP_DELNAME: op = JSOP_FALSE; break;
default: JS_NOT_REACHED("let");
}
break;
case JSDefinition::ARG:
switch (op) {
case JSOP_NAME: op = JSOP_GETARG; break;
case JSOP_SETNAME: op = JSOP_SETARG; break;
case JSOP_INCNAME: op = JSOP_INCARG; break;
case JSOP_NAMEINC: op = JSOP_ARGINC; break;
case JSOP_DECNAME: op = JSOP_DECARG; break;
case JSOP_NAMEDEC: op = JSOP_ARGDEC; break;
case JSOP_FORNAME: op = JSOP_FORARG; break;
- case JSOP_DELNAME: op = JSOP_FALSE; break;
default: JS_NOT_REACHED("arg");
}
JS_ASSERT(!pn->isConst());
break;
case JSDefinition::VAR:
if (PN_OP(dn) == JSOP_CALLEE) {
JS_ASSERT(op != JSOP_CALLEE);
JS_ASSERT((cg->fun->flags & JSFUN_LAMBDA) && atom == cg->fun->atom);
switch (op) {
- case JSOP_DELNAME:
- if (!(cg->flags & TCF_FUN_HEAVYWEIGHT))
- op = JSOP_FALSE;
- break;
default:
/*
* Leave pn->pn_op == JSOP_NAME if cg->fun is heavyweight, as
* we cannot be sure cg->fun is not something of the form:
*
* var ff = (function f(s) { eval(s); return f; });
*
* where a caller invokes ff("var f = 42"). The result returned
* for such an invocation must be 42, since the callee name is
* lexically bound in an outer declarative environment from the
* function's activation. See jsfun.cpp:call_resolve.
*/
+ JS_ASSERT(op != JSOP_DELNAME);
if (!(cg->flags & TCF_FUN_HEAVYWEIGHT)) {
op = JSOP_CALLEE;
pn->pn_dflags |= PND_CONST;
}
break;
}
pn->pn_op = op;
pn->pn_dflags |= PND_BOUND;
@@ -2290,17 +2301,16 @@ BindNameToSlot(JSContext *cx, JSCodeGene
case JSOP_NAME: op = JSOP_GETLOCAL; break;
case JSOP_SETNAME: op = JSOP_SETLOCAL; break;
case JSOP_SETCONST: op = JSOP_SETLOCAL; break;
case JSOP_INCNAME: op = JSOP_INCLOCAL; break;
case JSOP_NAMEINC: op = JSOP_LOCALINC; break;
case JSOP_DECNAME: op = JSOP_DECLOCAL; break;
case JSOP_NAMEDEC: op = JSOP_LOCALDEC; break;
case JSOP_FORNAME: op = JSOP_FORLOCAL; break;
- case JSOP_DELNAME: op = JSOP_FALSE; break;
default: JS_NOT_REACHED("local");
}
JS_ASSERT_IF(dn_kind == JSDefinition::CONST, pn->pn_dflags & PND_CONST);
break;
}
JS_ASSERT(op != PN_OP(pn));
pn->pn_op = op;
@@ -6745,16 +6755,20 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
return JS_FALSE;
break;
}
case TOK_XMLNAME:
if (pn->pn_arity == PN_LIST) {
JS_ASSERT(pn->pn_count != 0);
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
+ if (pn2->pn_type == TOK_LC &&
+ js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) {
+ return JS_FALSE;
+ }
if (!js_EmitTree(cx, cg, pn2))
return JS_FALSE;
if (pn2 != pn->pn_head && js_Emit1(cx, cg, JSOP_ADD) < 0)
return JS_FALSE;
}
} else {
JS_ASSERT(pn->pn_arity == PN_NULLARY);
ok = (pn->pn_op == JSOP_OBJECT)
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -2455,20 +2455,22 @@ js_CloneFunctionObject(JSContext *cx, JS
* Create a new flat closure, but don't initialize the imported upvar
* values. The tracer calls this function and then initializes the upvar
* slots on trace.
*/
JSObject * JS_FASTCALL
js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain)
{
JS_ASSERT(FUN_FLAT_CLOSURE(fun));
- JS_ASSERT(fun->u.i.script->upvarsOffset);
+ JS_ASSERT((fun->u.i.script->upvarsOffset
+ ? JS_SCRIPT_UPVARS(fun->u.i.script)->length
+ : 0) == fun->u.i.nupvars);
JSObject *closure = js_CloneFunctionObject(cx, fun, scopeChain);
- if (!closure)
+ if (!closure || fun->u.i.nupvars == 0)
return closure;
uint32 nslots = JSSLOT_FREE(&js_FunctionClass);
JS_ASSERT(nslots == JS_INITIAL_NSLOTS);
nslots += fun_reserveSlots(cx, closure);
if (!js_ReallocSlots(cx, closure, nslots, JS_TRUE))
return NULL;
@@ -2477,18 +2479,18 @@ js_AllocFlatClosure(JSContext *cx, JSFun
JS_DEFINE_CALLINFO_3(extern, OBJECT, js_AllocFlatClosure,
CONTEXT, FUNCTION, OBJECT, 0, 0)
JSObject *
js_NewFlatClosure(JSContext *cx, JSFunction *fun)
{
JSObject *closure = js_AllocFlatClosure(cx, fun, cx->fp->scopeChain);
- if (!closure)
- return NULL;
+ if (!closure || fun->u.i.nupvars == 0)
+ return closure;
JSUpvarArray *uva = JS_SCRIPT_UPVARS(fun->u.i.script);
JS_ASSERT(uva->length <= size_t(closure->dslots[-1]));
uintN level = fun->u.i.script->staticLevel;
for (uint32 i = 0, n = uva->length; i < n; i++)
closure->dslots[i] = js_GetUpvar(cx, level, uva->vector[i]);
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -3490,20 +3490,21 @@ typedef struct FindPropValData {
typedef struct FindPropValEntry {
JSDHashEntryHdr hdr;
JSParseNode *pnkey;
JSParseNode *pnval;
} FindPropValEntry;
#define ASSERT_VALID_PROPERTY_KEY(pnkey) \
- JS_ASSERT((pnkey)->pn_arity == PN_NULLARY && \
- ((pnkey)->pn_type == TOK_NUMBER || \
- (pnkey)->pn_type == TOK_STRING || \
- (pnkey)->pn_type == TOK_NAME))
+ JS_ASSERT(((pnkey)->pn_arity == PN_NULLARY && \
+ ((pnkey)->pn_type == TOK_NUMBER || \
+ (pnkey)->pn_type == TOK_STRING || \
+ (pnkey)->pn_type == TOK_NAME)) || \
+ (pnkey)->pn_arity == PN_NAME && (pnkey)->pn_type == TOK_NAME)
static JSDHashNumber
HashFindPropValKey(JSDHashTable *table, const void *key)
{
const JSParseNode *pnkey = (const JSParseNode *)key;
ASSERT_VALID_PROPERTY_KEY(pnkey);
return (pnkey->pn_type == TOK_NUMBER)
@@ -3621,16 +3622,19 @@ FindPropertyValue(JSParseNode *pn, JSPar
}
return pnhit->pn_right;
}
/*
* If data is null, the caller is AssignExpr and instead of binding variables,
* we specialize lvalues in the propery value positions of the left-hand side.
* If right is null, just check for well-formed lvalues.
+ *
+ * See also UndominateInitializers, immediately below. If you change either of
+ * these functions, you might have to change the other to match.
*/
static JSBool
CheckDestructuring(JSContext *cx, BindData *data,
JSParseNode *left, JSParseNode *right,
JSTreeContext *tc)
{
JSBool ok;
FindPropValData fpvd;
@@ -3773,16 +3777,96 @@ CheckDestructuring(JSContext *cx, BindDa
no_var_name:
js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn, JSREPORT_ERROR,
JSMSG_NO_VARIABLE_NAME);
ok = JS_FALSE;
goto out;
}
+/*
+ * This is a greatly pared down version of CheckDestructuring that extends the
+ * pn_pos.end source coordinate of each name in a destructuring binding such as
+ *
+ * var [x, y] = [function () y, 42];
+ *
+ * to cover its corresponding initializer, so that the initialized binding does
+ * not appear to dominate any closures in its initializer. See bug 496134.
+ *
+ * The quick-and-dirty dominance computation in JSCompiler::setFunctionKinds is
+ * not very precise. With one-pass SSA construction from structured source code
+ * (see "Single-Pass Generation of Static Single Assignment Form for Structured
+ * Languages", Brandis and Mössenböck), we could do much better.
+ *
+ * See CheckDestructuring, immediately above. If you change either of these
+ * functions, you might have to change the other to match.
+ */
+static JSBool
+UndominateInitializers(JSParseNode *left, JSParseNode *right, JSTreeContext *tc)
+{
+ FindPropValData fpvd;
+ JSParseNode *lhs, *rhs;
+
+ JS_ASSERT(left->pn_type != TOK_ARRAYCOMP);
+ JS_ASSERT(right);
+
+#if JS_HAS_DESTRUCTURING_SHORTHAND
+ if (right->pn_arity == PN_LIST && (right->pn_xflags & PNX_DESTRUCT)) {
+ js_ReportCompileErrorNumber(tc->compiler->context, TS(tc->compiler), right,
+ JSREPORT_ERROR, JSMSG_BAD_OBJECT_INIT);
+ return JS_FALSE;
+ }
+#endif
+
+ if (right->pn_type != left->pn_type)
+ return JS_TRUE;
+
+ fpvd.table.ops = NULL;
+ lhs = left->pn_head;
+ if (left->pn_type == TOK_RB) {
+ rhs = right->pn_head;
+
+ while (lhs && rhs) {
+ /* Nullary comma is an elision; binary comma is an expression.*/
+ if (lhs->pn_type != TOK_COMMA || lhs->pn_arity != PN_NULLARY) {
+ if (lhs->pn_type == TOK_RB || lhs->pn_type == TOK_RC) {
+ if (!UndominateInitializers(lhs, rhs, tc))
+ return JS_FALSE;
+ } else {
+ lhs->pn_pos.end = rhs->pn_pos.end;
+ }
+ }
+
+ lhs = lhs->pn_next;
+ rhs = rhs->pn_next;
+ }
+ } else {
+ JS_ASSERT(left->pn_type == TOK_RC);
+ fpvd.numvars = left->pn_count;
+ fpvd.maxstep = 0;
+
+ while (lhs) {
+ JS_ASSERT(lhs->pn_type == TOK_COLON);
+ JSParseNode *pn = lhs->pn_right;
+
+ rhs = FindPropertyValue(right, lhs->pn_left, &fpvd);
+ if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
+ if (rhs && !UndominateInitializers(pn, rhs, tc))
+ return JS_FALSE;
+ } else {
+ if (rhs)
+ pn->pn_pos.end = rhs->pn_pos.end;
+ }
+
+ lhs = lhs->pn_next;
+ }
+ }
+ return JS_TRUE;
+}
+
static JSParseNode *
DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc,
JSTokenType tt)
{
JSTokenStream *ts;
JSParseNode *pn;
ts = TS(tc->compiler);
@@ -5483,20 +5567,20 @@ Variables(JSContext *cx, JSTokenStream *
#if JS_HAS_DESTRUCTURING
if (tt == TOK_LB || tt == TOK_LC) {
ts->flags |= TSF_DESTRUCTURING;
pn2 = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
ts->flags &= ~TSF_DESTRUCTURING;
if (!pn2)
return NULL;
+ if (!CheckDestructuring(cx, &data, pn2, NULL, tc))
+ return NULL;
if ((tc->flags & TCF_IN_FOR_INIT) &&
js_PeekToken(cx, ts) == TOK_IN) {
- if (!CheckDestructuring(cx, &data, pn2, NULL, tc))
- return NULL;
pn->append(pn2);
continue;
}
MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
if (CURRENT_TOKEN(ts).t_op != JSOP_NOP)
goto bad_var_init;
@@ -5509,23 +5593,22 @@ Variables(JSContext *cx, JSTokenStream *
JSParseNode *init = AssignExpr(cx, ts, tc);
#if JS_HAS_BLOCK_SCOPE
if (popScope) {
tc->topStmt = save;
tc->topScopeStmt = saveScope;
}
#endif
+ if (!init || !UndominateInitializers(pn2, init, tc))
+ return NULL;
+
pn2 = NewBinary(TOK_ASSIGN, JSOP_NOP, pn2, init, tc);
- if (!pn2 ||
- !CheckDestructuring(cx, &data,
- pn2->pn_left, pn2->pn_right,
- tc)) {
+ if (!pn2)
return NULL;
- }
pn->append(pn2);
continue;
}
#endif /* JS_HAS_DESTRUCTURING */
if (tt != TOK_NAME) {
if (tt != TOK_ERROR) {
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -1898,25 +1898,24 @@ js_GetUpvarOnTrace(JSContext* cx, uint32
* nativeStackFramePos is the offset of the start of the
* activation record corresponding to *fip in the native
* stack.
*/
int32 nativeStackFramePos = state->callstackBase[0]->spoffset;
for (FrameInfo** fip2 = state->callstackBase; fip2 <= fip; fip2++)
nativeStackFramePos += (*fip2)->spdist;
nativeStackFramePos -= (2 + (*fip)->get_argc());
- uint8* typemap = (uint8*) (fi+1);
return GetUpvarOnTraceTail(state, cookie, nativeStackFramePos,
- typemap, result);
+ fi->get_typemap(), result);
}
}
if (state->outermostTree->script->staticLevel == upvarLevel) {
- return GetUpvarOnTraceTail(state, cookie, 0,
- state->outermostTree->stackTypeMap(), result);
+ return GetUpvarOnTraceTail(state, cookie, 0, state->callstackBase[0]->get_typemap(),
+ result);
}
/*
* If we did not find the upvar in the frames for the active traces,
* then we simply get the value from the interpreter state.
*/
jsval v = js_GetUpvar(cx, level, cookie);
uint8 type = getCoercedType(v);
@@ -2433,16 +2432,17 @@ TraceRecorder::snapshot(ExitType exitTyp
* side exit instead of creating a new one.
*/
VMSideExit** exits = treeInfo->sideExits.data();
unsigned nexits = treeInfo->sideExits.length();
if (exitType == LOOP_EXIT) {
for (unsigned n = 0; n < nexits; ++n) {
VMSideExit* e = exits[n];
if (e->pc == pc && e->imacpc == fp->imacpc &&
+ ngslots == e->numGlobalSlots &&
!memcmp(getFullTypeMap(exits[n]), typemap, typemap_size)) {
AUDIT(mergedLoopExits);
return e;
}
}
}
if (sizeof(VMSideExit) + (stackSlots + ngslots) * sizeof(uint8) >= MAX_SKIP_BYTES) {
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -312,16 +312,19 @@ struct FrameInfo {
// Safer accessors for argc.
enum { CONSTRUCTING_MASK = 0x8000 };
void set_argc(uint16 argc, bool constructing) {
this->argc = argc | (constructing ? CONSTRUCTING_MASK : 0);
}
uint16 get_argc() const { return argc & ~CONSTRUCTING_MASK; }
bool is_constructing() const { return (argc & CONSTRUCTING_MASK) != 0; }
+
+ // The typemap just before the callee is called.
+ uint8* get_typemap() { return (uint8*) (this+1); }
};
struct UnstableExit
{
nanojit::Fragment* fragment;
VMSideExit* exit;
UnstableExit* next;
};
--- a/js/src/nanojit/LIR.cpp
+++ b/js/src/nanojit/LIR.cpp
@@ -460,16 +460,17 @@ namespace nanojit
case LIR_ule:
case LIR_uge:
case LIR_2:
case LIR_xbarrier:
case LIR_xtbl:
case LIR_ldq:
case LIR_qiand:
case LIR_qiadd:
+ case LIR_qjoin:
case LIR_qcmov:
case LIR_fadd:
case LIR_fsub:
case LIR_fmul:
case LIR_fdiv:
case LIR_qior:
case LIR_qilsh:
return true;
--- a/xpcom/analysis/stack.js
+++ b/xpcom/analysis/stack.js
@@ -147,18 +147,21 @@ function process_cp_pre_genericize(fndec
if (name == "operator new" || name == "operator new []") {
let fncallobj = dehydra_convert(TREE_TYPE(fncall));
if (fncallobj.parameters.length == 2 &&
isVoidPtr(fncallobj.parameters[1]))
return;
let i;
for (i in xrange(stack.length - 1, -1, -1)) {
- if (TREE_CODE(stack[i]) != NOP_EXPR)
- break;
+ if (TREE_CODE(stack[i]) == NOP_EXPR ||
+ TREE_CODE(stack[i]) == COMPOUND_EXPR)
+ continue;
+
+ break;
}
let assign = stack[i];
switch (TREE_CODE(assign)) {
case VAR_DECL:
break;
case INIT_EXPR:
case MODIFY_EXPR: