45 |
#ifdef HAVE_MEMORY_H |
#ifdef HAVE_MEMORY_H |
46 |
#include <memory.h> |
#include <memory.h> |
47 |
#endif |
#endif |
48 |
|
#include <new> |
49 |
#include <string.h> |
#include <string.h> |
50 |
#include "jstypes.h" |
#include "jstypes.h" |
51 |
#include "jsarena.h" /* Added by JSIFY */ |
#include "jsarena.h" /* Added by JSIFY */ |
83 |
NewTryNote(JSContext *cx, JSCodeGenerator *cg, JSTryNoteKind kind, |
NewTryNote(JSContext *cx, JSCodeGenerator *cg, JSTryNoteKind kind, |
84 |
uintN stackDepth, size_t start, size_t end); |
uintN stackDepth, size_t start, size_t end); |
85 |
|
|
86 |
JS_FRIEND_API(void) |
JSCodeGenerator::JSCodeGenerator(JSCompiler *jsc, |
87 |
js_InitCodeGenerator(JSContext *cx, JSCodeGenerator *cg, JSParseContext *pc, |
JSArenaPool *cpool, JSArenaPool *npool, |
88 |
JSArenaPool *codePool, JSArenaPool *notePool, |
uintN lineno) |
89 |
uintN lineno) |
: JSTreeContext(jsc), |
90 |
{ |
codePool(cpool), notePool(npool), |
91 |
memset(cg, 0, sizeof *cg); |
codeMark(JS_ARENA_MARK(cpool)), noteMark(JS_ARENA_MARK(npool)), |
92 |
TREE_CONTEXT_INIT(&cg->treeContext, pc); |
stackDepth(0), maxStackDepth(0), |
93 |
cg->codePool = codePool; |
ntrynotes(0), lastTryNode(NULL), |
94 |
cg->notePool = notePool; |
spanDeps(NULL), jumpTargets(NULL), jtFreeList(NULL), |
95 |
cg->codeMark = JS_ARENA_MARK(codePool); |
numSpanDeps(0), numJumpTargets(0), spanDepTodo(0), |
96 |
cg->noteMark = JS_ARENA_MARK(notePool); |
arrayCompDepth(0), |
97 |
cg->current = &cg->main; |
emitLevel(0) |
98 |
cg->firstLine = cg->prolog.currentLine = cg->main.currentLine = lineno; |
{ |
99 |
ATOM_LIST_INIT(&cg->atomList); |
flags = TCF_COMPILING; |
100 |
cg->prolog.noteMask = cg->main.noteMask = SRCNOTE_CHUNK - 1; |
memset(&prolog, 0, sizeof prolog); |
101 |
ATOM_LIST_INIT(&cg->constList); |
memset(&main, 0, sizeof main); |
102 |
ATOM_LIST_INIT(&cg->upvarList); |
current = &main; |
103 |
} |
firstLine = prolog.currentLine = main.currentLine = lineno; |
104 |
|
prolog.noteMask = main.noteMask = SRCNOTE_CHUNK - 1; |
105 |
JS_FRIEND_API(void) |
memset(&upvarMap, 0, sizeof upvarMap); |
106 |
js_FinishCodeGenerator(JSContext *cx, JSCodeGenerator *cg) |
} |
107 |
{ |
|
108 |
TREE_CONTEXT_FINISH(cx, &cg->treeContext); |
JSCodeGenerator::~JSCodeGenerator() |
109 |
JS_ARENA_RELEASE(cg->codePool, cg->codeMark); |
{ |
110 |
JS_ARENA_RELEASE(cg->notePool, cg->noteMark); |
JS_ARENA_RELEASE(codePool, codeMark); |
111 |
|
JS_ARENA_RELEASE(notePool, noteMark); |
112 |
|
|
113 |
/* NB: non-null only after OOM. */ |
/* NB: non-null only after OOM. */ |
114 |
if (cg->spanDeps) |
if (spanDeps) |
115 |
JS_free(cx, cg->spanDeps); |
JS_free(compiler->context, spanDeps); |
116 |
|
|
117 |
if (cg->upvarMap.vector) |
if (upvarMap.vector) |
118 |
JS_free(cx, cg->upvarMap.vector); |
JS_free(compiler->context, upvarMap.vector); |
119 |
} |
} |
120 |
|
|
121 |
static ptrdiff_t |
static ptrdiff_t |
159 |
jsbytecode *pc; |
jsbytecode *pc; |
160 |
JSOp op; |
JSOp op; |
161 |
const JSCodeSpec *cs; |
const JSCodeSpec *cs; |
162 |
uintN depth; |
uintN extra, depth, nuses; |
163 |
intN nuses, ndefs; |
intN ndefs; |
164 |
|
|
165 |
pc = CG_CODE(cg, target); |
pc = CG_CODE(cg, target); |
166 |
op = (JSOp) *pc; |
op = (JSOp) *pc; |
167 |
cs = &js_CodeSpec[op]; |
cs = &js_CodeSpec[op]; |
168 |
if (cs->format & JOF_TMPSLOT_MASK) { |
#ifdef JS_TRACER |
169 |
|
extern uint8 js_opcode2extra[]; |
170 |
|
extra = js_opcode2extra[op]; |
171 |
|
#else |
172 |
|
extra = 0; |
173 |
|
#endif |
174 |
|
if ((cs->format & JOF_TMPSLOT_MASK) || extra) { |
175 |
depth = (uintN) cg->stackDepth + |
depth = (uintN) cg->stackDepth + |
176 |
((cs->format & JOF_TMPSLOT_MASK) >> JOF_TMPSLOT_SHIFT); |
((cs->format & JOF_TMPSLOT_MASK) >> JOF_TMPSLOT_SHIFT) + |
177 |
|
extra; |
178 |
if (depth > cg->maxStackDepth) |
if (depth > cg->maxStackDepth) |
179 |
cg->maxStackDepth = depth; |
cg->maxStackDepth = depth; |
180 |
} |
} |
181 |
nuses = cs->nuses; |
|
182 |
if (nuses < 0) |
nuses = js_GetStackUses(cs, op, pc); |
|
nuses = js_GetVariableStackUseLength(op, pc); |
|
183 |
cg->stackDepth -= nuses; |
cg->stackDepth -= nuses; |
184 |
JS_ASSERT(cg->stackDepth >= 0); |
JS_ASSERT(cg->stackDepth >= 0); |
185 |
if (cg->stackDepth < 0) { |
if (cg->stackDepth < 0) { |
187 |
JSTokenStream *ts; |
JSTokenStream *ts; |
188 |
|
|
189 |
JS_snprintf(numBuf, sizeof numBuf, "%d", target); |
JS_snprintf(numBuf, sizeof numBuf, "%d", target); |
190 |
ts = &cg->treeContext.parseContext->tokenStream; |
ts = &cg->compiler->tokenStream; |
191 |
JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, |
JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, |
192 |
js_GetErrorMessage, NULL, |
js_GetErrorMessage, NULL, |
193 |
JSMSG_STACK_UNDERFLOW, |
JSMSG_STACK_UNDERFLOW, |
201 |
/* We just executed IndexParsedObject */ |
/* We just executed IndexParsedObject */ |
202 |
JS_ASSERT(op == JSOP_ENTERBLOCK); |
JS_ASSERT(op == JSOP_ENTERBLOCK); |
203 |
JS_ASSERT(nuses == 0); |
JS_ASSERT(nuses == 0); |
204 |
blockObj = cg->objectList.lastPob->object; |
blockObj = cg->objectList.lastbox->object; |
205 |
JS_ASSERT(STOBJ_GET_CLASS(blockObj) == &js_BlockClass); |
JS_ASSERT(STOBJ_GET_CLASS(blockObj) == &js_BlockClass); |
206 |
JS_ASSERT(JSVAL_IS_VOID(blockObj->fslots[JSSLOT_BLOCK_DEPTH])); |
JS_ASSERT(JSVAL_IS_VOID(blockObj->fslots[JSSLOT_BLOCK_DEPTH])); |
207 |
|
|
307 |
static const char * |
static const char * |
308 |
StatementName(JSCodeGenerator *cg) |
StatementName(JSCodeGenerator *cg) |
309 |
{ |
{ |
310 |
if (!cg->treeContext.topStmt) |
if (!cg->topStmt) |
311 |
return js_script_str; |
return js_script_str; |
312 |
return statementName[cg->treeContext.topStmt->type]; |
return statementName[cg->topStmt->type]; |
313 |
} |
} |
314 |
|
|
315 |
static void |
static void |
893 |
|
|
894 |
if (growth) { |
if (growth) { |
895 |
#ifdef DEBUG_brendan |
#ifdef DEBUG_brendan |
896 |
JSTokenStream *ts = &cg->treeContext.parseContext->tokenStream; |
JSTokenStream *ts = &cg->compiler->tokenStream; |
897 |
|
|
898 |
printf("%s:%u: %u/%u jumps extended in %d passes (%d=%d+%d)\n", |
printf("%s:%u: %u/%u jumps extended in %d passes (%d=%d+%d)\n", |
899 |
ts->filename ? ts->filename : "stdin", cg->firstLine, |
ts->filename ? ts->filename : "stdin", cg->firstLine, |
1233 |
return SetSpanDepTarget(cx, cg, GetSpanDep(cg, pc), off); |
return SetSpanDepTarget(cx, cg, GetSpanDep(cg, pc), off); |
1234 |
} |
} |
1235 |
|
|
1236 |
JSBool |
bool |
1237 |
js_InStatement(JSTreeContext *tc, JSStmtType type) |
JSTreeContext::inStatement(JSStmtType type) |
1238 |
{ |
{ |
1239 |
JSStmtInfo *stmt; |
for (JSStmtInfo *stmt = topStmt; stmt; stmt = stmt->down) { |
|
|
|
|
for (stmt = tc->topStmt; stmt; stmt = stmt->down) { |
|
1240 |
if (stmt->type == type) |
if (stmt->type == type) |
1241 |
return JS_TRUE; |
return true; |
1242 |
} |
} |
1243 |
return JS_FALSE; |
return false; |
1244 |
} |
} |
1245 |
|
|
1246 |
void |
void |
1249 |
{ |
{ |
1250 |
stmt->type = type; |
stmt->type = type; |
1251 |
stmt->flags = 0; |
stmt->flags = 0; |
1252 |
|
stmt->blockid = tc->blockid(); |
1253 |
SET_STATEMENT_TOP(stmt, top); |
SET_STATEMENT_TOP(stmt, top); |
1254 |
stmt->u.label = NULL; |
stmt->label = NULL; |
1255 |
JS_ASSERT(!stmt->u.blockObj); |
JS_ASSERT(!stmt->blockObj); |
1256 |
stmt->down = tc->topStmt; |
stmt->down = tc->topStmt; |
1257 |
tc->topStmt = stmt; |
tc->topStmt = stmt; |
1258 |
if (STMT_LINKS_SCOPE(stmt)) { |
if (STMT_LINKS_SCOPE(stmt)) { |
1267 |
js_PushBlockScope(JSTreeContext *tc, JSStmtInfo *stmt, JSObject *blockObj, |
js_PushBlockScope(JSTreeContext *tc, JSStmtInfo *stmt, JSObject *blockObj, |
1268 |
ptrdiff_t top) |
ptrdiff_t top) |
1269 |
{ |
{ |
|
|
|
1270 |
js_PushStatement(tc, stmt, STMT_BLOCK, top); |
js_PushStatement(tc, stmt, STMT_BLOCK, top); |
1271 |
stmt->flags |= SIF_SCOPE; |
stmt->flags |= SIF_SCOPE; |
1272 |
STOBJ_SET_PARENT(blockObj, tc->blockChain); |
STOBJ_SET_PARENT(blockObj, tc->blockChain); |
1273 |
stmt->downScope = tc->topScopeStmt; |
stmt->downScope = tc->topScopeStmt; |
1274 |
tc->topScopeStmt = stmt; |
tc->topScopeStmt = stmt; |
1275 |
tc->blockChain = blockObj; |
tc->blockChain = blockObj; |
1276 |
stmt->u.blockObj = blockObj; |
stmt->blockObj = blockObj; |
1277 |
} |
} |
1278 |
|
|
1279 |
/* |
/* |
1335 |
|
|
1336 |
#define FLUSH_POPS() if (npops && !FlushPops(cx, cg, &npops)) return JS_FALSE |
#define FLUSH_POPS() if (npops && !FlushPops(cx, cg, &npops)) return JS_FALSE |
1337 |
|
|
1338 |
for (stmt = cg->treeContext.topStmt; stmt != toStmt; stmt = stmt->down) { |
for (stmt = cg->topStmt; stmt != toStmt; stmt = stmt->down) { |
1339 |
switch (stmt->type) { |
switch (stmt->type) { |
1340 |
case STMT_FINALLY: |
case STMT_FINALLY: |
1341 |
FLUSH_POPS(); |
FLUSH_POPS(); |
1383 |
FLUSH_POPS(); |
FLUSH_POPS(); |
1384 |
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) |
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) |
1385 |
return JS_FALSE; |
return JS_FALSE; |
1386 |
i = OBJ_BLOCK_COUNT(cx, stmt->u.blockObj); |
i = OBJ_BLOCK_COUNT(cx, stmt->blockObj); |
1387 |
EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, i); |
EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, i); |
1388 |
} |
} |
1389 |
} |
} |
1451 |
if (STMT_LINKS_SCOPE(stmt)) { |
if (STMT_LINKS_SCOPE(stmt)) { |
1452 |
tc->topScopeStmt = stmt->downScope; |
tc->topScopeStmt = stmt->downScope; |
1453 |
if (stmt->flags & SIF_SCOPE) { |
if (stmt->flags & SIF_SCOPE) { |
1454 |
tc->blockChain = STOBJ_GET_PARENT(stmt->u.blockObj); |
tc->blockChain = STOBJ_GET_PARENT(stmt->blockObj); |
1455 |
JS_SCOPE_DEPTH_METERING(--tc->scopeDepth); |
JS_SCOPE_DEPTH_METERING(--tc->scopeDepth); |
1456 |
} |
} |
1457 |
} |
} |
1462 |
{ |
{ |
1463 |
JSStmtInfo *stmt; |
JSStmtInfo *stmt; |
1464 |
|
|
1465 |
stmt = cg->treeContext.topStmt; |
stmt = cg->topStmt; |
1466 |
if (!STMT_IS_TRYING(stmt) && |
if (!STMT_IS_TRYING(stmt) && |
1467 |
(!BackPatch(cx, cg, stmt->breaks, CG_NEXT(cg), JSOP_GOTO) || |
(!BackPatch(cx, cg, stmt->breaks, CG_NEXT(cg), JSOP_GOTO) || |
1468 |
!BackPatch(cx, cg, stmt->continues, CG_CODE(cg, stmt->update), |
!BackPatch(cx, cg, stmt->continues, CG_CODE(cg, stmt->update), |
1469 |
JSOP_GOTO))) { |
JSOP_GOTO))) { |
1470 |
return JS_FALSE; |
return JS_FALSE; |
1471 |
} |
} |
1472 |
js_PopStatement(&cg->treeContext); |
js_PopStatement(cg); |
1473 |
return JS_TRUE; |
return JS_TRUE; |
1474 |
} |
} |
1475 |
|
|
1499 |
return JS_FALSE; |
return JS_FALSE; |
1500 |
v = ATOM_KEY(valueAtom); |
v = ATOM_KEY(valueAtom); |
1501 |
} |
} |
1502 |
ale = js_IndexAtom(cx, atom, &cg->constList); |
ale = cg->constList.add(cg->compiler, atom); |
1503 |
if (!ale) |
if (!ale) |
1504 |
return JS_FALSE; |
return JS_FALSE; |
1505 |
ALE_SET_VALUE(ale, v); |
ALE_SET_VALUE(ale, v); |
1508 |
} |
} |
1509 |
|
|
1510 |
JSStmtInfo * |
JSStmtInfo * |
1511 |
js_LexicalLookup(JSTreeContext *tc, JSAtom *atom, jsint *slotp) |
js_LexicalLookup(JSTreeContext *tc, JSAtom *atom, jsint *slotp, JSStmtInfo *stmt) |
1512 |
{ |
{ |
|
JSStmtInfo *stmt; |
|
1513 |
JSObject *obj; |
JSObject *obj; |
1514 |
JSScope *scope; |
JSScope *scope; |
1515 |
JSScopeProperty *sprop; |
JSScopeProperty *sprop; |
1516 |
|
|
1517 |
for (stmt = tc->topScopeStmt; stmt; stmt = stmt->downScope) { |
if (!stmt) |
1518 |
|
stmt = tc->topScopeStmt; |
1519 |
|
for (; stmt; stmt = stmt->downScope) { |
1520 |
if (stmt->type == STMT_WITH) |
if (stmt->type == STMT_WITH) |
1521 |
break; |
break; |
1522 |
|
|
1524 |
if (!(stmt->flags & SIF_SCOPE)) |
if (!(stmt->flags & SIF_SCOPE)) |
1525 |
continue; |
continue; |
1526 |
|
|
1527 |
obj = stmt->u.blockObj; |
obj = stmt->blockObj; |
1528 |
JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass); |
JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass); |
1529 |
scope = OBJ_SCOPE(obj); |
scope = OBJ_SCOPE(obj); |
1530 |
sprop = SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom)); |
sprop = SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom)); |
1564 |
JSBool ok; |
JSBool ok; |
1565 |
JSStmtInfo *stmt; |
JSStmtInfo *stmt; |
1566 |
JSAtomListElement *ale; |
JSAtomListElement *ale; |
1567 |
JSObject *obj, *pobj; |
JSObject *obj, *objbox; |
1568 |
JSProperty *prop; |
JSProperty *prop; |
1569 |
uintN attrs; |
uintN attrs; |
1570 |
|
|
1575 |
*/ |
*/ |
1576 |
*vp = JSVAL_HOLE; |
*vp = JSVAL_HOLE; |
1577 |
do { |
do { |
1578 |
if (cg->treeContext.flags & (TCF_IN_FUNCTION | TCF_COMPILE_N_GO)) { |
if (cg->flags & (TCF_IN_FUNCTION | TCF_COMPILE_N_GO)) { |
1579 |
/* XXX this will need revising when 'let const' is added. */ |
/* XXX this will need revising if 'const' becomes block-scoped. */ |
1580 |
stmt = js_LexicalLookup(&cg->treeContext, atom, NULL); |
stmt = js_LexicalLookup(cg, atom, NULL); |
1581 |
if (stmt) |
if (stmt) |
1582 |
return JS_TRUE; |
return JS_TRUE; |
1583 |
|
|
1584 |
ATOM_LIST_SEARCH(ale, &cg->constList, atom); |
ale = cg->constList.lookup(atom); |
1585 |
if (ale) { |
if (ale) { |
1586 |
JS_ASSERT(ALE_VALUE(ale) != JSVAL_HOLE); |
JS_ASSERT(ALE_VALUE(ale) != JSVAL_HOLE); |
1587 |
*vp = ALE_VALUE(ale); |
*vp = ALE_VALUE(ale); |
1595 |
* with object or catch variable; nor can prop's value be changed, |
* with object or catch variable; nor can prop's value be changed, |
1596 |
* nor can prop be deleted. |
* nor can prop be deleted. |
1597 |
*/ |
*/ |
1598 |
if (cg->treeContext.flags & TCF_IN_FUNCTION) { |
if (cg->flags & TCF_IN_FUNCTION) { |
1599 |
if (js_LookupLocal(cx, cg->treeContext.u.fun, atom, NULL) != |
if (js_LookupLocal(cx, cg->fun, atom, NULL) != JSLOCAL_NONE) |
|
JSLOCAL_NONE) { |
|
1600 |
break; |
break; |
|
} |
|
1601 |
} else { |
} else { |
1602 |
JS_ASSERT(cg->treeContext.flags & TCF_COMPILE_N_GO); |
JS_ASSERT(cg->flags & TCF_COMPILE_N_GO); |
1603 |
obj = cg->treeContext.u.scopeChain; |
obj = cg->scopeChain; |
1604 |
ok = OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &pobj, |
ok = OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &objbox, |
1605 |
&prop); |
&prop); |
1606 |
if (!ok) |
if (!ok) |
1607 |
return JS_FALSE; |
return JS_FALSE; |
1608 |
if (pobj == obj) { |
if (objbox == obj) { |
1609 |
/* |
/* |
1610 |
* We're compiling code that will be executed immediately, |
* We're compiling code that will be executed immediately, |
1611 |
* not re-executed against a different scope chain and/or |
* not re-executed against a different scope chain and/or |
1620 |
} |
} |
1621 |
} |
} |
1622 |
if (prop) |
if (prop) |
1623 |
OBJ_DROP_PROPERTY(cx, pobj, prop); |
OBJ_DROP_PROPERTY(cx, objbox, prop); |
1624 |
if (!ok) |
if (!ok) |
1625 |
return JS_FALSE; |
return JS_FALSE; |
1626 |
if (prop) |
if (prop) |
1627 |
break; |
break; |
1628 |
} |
} |
1629 |
} |
} |
1630 |
} while ((cg = cg->parent) != NULL); |
} while ((cg = (JSCodeGenerator *) cg->parent) != NULL); |
1631 |
return JS_TRUE; |
return JS_TRUE; |
1632 |
} |
} |
1633 |
|
|
1702 |
return JS_FALSE; \ |
return JS_FALSE; \ |
1703 |
JS_END_MACRO |
JS_END_MACRO |
1704 |
|
|
|
|
|
1705 |
static JSBool |
static JSBool |
1706 |
EmitAtomOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) |
EmitAtomOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) |
1707 |
{ |
{ |
1712 |
pn->pn_atom == cx->runtime->atomState.lengthAtom) { |
pn->pn_atom == cx->runtime->atomState.lengthAtom) { |
1713 |
return js_Emit1(cx, cg, JSOP_LENGTH) >= 0; |
return js_Emit1(cx, cg, JSOP_LENGTH) >= 0; |
1714 |
} |
} |
1715 |
ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList); |
ale = cg->atomList.add(cg->compiler, pn->pn_atom); |
1716 |
if (!ale) |
if (!ale) |
1717 |
return JS_FALSE; |
return JS_FALSE; |
1718 |
return EmitIndexOp(cx, op, ALE_INDEX(ale), cg); |
return EmitIndexOp(cx, op, ALE_INDEX(ale), cg); |
1719 |
} |
} |
1720 |
|
|
|
static uintN |
|
|
IndexParsedObject(JSParsedObjectBox *pob, JSEmittedObjectList *list); |
|
|
|
|
1721 |
static JSBool |
static JSBool |
1722 |
EmitObjectOp(JSContext *cx, JSParsedObjectBox *pob, JSOp op, |
EmitObjectOp(JSContext *cx, JSObjectBox *objbox, JSOp op, |
1723 |
JSCodeGenerator *cg) |
JSCodeGenerator *cg) |
1724 |
{ |
{ |
1725 |
JS_ASSERT(JOF_OPTYPE(op) == JOF_OBJECT); |
JS_ASSERT(JOF_OPTYPE(op) == JOF_OBJECT); |
1726 |
return EmitIndexOp(cx, op, IndexParsedObject(pob, &cg->objectList), cg); |
return EmitIndexOp(cx, op, cg->objectList.index(objbox), cg); |
1727 |
} |
} |
1728 |
|
|
1729 |
/* |
/* |
1738 |
|
|
1739 |
static JSBool |
static JSBool |
1740 |
EmitSlotIndexOp(JSContext *cx, JSOp op, uintN slot, uintN index, |
EmitSlotIndexOp(JSContext *cx, JSOp op, uintN slot, uintN index, |
1741 |
JSCodeGenerator *cg) |
JSCodeGenerator *cg) |
1742 |
{ |
{ |
1743 |
JSOp bigSuffix; |
JSOp bigSuffix; |
1744 |
ptrdiff_t off; |
ptrdiff_t off; |
1765 |
* Adjust the slot for a block local to account for the number of variables |
* Adjust the slot for a block local to account for the number of variables |
1766 |
* that share the same index space with locals. Due to the incremental code |
* that share the same index space with locals. Due to the incremental code |
1767 |
* generation for top-level script, we do the adjustment via code patching in |
* generation for top-level script, we do the adjustment via code patching in |
1768 |
* js_CompileScript; see comments there. |
* JSCompiler::compileScript; see comments there. |
1769 |
* |
* |
1770 |
* The function returns -1 on failures. |
* The function returns -1 on failures. |
1771 |
*/ |
*/ |
1773 |
AdjustBlockSlot(JSContext *cx, JSCodeGenerator *cg, jsint slot) |
AdjustBlockSlot(JSContext *cx, JSCodeGenerator *cg, jsint slot) |
1774 |
{ |
{ |
1775 |
JS_ASSERT((jsuint) slot < cg->maxStackDepth); |
JS_ASSERT((jsuint) slot < cg->maxStackDepth); |
1776 |
if (cg->treeContext.flags & TCF_IN_FUNCTION) { |
if (cg->flags & TCF_IN_FUNCTION) { |
1777 |
slot += cg->treeContext.u.fun->u.i.nvars; |
slot += cg->fun->u.i.nvars; |
1778 |
if ((uintN) slot >= SLOTNO_LIMIT) { |
if ((uintN) slot >= SLOTNO_LIMIT) { |
1779 |
js_ReportCompileErrorNumber(cx, CG_TS(cg), NULL, |
js_ReportCompileErrorNumber(cx, CG_TS(cg), NULL, |
1780 |
JSREPORT_ERROR, |
JSREPORT_ERROR, |
1785 |
return slot; |
return slot; |
1786 |
} |
} |
1787 |
|
|
1788 |
|
static bool |
1789 |
|
EmitEnterBlock(JSContext *cx, JSParseNode *pn, JSCodeGenerator *cg) |
1790 |
|
{ |
1791 |
|
JS_ASSERT(PN_TYPE(pn) == TOK_LEXICALSCOPE); |
1792 |
|
if (!EmitObjectOp(cx, pn->pn_objbox, JSOP_ENTERBLOCK, cg)) |
1793 |
|
return false; |
1794 |
|
|
1795 |
|
JSObject *blockObj = pn->pn_objbox->object; |
1796 |
|
jsint depth = AdjustBlockSlot(cx, cg, OBJ_BLOCK_DEPTH(cx, blockObj)); |
1797 |
|
if (depth < 0) |
1798 |
|
return false; |
1799 |
|
|
1800 |
|
for (uintN slot = JSSLOT_FREE(&js_BlockClass), |
1801 |
|
limit = slot + OBJ_BLOCK_COUNT(cx, blockObj); |
1802 |
|
slot < limit; slot++) { |
1803 |
|
jsval v = STOBJ_GET_SLOT(blockObj, slot); |
1804 |
|
|
1805 |
|
/* Beware the empty destructuring dummy. */ |
1806 |
|
if (JSVAL_IS_VOID(v)) { |
1807 |
|
JS_ASSERT(slot + 1 <= limit); |
1808 |
|
continue; |
1809 |
|
} |
1810 |
|
|
1811 |
|
JSDefinition *dn = (JSDefinition *) JSVAL_TO_PRIVATE(v); |
1812 |
|
JS_ASSERT(dn->pn_defn); |
1813 |
|
JS_ASSERT(uintN(dn->frameSlot() + depth) < JS_BIT(16)); |
1814 |
|
dn->pn_cookie += depth; |
1815 |
|
#ifdef DEBUG |
1816 |
|
for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) { |
1817 |
|
JS_ASSERT(pnu->pn_lexdef == dn); |
1818 |
|
JS_ASSERT(!(pnu->pn_dflags & PND_BOUND)); |
1819 |
|
JS_ASSERT(pnu->pn_cookie == FREE_UPVAR_COOKIE); |
1820 |
|
} |
1821 |
|
#endif |
1822 |
|
} |
1823 |
|
|
1824 |
|
OBJ_SCOPE(blockObj)->freeslot = JSSLOT_FREE(&js_BlockClass); |
1825 |
|
js_ReallocSlots(cx, blockObj, JSSLOT_FREE(&js_BlockClass), JS_TRUE); |
1826 |
|
return true; |
1827 |
|
} |
1828 |
|
|
1829 |
/* |
/* |
1830 |
* This routine tries to optimize name gets and sets to stack slot loads and |
* When eval is called from a function, the eval code or function code it |
1831 |
* stores, given the variables object and scope chain in cx's top frame, the |
* compiles may reference upvars that live in the eval-calling function. The |
1832 |
* compile-time context in tc, and a TOK_NAME node pn. It returns false on |
* eval-invoked compiler does not have explicit definitions for these upvars |
1833 |
* error, true on success. |
* and we do not attempt to create them a-priori (by inspecting the function's |
1834 |
|
* args and vars) -- we could, but we'd take an avoidable penalty for each |
1835 |
|
* function local not referenced by any upvar. Instead, we map such upvars |
1836 |
|
* lazily, growing upvarMap.vector by powers of two. |
1837 |
* |
* |
1838 |
* The caller can inspect pn->pn_slot for a non-negative slot number to tell |
* This function knows that it is called with pn pointing to a PN_NAME-arity |
1839 |
* whether optimization occurred, in which case BindNameToSlot also updated |
* node, and cg->compiler->callerFrame having a non-null fun member, and the |
1840 |
* pn->pn_op. If pn->pn_slot is still -1 on return, pn->pn_op nevertheless |
* static level of cg at least one greater than the eval-calling function's |
1841 |
* may have been optimized, e.g., from JSOP_NAME to JSOP_ARGUMENTS. Whether |
* static level. |
1842 |
* or not pn->pn_op was modified, if this function finds an argument or local |
*/ |
1843 |
* variable name, pn->pn_const will be true for const properties after a |
static bool |
1844 |
|
MakeUpvarForEval(JSParseNode *pn, JSCodeGenerator *cg) |
1845 |
|
{ |
1846 |
|
JSContext *cx = cg->compiler->context; |
1847 |
|
JSFunction *fun = cg->compiler->callerFrame->fun; |
1848 |
|
uintN upvarLevel = fun->u.i.script->staticLevel; |
1849 |
|
|
1850 |
|
JSFunctionBox *funbox = cg->funbox; |
1851 |
|
if (funbox) { |
1852 |
|
/* |
1853 |
|
* Treat top-level function definitions as escaping (i.e., as funargs), |
1854 |
|
* required since we compile each such top level function or statement |
1855 |
|
* and throw away the AST, so we can't yet see all funarg uses of this |
1856 |
|
* function being compiled (cg->funbox->object). See bug 493177. |
1857 |
|
*/ |
1858 |
|
if (funbox->level == fun->u.i.script->staticLevel + 1U && |
1859 |
|
!(((JSFunction *) funbox->object)->flags & JSFUN_LAMBDA)) { |
1860 |
|
JS_ASSERT_IF(cx->options & JSOPTION_ANONFUNFIX, |
1861 |
|
((JSFunction *) funbox->object)->atom); |
1862 |
|
return true; |
1863 |
|
} |
1864 |
|
|
1865 |
|
while (funbox->level >= upvarLevel) { |
1866 |
|
if (funbox->node->pn_dflags & PND_FUNARG) |
1867 |
|
return true; |
1868 |
|
funbox = funbox->parent; |
1869 |
|
if (!funbox) |
1870 |
|
break; |
1871 |
|
} |
1872 |
|
} |
1873 |
|
|
1874 |
|
JSAtom *atom = pn->pn_atom; |
1875 |
|
|
1876 |
|
uintN index; |
1877 |
|
JSLocalKind localKind = js_LookupLocal(cx, fun, atom, &index); |
1878 |
|
if (localKind == JSLOCAL_NONE) |
1879 |
|
return true; |
1880 |
|
|
1881 |
|
JS_ASSERT(cg->staticLevel > upvarLevel); |
1882 |
|
if (cg->staticLevel >= JS_DISPLAY_SIZE || upvarLevel >= JS_DISPLAY_SIZE) |
1883 |
|
return true; |
1884 |
|
|
1885 |
|
JSAtomListElement *ale = cg->upvarList.lookup(atom); |
1886 |
|
if (!ale) { |
1887 |
|
if ((cg->flags & TCF_IN_FUNCTION) && |
1888 |
|
!js_AddLocal(cx, cg->fun, atom, JSLOCAL_UPVAR)) { |
1889 |
|
return false; |
1890 |
|
} |
1891 |
|
|
1892 |
|
ale = cg->upvarList.add(cg->compiler, atom); |
1893 |
|
if (!ale) |
1894 |
|
return false; |
1895 |
|
JS_ASSERT(ALE_INDEX(ale) == cg->upvarList.count - 1); |
1896 |
|
|
1897 |
|
uint32 *vector = cg->upvarMap.vector; |
1898 |
|
uint32 length = cg->upvarMap.length; |
1899 |
|
|
1900 |
|
JS_ASSERT(ALE_INDEX(ale) <= length); |
1901 |
|
if (ALE_INDEX(ale) == length) { |
1902 |
|
length = 2 * JS_MAX(2, length); |
1903 |
|
vector = (uint32 *) JS_realloc(cx, vector, length * sizeof *vector); |
1904 |
|
if (!vector) |
1905 |
|
return false; |
1906 |
|
cg->upvarMap.vector = vector; |
1907 |
|
cg->upvarMap.length = length; |
1908 |
|
} |
1909 |
|
|
1910 |
|
if (localKind != JSLOCAL_ARG) |
1911 |
|
index += fun->nargs; |
1912 |
|
JS_ASSERT(index < JS_BIT(16)); |
1913 |
|
|
1914 |
|
uintN skip = cg->staticLevel - upvarLevel; |
1915 |
|
vector[ALE_INDEX(ale)] = MAKE_UPVAR_COOKIE(skip, index); |
1916 |
|
} |
1917 |
|
|
1918 |
|
pn->pn_op = JSOP_GETUPVAR; |
1919 |
|
pn->pn_cookie = MAKE_UPVAR_COOKIE(cg->staticLevel, ALE_INDEX(ale)); |
1920 |
|
pn->pn_dflags |= PND_BOUND; |
1921 |
|
return true; |
1922 |
|
} |
1923 |
|
|
1924 |
|
/* |
1925 |
|
* BindNameToSlot attempts to optimize name gets and sets to stack slot loads |
1926 |
|
* and stores, given the compile-time information in cg and a TOK_NAME node pn. |
1927 |
|
* It returns false on error, true on success. |
1928 |
|
* |
1929 |
|
* The caller can inspect pn->pn_cookie for FREE_UPVAR_COOKIE to tell whether |
1930 |
|
* optimization occurred, in which case BindNameToSlot also updated pn->pn_op. |
1931 |
|
* If pn->pn_cookie is still FREE_UPVAR_COOKIE on return, pn->pn_op still may |
1932 |
|
* have been optimized, e.g., from JSOP_NAME to JSOP_CALLEE. Whether or not |
1933 |
|
* pn->pn_op was modified, if this function finds an argument or local variable |
1934 |
|
* name, PND_CONST will be set in pn_dflags for read-only properties after a |
1935 |
* successful return. |
* successful return. |
1936 |
* |
* |
1937 |
* NB: if you add more opcodes specialized from JSOP_NAME, etc., don't forget |
* NB: if you add more opcodes specialized from JSOP_NAME, etc., don't forget |
1941 |
static JSBool |
static JSBool |
1942 |
BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) |
BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) |
1943 |
{ |
{ |
1944 |
JSTreeContext *tc; |
JSDefinition *dn; |
|
JSAtom *atom; |
|
|
JSStmtInfo *stmt; |
|
|
jsint slot; |
|
1945 |
JSOp op; |
JSOp op; |
1946 |
JSLocalKind localKind; |
JSAtom *atom; |
1947 |
uintN index; |
uint32 cookie; |
1948 |
|
JSDefinition::Kind dn_kind; |
1949 |
JSAtomListElement *ale; |
JSAtomListElement *ale; |
1950 |
JSBool constOp; |
uintN index; |
1951 |
|
|
1952 |
JS_ASSERT(pn->pn_type == TOK_NAME); |
JS_ASSERT(pn->pn_type == TOK_NAME); |
|
if (pn->pn_slot >= 0 || pn->pn_op == JSOP_ARGUMENTS) |
|
|
return JS_TRUE; |
|
1953 |
|
|
1954 |
/* QNAME references can never be optimized to use arg/var storage. */ |
/* Idempotency tests come first, since we may be called more than once. */ |
1955 |
if (pn->pn_op == JSOP_QNAMEPART) |
if (pn->pn_dflags & PND_BOUND) |
1956 |
return JS_TRUE; |
return JS_TRUE; |
1957 |
|
|
1958 |
|
/* No cookie initialized for these two, they're pre-bound by definition. */ |
1959 |
|
JS_ASSERT(pn->pn_op != JSOP_ARGUMENTS && pn->pn_op != JSOP_CALLEE); |
1960 |
|
|
1961 |
/* |
/* |
1962 |
* We can't optimize if we are compiling a with statement and its body, |
* The parser linked all uses (including forward references) to their |
1963 |
* or we're in a catch block whose exception variable has the same name |
* definitions, unless a with statement or direct eval intervened. |
|
* as this node. FIXME: we should be able to optimize catch vars to be |
|
|
* block-locals. |
|
1964 |
*/ |
*/ |
1965 |
tc = &cg->treeContext; |
if (pn->pn_used) { |
1966 |
atom = pn->pn_atom; |
JS_ASSERT(pn->pn_cookie == FREE_UPVAR_COOKIE); |
1967 |
stmt = js_LexicalLookup(tc, atom, &slot); |
dn = pn->pn_lexdef; |
1968 |
if (stmt) { |
JS_ASSERT(dn->pn_defn); |
1969 |
if (stmt->type == STMT_WITH) |
pn->pn_dflags |= (dn->pn_dflags & PND_CONST); |
1970 |
|
} else { |
1971 |
|
if (!pn->pn_defn) |
1972 |
return JS_TRUE; |
return JS_TRUE; |
1973 |
|
dn = (JSDefinition *) pn; |
1974 |
|
} |
1975 |
|
|
1976 |
JS_ASSERT(stmt->flags & SIF_SCOPE); |
op = PN_OP(pn); |
1977 |
JS_ASSERT(slot >= 0); |
if (op == JSOP_NOP) |
|
op = PN_OP(pn); |
|
|
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_ASSERT(0); |
|
|
} |
|
|
if (op != pn->pn_op) { |
|
|
slot = AdjustBlockSlot(cx, cg, slot); |
|
|
if (slot < 0) |
|
|
return JS_FALSE; |
|
|
pn->pn_op = op; |
|
|
pn->pn_slot = slot; |
|
|
} |
|
1978 |
return JS_TRUE; |
return JS_TRUE; |
1979 |
} |
|
1980 |
|
JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM); |
1981 |
|
atom = pn->pn_atom; |
1982 |
|
cookie = dn->pn_cookie; |
1983 |
|
dn_kind = dn->kind(); |
1984 |
|
|
1985 |
/* |
/* |
1986 |
* We can't optimize if var and closure (a local function not in a larger |
* Turn attempts to mutate const-declared bindings into get ops (for |
1987 |
* expression and not at top-level within another's body) collide. |
* pre-increment and pre-decrement ops, our caller will have to emit |
1988 |
* XXX suboptimal: keep track of colliding names and deoptimize only those |
* JSOP_POS, JSOP_ONE, and JSOP_ADD as well). |
1989 |
|
* |
1990 |
|
* Turn JSOP_DELNAME into JSOP_FALSE if dn is known, as all declared |
1991 |
|
* bindings visible to the compiler are permanent in JS unless the |
1992 |
|
* declaration originates in eval code. We detect eval code by testing |
1993 |
|
* cg->compiler->callerFrame, which is set only by eval or a debugger |
1994 |
|
* equivalent. |
1995 |
|
* |
1996 |
|
* Note that this callerFrame non-null test must be qualified by testing |
1997 |
|
* !cg->funbox to exclude function code nested in eval code, which is not |
1998 |
|
* subject to the deletable binding exception. |
1999 |
*/ |
*/ |
2000 |
if (tc->flags & TCF_FUN_CLOSURE_VS_VAR) |
switch (op) { |
2001 |
return JS_TRUE; |
case JSOP_NAME: |
2002 |
|
case JSOP_SETCONST: |
2003 |
if (!(tc->flags & TCF_IN_FUNCTION)) { |
break; |
2004 |
JSStackFrame *caller; |
case JSOP_DELNAME: |
2005 |
|
if (dn_kind != JSDefinition::UNKNOWN) { |
2006 |
|
if (cg->compiler->callerFrame && !cg->funbox) |
2007 |
|
JS_ASSERT(cg->flags & TCF_COMPILE_N_GO); |
2008 |
|
else |
2009 |
|
pn->pn_op = JSOP_FALSE; |
2010 |
|
pn->pn_dflags |= PND_BOUND; |
2011 |
|
return JS_TRUE; |
2012 |
|
} |
2013 |
|
break; |
2014 |
|
default: |
2015 |
|
if (pn->isConst()) |
2016 |
|
pn->pn_op = op = JSOP_NAME; |
2017 |
|
} |
2018 |
|
|
2019 |
caller = tc->parseContext->callerFrame; |
if (cookie == FREE_UPVAR_COOKIE) { |
2020 |
|
JSStackFrame *caller = cg->compiler->callerFrame; |
2021 |
if (caller) { |
if (caller) { |
2022 |
JS_ASSERT(tc->flags & TCF_COMPILE_N_GO); |
JS_ASSERT(cg->flags & TCF_COMPILE_N_GO); |
2023 |
|
|
2024 |
|
/* |
2025 |
|
* Don't generate upvars on the left side of a for loop. See |
2026 |
|
* bug 470758. |
2027 |
|
*/ |
2028 |
|
if (cg->flags & TCF_IN_FOR_INIT) |
2029 |
|
return JS_TRUE; |
2030 |
|
|
2031 |
JS_ASSERT(caller->script); |
JS_ASSERT(caller->script); |
2032 |
if (!caller->fun || caller->varobj != tc->u.scopeChain) |
if (!caller->fun) |
2033 |
|
return JS_TRUE; |
2034 |
|
|
2035 |
|
/* |
2036 |
|
* Make sure the variable object used by the compiler to initialize |
2037 |
|
* parent links matches the caller's varobj. Compile-n-go compiler- |
2038 |
|
* created function objects have the top-level cg's scopeChain set |
2039 |
|
* as their parent by JSCompiler::newFunction. |
2040 |
|
*/ |
2041 |
|
JSObject *scopeobj = (cg->flags & TCF_IN_FUNCTION) |
2042 |
|
? STOBJ_GET_PARENT(FUN_OBJECT(cg->fun)) |
2043 |
|
: cg->scopeChain; |
2044 |
|
if (scopeobj != caller->varobj) |
2045 |
return JS_TRUE; |
return JS_TRUE; |
2046 |
|
|
2047 |
/* |
/* |
2048 |
* We are compiling eval or debug script inside a function frame |
* We are compiling eval or debug script inside a function frame |
2049 |
* and the scope chain matches function's variable object. |
* and the scope chain matches the function's variable object. |
2050 |
* Optimize access to function's arguments and variable and the |
* Optimize access to function's arguments and variable and the |
2051 |
* arguments object. |
* arguments object. |
2052 |
*/ |
*/ |
2053 |
if (PN_OP(pn) != JSOP_NAME || cg->staticDepth > JS_DISPLAY_SIZE) |
if (op != JSOP_NAME) |
2054 |
goto arguments_check; |
return JS_TRUE; |
|
localKind = js_LookupLocal(cx, caller->fun, atom, &index); |
|
|
if (localKind == JSLOCAL_NONE) |
|
|
goto arguments_check; |
|
|
|
|
|
ATOM_LIST_SEARCH(ale, &cg->upvarList, atom); |
|
|
if (!ale) { |
|
|
uint32 length, *vector; |
|
|
|
|
|
ale = js_IndexAtom(cx, atom, &cg->upvarList); |
|
|
if (!ale) |
|
|
return JS_FALSE; |
|
|
JS_ASSERT(ALE_INDEX(ale) == cg->upvarList.count - 1); |
|
|
|
|
|
length = cg->upvarMap.length; |
|
|
JS_ASSERT(ALE_INDEX(ale) <= length); |
|
|
if (ALE_INDEX(ale) == length) { |
|
|
length = 2 * JS_MAX(2, length); |
|
|
vector = (uint32 *) |
|
|
JS_realloc(cx, cg->upvarMap.vector, |
|
|
length * sizeof *vector); |
|
|
if (!vector) |
|
|
return JS_FALSE; |
|
|
cg->upvarMap.vector = vector; |
|
|
cg->upvarMap.length = length; |
|
|
} |
|
|
|
|
|
if (localKind != JSLOCAL_ARG) |
|
|
index += caller->fun->nargs; |
|
|
if (index >= JS_BIT(16)) { |
|
|
cg->treeContext.flags |= TCF_FUN_USES_NONLOCALS; |
|
|
return JS_TRUE; |
|
|
} |
|
|
|
|
|
JS_ASSERT(cg->staticDepth > caller->fun->u.i.script->staticDepth); |
|
|
uintN skip = cg->staticDepth - caller->fun->u.i.script->staticDepth; |
|
|
cg->upvarMap.vector[ALE_INDEX(ale)] = MAKE_UPVAR_COOKIE(skip, index); |
|
|
} |
|
2055 |
|
|
2056 |
pn->pn_op = JSOP_GETUPVAR; |
return MakeUpvarForEval(pn, cg); |
|
pn->pn_slot = ALE_INDEX(ale); |
|
|
return JS_TRUE; |
|
2057 |
} |
} |
2058 |
|
return JS_TRUE; |
2059 |
|
} |
2060 |
|
|
2061 |
|
if (dn->pn_dflags & PND_GVAR) { |
2062 |
/* |
/* |
2063 |
* We are optimizing global variables and there may be no pre-existing |
* If this is a global reference from within a function, leave pn_op as |
2064 |
* global property named atom. If atom was declared via const or var, |
* JSOP_NAME, etc. We could emit JSOP_*GVAR ops within function code if |
2065 |
* optimize pn to access fp->vars using the appropriate JSOP_*GVAR op. |
* only we could depend on the global frame's slots being valid for all |
2066 |
|
* calls to the function. |
2067 |
*/ |
*/ |
2068 |
ATOM_LIST_SEARCH(ale, &tc->decls, atom); |
if (cg->flags & TCF_IN_FUNCTION) |
|
if (!ale) { |
|
|
/* Use precedes declaration, or name is never declared. */ |
|
|
return JS_TRUE; |
|
|
} |
|
|
constOp = (ALE_JSOP(ale) == JSOP_DEFCONST); |
|
|
|
|
|
/* Index atom so we can map fast global number to name. */ |
|
|
ale = js_IndexAtom(cx, atom, &cg->atomList); |
|
|
if (!ale) |
|
|
return JS_FALSE; |
|
|
|
|
|
/* Defend against tc->ngvars 16-bit overflow. */ |
|
|
slot = ALE_INDEX(ale); |
|
|
if ((slot + 1) >> 16) |
|
2069 |
return JS_TRUE; |
return JS_TRUE; |
2070 |
|
|
2071 |
if ((uint16)(slot + 1) > tc->ngvars) |
/* |
2072 |
tc->ngvars = (uint16)(slot + 1); |
* We are optimizing global variables and there may be no pre-existing |
2073 |
|
* global property named atom when this global script runs. If atom was |
2074 |
|
* declared via const or var, optimize pn to access fp->vars using the |
2075 |
|
* appropriate JSOP_*GVAR op. |
2076 |
|
* |
2077 |
|
* FIXME: should be able to optimize global function access too. |
2078 |
|
*/ |
2079 |
|
JS_ASSERT(dn_kind == JSDefinition::VAR || dn_kind == JSDefinition::CONST); |
2080 |
|
|
|
op = PN_OP(pn); |
|
2081 |
switch (op) { |
switch (op) { |
2082 |
case JSOP_NAME: op = JSOP_GETGVAR; break; |
case JSOP_NAME: op = JSOP_GETGVAR; break; |
2083 |
case JSOP_SETNAME: op = JSOP_SETGVAR; break; |
case JSOP_SETNAME: op = JSOP_SETGVAR; break; |
2090 |
case JSOP_DELNAME: /* NB: no change */ break; |
case JSOP_DELNAME: /* NB: no change */ break; |
2091 |
default: JS_NOT_REACHED("gvar"); |
default: JS_NOT_REACHED("gvar"); |
2092 |
} |
} |
2093 |
pn->pn_const = constOp; |
pn->pn_op = op; |
2094 |
if (op != pn->pn_op) { |
pn->pn_cookie = cookie; |
2095 |
pn->pn_op = op; |
pn->pn_dflags |= PND_BOUND; |
|
pn->pn_slot = slot; |
|
|
} |
|
2096 |
return JS_TRUE; |
return JS_TRUE; |
2097 |
} |
} |
2098 |
|
|
2099 |
if (tc->flags & TCF_IN_FUNCTION) { |
uintN level = UPVAR_FRAME_SKIP(cookie); |
2100 |
|
JS_ASSERT(cg->staticLevel >= level); |
2101 |
|
|
2102 |
|
/* |
2103 |
|
* A JSDefinition witnessed as a declaration by the parser cannot be an |
2104 |
|
* upvar, unless it is the degenerate kind of upvar selected above (in the |
2105 |
|
* code before the PND_GVAR test) for the special case of compile-and-go |
2106 |
|
* code generated from eval called from a function, where the eval code |
2107 |
|
* uses local vars defined in the function. We detect this upvar-for-eval |
2108 |
|
* case by checking dn's op. |
2109 |
|
*/ |
2110 |
|
if (PN_OP(dn) == JSOP_GETUPVAR) { |
2111 |
|
JS_ASSERT(cg->staticLevel >= level); |
2112 |
|
if (op != JSOP_NAME) |
2113 |
|
return JS_TRUE; |
2114 |
|
|
2115 |
|
#ifdef DEBUG |
2116 |
|
JSStackFrame *caller = cg->compiler->callerFrame; |
2117 |
|
JS_ASSERT(caller); |
2118 |
|
|
2119 |
|
JSTreeContext *tc = cg; |
2120 |
|
while (tc->staticLevel != level) |
2121 |
|
tc = tc->parent; |
2122 |
|
JS_ASSERT(tc->flags & TCF_COMPILING); |
2123 |
|
|
2124 |
|
JSCodeGenerator *evalcg = (JSCodeGenerator *) tc; |
2125 |
|
JS_ASSERT(evalcg->flags & TCF_COMPILE_N_GO); |
2126 |
|
JS_ASSERT(!(evalcg->flags & TCF_IN_FOR_INIT)); |
2127 |
|
JS_ASSERT(caller->script); |
2128 |
|
JS_ASSERT(caller->fun && caller->varobj == evalcg->scopeChain); |
2129 |
|
#endif |
2130 |
|
|
2131 |
|
if (cg->staticLevel == level) { |
2132 |
|
pn->pn_op = JSOP_GETUPVAR; |
2133 |
|
pn->pn_cookie = cookie; |
2134 |
|
pn->pn_dflags |= PND_BOUND; |
2135 |
|
return JS_TRUE; |
2136 |
|
} |
2137 |
|
|
2138 |
|
return MakeUpvarForEval(pn, cg); |
2139 |
|
} |
2140 |
|
|
2141 |
|
uintN skip = cg->staticLevel - level; |
2142 |
|
if (skip != 0) { |
2143 |
|
JS_ASSERT(cg->flags & TCF_IN_FUNCTION); |
2144 |
|
JS_ASSERT(cg->lexdeps.lookup(atom)); |
2145 |
|
JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM); |
2146 |
|
JS_ASSERT(cg->fun->u.i.skipmin <= skip); |
2147 |
|
|
2148 |
/* |
/* |
2149 |
* We are compiling a function body and may be able to optimize name |
* If op is a mutating opcode, this upvar's static level is too big to |
2150 |
* to stack slot. Look for an argument or variable in the function and |
* index into the display, or the function is heavyweight, we fall back |
2151 |
* rewrite pn_op and update pn accordingly. |
* on JSOP_*NAME*. |
2152 |
*/ |
*/ |
2153 |
localKind = js_LookupLocal(cx, tc->u.fun, atom, &index); |
if (op != JSOP_NAME) |
2154 |
if (localKind != JSLOCAL_NONE) { |
return JS_TRUE; |
2155 |
op = PN_OP(pn); |
if (level >= JS_DISPLAY_SIZE) |
2156 |
if (localKind == JSLOCAL_ARG) { |
return JS_TRUE; |
2157 |
switch (op) { |
if (cg->flags & TCF_FUN_HEAVYWEIGHT) |
2158 |
case JSOP_NAME: op = JSOP_GETARG; break; |
return JS_TRUE; |
2159 |
case JSOP_SETNAME: op = JSOP_SETARG; break; |
|
2160 |
case JSOP_INCNAME: op = JSOP_INCARG; break; |
if (FUN_FLAT_CLOSURE(cg->fun)) { |
2161 |
case JSOP_NAMEINC: op = JSOP_ARGINC; break; |
op = JSOP_GETDSLOT; |
2162 |
case JSOP_DECNAME: op = JSOP_DECARG; break; |
} else { |
2163 |
case JSOP_NAMEDEC: op = JSOP_ARGDEC; break; |
/* |
2164 |
case JSOP_FORNAME: op = JSOP_FORARG; break; |
* The function we're compiling may not be heavyweight, but if it |
2165 |
case JSOP_DELNAME: op = JSOP_FALSE; break; |
* escapes as a funarg, we can't use JSOP_GETUPVAR/JSOP_CALLUPVAR. |
2166 |
default: JS_NOT_REACHED("arg"); |
* JSCompiler::analyzeFunctions has arranged for this function's |
2167 |
} |
* enclosing functions to be heavyweight, so we can safely stick |
2168 |
pn->pn_const = JS_FALSE; |
* with JSOP_NAME/JSOP_CALLNAME. |
2169 |
} else { |
*/ |
2170 |
JS_ASSERT(localKind == JSLOCAL_VAR || |
if (cg->funbox->node->pn_dflags & PND_FUNARG) |
2171 |
localKind == JSLOCAL_CONST); |
return JS_TRUE; |
2172 |
switch (op) { |
|
2173 |
case JSOP_NAME: op = JSOP_GETLOCAL; break; |
/* |
2174 |
case JSOP_SETNAME: op = JSOP_SETLOCAL; break; |
* Generator functions may be resumed from any call stack, which |
2175 |
case JSOP_SETCONST: op = JSOP_SETLOCAL; break; |
* defeats the display optimization to static link searching used |
2176 |
case JSOP_INCNAME: op = JSOP_INCLOCAL; break; |
* by JSOP_{GET,CALL}UPVAR. |
2177 |
case JSOP_NAMEINC: op = JSOP_LOCALINC; break; |
*/ |
2178 |
case JSOP_DECNAME: op = JSOP_DECLOCAL; break; |
if (cg->flags & TCF_FUN_IS_GENERATOR) |
2179 |
case JSOP_NAMEDEC: op = JSOP_LOCALDEC; break; |
return JS_TRUE; |
2180 |
case JSOP_FORNAME: op = JSOP_FORLOCAL; break; |
|
2181 |
case JSOP_DELNAME: op = JSOP_FALSE; break; |
op = JSOP_GETUPVAR; |
2182 |
default: JS_NOT_REACHED("local"); |
} |
2183 |
|
|
2184 |
|
ale = cg->upvarList.lookup(atom); |
2185 |
|
if (ale) { |
2186 |
|
index = ALE_INDEX(ale); |
2187 |
|
} else { |
2188 |
|
if (!js_AddLocal(cx, cg->fun, atom, JSLOCAL_UPVAR)) |
2189 |
|
return JS_FALSE; |
2190 |
|
|
2191 |
|
ale = cg->upvarList.add(cg->compiler, atom); |
2192 |
|
if (!ale) |
2193 |
|
return JS_FALSE; |
2194 |
|
index = ALE_INDEX(ale); |
2195 |
|
JS_ASSERT(index == cg->upvarList.count - 1); |
2196 |
|
|
2197 |
|
uint32 *vector = cg->upvarMap.vector; |
2198 |
|
if (!vector) { |
2199 |
|
uint32 length = cg->lexdeps.count; |
2200 |
|
|
2201 |
|
vector = (uint32 *) calloc(length, sizeof *vector); |
2202 |
|
if (!vector) { |
2203 |
|
JS_ReportOutOfMemory(cx); |
2204 |
|
return JS_FALSE; |
2205 |
} |
} |
2206 |
pn->pn_const = (localKind == JSLOCAL_CONST); |
cg->upvarMap.vector = vector; |
2207 |
|
cg->upvarMap.length = length; |
2208 |
} |
} |
2209 |
pn->pn_op = op; |
|
2210 |
pn->pn_slot = index; |
uintN slot = UPVAR_FRAME_SLOT(cookie); |
2211 |
return JS_TRUE; |
if (dn_kind != JSDefinition::ARG) { |
2212 |
|
JSTreeContext *tc = cg; |
2213 |
|
do { |
2214 |
|
tc = tc->parent; |
2215 |
|
} while (tc->staticLevel != level); |
2216 |
|
if (tc->flags & TCF_IN_FUNCTION) |
2217 |
|
slot += tc->fun->nargs; |
2218 |
|
} |
2219 |
|
|
2220 |
|
vector[index] = MAKE_UPVAR_COOKIE(skip, slot); |
2221 |
} |
} |
2222 |
tc->flags |= TCF_FUN_USES_NONLOCALS; |
|
2223 |
|
pn->pn_op = op; |
2224 |
|
pn->pn_cookie = index; |
2225 |
|
pn->pn_dflags |= PND_BOUND; |
2226 |
|
return JS_TRUE; |
2227 |
} |
} |
2228 |
|
|
|
arguments_check: |
|
2229 |
/* |
/* |
2230 |
* Here we either compiling a function body or an eval or debug script |
* We are compiling a function body and may be able to optimize name |
2231 |
* inside a function and couldn't optimize pn, so it's not a global or |
* to stack slot. Look for an argument or variable in the function and |
2232 |
* local slot name. We are also outside of any with blocks. Check if we |
* rewrite pn_op and update pn accordingly. |
|
* can optimize the predefined arguments variable. |
|
2233 |
*/ |
*/ |
2234 |
JS_ASSERT((tc->flags & TCF_IN_FUNCTION) || |
switch (dn_kind) { |
2235 |
(tc->parseContext->callerFrame && |
case JSDefinition::UNKNOWN: |
|
tc->parseContext->callerFrame->fun && |
|
|
tc->parseContext->callerFrame->varobj == tc->u.scopeChain)); |
|
|
if (pn->pn_op == JSOP_NAME && |
|
|
atom == cx->runtime->atomState.argumentsAtom) { |
|
|
pn->pn_op = JSOP_ARGUMENTS; |
|
2236 |
return JS_TRUE; |
return JS_TRUE; |
2237 |
|
|
2238 |
|
case JSDefinition::LET: |
2239 |
|
switch (op) { |
2240 |
|
case JSOP_NAME: op = JSOP_GETLOCAL; break; |
2241 |
|
case JSOP_SETNAME: op = JSOP_SETLOCAL; break; |
2242 |
|
case JSOP_INCNAME: op = JSOP_INCLOCAL; break; |
2243 |
|
case JSOP_NAMEINC: op = JSOP_LOCALINC; break; |
2244 |
|
case JSOP_DECNAME: op = JSOP_DECLOCAL; break; |
2245 |
|
case JSOP_NAMEDEC: op = JSOP_LOCALDEC; break; |
2246 |
|
case JSOP_FORNAME: op = JSOP_FORLOCAL; break; |
2247 |
|
default: JS_NOT_REACHED("let"); |
2248 |
|
} |
2249 |
|
break; |
2250 |
|
|
2251 |
|
case JSDefinition::ARG: |
2252 |
|
switch (op) { |
2253 |
|
case JSOP_NAME: op = JSOP_GETARG; break; |
2254 |
|
case JSOP_SETNAME: op = JSOP_SETARG; break; |
2255 |
|
case JSOP_INCNAME: op = JSOP_INCARG; break; |
2256 |
|
case JSOP_NAMEINC: op = JSOP_ARGINC; break; |
2257 |
|
case JSOP_DECNAME: op = JSOP_DECARG; break; |
2258 |
|
case JSOP_NAMEDEC: op = JSOP_ARGDEC; break; |
2259 |
|
case JSOP_FORNAME: op = JSOP_FORARG; break; |
2260 |
|
default: JS_NOT_REACHED("arg"); |
2261 |
|
} |
2262 |
|
JS_ASSERT(!pn->isConst()); |
2263 |
|
break; |
2264 |
|
|
2265 |
|
case JSDefinition::VAR: |
2266 |
|
if (PN_OP(dn) == JSOP_CALLEE) { |
2267 |
|
JS_ASSERT(op != JSOP_CALLEE); |
2268 |
|
JS_ASSERT((cg->fun->flags & JSFUN_LAMBDA) && atom == cg->fun->atom); |
2269 |
|
|
2270 |
|
switch (op) { |
2271 |
|
default: |
2272 |
|
/* |
2273 |
|
* Leave pn->pn_op == JSOP_NAME if cg->fun is heavyweight, as |
2274 |
|
* we cannot be sure cg->fun is not something of the form: |
2275 |
|
* |
2276 |
|
* var ff = (function f(s) { eval(s); return f; }); |
2277 |
|
* |
2278 |
|
* where a caller invokes ff("var f = 42"). The result returned |
2279 |
|
* for such an invocation must be 42, since the callee name is |
2280 |
|
* lexically bound in an outer declarative environment from the |
2281 |
|
* function's activation. See jsfun.cpp:call_resolve. |
2282 |
|
*/ |
2283 |
|
JS_ASSERT(op != JSOP_DELNAME); |
2284 |
|
if (!(cg->flags & TCF_FUN_HEAVYWEIGHT)) { |
2285 |
|
op = JSOP_CALLEE; |
2286 |
|
pn->pn_dflags |= PND_CONST; |
2287 |
|
} |
2288 |
|
break; |
2289 |
|
} |
2290 |
|
pn->pn_op = op; |
2291 |
|
pn->pn_dflags |= PND_BOUND; |
2292 |
|
return JS_TRUE; |
2293 |
|
} |
2294 |
|
/* FALL THROUGH */ |
2295 |
|
|
2296 |
|
default: |
2297 |
|
JS_ASSERT_IF(dn_kind != JSDefinition::FUNCTION, |
2298 |
|
dn_kind == JSDefinition::VAR || |
2299 |
|
dn_kind == JSDefinition::CONST); |
2300 |
|
switch (op) { |
2301 |
|
case JSOP_NAME: op = JSOP_GETLOCAL; break; |
2302 |
|
case JSOP_SETNAME: op = JSOP_SETLOCAL; break; |
2303 |
|
case JSOP_SETCONST: op = JSOP_SETLOCAL; break; |
2304 |
|
case JSOP_INCNAME: op = JSOP_INCLOCAL; break; |
2305 |
|
case JSOP_NAMEINC: op = JSOP_LOCALINC; break; |
2306 |
|
case JSOP_DECNAME: op = JSOP_DECLOCAL; break; |
2307 |
|
case JSOP_NAMEDEC: op = JSOP_LOCALDEC; break; |
2308 |
|
case JSOP_FORNAME: op = JSOP_FORLOCAL; break; |
2309 |
|
default: JS_NOT_REACHED("local"); |
2310 |
|
} |
2311 |
|
JS_ASSERT_IF(dn_kind == JSDefinition::CONST, pn->pn_dflags & PND_CONST); |
2312 |
|
break; |
2313 |
} |
} |
2314 |
|
|
2315 |
|
JS_ASSERT(op != PN_OP(pn)); |
2316 |
|
pn->pn_op = op; |
2317 |
|
pn->pn_cookie = UPVAR_FRAME_SLOT(cookie); |
2318 |
|
pn->pn_dflags |= PND_BOUND; |
2319 |
return JS_TRUE; |
return JS_TRUE; |
2320 |
} |
} |
2321 |
|
|
2336 |
JSBool *answer) |
JSBool *answer) |
2337 |
{ |
{ |
2338 |
JSBool ok; |
JSBool ok; |
|
JSFunction *fun; |
|
2339 |
JSParseNode *pn2; |
JSParseNode *pn2; |
2340 |
|
|
2341 |
ok = JS_TRUE; |
ok = JS_TRUE; |
2345 |
switch (pn->pn_arity) { |
switch (pn->pn_arity) { |
2346 |
case PN_FUNC: |
case PN_FUNC: |
2347 |
/* |
/* |
2348 |
* A named function is presumed useful: we can't yet know that it is |
* A named function, contrary to ES3, is no longer useful, because we |
2349 |
* not called. The side effects are the creation of a scope object |
* bind its name lexically (using JSOP_CALLEE) instead of creating an |
2350 |
* to parent this function object, and the binding of the function's |
* Object instance and binding a readonly, permanent property in it |
2351 |
* name in that scope object. See comments at case JSOP_NAMEDFUNOBJ: |
* (the object and binding can be detected and hijacked or captured). |
2352 |
* in jsinterp.c. |
* This is a bug fix to ES3; it is fixed in ES3.1 drafts. |
2353 |
*/ |
*/ |
2354 |
fun = (JSFunction *) pn->pn_funpob->object; |
*answer = JS_FALSE; |
|
if (fun->atom) |
|
|
*answer = JS_TRUE; |
|
2355 |
break; |
break; |
2356 |
|
|
2357 |
case PN_LIST: |
case PN_LIST: |
2410 |
return JS_FALSE; |
return JS_FALSE; |
2411 |
if (!CheckSideEffects(cx, cg, pn->pn_right, answer)) |
if (!CheckSideEffects(cx, cg, pn->pn_right, answer)) |
2412 |
return JS_FALSE; |
return JS_FALSE; |
2413 |
if (!*answer && |
if (!*answer && (pn->pn_op != JSOP_NOP || !pn2->isConst())) |
|
(pn->pn_op != JSOP_NOP || |
|
|
pn2->pn_slot < 0 || |
|
|
!pn2->pn_const)) { |
|
2414 |
*answer = JS_TRUE; |
*answer = JS_TRUE; |
|
} |
|
2415 |
} |
} |
2416 |
} else { |
} else { |
2417 |
if (pn->pn_op == JSOP_OR || pn->pn_op == JSOP_AND || |
if (pn->pn_op == JSOP_OR || pn->pn_op == JSOP_AND || |
2442 |
pn2 = pn->pn_kid; |
pn2 = pn->pn_kid; |
2443 |
switch (pn2->pn_type) { |
switch (pn2->pn_type) { |
2444 |
case TOK_NAME: |
case TOK_NAME: |
2445 |
|
if (!BindNameToSlot(cx, cg, pn2)) |
2446 |
|
return JS_FALSE; |
2447 |
|
if (pn2->isConst()) { |
2448 |
|
*answer = JS_FALSE; |
2449 |
|
break; |
2450 |
|
} |
2451 |
|
/* FALL THROUGH */ |
2452 |
case TOK_DOT: |
case TOK_DOT: |
2453 |
#if JS_HAS_XML_SUPPORT |
#if JS_HAS_XML_SUPPORT |
2454 |
case TOK_DBLDOT: |
case TOK_DBLDOT: |
2495 |
if (pn->pn_type == TOK_NAME && pn->pn_op != JSOP_NOP) { |
if (pn->pn_type == TOK_NAME && pn->pn_op != JSOP_NOP) { |
2496 |
if (!BindNameToSlot(cx, cg, pn)) |
if (!BindNameToSlot(cx, cg, pn)) |
2497 |
return JS_FALSE; |
return JS_FALSE; |
2498 |
if (pn->pn_slot < 0 && pn->pn_op != JSOP_ARGUMENTS) { |
if (pn->pn_op != JSOP_ARGUMENTS && pn->pn_op != JSOP_CALLEE && |
2499 |
|
pn->pn_cookie == FREE_UPVAR_COOKIE) { |
2500 |
/* |
/* |
2501 |
* Not an argument or local variable use, so this expression |
* Not an argument or local variable use, and not a use of a |
2502 |
* could invoke a getter that has side effects. |
* unshadowed named function expression's given name, so this |
2503 |
|
* expression could invoke a getter that has side effects. |
2504 |
*/ |
*/ |
2505 |
*answer = JS_TRUE; |
*answer = JS_TRUE; |
2506 |
} |
} |
2507 |
} |
} |
2508 |
pn2 = pn->pn_expr; |
pn2 = pn->maybeExpr(); |
2509 |
if (pn->pn_type == TOK_DOT) { |
if (pn->pn_type == TOK_DOT) { |
2510 |
if (pn2->pn_type == TOK_NAME && !BindNameToSlot(cx, cg, pn2)) |
if (pn2->pn_type == TOK_NAME && !BindNameToSlot(cx, cg, pn2)) |
2511 |
return JS_FALSE; |
return JS_FALSE; |
2521 |
ok = CheckSideEffects(cx, cg, pn2, answer); |
ok = CheckSideEffects(cx, cg, pn2, answer); |
2522 |
break; |
break; |
2523 |
|
|
2524 |
|
case PN_NAMESET: |
2525 |
|
ok = CheckSideEffects(cx, cg, pn->pn_tree, answer); |
2526 |
|
break; |
2527 |
|
|
2528 |
case PN_NULLARY: |
case PN_NULLARY: |
2529 |
if (pn->pn_type == TOK_DEBUGGER) |
if (pn->pn_type == TOK_DEBUGGER) |
2530 |
*answer = JS_TRUE; |
*answer = JS_TRUE; |
2560 |
case JSOP_GETUPVAR: |
case JSOP_GETUPVAR: |
2561 |
op = JSOP_CALLUPVAR; |
op = JSOP_CALLUPVAR; |
2562 |
break; |
break; |
2563 |
|
case JSOP_GETDSLOT: |
2564 |
|
op = JSOP_CALLDSLOT; |
2565 |
|
break; |
2566 |
default: |
default: |
2567 |
JS_ASSERT(op == JSOP_ARGUMENTS); |
JS_ASSERT(op == JSOP_ARGUMENTS || op == JSOP_CALLEE); |
2568 |
break; |
break; |
2569 |
} |
} |
2570 |
} |
} |
2571 |
|
|
2572 |
if (op == JSOP_ARGUMENTS) { |
if (op == JSOP_ARGUMENTS || op == JSOP_CALLEE) { |
2573 |
if (js_Emit1(cx, cg, op) < 0) |
if (js_Emit1(cx, cg, op) < 0) |
2574 |
return JS_FALSE; |
return JS_FALSE; |
2575 |
if (callContext && js_Emit1(cx, cg, JSOP_NULL) < 0) |
if (callContext && js_Emit1(cx, cg, JSOP_NULL) < 0) |
2576 |
return JS_FALSE; |
return JS_FALSE; |
2577 |
} else { |
} else { |
2578 |
if (pn->pn_slot >= 0) { |
if (pn->pn_cookie != FREE_UPVAR_COOKIE) { |
2579 |
EMIT_UINT16_IMM_OP(op, pn->pn_slot); |
EMIT_UINT16_IMM_OP(op, pn->pn_cookie); |
2580 |
} else { |
} else { |
2581 |
if (!EmitAtomOp(cx, pn, op, cg)) |
if (!EmitAtomOp(cx, pn, op, cg)) |
2582 |
return JS_FALSE; |
return JS_FALSE; |
2598 |
JS_ASSERT(op == JSOP_XMLNAME || op == JSOP_CALLXMLNAME); |
JS_ASSERT(op == JSOP_XMLNAME || op == JSOP_CALLXMLNAME); |
2599 |
|
|
2600 |
pn2 = pn->pn_kid; |
pn2 = pn->pn_kid; |
2601 |
oldflags = cg->treeContext.flags; |
oldflags = cg->flags; |
2602 |
cg->treeContext.flags &= ~TCF_IN_FOR_INIT; |
cg->flags &= ~TCF_IN_FOR_INIT; |
2603 |
if (!js_EmitTree(cx, cg, pn2)) |
if (!js_EmitTree(cx, cg, pn2)) |
2604 |
return JS_FALSE; |
return JS_FALSE; |
2605 |
cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT; |
cg->flags |= oldflags & TCF_IN_FOR_INIT; |
2606 |
if (js_NewSrcNote2(cx, cg, SRC_PCBASE, |
if (js_NewSrcNote2(cx, cg, SRC_PCBASE, |
2607 |
CG_OFFSET(cg) - pn2->pn_offset) < 0) { |
CG_OFFSET(cg) - pn2->pn_offset) < 0) { |
2608 |
return JS_FALSE; |
return JS_FALSE; |
2613 |
#endif |
#endif |
2614 |
|
|
2615 |
static JSBool |
static JSBool |
2616 |
|
EmitSpecialPropOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) |
2617 |
|
{ |
2618 |
|
/* |
2619 |
|
* Special case for obj.__proto__, obj.__parent__, obj.__count__ to |
2620 |
|
* deoptimize away from fast paths in the interpreter and trace recorder, |
2621 |
|
* which skip dense array instances by going up to Array.prototype before |
2622 |
|
* looking up the property name. |
2623 |
|
*/ |
2624 |
|
JSAtomListElement *ale = cg->atomList.add(cg->compiler, pn->pn_atom); |
2625 |
|
if (!ale) |
2626 |
|
return JS_FALSE; |
2627 |
|
if (!EmitIndexOp(cx, JSOP_QNAMEPART, ALE_INDEX(ale), cg)) |
2628 |
|
return JS_FALSE; |
2629 |
|
if (js_Emit1(cx, cg, op) < 0) |
2630 |
|
return JS_FALSE; |
2631 |
|
return JS_TRUE; |
2632 |
|
} |
2633 |
|
|
2634 |
|
static JSBool |
2635 |
EmitPropOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg, |
EmitPropOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg, |
2636 |
JSBool callContext) |
JSBool callContext) |
2637 |
{ |
{ |
2638 |
JSParseNode *pn2, *pndot, *pnup, *pndown; |
JSParseNode *pn2, *pndot, *pnup, *pndown; |
2639 |
ptrdiff_t top; |
ptrdiff_t top; |
2640 |
|
|
2641 |
pn2 = pn->pn_expr; |
JS_ASSERT(pn->pn_arity == PN_NAME); |
2642 |
|
pn2 = pn->maybeExpr(); |
2643 |
|
|
2644 |
|
/* Special case deoptimization on __proto__, __count__ and __parent__. */ |
2645 |
|
if ((op == JSOP_GETPROP || op == JSOP_CALLPROP) && |
2646 |
|
(pn->pn_atom == cx->runtime->atomState.protoAtom || |
2647 |
|
pn->pn_atom == cx->runtime->atomState.parentAtom || |
2648 |
|
pn->pn_atom == cx->runtime->atomState.countAtom)) { |
2649 |
|
if (pn2 && !js_EmitTree(cx, cg, pn2)) |
2650 |
|
return JS_FALSE; |
2651 |
|
return EmitSpecialPropOp(cx, pn, callContext ? JSOP_CALLELEM : JSOP_GETELEM, cg); |
2652 |
|
} |
2653 |
|
|
2654 |
if (callContext) { |
if (callContext) { |
2655 |
JS_ASSERT(pn->pn_type == TOK_DOT); |
JS_ASSERT(pn->pn_type == TOK_DOT); |
2656 |
JS_ASSERT(op == JSOP_GETPROP); |
JS_ASSERT(op == JSOP_GETPROP); |
2686 |
JSAtomListElement *ale; |
JSAtomListElement *ale; |
2687 |
jsatomid atomIndex; |
jsatomid atomIndex; |
2688 |
|
|
2689 |
ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList); |
ale = cg->atomList.add(cg->compiler, pn->pn_atom); |
2690 |
if (!ale) |
if (!ale) |
2691 |
return JS_FALSE; |
return JS_FALSE; |
2692 |
atomIndex = ALE_INDEX(ale); |
atomIndex = ALE_INDEX(ale); |
2693 |
return EmitSlotIndexOp(cx, op, pn2->pn_slot, atomIndex, cg); |
return EmitSlotIndexOp(cx, op, pn2->pn_cookie, atomIndex, cg); |
2694 |
} |
} |
2695 |
|
|
2696 |
default:; |
default:; |
2711 |
for (;;) { |
for (;;) { |
2712 |
/* Reverse pndot->pn_expr to point up, not down. */ |
/* Reverse pndot->pn_expr to point up, not down. */ |
2713 |
pndot->pn_offset = top; |
pndot->pn_offset = top; |
2714 |
|
JS_ASSERT(!pndot->pn_used); |
2715 |
pndown = pndot->pn_expr; |
pndown = pndot->pn_expr; |
2716 |
pndot->pn_expr = pnup; |
pndot->pn_expr = pnup; |
2717 |
if (pndown->pn_type != TOK_DOT) |
if (pndown->pn_type != TOK_DOT) |
2730 |
CG_OFFSET(cg) - pndown->pn_offset) < 0) { |
CG_OFFSET(cg) - pndown->pn_offset) < 0) { |
2731 |
return JS_FALSE; |
return JS_FALSE; |
2732 |
} |
} |
2733 |
if (!EmitAtomOp(cx, pndot, PN_OP(pndot), cg)) |
|
2734 |
|
/* |
2735 |
|
* Special case deoptimization on __proto__, __count__ and |
2736 |
|
* __parent__, as above. |
2737 |
|
*/ |
2738 |
|
if (pndot->pn_arity == PN_NAME && |
2739 |
|
(pndot->pn_atom == cx->runtime->atomState.protoAtom || |
2740 |
|
pndot->pn_atom == cx->runtime->atomState.parentAtom || |
2741 |
|
pndot->pn_atom == cx->runtime->atomState.countAtom)) { |
2742 |
|
if (!EmitSpecialPropOp(cx, pndot, JSOP_GETELEM, cg)) |
2743 |
|
return JS_FALSE; |
2744 |
|
} else if (!EmitAtomOp(cx, pndot, PN_OP(pndot), cg)) { |
2745 |
return JS_FALSE; |
return JS_FALSE; |
2746 |
|
} |
2747 |
|
|
2748 |
/* Reverse the pn_expr link again. */ |
/* Reverse the pn_expr link again. */ |
2749 |
pnup = pndot->pn_expr; |
pnup = pndot->pn_expr; |
2776 |
JS_ASSERT(pn->pn_op == JSOP_GETELEM); |
JS_ASSERT(pn->pn_op == JSOP_GETELEM); |
2777 |
JS_ASSERT(pn->pn_count >= 3); |
JS_ASSERT(pn->pn_count >= 3); |
2778 |
left = pn->pn_head; |
left = pn->pn_head; |
2779 |
right = PN_LAST(pn); |
right = pn->last(); |
2780 |
next = left->pn_next; |
next = left->pn_next; |
2781 |
JS_ASSERT(next != right); |
JS_ASSERT(next != right); |
2782 |
|
|
2832 |
* the base expression (pn_expr) of the name may be null, which |
* the base expression (pn_expr) of the name may be null, which |
2833 |
* means we have to emit a JSOP_BINDNAME. |
* means we have to emit a JSOP_BINDNAME. |
2834 |
*/ |
*/ |
2835 |
left = pn->pn_expr; |
left = pn->maybeExpr(); |
2836 |
if (!left) { |
if (!left) { |
2837 |
left = <mp; |
left = <mp; |
2838 |
left->pn_type = TOK_STRING; |
left->pn_type = TOK_STRING; |
2926 |
if (!atom) |
if (!atom) |
2927 |
return JS_FALSE; |
return JS_FALSE; |
2928 |
|
|
2929 |
ale = js_IndexAtom(cx, atom, &cg->atomList); |
ale = cg->atomList.add(cg->compiler, atom); |
2930 |
if (!ale) |
if (!ale) |
2931 |
return JS_FALSE; |
return JS_FALSE; |
2932 |
return EmitIndexOp(cx, JSOP_DOUBLE, ALE_INDEX(ale), cg); |
return EmitIndexOp(cx, JSOP_DOUBLE, ALE_INDEX(ale), cg); |
2976 |
* the stack so that case-dispatch bytecodes can find the discriminant |
* the stack so that case-dispatch bytecodes can find the discriminant |
2977 |
* on top of stack. |
* on top of stack. |
2978 |
*/ |
*/ |
2979 |
count = OBJ_BLOCK_COUNT(cx, pn2->pn_pob->object); |
count = OBJ_BLOCK_COUNT(cx, pn2->pn_objbox->object); |
2980 |
js_PushBlockScope(&cg->treeContext, stmtInfo, pn2->pn_pob->object, -1); |
js_PushBlockScope(cg, stmtInfo, pn2->pn_objbox->object, -1); |
2981 |
stmtInfo->type = STMT_SWITCH; |
stmtInfo->type = STMT_SWITCH; |
2982 |
|
|
2983 |
/* Emit JSOP_ENTERBLOCK before code to evaluate the discriminant. */ |
/* Emit JSOP_ENTERBLOCK before code to evaluate the discriminant. */ |
2984 |
if (!EmitObjectOp(cx, pn2->pn_pob, JSOP_ENTERBLOCK, cg)) |
if (!EmitEnterBlock(cx, pn2, cg)) |
2985 |
return JS_FALSE; |
return JS_FALSE; |
2986 |
|
|
2987 |
/* |
/* |
2988 |
* Pop the switch's statement info around discriminant code-gen. Note |
* Pop the switch's statement info around discriminant code-gen. Note |
2989 |
* how this leaves cg->treeContext.blockChain referencing the switch's |
* how this leaves cg->blockChain referencing the switch's |
2990 |
* block scope object, which is necessary for correct block parenting |
* block scope object, which is necessary for correct block parenting |
2991 |
* in the case where the discriminant contains a let expression. |
* in the case where the discriminant contains a let expression. |
2992 |
*/ |
*/ |
2993 |
cg->treeContext.topStmt = stmtInfo->down; |
cg->topStmt = stmtInfo->down; |
2994 |
cg->treeContext.topScopeStmt = stmtInfo->downScope; |
cg->topScopeStmt = stmtInfo->downScope; |
2995 |
} |
} |
2996 |
#ifdef __GNUC__ |
#ifdef __GNUC__ |
2997 |
else { |
else { |
3010 |
/* Switch bytecodes run from here till end of final case. */ |
/* Switch bytecodes run from here till end of final case. */ |
3011 |
top = CG_OFFSET(cg); |
top = CG_OFFSET(cg); |
3012 |
#if !JS_HAS_BLOCK_SCOPE |
#if !JS_HAS_BLOCK_SCOPE |
3013 |
js_PushStatement(&cg->treeContext, stmtInfo, STMT_SWITCH, top); |
js_PushStatement(cg, stmtInfo, STMT_SWITCH, top); |
3014 |
#else |
#else |
3015 |
if (pn2->pn_type == TOK_LC) { |
if (pn2->pn_type == TOK_LC) { |
3016 |
js_PushStatement(&cg->treeContext, stmtInfo, STMT_SWITCH, top); |
js_PushStatement(cg, stmtInfo, STMT_SWITCH, top); |
3017 |
} else { |
} else { |
3018 |
/* Re-push the switch's statement info record. */ |
/* Re-push the switch's statement info record. */ |
3019 |
cg->treeContext.topStmt = cg->treeContext.topScopeStmt = stmtInfo; |
cg->topStmt = cg->topScopeStmt = stmtInfo; |
3020 |
|
|
3021 |
/* Set the statement info record's idea of top. */ |
/* Set the statement info record's idea of top. */ |
3022 |
stmtInfo->update = top; |
stmtInfo->update = top; |
3023 |
|
|
3024 |
/* Advance pn2 to refer to the switch case list. */ |
/* Advance pn2 to refer to the switch case list. */ |
3025 |
pn2 = pn2->pn_expr; |
pn2 = pn2->expr(); |
3026 |
} |
} |
3027 |
#endif |
#endif |
3028 |
|
|
3057 |
continue; |
continue; |
3058 |
|
|
3059 |
pn4 = pn3->pn_left; |
pn4 = pn3->pn_left; |
3060 |
|
while (pn4->pn_type == TOK_RP) |
3061 |
|
pn4 = pn4->pn_kid; |
3062 |
switch (pn4->pn_type) { |
switch (pn4->pn_type) { |
3063 |
case TOK_NUMBER: |
case TOK_NUMBER: |
3064 |
d = pn4->pn_dval; |
d = pn4->pn_dval; |
3077 |
pn3->pn_val = ATOM_KEY(pn4->pn_atom); |
pn3->pn_val = ATOM_KEY(pn4->pn_atom); |
3078 |
break; |
break; |
3079 |
case TOK_NAME: |
case TOK_NAME: |
3080 |
if (!pn4->pn_expr) { |
if (!pn4->maybeExpr()) { |
3081 |
ok = LookupCompileTimeConstant(cx, cg, pn4->pn_atom, &v); |
ok = LookupCompileTimeConstant(cx, cg, pn4->pn_atom, &v); |
3082 |
if (!ok) |
if (!ok) |
3083 |
goto release; |
goto release; |
3361 |
(pn4 = pn3->pn_left) != NULL && |
(pn4 = pn3->pn_left) != NULL && |
3362 |
pn4->pn_type == TOK_NAME) { |
pn4->pn_type == TOK_NAME) { |
3363 |
/* Note a propagated constant with the const's name. */ |
/* Note a propagated constant with the const's name. */ |
3364 |
JS_ASSERT(!pn4->pn_expr); |
JS_ASSERT(!pn4->maybeExpr()); |
3365 |
ale = js_IndexAtom(cx, pn4->pn_atom, &cg->atomList); |
ale = cg->atomList.add(cg->compiler, pn4->pn_atom); |
3366 |
if (!ale) |
if (!ale) |
3367 |
goto bad; |
goto bad; |
3368 |
CG_NEXT(cg) = pc; |
CG_NEXT(cg) = pc; |
3378 |
pn4 = pn3->pn_left; |
pn4 = pn3->pn_left; |
3379 |
if (pn4 && pn4->pn_type == TOK_NAME) { |
if (pn4 && pn4->pn_type == TOK_NAME) { |
3380 |
/* Note a propagated constant with the const's name. */ |
/* Note a propagated constant with the const's name. */ |
3381 |
JS_ASSERT(!pn4->pn_expr); |
JS_ASSERT(!pn4->maybeExpr()); |
3382 |
ale = js_IndexAtom(cx, pn4->pn_atom, &cg->atomList); |
ale = cg->atomList.add(cg->compiler, pn4->pn_atom); |
3383 |
if (!ale) |
if (!ale) |
3384 |
goto bad; |
goto bad; |
3385 |
CG_NEXT(cg) = pc; |
CG_NEXT(cg) = pc; |
3460 |
continue; |
continue; |
3461 |
if (!js_AtomizePrimitiveValue(cx, pn3->pn_val, &atom)) |
if (!js_AtomizePrimitiveValue(cx, pn3->pn_val, &atom)) |
3462 |
goto bad; |
goto bad; |
3463 |
ale = js_IndexAtom(cx, atom, &cg->atomList); |
ale = cg->atomList.add(cg->compiler, atom); |
3464 |
if (!ale) |
if (!ale) |
3465 |
goto bad; |
goto bad; |
3466 |
SET_INDEX(pc, ALE_INDEX(ale)); |
SET_INDEX(pc, ALE_INDEX(ale)); |
3495 |
JSBool |
JSBool |
3496 |
js_EmitFunctionScript(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body) |
js_EmitFunctionScript(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body) |
3497 |
{ |
{ |
3498 |
if (cg->treeContext.flags & TCF_FUN_IS_GENERATOR) { |
if (cg->flags & TCF_FUN_IS_GENERATOR) { |
3499 |
/* JSOP_GENERATOR must be the first instruction. */ |
/* JSOP_GENERATOR must be the first instruction. */ |
3500 |
CG_SWITCH_TO_PROLOG(cg); |
CG_SWITCH_TO_PROLOG(cg); |
3501 |
JS_ASSERT(CG_NEXT(cg) == CG_BASE(cg)); |
JS_ASSERT(CG_NEXT(cg) == CG_BASE(cg)); |
3510 |
} |
} |
3511 |
|
|
3512 |
/* A macro for inlining at the top of js_EmitTree (whence it came). */ |
/* A macro for inlining at the top of js_EmitTree (whence it came). */ |
3513 |
#define UPDATE_LINE_NUMBER_NOTES(cx, cg, pn) \ |
#define UPDATE_LINE_NUMBER_NOTES(cx, cg, line) \ |
3514 |
JS_BEGIN_MACRO \ |
JS_BEGIN_MACRO \ |
3515 |
uintN line_ = (pn)->pn_pos.begin.lineno; \ |
uintN line_ = (line); \ |
3516 |
uintN delta_ = line_ - CG_CURRENT_LINE(cg); \ |
uintN delta_ = line_ - CG_CURRENT_LINE(cg); \ |
3517 |
if (delta_ != 0) { \ |
if (delta_ != 0) { \ |
3518 |
/* \ |
/* \ |
3541 |
|
|
3542 |
/* A function, so that we avoid macro-bloating all the other callsites. */ |
/* A function, so that we avoid macro-bloating all the other callsites. */ |
3543 |
static JSBool |
static JSBool |
3544 |
UpdateLineNumberNotes(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) |
UpdateLineNumberNotes(JSContext *cx, JSCodeGenerator *cg, uintN line) |
3545 |
{ |
{ |
3546 |
UPDATE_LINE_NUMBER_NOTES(cx, cg, pn); |
UPDATE_LINE_NUMBER_NOTES(cx, cg, line); |
3547 |
return JS_TRUE; |
return JS_TRUE; |
3548 |
} |
} |
3549 |
|
|
3554 |
jsatomid atomIndex; |
jsatomid atomIndex; |
3555 |
JSAtomListElement *ale; |
JSAtomListElement *ale; |
3556 |
|
|
3557 |
if (pn->pn_slot >= 0) { |
if (pn->pn_cookie != FREE_UPVAR_COOKIE) { |
3558 |
atomIndex = (jsatomid) pn->pn_slot; |
atomIndex = (jsatomid) UPVAR_FRAME_SLOT(pn->pn_cookie); |
3559 |
} else { |
} else { |
3560 |
ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList); |
ale = cg->atomList.add(cg->compiler, pn->pn_atom); |
3561 |
if (!ale) |
if (!ale) |
3562 |
return JS_FALSE; |
return JS_FALSE; |
3563 |
atomIndex = ALE_INDEX(ale); |
atomIndex = ALE_INDEX(ale); |
3564 |
} |
} |
3565 |
|
|
3566 |
if (JOF_OPTYPE(pn->pn_op) == JOF_ATOM && |
if (JOF_OPTYPE(pn->pn_op) == JOF_ATOM && |
3567 |
(!(cg->treeContext.flags & TCF_IN_FUNCTION) || |
(!(cg->flags & TCF_IN_FUNCTION) || (cg->flags & TCF_FUN_HEAVYWEIGHT))) { |
|
(cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT))) { |
|
|
/* Emit a prolog bytecode to predefine the variable. */ |
|
3568 |
CG_SWITCH_TO_PROLOG(cg); |
CG_SWITCH_TO_PROLOG(cg); |
3569 |
if (!UpdateLineNumberNotes(cx, cg, pn)) |
if (!UpdateLineNumberNotes(cx, cg, pn->pn_pos.begin.lineno)) |
3570 |
return JS_FALSE; |
return JS_FALSE; |
3571 |
EMIT_INDEX_OP(prologOp, atomIndex); |
EMIT_INDEX_OP(prologOp, atomIndex); |
3572 |
CG_SWITCH_TO_MAIN(cg); |
CG_SWITCH_TO_MAIN(cg); |
3591 |
if (!BindNameToSlot(cx, cg, pn)) |
if (!BindNameToSlot(cx, cg, pn)) |
3592 |
return JS_FALSE; |
return JS_FALSE; |
3593 |
|
|
3594 |
JS_ASSERT(pn->pn_op != JSOP_ARGUMENTS); |
JS_ASSERT(PN_OP(pn) != JSOP_ARGUMENTS && PN_OP(pn) != JSOP_CALLEE); |
3595 |
return MaybeEmitVarDecl(cx, cg, prologOp, pn, NULL); |
return MaybeEmitVarDecl(cx, cg, prologOp, pn, NULL); |
3596 |
} |
} |
3597 |
|
|
3650 |
if (js_Emit1(cx, cg, JSOP_POP) < 0) |
if (js_Emit1(cx, cg, JSOP_POP) < 0) |
3651 |
return JS_FALSE; |
return JS_FALSE; |
3652 |
} else { |
} else { |
3653 |
if (pn->pn_type == TOK_NAME && !BindNameToSlot(cx, cg, pn)) |
if (pn->pn_type == TOK_NAME) { |
3654 |
return JS_FALSE; |
if (!BindNameToSlot(cx, cg, pn)) |
3655 |
|
return JS_FALSE; |
3656 |
|
if (pn->isConst() && !pn->isInitialized()) |
3657 |
|
return js_Emit1(cx, cg, JSOP_POP) >= 0; |
3658 |
|
} |
3659 |
|
|
3660 |
switch (pn->pn_op) { |
switch (pn->pn_op) { |
3661 |
case JSOP_SETNAME: |
case JSOP_SETNAME: |
3674 |
break; |
break; |
3675 |
|
|
3676 |
case JSOP_SETLOCAL: |
case JSOP_SETLOCAL: |
3677 |
slot = (jsuint) pn->pn_slot; |
slot = (jsuint) pn->pn_cookie; |
3678 |
EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, slot); |
EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, slot); |
3679 |
break; |
break; |
3680 |
|
|
3681 |
case JSOP_SETARG: |
case JSOP_SETARG: |
3682 |
case JSOP_SETGVAR: |
case JSOP_SETGVAR: |
3683 |
slot = (jsuint) pn->pn_slot; |
slot = (jsuint) pn->pn_cookie; |
3684 |
EMIT_UINT16_IMM_OP(PN_OP(pn), slot); |
EMIT_UINT16_IMM_OP(PN_OP(pn), slot); |
3685 |
if (js_Emit1(cx, cg, JSOP_POP) < 0) |
if (js_Emit1(cx, cg, JSOP_POP) < 0) |
3686 |
return JS_FALSE; |
return JS_FALSE; |
3740 |
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { |
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { |
3741 |
/* |
/* |
3742 |
* Duplicate the value being destructured to use as a reference base. |
* Duplicate the value being destructured to use as a reference base. |
3743 |
|
* If dup is not the first one, annotate it for the decompiler. |
3744 |
*/ |
*/ |
3745 |
|
if (pn2 != pn->pn_head && js_NewSrcNote(cx, cg, SRC_CONTINUE) < 0) |
3746 |
|
return JS_FALSE; |
3747 |
if (js_Emit1(cx, cg, JSOP_DUP) < 0) |
if (js_Emit1(cx, cg, JSOP_DUP) < 0) |
3748 |
return JS_FALSE; |
return JS_FALSE; |
3749 |
|
|
3827 |
} |
} |
3828 |
|
|
3829 |
static JSBool |
static JSBool |
3830 |
EmitDestructuringOps(JSContext *cx, JSCodeGenerator *cg, JSOp declOp, |
EmitDestructuringOps(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, |
3831 |
JSParseNode *pn) |
JSParseNode *pn) |
3832 |
{ |
{ |
3833 |
/* |
/* |
3836 |
* If the destructuring initialiser is empty, our helper will emit a |
* If the destructuring initialiser is empty, our helper will emit a |
3837 |
* JSOP_DUP followed by a JSOP_POP for the decompiler. |
* JSOP_DUP followed by a JSOP_POP for the decompiler. |
3838 |
*/ |
*/ |
3839 |
if (js_NewSrcNote2(cx, cg, SRC_DESTRUCT, OpToDeclType(declOp)) < 0) |
if (js_NewSrcNote2(cx, cg, SRC_DESTRUCT, OpToDeclType(prologOp)) < 0) |
3840 |
return JS_FALSE; |
return JS_FALSE; |
3841 |
|
|
3842 |
/* |
/* |
3847 |
} |
} |
3848 |
|
|
3849 |
static JSBool |
static JSBool |
3850 |
EmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSOp declOp, |
EmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, |
3851 |
JSParseNode *lhs, JSParseNode *rhs) |
JSParseNode *lhs, JSParseNode *rhs) |
3852 |
{ |
{ |
3853 |
jsuint depth, limit, i, nslots; |
jsuint depth, limit, i, nslots; |
3861 |
return JS_FALSE; |
return JS_FALSE; |
3862 |
} |
} |
3863 |
|
|
3864 |
if (pn->pn_type == TOK_COMMA) { |
/* MaybeEmitGroupAssignment won't call us if rhs is holey. */ |
3865 |
if (js_Emit1(cx, cg, JSOP_PUSH) < 0) |
JS_ASSERT(pn->pn_type != TOK_COMMA); |
3866 |
return JS_FALSE; |
if (!js_EmitTree(cx, cg, pn)) |
3867 |
} else { |
return JS_FALSE; |
|
JS_ASSERT(pn->pn_type != TOK_DEFSHARP); |
|
|
if (!js_EmitTree(cx, cg, pn)) |
|
|
return JS_FALSE; |
|
|
} |
|
3868 |
++limit; |
++limit; |
3869 |
} |
} |
3870 |
|
|
3871 |
if (js_NewSrcNote2(cx, cg, SRC_GROUPASSIGN, OpToDeclType(declOp)) < 0) |
if (js_NewSrcNote2(cx, cg, SRC_GROUPASSIGN, OpToDeclType(prologOp)) < 0) |
3872 |
return JS_FALSE; |
return JS_FALSE; |
3873 |
|
|
3874 |
i = depth; |
i = depth; |
3875 |
for (pn = lhs->pn_head; pn; pn = pn->pn_next, ++i) { |
for (pn = lhs->pn_head; pn; pn = pn->pn_next, ++i) { |
3876 |
if (i < limit) { |
/* MaybeEmitGroupAssignment requires lhs->pn_count <= rhs->pn_count. */ |
3877 |
jsint slot; |
JS_ASSERT(i < limit); |
3878 |
|
jsint slot = AdjustBlockSlot(cx, cg, i); |
3879 |
|
if (slot < 0) |
3880 |
|
return JS_FALSE; |
3881 |
|
EMIT_UINT16_IMM_OP(JSOP_GETLOCAL, slot); |
3882 |
|
|
|
slot = AdjustBlockSlot(cx, cg, i); |
|
|
if (slot < 0) |
|
|
return JS_FALSE; |
|
|
EMIT_UINT16_IMM_OP(JSOP_GETLOCAL, slot); |
|
|
} else { |
|
|
if (js_Emit1(cx, cg, JSOP_PUSH) < 0) |
|
|
return JS_FALSE; |
|
|
} |
|
3883 |
if (pn->pn_type == TOK_COMMA && pn->pn_arity == PN_NULLARY) { |
if (pn->pn_type == TOK_COMMA && pn->pn_arity == PN_NULLARY) { |
3884 |
if (js_Emit1(cx, cg, JSOP_POP) < 0) |
if (js_Emit1(cx, cg, JSOP_POP) < 0) |
3885 |
return JS_FALSE; |
return JS_FALSE; |
3901 |
* we set *pop to JSOP_NOP so callers can veto emitting pn followed by a pop. |
* we set *pop to JSOP_NOP so callers can veto emitting pn followed by a pop. |
3902 |
*/ |
*/ |
3903 |
static JSBool |
static JSBool |
3904 |
MaybeEmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSOp declOp, |
MaybeEmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, |
3905 |
JSParseNode *pn, JSOp *pop) |
JSParseNode *pn, JSOp *pop) |
3906 |
{ |
{ |
3907 |
JSParseNode *lhs, *rhs; |
JSParseNode *lhs, *rhs; |
3911 |
lhs = pn->pn_left; |
lhs = pn->pn_left; |
3912 |
rhs = pn->pn_right; |
rhs = pn->pn_right; |
3913 |
if (lhs->pn_type == TOK_RB && rhs->pn_type == TOK_RB && |
if (lhs->pn_type == TOK_RB && rhs->pn_type == TOK_RB && |
3914 |
lhs->pn_count <= rhs->pn_count && |
!(rhs->pn_xflags & PNX_HOLEY) && |
3915 |
(rhs->pn_count == 0 || |
lhs->pn_count <= rhs->pn_count) { |
3916 |
rhs->pn_head->pn_type != TOK_DEFSHARP)) { |
if (!EmitGroupAssignment(cx, cg, prologOp, lhs, rhs)) |
|
if (!EmitGroupAssignment(cx, cg, declOp, lhs, rhs)) |
|
3917 |
return JS_FALSE; |
return JS_FALSE; |
3918 |
*pop = JSOP_NOP; |
*pop = JSOP_NOP; |
3919 |
} |
} |
3926 |
EmitVariables(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, |
EmitVariables(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, |
3927 |
JSBool inLetHead, ptrdiff_t *headNoteIndex) |
JSBool inLetHead, ptrdiff_t *headNoteIndex) |
3928 |
{ |
{ |
3929 |
JSTreeContext *tc; |
bool let, forInVar, first; |
|
JSBool let, forInVar; |
|
3930 |
#if JS_HAS_BLOCK_SCOPE |
#if JS_HAS_BLOCK_SCOPE |
3931 |
JSBool forInLet, popScope; |
bool forInLet, popScope; |
3932 |
JSStmtInfo *stmt, *scopeStmt; |
JSStmtInfo *stmt, *scopeStmt; |
3933 |
#endif |
#endif |
3934 |
ptrdiff_t off, noteIndex, tmp; |
ptrdiff_t off, noteIndex, tmp; |
3935 |
JSParseNode *pn2, *pn3; |
JSParseNode *pn2, *pn3, *next; |
3936 |
JSOp op; |
JSOp op; |
3937 |
jsatomid atomIndex; |
jsatomid atomIndex; |
3938 |
uintN oldflags; |
uintN oldflags; |
3952 |
* to the start of the block, a 'for (let x = i...) ...' loop evaluates i |
* to the start of the block, a 'for (let x = i...) ...' loop evaluates i |
3953 |
* in the containing scope, and puts x in the loop body's scope. |
* in the containing scope, and puts x in the loop body's scope. |
3954 |
*/ |
*/ |
|
tc = &cg->treeContext; |
|
3955 |
let = (pn->pn_op == JSOP_NOP); |
let = (pn->pn_op == JSOP_NOP); |
3956 |
forInVar = (pn->pn_extra & PNX_FORINVAR) != 0; |
forInVar = (pn->pn_xflags & PNX_FORINVAR) != 0; |
3957 |
#if JS_HAS_BLOCK_SCOPE |
#if JS_HAS_BLOCK_SCOPE |
3958 |
forInLet = let && forInVar; |
forInLet = let && forInVar; |
3959 |
popScope = (inLetHead || (let && (tc->flags & TCF_IN_FOR_INIT))); |
popScope = (inLetHead || (let && (cg->flags & TCF_IN_FOR_INIT))); |
3960 |
|
if (popScope) { |
3961 |
|
stmt = cg->topStmt; |
3962 |
|
scopeStmt = cg->topScopeStmt; |
3963 |
|
} |
3964 |
|
# ifdef __GNUC__ |
3965 |
|
else stmt = scopeStmt = NULL; /* quell GCC overwarning */ |
3966 |
|
# endif |
3967 |
JS_ASSERT(!popScope || let); |
JS_ASSERT(!popScope || let); |
3968 |
#endif |
#endif |
3969 |
|
|
3970 |
off = noteIndex = -1; |
off = noteIndex = -1; |
3971 |
for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) { |
for (pn2 = pn->pn_head; ; pn2 = next) { |
3972 |
#if JS_HAS_DESTRUCTURING |
first = pn2 == pn->pn_head; |
3973 |
|
next = pn2->pn_next; |
3974 |
|
|
3975 |
if (pn2->pn_type != TOK_NAME) { |
if (pn2->pn_type != TOK_NAME) { |
3976 |
|
#if JS_HAS_DESTRUCTURING |
3977 |
if (pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC) { |
if (pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC) { |
3978 |
/* |
/* |
3979 |
* Emit variable binding ops, but not destructuring ops. |
* Emit variable binding ops, but not destructuring ops. |
3989 |
return JS_FALSE; |
return JS_FALSE; |
3990 |
break; |
break; |
3991 |
} |
} |
3992 |
|
#endif |
3993 |
|
|
3994 |
/* |
/* |
3995 |
* A destructuring initialiser assignment preceded by var will |
* A destructuring initialiser assignment preceded by var will |
3999 |
*/ |
*/ |
4000 |
JS_ASSERT(pn2->pn_type == TOK_ASSIGN); |
JS_ASSERT(pn2->pn_type == TOK_ASSIGN); |
4001 |
JS_ASSERT(!forInVar); |
JS_ASSERT(!forInVar); |
4002 |
|
|
4003 |
|
/* |
4004 |
|
* To allow the front end to rewrite var f = x; as f = x; when a |
4005 |
|
* function f(){} precedes the var, detect simple name assignment |
4006 |
|
* here and initialize the name. |
4007 |
|
*/ |
4008 |
|
#if !JS_HAS_DESTRUCTURING |
4009 |
|
JS_ASSERT(pn2->pn_left->pn_type == TOK_NAME); |
4010 |
|
#else |
4011 |
|
if (pn2->pn_left->pn_type == TOK_NAME) |
4012 |
|
#endif |
4013 |
|
{ |
4014 |
|
pn3 = pn2->pn_right; |
4015 |
|
pn2 = pn2->pn_left; |
4016 |
|
goto do_name; |
4017 |
|
} |
4018 |
|
|
4019 |
|
#if JS_HAS_DESTRUCTURING |
4020 |
if (pn->pn_count == 1) { |
if (pn->pn_count == 1) { |
4021 |
/* |
/* |
4022 |
* If this is the only destructuring assignment in the list, |
* If this is the only destructuring assignment in the list, |
4032 |
return JS_FALSE; |
return JS_FALSE; |
4033 |
} |
} |
4034 |
if (op == JSOP_NOP) { |
if (op == JSOP_NOP) { |
4035 |
pn->pn_extra = (pn->pn_extra & ~PNX_POPVAR) | PNX_GROUPINIT; |
pn->pn_xflags = (pn->pn_xflags & ~PNX_POPVAR) | PNX_GROUPINIT; |
4036 |
break; |
break; |
4037 |
} |
} |
4038 |
} |
} |
4055 |
return JS_FALSE; |
return JS_FALSE; |
4056 |
} |
} |
4057 |
goto emit_note_pop; |
goto emit_note_pop; |
|
} |
|
|
#else |
|
|
JS_ASSERT(pn2->pn_type == TOK_NAME); |
|
4058 |
#endif |
#endif |
4059 |
|
} |
4060 |
|
|
4061 |
|
/* |
4062 |
|
* Load initializer early to share code above that jumps to do_name. |
4063 |
|
* NB: if this var redeclares an existing binding, then pn2 is linked |
4064 |
|
* on its definition's use-chain and pn_expr has been overlayed with |
4065 |
|
* pn_lexdef. |
4066 |
|
*/ |
4067 |
|
pn3 = pn2->maybeExpr(); |
4068 |
|
|
4069 |
|
do_name: |
4070 |
if (!BindNameToSlot(cx, cg, pn2)) |
if (!BindNameToSlot(cx, cg, pn2)) |
4071 |
return JS_FALSE; |
return JS_FALSE; |
|
JS_ASSERT(pn2->pn_slot >= 0 || !let); |
|
4072 |
|
|
4073 |
op = PN_OP(pn2); |
op = PN_OP(pn2); |
4074 |
if (op == JSOP_ARGUMENTS) { |
if (op == JSOP_ARGUMENTS) { |
4075 |
/* JSOP_ARGUMENTS => no initializer */ |
/* JSOP_ARGUMENTS => no initializer */ |
4076 |
JS_ASSERT(!pn2->pn_expr && !let); |
JS_ASSERT(!pn3 && !let); |
4077 |
pn3 = NULL; |
pn3 = NULL; |
4078 |
#ifdef __GNUC__ |
#ifdef __GNUC__ |
4079 |
atomIndex = 0; /* quell GCC overwarning */ |
atomIndex = 0; /* quell GCC overwarning */ |
4080 |
#endif |
#endif |
4081 |
} else { |
} else { |
4082 |
|
JS_ASSERT(op != JSOP_CALLEE); |
4083 |
|
JS_ASSERT(pn2->pn_cookie != FREE_UPVAR_COOKIE || !let); |
4084 |
if (!MaybeEmitVarDecl(cx, cg, PN_OP(pn), pn2, &atomIndex)) |
if (!MaybeEmitVarDecl(cx, cg, PN_OP(pn), pn2, &atomIndex)) |
4085 |
return JS_FALSE; |
return JS_FALSE; |
4086 |
|
|
|
pn3 = pn2->pn_expr; |
|
4087 |
if (pn3) { |
if (pn3) { |
4088 |
JS_ASSERT(!forInVar); |
JS_ASSERT(!forInVar); |
4089 |
if (op == JSOP_SETNAME) { |
if (op == JSOP_SETNAME) { |
4091 |
EMIT_INDEX_OP(JSOP_BINDNAME, atomIndex); |
EMIT_INDEX_OP(JSOP_BINDNAME, atomIndex); |
4092 |
} |
} |
4093 |
if (pn->pn_op == JSOP_DEFCONST && |
if (pn->pn_op == JSOP_DEFCONST && |
4094 |
!js_DefineCompileTimeConstant(cx, cg, pn2->pn_atom, |
!js_DefineCompileTimeConstant(cx, cg, pn2->pn_atom, pn3)) { |
|
pn3)) { |
|
4095 |
return JS_FALSE; |
return JS_FALSE; |
4096 |
} |
} |
4097 |
|
|
4098 |
#if JS_HAS_BLOCK_SCOPE |
#if JS_HAS_BLOCK_SCOPE |
4099 |
/* Evaluate expr in the outer lexical scope if requested. */ |
/* Evaluate expr in the outer lexical scope if requested. */ |
4100 |
if (popScope) { |
if (popScope) { |
4101 |
stmt = tc->topStmt; |
cg->topStmt = stmt->down; |
4102 |
scopeStmt = tc->topScopeStmt; |
cg->topScopeStmt = scopeStmt->downScope; |
|
|
|
|
tc->topStmt = stmt->down; |
|
|
tc->topScopeStmt = scopeStmt->downScope; |
|
4103 |
} |
} |
|
# ifdef __GNUC__ |
|
|
else stmt = scopeStmt = NULL; /* quell GCC overwarning */ |
|
|
# endif |
|
4104 |
#endif |
#endif |
4105 |
|
|
4106 |
oldflags = cg->treeContext.flags; |
oldflags = cg->flags; |
4107 |
cg->treeContext.flags &= ~TCF_IN_FOR_INIT; |
cg->flags &= ~TCF_IN_FOR_INIT; |
4108 |
if (!js_EmitTree(cx, cg, pn3)) |
if (!js_EmitTree(cx, cg, pn3)) |
4109 |
return JS_FALSE; |
return JS_FALSE; |
4110 |
cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT; |
cg->flags |= oldflags & TCF_IN_FOR_INIT; |
4111 |
|
|
4112 |
#if JS_HAS_BLOCK_SCOPE |
#if JS_HAS_BLOCK_SCOPE |
4113 |
if (popScope) { |
if (popScope) { |
4114 |
tc->topStmt = stmt; |
cg->topStmt = stmt; |
4115 |
tc->topScopeStmt = scopeStmt; |
cg->topScopeStmt = scopeStmt; |
4116 |
} |
} |
4117 |
#endif |
#endif |
4118 |
} |
} |
4126 |
* emit op or any source note. Our caller, the TOK_FOR/TOK_IN case in |
* emit op or any source note. Our caller, the TOK_FOR/TOK_IN case in |
4127 |
* js_EmitTree, will annotate appropriately. |
* js_EmitTree, will annotate appropriately. |
4128 |
*/ |
*/ |
4129 |
JS_ASSERT(pn3 == pn2->pn_expr); |
JS_ASSERT_IF(pn2->pn_defn, pn3 == pn2->pn_expr); |
4130 |
if (forInVar) { |
if (forInVar) { |
4131 |
JS_ASSERT(pn->pn_count == 1); |
JS_ASSERT(pn->pn_count == 1); |
4132 |
JS_ASSERT(!pn3); |
JS_ASSERT(!pn3); |
4133 |
break; |
break; |
4134 |
} |
} |
4135 |
|
|
4136 |
if (pn2 == pn->pn_head && |
if (first && |
4137 |
!inLetHead && |
!inLetHead && |
4138 |
js_NewSrcNote2(cx, cg, SRC_DECL, |
js_NewSrcNote2(cx, cg, SRC_DECL, |
4139 |
(pn->pn_op == JSOP_DEFCONST) |
(pn->pn_op == JSOP_DEFCONST) |
4146 |
if (op == JSOP_ARGUMENTS) { |
if (op == JSOP_ARGUMENTS) { |
4147 |
if (js_Emit1(cx, cg, op) < 0) |
if (js_Emit1(cx, cg, op) < 0) |
4148 |
return JS_FALSE; |
return JS_FALSE; |
4149 |
} else if (pn2->pn_slot >= 0) { |
} else if (pn2->pn_cookie != FREE_UPVAR_COOKIE) { |
4150 |
EMIT_UINT16_IMM_OP(op, atomIndex); |
EMIT_UINT16_IMM_OP(op, atomIndex); |
4151 |
} else { |
} else { |
4152 |
EMIT_INDEX_OP(op, atomIndex); |
EMIT_INDEX_OP(op, atomIndex); |
4160 |
if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off)) |
if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off)) |
4161 |
return JS_FALSE; |
return JS_FALSE; |
4162 |
} |
} |
4163 |
if (!pn2->pn_next) |
if (!next) |
4164 |
break; |
break; |
4165 |
off = tmp; |
off = tmp; |
4166 |
noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0); |
noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0); |
4173 |
*headNoteIndex = js_NewSrcNote(cx, cg, SRC_DECL); |
*headNoteIndex = js_NewSrcNote(cx, cg, SRC_DECL); |
4174 |
if (*headNoteIndex < 0) |
if (*headNoteIndex < 0) |
4175 |
return JS_FALSE; |
return JS_FALSE; |
4176 |
if (!(pn->pn_extra & PNX_POPVAR)) |
if (!(pn->pn_xflags & PNX_POPVAR)) |
4177 |
return js_Emit1(cx, cg, JSOP_NOP) >= 0; |
return js_Emit1(cx, cg, JSOP_NOP) >= 0; |
4178 |
} |
} |
4179 |
|
|
4180 |
return !(pn->pn_extra & PNX_POPVAR) || js_Emit1(cx, cg, JSOP_POP) >= 0; |
return !(pn->pn_xflags & PNX_POPVAR) || js_Emit1(cx, cg, JSOP_POP) >= 0; |
4181 |
} |
} |
4182 |
|
|
4183 |
#if defined DEBUG_brendanXXX || defined DEBUG_mrbkap |
#if defined DEBUG_brendan || defined DEBUG_mrbkap |
4184 |
static JSBool |
static JSBool |
4185 |
GettableNoteForNextOp(JSCodeGenerator *cg) |
GettableNoteForNextOp(JSCodeGenerator *cg) |
4186 |
{ |
{ |
4228 |
JSOp op; |
JSOp op; |
4229 |
JSTokenType type; |
JSTokenType type; |
4230 |
uint32 argc; |
uint32 argc; |
4231 |
|
#if JS_HAS_SHARP_VARS |
4232 |
|
jsint sharpnum; |
4233 |
|
#endif |
4234 |
|
|
4235 |
JS_CHECK_RECURSION(cx, return JS_FALSE); |
JS_CHECK_RECURSION(cx, return JS_FALSE); |
4236 |
|
|
4239 |
pn->pn_offset = top = CG_OFFSET(cg); |
pn->pn_offset = top = CG_OFFSET(cg); |
4240 |
|
|
4241 |
/* Emit notes to tell the current bytecode's source line number. */ |
/* Emit notes to tell the current bytecode's source line number. */ |
4242 |
UPDATE_LINE_NUMBER_NOTES(cx, cg, pn); |
UPDATE_LINE_NUMBER_NOTES(cx, cg, pn->pn_pos.begin.lineno); |
4243 |
|
|
4244 |
switch (pn->pn_type) { |
switch (pn->pn_type) { |
4245 |
case TOK_FUNCTION: |
case TOK_FUNCTION: |
4246 |
{ |
{ |
4247 |
JSFunction *fun; |
JSFunction *fun; |
|
void *cg2mark; |
|
|
JSCodeGenerator *cg2; |
|
4248 |
uintN slot; |
uintN slot; |
4249 |
|
|
4250 |
#if JS_HAS_XML_SUPPORT |
#if JS_HAS_XML_SUPPORT |
4255 |
} |
} |
4256 |
#endif |
#endif |
4257 |
|
|
4258 |
fun = (JSFunction *) pn->pn_funpob->object; |
fun = (JSFunction *) pn->pn_funbox->object; |
4259 |
|
JS_ASSERT(FUN_INTERPRETED(fun)); |
4260 |
if (fun->u.i.script) { |
if (fun->u.i.script) { |
4261 |
/* |
/* |
4262 |
* This second pass is needed to emit JSOP_NOP with a source note |
* This second pass is needed to emit JSOP_NOP with a source note |
4264 |
* comments in the TOK_LC case. |
* comments in the TOK_LC case. |
4265 |
*/ |
*/ |
4266 |
JS_ASSERT(pn->pn_op == JSOP_NOP); |
JS_ASSERT(pn->pn_op == JSOP_NOP); |
4267 |
JS_ASSERT(cg->treeContext.flags & TCF_IN_FUNCTION); |
JS_ASSERT(cg->flags & TCF_IN_FUNCTION); |
|
JS_ASSERT(pn->pn_index != (uint32) -1); |
|
4268 |
if (!EmitFunctionDefNop(cx, cg, pn->pn_index)) |
if (!EmitFunctionDefNop(cx, cg, pn->pn_index)) |
4269 |
return JS_FALSE; |
return JS_FALSE; |
4270 |
break; |
break; |
4271 |
} |
} |
4272 |
|
|
4273 |
/* |
JS_ASSERT_IF(cx->options & JSOPTION_ANONFUNFIX, |
4274 |
* Limit static nesting depth to fit in 16 bits. See cg2->staticDepth |
pn->pn_defn || |
4275 |
* assignment below. |
(!pn->pn_used && !pn->isTopLevel()) || |
4276 |
*/ |
(fun->flags & JSFUN_LAMBDA)); |
4277 |
if (cg->staticDepth == JS_BITMASK(16)) { |
|
4278 |
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TOO_DEEP, |
JS_ASSERT_IF(pn->pn_funbox->tcflags & TCF_FUN_HEAVYWEIGHT, |
4279 |
js_function_str); |
FUN_KIND(fun) == JSFUN_INTERPRETED); |
|
return JS_FALSE; |
|
|
} |
|
4280 |
|
|
4281 |
/* Generate code for the function's body. */ |
/* Generate code for the function's body. */ |
4282 |
cg2mark = JS_ARENA_MARK(cg->codePool); |
void *cg2mark = JS_ARENA_MARK(cg->codePool); |
4283 |
JS_ARENA_ALLOCATE_TYPE(cg2, JSCodeGenerator, cg->codePool); |
void *cg2space; |
4284 |
if (!cg2) { |
JS_ARENA_ALLOCATE_TYPE(cg2space, JSCodeGenerator, cg->codePool); |
4285 |
|
if (!cg2space) { |
4286 |
js_ReportOutOfScriptQuota(cx); |
js_ReportOutOfScriptQuota(cx); |
4287 |
return JS_FALSE; |
return JS_FALSE; |
4288 |
} |
} |
4289 |
js_InitCodeGenerator(cx, cg2, cg->treeContext.parseContext, |
JSCodeGenerator *cg2 = |
4290 |
cg->codePool, cg->notePool, |
new (cg2space) JSCodeGenerator(cg->compiler, |
4291 |
pn->pn_pos.begin.lineno); |
cg->codePool, cg->notePool, |
4292 |
cg2->treeContext.flags = (uint16) (pn->pn_flags | TCF_IN_FUNCTION); |
pn->pn_pos.begin.lineno); |
4293 |
cg2->treeContext.u.fun = fun; |
cg2->flags = (uint16) (pn->pn_funbox->tcflags | TCF_IN_FUNCTION); |
4294 |
cg2->staticDepth = cg->staticDepth + 1; |
cg2->fun = fun; |
4295 |
|
cg2->funbox = pn->pn_funbox; |
4296 |
cg2->parent = cg; |
cg2->parent = cg; |
4297 |
|
|
4298 |
/* We metered the max scope depth when parsed the function. */ |
/* |
4299 |
JS_SCOPE_DEPTH_METERING(cg2->treeContext.maxScopeDepth = (uintN) -1); |
* jsparse.cpp:SetStaticLevel limited static nesting depth to fit in 16 |
4300 |
if (!js_EmitFunctionScript(cx, cg2, pn->pn_body)) { |
* bits and to reserve the all-ones value, thereby reserving the magic |
4301 |
|
* FREE_UPVAR_COOKIE value. Note the cg2->staticLevel assignment below. |
4302 |
|
*/ |
4303 |
|
JS_ASSERT(cg->staticLevel < JS_BITMASK(16) - 1); |
4304 |
|
cg2->staticLevel = cg->staticLevel + 1; |
4305 |
|
|
4306 |
|
/* We measured the max scope depth when we parsed the function. */ |
4307 |
|
JS_SCOPE_DEPTH_METERING(cg2->maxScopeDepth = (uintN) -1); |
4308 |
|
if (!js_EmitFunctionScript(cx, cg2, pn->pn_body)) |
4309 |
pn = NULL; |
pn = NULL; |
|
} else { |
|
|
/* |
|
|
* We need an activation object if an inner peeks out, or if such |
|
|
* inner-peeking caused one of our inners to become heavyweight. |
|
|
*/ |
|
|
if (cg2->treeContext.flags & |
|
|
(TCF_FUN_USES_NONLOCALS | TCF_FUN_HEAVYWEIGHT)) { |
|
|
cg->treeContext.flags |= TCF_FUN_HEAVYWEIGHT; |
|
|
} |
|
|
} |
|
4310 |
|
|
4311 |
js_FinishCodeGenerator(cx, cg2); |
cg2->~JSCodeGenerator(); |
4312 |
JS_ARENA_RELEASE(cg->codePool, cg2mark); |
JS_ARENA_RELEASE(cg->codePool, cg2mark); |
4313 |
cg2 = NULL; |
cg2 = NULL; |
4314 |
if (!pn) |
if (!pn) |
4315 |
return JS_FALSE; |
return JS_FALSE; |
4316 |
|
|
4317 |
/* Make the function object a literal in the outer script's pool. */ |
/* Make the function object a literal in the outer script's pool. */ |
4318 |
index = IndexParsedObject(pn->pn_funpob, &cg->objectList); |
index = cg->objectList.index(pn->pn_funbox); |
4319 |
|
|
4320 |
/* Emit a bytecode pointing to the closure object in its immediate. */ |
/* Emit a bytecode pointing to the closure object in its immediate. */ |
4321 |
if (pn->pn_op != JSOP_NOP) { |
op = PN_OP(pn); |
4322 |
if ((pn->pn_flags & TCF_GENEXP_LAMBDA) && |
if (op != JSOP_NOP) { |
4323 |
|
if ((pn->pn_funbox->tcflags & TCF_GENEXP_LAMBDA) && |
4324 |
js_NewSrcNote(cx, cg, SRC_GENEXP) < 0) { |
js_NewSrcNote(cx, cg, SRC_GENEXP) < 0) { |
4325 |
return JS_FALSE; |
return JS_FALSE; |
4326 |
} |
} |
4327 |
EMIT_INDEX_OP(PN_OP(pn), index); |
EMIT_INDEX_OP(op, index); |
4328 |
break; |
break; |
4329 |
} |
} |
4330 |
|
|
4337 |
* invocation of the emitter and calls to js_EmitTree for function |
* invocation of the emitter and calls to js_EmitTree for function |
4338 |
* definitions can be scheduled before generating the rest of code. |
* definitions can be scheduled before generating the rest of code. |
4339 |
*/ |
*/ |
4340 |
if (!(cg->treeContext.flags & TCF_IN_FUNCTION)) { |
if (!(cg->flags & TCF_IN_FUNCTION)) { |
4341 |
JS_ASSERT(!cg->treeContext.topStmt); |
JS_ASSERT(!cg->topStmt); |
4342 |
CG_SWITCH_TO_PROLOG(cg); |
CG_SWITCH_TO_PROLOG(cg); |
4343 |
EMIT_INDEX_OP(JSOP_DEFFUN, index); |
op = FUN_FLAT_CLOSURE(fun) ? JSOP_DEFFUN_FC : JSOP_DEFFUN; |
4344 |
|
EMIT_INDEX_OP(op, index); |
4345 |
CG_SWITCH_TO_MAIN(cg); |
CG_SWITCH_TO_MAIN(cg); |
4346 |
|
|
4347 |
/* Emit NOP for the decompiler. */ |
/* Emit NOP for the decompiler. */ |
4351 |
#ifdef DEBUG |
#ifdef DEBUG |
4352 |
JSLocalKind localKind = |
JSLocalKind localKind = |
4353 |
#endif |
#endif |
4354 |
js_LookupLocal(cx, cg->treeContext.u.fun, fun->atom, &slot); |
js_LookupLocal(cx, cg->fun, fun->atom, &slot); |
4355 |
JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST); |
JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST); |
4356 |
JS_ASSERT(pn->pn_index == (uint32) -1); |
JS_ASSERT(index < JS_BIT(20)); |
4357 |
pn->pn_index = index; |
pn->pn_index = index; |
4358 |
if (!EmitSlotIndexOp(cx, JSOP_DEFLOCALFUN, slot, index, cg)) |
op = FUN_FLAT_CLOSURE(fun) ? JSOP_DEFLOCALFUN_FC : JSOP_DEFLOCALFUN; |
4359 |
|
if (!EmitSlotIndexOp(cx, op, slot, index, cg)) |
4360 |
return JS_FALSE; |
return JS_FALSE; |
4361 |
} |
} |
4362 |
break; |
break; |
4363 |
} |
} |
4364 |
|
|
4365 |
|
case TOK_ARGSBODY: |
4366 |
|
ok = js_EmitTree(cx, cg, pn->last()); |
4367 |
|
break; |
4368 |
|
|
4369 |
|
case TOK_UPVARS: |
4370 |
|
JS_ASSERT(cg->lexdeps.count == 0); |
4371 |
|
JS_ASSERT(pn->pn_names.count != 0); |
4372 |
|
cg->lexdeps = pn->pn_names; |
4373 |
|
ok = js_EmitTree(cx, cg, pn->pn_tree); |
4374 |
|
break; |
4375 |
|
|
4376 |
case TOK_IF: |
case TOK_IF: |
4377 |
/* Initialize so we can detect else-if chains and avoid recursion. */ |
/* Initialize so we can detect else-if chains and avoid recursion. */ |
4385 |
return JS_FALSE; |
return JS_FALSE; |
4386 |
top = CG_OFFSET(cg); |
top = CG_OFFSET(cg); |
4387 |
if (stmtInfo.type == STMT_IF) { |
if (stmtInfo.type == STMT_IF) { |
4388 |
js_PushStatement(&cg->treeContext, &stmtInfo, STMT_IF, top); |
js_PushStatement(cg, &stmtInfo, STMT_IF, top); |
4389 |
} else { |
} else { |
4390 |
/* |
/* |
4391 |
* We came here from the goto further below that detects else-if |
* We came here from the goto further below that detects else-if |
4483 |
* that bracketed the body. But given the SRC_WHILE note, it is easy |
* that bracketed the body. But given the SRC_WHILE note, it is easy |
4484 |
* to support the more efficient scheme. |
* to support the more efficient scheme. |
4485 |
*/ |
*/ |
4486 |
js_PushStatement(&cg->treeContext, &stmtInfo, STMT_WHILE_LOOP, top); |
js_PushStatement(cg, &stmtInfo, STMT_WHILE_LOOP, top); |
4487 |
noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE); |
noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE); |
4488 |
if (noteIndex < 0) |
if (noteIndex < 0) |
4489 |
return JS_FALSE; |
return JS_FALSE; |
4491 |
if (jmp < 0) |
if (jmp < 0) |
4492 |
return JS_FALSE; |
return JS_FALSE; |
4493 |
top = CG_OFFSET(cg); |
top = CG_OFFSET(cg); |
4494 |
|
if (!js_Emit1(cx, cg, JSOP_LOOP)) |
4495 |
|
return JS_FALSE; |
4496 |
if (!js_EmitTree(cx, cg, pn->pn_right)) |
if (!js_EmitTree(cx, cg, pn->pn_right)) |
4497 |
return JS_FALSE; |
return JS_FALSE; |
4498 |
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); |
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); |
4514 |
|
|
4515 |
/* Compile the loop body. */ |
/* Compile the loop body. */ |
4516 |
top = CG_OFFSET(cg); |
top = CG_OFFSET(cg); |
4517 |
js_PushStatement(&cg->treeContext, &stmtInfo, STMT_DO_LOOP, top); |
if (!js_Emit1(cx, cg, JSOP_LOOP)) |
4518 |
|
return JS_FALSE; |
4519 |
|
js_PushStatement(cg, &stmtInfo, STMT_DO_LOOP, top); |
4520 |
if (!js_EmitTree(cx, cg, pn->pn_left)) |
if (!js_EmitTree(cx, cg, pn->pn_left)) |
4521 |
return JS_FALSE; |
return JS_FALSE; |
4522 |
|
|
4547 |
beq = 0; /* suppress gcc warnings */ |
beq = 0; /* suppress gcc warnings */ |
4548 |
jmp = -1; |
jmp = -1; |
4549 |
pn2 = pn->pn_left; |
pn2 = pn->pn_left; |
4550 |
js_PushStatement(&cg->treeContext, &stmtInfo, STMT_FOR_LOOP, top); |
js_PushStatement(cg, &stmtInfo, STMT_FOR_LOOP, top); |
4551 |
|
|
4552 |
if (pn2->pn_type == TOK_IN) { |
if (pn2->pn_type == TOK_IN) { |
4553 |
/* Set stmtInfo type for later testing. */ |
/* Set stmtInfo type for later testing. */ |
4561 |
* result off the stack. |
* result off the stack. |
4562 |
* |
* |
4563 |
* All the logic to do this is implemented in the outer switch's |
* All the logic to do this is implemented in the outer switch's |
4564 |
* TOK_VAR case, conditioned on pn_extra flags set by the parser. |
* TOK_VAR case, conditioned on pn_xflags flags set by the parser. |
4565 |
* |
* |
4566 |
* In the 'for (var x = i in o) ...' case, the js_EmitTree(...pn3) |
* In the 'for (var x = i in o) ...' case, the js_EmitTree(...pn3) |
4567 |
* called here will generate the proper note for the assignment |
* called here will generate the proper note for the assignment |
4580 |
*/ |
*/ |
4581 |
pn3 = pn2->pn_left; |
pn3 = pn2->pn_left; |
4582 |
type = PN_TYPE(pn3); |
type = PN_TYPE(pn3); |
4583 |
cg->treeContext.flags |= TCF_IN_FOR_INIT; |
cg->flags |= TCF_IN_FOR_INIT; |
4584 |
if (TOKEN_TYPE_IS_DECL(type) && !js_EmitTree(cx, cg, pn3)) |
if (TOKEN_TYPE_IS_DECL(type) && !js_EmitTree(cx, cg, pn3)) |
4585 |
return JS_FALSE; |
return JS_FALSE; |
4586 |
cg->treeContext.flags &= ~TCF_IN_FOR_INIT; |
cg->flags &= ~TCF_IN_FOR_INIT; |
4587 |
|
|
4588 |
/* Compile the object expression to the right of 'in'. */ |
/* Compile the object expression to the right of 'in'. */ |
4589 |
if (!js_EmitTree(cx, cg, pn2->pn_right)) |
if (!js_EmitTree(cx, cg, pn2->pn_right)) |
4613 |
|
|
4614 |
top = CG_OFFSET(cg); |
top = CG_OFFSET(cg); |
4615 |
SET_STATEMENT_TOP(&stmtInfo, top); |
SET_STATEMENT_TOP(&stmtInfo, top); |
4616 |
|
if (!js_Emit1(cx, cg, JSOP_LOOP)) |
4617 |
|
return JS_FALSE; |
4618 |
|
|
4619 |
#ifdef DEBUG |
#ifdef DEBUG |
4620 |
intN loopDepth = cg->stackDepth; |
intN loopDepth = cg->stackDepth; |
4665 |
#if JS_HAS_BLOCK_SCOPE |
#if JS_HAS_BLOCK_SCOPE |
4666 |
type == TOK_LET || |
type == TOK_LET || |
4667 |
#endif |
#endif |
4668 |
(type == TOK_VAR && !pn3->pn_expr)) && |
(type == TOK_VAR && !pn3->maybeExpr())) && |
4669 |
js_NewSrcNote2(cx, cg, SRC_DECL, |
js_NewSrcNote2(cx, cg, SRC_DECL, |
4670 |
(type == TOK_VAR) |
(type == TOK_VAR) |
4671 |
? SRC_DECL_VAR |
? SRC_DECL_VAR |
4672 |
: SRC_DECL_LET) < 0) { |
: SRC_DECL_LET) < 0) { |
4673 |
return JS_FALSE; |
return JS_FALSE; |
4674 |
} |
} |
4675 |
if (pn3->pn_slot >= 0) { |
if (pn3->pn_cookie != FREE_UPVAR_COOKIE) { |
4676 |
op = PN_OP(pn3); |
op = PN_OP(pn3); |
4677 |
switch (op) { |
switch (op) { |
4678 |
case JSOP_GETARG: /* FALL THROUGH */ |
case JSOP_GETARG: /* FALL THROUGH */ |
4689 |
return JS_FALSE; |
return JS_FALSE; |
4690 |
op = PN_OP(pn3); |
op = PN_OP(pn3); |
4691 |
} |
} |
4692 |
if (pn3->pn_slot >= 0) { |
if (pn3->isConst()) { |
4693 |
if (pn3->pn_const) { |
js_ReportCompileErrorNumber(cx, CG_TS(cg), pn3, JSREPORT_ERROR, |
4694 |
JS_ASSERT(op == JSOP_FORLOCAL); |
JSMSG_BAD_FOR_LEFTSIDE); |
4695 |
js_ReportCompileErrorNumber(cx, CG_TS(cg), pn3, JSREPORT_ERROR, |
return JS_FALSE; |
4696 |
JSMSG_BAD_FOR_LEFTSIDE); |
} |
4697 |
return JS_FALSE; |
if (pn3->pn_cookie != FREE_UPVAR_COOKIE) { |
4698 |
} |
atomIndex = (jsatomid) pn3->pn_cookie; |
|
atomIndex = (jsatomid) pn3->pn_slot; |
|
4699 |
EMIT_UINT16_IMM_OP(op, atomIndex); |
EMIT_UINT16_IMM_OP(op, atomIndex); |
4700 |
} else { |
} else { |
4701 |
if (!EmitAtomOp(cx, pn3, op, cg)) |
if (!EmitAtomOp(cx, pn3, op, cg)) |
4709 |
* has no side effects. |
* has no side effects. |
4710 |
*/ |
*/ |
4711 |
useful = JS_FALSE; |
useful = JS_FALSE; |
4712 |
if (!CheckSideEffects(cx, cg, pn3->pn_expr, &useful)) |
if (!CheckSideEffects(cx, cg, pn3->expr(), &useful)) |
4713 |
return JS_FALSE; |
return JS_FALSE; |
4714 |
if (!useful) { |
if (!useful) { |
4715 |
if (!EmitPropOp(cx, pn3, JSOP_FORPROP, cg, JS_FALSE)) |
if (!EmitPropOp(cx, pn3, JSOP_FORPROP, cg, JS_FALSE)) |
4795 |
/* No initializer: emit an annotated nop for the decompiler. */ |
/* No initializer: emit an annotated nop for the decompiler. */ |
4796 |
op = JSOP_NOP; |
op = JSOP_NOP; |
4797 |
} else { |
} else { |
4798 |
cg->treeContext.flags |= TCF_IN_FOR_INIT; |
cg->flags |= TCF_IN_FOR_INIT; |
4799 |
#if JS_HAS_DESTRUCTURING |
#if JS_HAS_DESTRUCTURING |
4800 |
if (pn3->pn_type == TOK_ASSIGN && |
if (pn3->pn_type == TOK_ASSIGN && |
4801 |
!MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) { |
!MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) { |
4813 |
* just for the decompiler. |
* just for the decompiler. |
4814 |
*/ |
*/ |
4815 |
JS_ASSERT(pn3->pn_arity == PN_LIST); |
JS_ASSERT(pn3->pn_arity == PN_LIST); |
4816 |
if (pn3->pn_extra & PNX_GROUPINIT) |
if (pn3->pn_xflags & PNX_GROUPINIT) |
4817 |
op = JSOP_NOP; |
op = JSOP_NOP; |
4818 |
} |
} |
4819 |
} |
} |
4820 |
cg->treeContext.flags &= ~TCF_IN_FOR_INIT; |
cg->flags &= ~TCF_IN_FOR_INIT; |
4821 |
} |
} |
4822 |
|
|
4823 |
/* |
/* |
4842 |
SET_STATEMENT_TOP(&stmtInfo, top); |
SET_STATEMENT_TOP(&stmtInfo, top); |
4843 |
|
|
4844 |
/* Emit code for the loop body. */ |
/* Emit code for the loop body. */ |
4845 |
|
if (!js_Emit1(cx, cg, JSOP_LOOP)) |
4846 |
|
return JS_FALSE; |
4847 |
if (!js_EmitTree(cx, cg, pn->pn_right)) |
if (!js_EmitTree(cx, cg, pn->pn_right)) |
4848 |
return JS_FALSE; |
return JS_FALSE; |
4849 |
|
|
4939 |
break; |
break; |
4940 |
|
|
4941 |
case TOK_BREAK: |
case TOK_BREAK: |
4942 |
stmt = cg->treeContext.topStmt; |
stmt = cg->topStmt; |
4943 |
atom = pn->pn_atom; |
atom = pn->pn_atom; |
4944 |
if (atom) { |
if (atom) { |
4945 |
ale = js_IndexAtom(cx, atom, &cg->atomList); |
ale = cg->atomList.add(cg->compiler, atom); |
4946 |
if (!ale) |
if (!ale) |
4947 |
return JS_FALSE; |
return JS_FALSE; |
4948 |
while (stmt->type != STMT_LABEL || stmt->u.label != atom) |
while (stmt->type != STMT_LABEL || stmt->label != atom) |
4949 |
stmt = stmt->down; |
stmt = stmt->down; |
4950 |
noteType = SRC_BREAK2LABEL; |
noteType = SRC_BREAK2LABEL; |
4951 |
} else { |
} else { |
4960 |
break; |
break; |
4961 |
|
|
4962 |
case TOK_CONTINUE: |
case TOK_CONTINUE: |
4963 |
stmt = cg->treeContext.topStmt; |
stmt = cg->topStmt; |
4964 |
atom = pn->pn_atom; |
atom = pn->pn_atom; |
4965 |
if (atom) { |
if (atom) { |
4966 |
/* Find the loop statement enclosed by the matching label. */ |
/* Find the loop statement enclosed by the matching label. */ |
4967 |
JSStmtInfo *loop = NULL; |
JSStmtInfo *loop = NULL; |
4968 |
ale = js_IndexAtom(cx, atom, &cg->atomList); |
ale = cg->atomList.add(cg->compiler, atom); |
4969 |
if (!ale) |
if (!ale) |
4970 |
return JS_FALSE; |
return JS_FALSE; |
4971 |
while (stmt->type != STMT_LABEL || stmt->u.label != atom) { |
while (stmt->type != STMT_LABEL || stmt->label != atom) { |
4972 |
if (STMT_IS_LOOP(stmt)) |
if (STMT_IS_LOOP(stmt)) |
4973 |
loop = stmt; |
loop = stmt; |
4974 |
stmt = stmt->down; |
stmt = stmt->down; |
4989 |
case TOK_WITH: |
case TOK_WITH: |
4990 |
if (!js_EmitTree(cx, cg, pn->pn_left)) |
if (!js_EmitTree(cx, cg, pn->pn_left)) |
4991 |
return JS_FALSE; |
return JS_FALSE; |
4992 |
js_PushStatement(&cg->treeContext, &stmtInfo, STMT_WITH, CG_OFFSET(cg)); |
js_PushStatement(cg, &stmtInfo, STMT_WITH, CG_OFFSET(cg)); |
4993 |
if (js_Emit1(cx, cg, JSOP_ENTERWITH) < 0) |
if (js_Emit1(cx, cg, JSOP_ENTERWITH) < 0) |
4994 |
return JS_FALSE; |
return JS_FALSE; |
4995 |
if (!js_EmitTree(cx, cg, pn->pn_right)) |
if (!js_EmitTree(cx, cg, pn->pn_right)) |
5011 |
* Push stmtInfo to track jumps-over-catches and gosubs-to-finally |
* Push stmtInfo to track jumps-over-catches and gosubs-to-finally |
5012 |
* for later fixup. |
* for later fixup. |
5013 |
* |
* |
5014 |
* When a finally block is 'active' (STMT_FINALLY on the treeContext), |
* When a finally block is active (STMT_FINALLY in our tree context), |
5015 |
* non-local jumps (including jumps-over-catches) result in a GOSUB |
* non-local jumps (including jumps-over-catches) result in a GOSUB |
5016 |
* being written into the bytecode stream and fixed-up later (c.f. |
* being written into the bytecode stream and fixed-up later (c.f. |
5017 |
* EmitBackPatchOp and BackPatch). |
* EmitBackPatchOp and BackPatch). |
5018 |
*/ |
*/ |
5019 |
js_PushStatement(&cg->treeContext, &stmtInfo, |
js_PushStatement(cg, &stmtInfo, |
5020 |
pn->pn_kid3 ? STMT_FINALLY : STMT_TRY, |
pn->pn_kid3 ? STMT_FINALLY : STMT_TRY, |
5021 |
CG_OFFSET(cg)); |
CG_OFFSET(cg)); |
5022 |
|
|
5135 |
* guardJump at the next catch (the guard mismatch case). |
* guardJump at the next catch (the guard mismatch case). |
5136 |
*/ |
*/ |
5137 |
JS_ASSERT(pn3->pn_type == TOK_LEXICALSCOPE); |
JS_ASSERT(pn3->pn_type == TOK_LEXICALSCOPE); |
5138 |
count = OBJ_BLOCK_COUNT(cx, pn3->pn_pob->object); |
count = OBJ_BLOCK_COUNT(cx, pn3->pn_objbox->object); |
5139 |
if (!js_EmitTree(cx, cg, pn3)) |
if (!js_EmitTree(cx, cg, pn3)) |
5140 |
return JS_FALSE; |
return JS_FALSE; |
5141 |
|
|
5162 |
* Save a pointer to the last catch node to handle try-finally |
* Save a pointer to the last catch node to handle try-finally |
5163 |
* and try-catch(guard)-finally special cases. |
* and try-catch(guard)-finally special cases. |
5164 |
*/ |
*/ |
5165 |
lastCatch = pn3->pn_expr; |
lastCatch = pn3->expr(); |
5166 |
} |
} |
5167 |
} |
} |
5168 |
|
|
5205 |
|
|
5206 |
/* Indicate that we're emitting a subroutine body. */ |
/* Indicate that we're emitting a subroutine body. */ |
5207 |
stmtInfo.type = STMT_SUBROUTINE; |
stmtInfo.type = STMT_SUBROUTINE; |
5208 |
if (!UpdateLineNumberNotes(cx, cg, pn->pn_kid3)) |
if (!UpdateLineNumberNotes(cx, cg, pn->pn_kid3->pn_pos.begin.lineno)) |
5209 |
return JS_FALSE; |
return JS_FALSE; |
5210 |
if (js_Emit1(cx, cg, JSOP_FINALLY) < 0 || |
if (js_Emit1(cx, cg, JSOP_FINALLY) < 0 || |
5211 |
!js_EmitTree(cx, cg, pn->pn_kid3) || |
!js_EmitTree(cx, cg, pn->pn_kid3) || |
5256 |
* Morph STMT_BLOCK to STMT_CATCH, note the block entry code offset, |
* Morph STMT_BLOCK to STMT_CATCH, note the block entry code offset, |
5257 |
* and save the block object atom. |
* and save the block object atom. |
5258 |
*/ |
*/ |
5259 |
stmt = cg->treeContext.topStmt; |
stmt = cg->topStmt; |
5260 |
JS_ASSERT(stmt->type == STMT_BLOCK && (stmt->flags & SIF_SCOPE)); |
JS_ASSERT(stmt->type == STMT_BLOCK && (stmt->flags & SIF_SCOPE)); |
5261 |
stmt->type = STMT_CATCH; |
stmt->type = STMT_CATCH; |
5262 |
catchStart = stmt->update; |
catchStart = stmt->update; |
5263 |
blockObj = stmt->u.blockObj; |
blockObj = stmt->blockObj; |
5264 |
|
|
5265 |
/* Go up one statement info record to the TRY or FINALLY record. */ |
/* Go up one statement info record to the TRY or FINALLY record. */ |
5266 |
stmt = stmt->down; |
stmt = stmt->down; |
5290 |
#endif |
#endif |
5291 |
|
|
5292 |
case TOK_NAME: |
case TOK_NAME: |
5293 |
/* Inline BindNameToSlot for pn2. */ |
/* Inline and specialize BindNameToSlot for pn2. */ |
5294 |
JS_ASSERT(pn2->pn_slot == -1); |
JS_ASSERT(pn2->pn_cookie != FREE_UPVAR_COOKIE); |
5295 |
pn2->pn_slot = AdjustBlockSlot(cx, cg, |
EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, pn2->pn_cookie); |
|
OBJ_BLOCK_DEPTH(cx, blockObj)); |
|
|
if (pn2->pn_slot < 0) |
|
|
return JS_FALSE; |
|
|
EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, pn2->pn_slot); |
|
5296 |
break; |
break; |
5297 |
|
|
5298 |
default: |
default: |
5373 |
|
|
5374 |
#if JS_HAS_GENERATORS |
#if JS_HAS_GENERATORS |
5375 |
case TOK_YIELD: |
case TOK_YIELD: |
5376 |
if (!(cg->treeContext.flags & TCF_IN_FUNCTION)) { |
if (!(cg->flags & TCF_IN_FUNCTION)) { |
5377 |
js_ReportCompileErrorNumber(cx, CG_TS(cg), pn, JSREPORT_ERROR, |
js_ReportCompileErrorNumber(cx, CG_TS(cg), pn, JSREPORT_ERROR, |
5378 |
JSMSG_BAD_RETURN_OR_YIELD, |
JSMSG_BAD_RETURN_OR_YIELD, |
5379 |
js_yield_str); |
js_yield_str); |
5394 |
#endif |
#endif |
5395 |
|
|
5396 |
case TOK_LC: |
case TOK_LC: |
5397 |
|
{ |
5398 |
#if JS_HAS_XML_SUPPORT |
#if JS_HAS_XML_SUPPORT |
5399 |
if (pn->pn_arity == PN_UNARY) { |
if (pn->pn_arity == PN_UNARY) { |
5400 |
if (!js_EmitTree(cx, cg, pn->pn_kid)) |
if (!js_EmitTree(cx, cg, pn->pn_kid)) |
5409 |
|
|
5410 |
noteIndex = -1; |
noteIndex = -1; |
5411 |
tmp = CG_OFFSET(cg); |
tmp = CG_OFFSET(cg); |
5412 |
if (pn->pn_extra & PNX_NEEDBRACES) { |
if (pn->pn_xflags & PNX_NEEDBRACES) { |
5413 |
noteIndex = js_NewSrcNote2(cx, cg, SRC_BRACE, 0); |
noteIndex = js_NewSrcNote2(cx, cg, SRC_BRACE, 0); |
5414 |
if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_NOP) < 0) |
if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_NOP) < 0) |
5415 |
return JS_FALSE; |
return JS_FALSE; |
5416 |
} |
} |
5417 |
|
|
5418 |
js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BLOCK, top); |
js_PushStatement(cg, &stmtInfo, STMT_BLOCK, top); |
5419 |
if (pn->pn_extra & PNX_FUNCDEFS) { |
|
5420 |
|
JSParseNode *pnchild = pn->pn_head; |
5421 |
|
if (pn->pn_xflags & PNX_FUNCDEFS) { |
5422 |
/* |
/* |
5423 |
* This block contains top-level function definitions. To ensure |
* This block contains top-level function definitions. To ensure |
5424 |
* that we emit the bytecode defining them prior the rest of code |
* that we emit the bytecode defining them before the rest of code |
5425 |
* in the block we use a separate pass over functions. During the |
* in the block we use a separate pass over functions. During the |
5426 |
* main pass later the emitter will add JSOP_NOP with source notes |
* main pass later the emitter will add JSOP_NOP with source notes |
5427 |
* for the function to preserve the original functions position |
* for the function to preserve the original functions position |
5430 |
* Currently this is used only for functions, as compile-as-we go |
* Currently this is used only for functions, as compile-as-we go |
5431 |
* mode for scripts does not allow separate emitter passes. |
* mode for scripts does not allow separate emitter passes. |
5432 |
*/ |
*/ |
5433 |
JS_ASSERT(cg->treeContext.flags & TCF_IN_FUNCTION); |
JS_ASSERT(cg->flags & TCF_IN_FUNCTION); |
5434 |
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { |
if (pn->pn_xflags & PNX_DESTRUCT) { |
5435 |
|
/* |
5436 |
|
* Assign the destructuring arguments before defining any |
5437 |
|
* functions, see bug 419662. |
5438 |
|
*/ |
5439 |
|
JS_ASSERT(pnchild->pn_type == TOK_SEMI); |
5440 |
|
JS_ASSERT(pnchild->pn_kid->pn_type == TOK_COMMA); |
5441 |
|
if (!js_EmitTree(cx, cg, pnchild)) |
5442 |
|
return JS_FALSE; |
5443 |
|
pnchild = pnchild->pn_next; |
5444 |
|
} |
5445 |
|
|
5446 |
|
for (pn2 = pnchild; pn2; pn2 = pn2->pn_next) { |
5447 |
if (pn2->pn_type == TOK_FUNCTION) { |
if (pn2->pn_type == TOK_FUNCTION) { |
5448 |
if (pn2->pn_op == JSOP_NOP) { |
if (pn2->pn_op == JSOP_NOP) { |
5449 |
if (!js_EmitTree(cx, cg, pn2)) |
if (!js_EmitTree(cx, cg, pn2)) |
5460 |
} |
} |
5461 |
} |
} |
5462 |
} |
} |
5463 |
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { |
for (pn2 = pnchild; pn2; pn2 = pn2->pn_next) { |
5464 |
if (!js_EmitTree(cx, cg, pn2)) |
if (!js_EmitTree(cx, cg, pn2)) |
5465 |
return JS_FALSE; |
return JS_FALSE; |
5466 |
} |
} |
5473 |
|
|
5474 |
ok = js_PopStatementCG(cx, cg); |
ok = js_PopStatementCG(cx, cg); |
5475 |
break; |
break; |
5476 |
|
} |
5477 |
|
|
5478 |
case TOK_SEQ: |
case TOK_SEQ: |
5479 |
JS_ASSERT(pn->pn_arity == PN_LIST); |
JS_ASSERT(pn->pn_arity == PN_LIST); |
5480 |
js_PushStatement(&cg->treeContext, &stmtInfo, STMT_SEQ, top); |
js_PushStatement(cg, &stmtInfo, STMT_SEQ, top); |
5481 |
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { |
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { |
5482 |
if (!js_EmitTree(cx, cg, pn2)) |
if (!js_EmitTree(cx, cg, pn2)) |
5483 |
return JS_FALSE; |
return JS_FALSE; |
5497 |
* API users may also set the JSOPTION_NO_SCRIPT_RVAL option when |
* API users may also set the JSOPTION_NO_SCRIPT_RVAL option when |
5498 |
* calling JS_Compile* to suppress JSOP_POPV. |
* calling JS_Compile* to suppress JSOP_POPV. |
5499 |
*/ |
*/ |
5500 |
useful = wantval = |
useful = wantval = !(cg->flags & (TCF_IN_FUNCTION | TCF_NO_SCRIPT_RVAL)); |
|
!(cg->treeContext.flags & (TCF_IN_FUNCTION | TCF_NO_SCRIPT_RVAL)); |
|
5501 |
if (!useful) { |
if (!useful) { |
5502 |
if (!CheckSideEffects(cx, cg, pn2, &useful)) |
if (!CheckSideEffects(cx, cg, pn2, &useful)) |
5503 |
return JS_FALSE; |
return JS_FALSE; |
5510 |
* labeled compound statement. |
* labeled compound statement. |
5511 |
*/ |
*/ |
5512 |
if (!useful && |
if (!useful && |
5513 |
(!cg->treeContext.topStmt || |
(!cg->topStmt || |
5514 |
cg->treeContext.topStmt->type != STMT_LABEL || |
cg->topStmt->type != STMT_LABEL || |
5515 |
cg->treeContext.topStmt->update < CG_OFFSET(cg))) { |
cg->topStmt->update < CG_OFFSET(cg))) { |
5516 |
CG_CURRENT_LINE(cg) = pn2->pn_pos.begin.lineno; |
CG_CURRENT_LINE(cg) = pn2->pn_pos.begin.lineno; |
5517 |
if (!js_ReportCompileErrorNumber(cx, CG_TS(cg), pn2, |
if (!js_ReportCompileErrorNumber(cx, CG_TS(cg), pn2, |
5518 |
JSREPORT_WARNING | |
JSREPORT_WARNING | |
5542 |
case TOK_COLON: |
case TOK_COLON: |
5543 |
/* Emit an annotated nop so we know to decompile a label. */ |
/* Emit an annotated nop so we know to decompile a label. */ |
5544 |
atom = pn->pn_atom; |
atom = pn->pn_atom; |
5545 |
ale = js_IndexAtom(cx, atom, &cg->atomList); |
ale = cg->atomList.add(cg->compiler, atom); |
5546 |
if (!ale) |
if (!ale) |
5547 |
return JS_FALSE; |
return JS_FALSE; |
5548 |
pn2 = pn->pn_expr; |
pn2 = pn->expr(); |
5549 |
noteType = (pn2->pn_type == TOK_LC || |
noteType = (pn2->pn_type == TOK_LC || |
5550 |
(pn2->pn_type == TOK_LEXICALSCOPE && |
(pn2->pn_type == TOK_LEXICALSCOPE && |
5551 |
pn2->pn_expr->pn_type == TOK_LC)) |
pn2->expr()->pn_type == TOK_LC)) |
5552 |
? SRC_LABELBRACE |
? SRC_LABELBRACE |
5553 |
: SRC_LABEL; |
: SRC_LABEL; |
5554 |
noteIndex = js_NewSrcNote2(cx, cg, noteType, |
noteIndex = js_NewSrcNote2(cx, cg, noteType, |
5559 |
} |
} |
5560 |
|
|
5561 |
/* Emit code for the labeled statement. */ |
/* Emit code for the labeled statement. */ |
5562 |
js_PushStatement(&cg->treeContext, &stmtInfo, STMT_LABEL, |
js_PushStatement(cg, &stmtInfo, STMT_LABEL, CG_OFFSET(cg)); |
5563 |
CG_OFFSET(cg)); |
stmtInfo.label = atom; |
|
stmtInfo.u.label = atom; |
|
5564 |
if (!js_EmitTree(cx, cg, pn2)) |
if (!js_EmitTree(cx, cg, pn2)) |
5565 |
return JS_FALSE; |
return JS_FALSE; |
5566 |
if (!js_PopStatementCG(cx, cg)) |
if (!js_PopStatementCG(cx, cg)) |
5614 |
case TOK_NAME: |
case TOK_NAME: |
5615 |
if (!BindNameToSlot(cx, cg, pn2)) |
if (!BindNameToSlot(cx, cg, pn2)) |
5616 |
return JS_FALSE; |
return JS_FALSE; |
5617 |
if (pn2->pn_slot >= 0) { |
if (pn2->pn_cookie != FREE_UPVAR_COOKIE) { |
5618 |
atomIndex = (jsatomid) pn2->pn_slot; |
atomIndex = (jsatomid) pn2->pn_cookie; |
5619 |
} else { |
} else { |
5620 |
ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList); |
ale = cg->atomList.add(cg->compiler, pn2->pn_atom); |
5621 |
if (!ale) |
if (!ale) |
5622 |
return JS_FALSE; |
return JS_FALSE; |
5623 |
atomIndex = ALE_INDEX(ale); |
atomIndex = ALE_INDEX(ale); |
5624 |
EMIT_INDEX_OP(JSOP_BINDNAME, atomIndex); |
if (!pn2->isConst()) |
5625 |
|
EMIT_INDEX_OP(JSOP_BINDNAME, atomIndex); |
5626 |
} |
} |
5627 |
break; |
break; |
5628 |
case TOK_DOT: |
case TOK_DOT: |
5629 |
if (!js_EmitTree(cx, cg, pn2->pn_expr)) |
if (!js_EmitTree(cx, cg, pn2->expr())) |
5630 |
return JS_FALSE; |
return JS_FALSE; |
5631 |
ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList); |
ale = cg->atomList.add(cg->compiler, pn2->pn_atom); |
5632 |
if (!ale) |
if (!ale) |
5633 |
return JS_FALSE; |
return JS_FALSE; |
5634 |
atomIndex = ALE_INDEX(ale); |
atomIndex = ALE_INDEX(ale); |
5673 |
* supported. |
* supported. |
5674 |
*/ |
*/ |
5675 |
js_ReportCompileErrorNumber(cx, |
js_ReportCompileErrorNumber(cx, |
5676 |
TS(cg->treeContext.parseContext), |
TS(cg->compiler), |
5677 |
pn2, JSREPORT_ERROR, |
pn2, JSREPORT_ERROR, |
5678 |
JSMSG_BAD_GETTER_OR_SETTER, |
JSMSG_BAD_GETTER_OR_SETTER, |
5679 |
(op == JSOP_GETTER) |
(op == JSOP_GETTER) |
5689 |
if (op != JSOP_NOP) { |
if (op != JSOP_NOP) { |
5690 |
switch (pn2->pn_type) { |
switch (pn2->pn_type) { |
5691 |
case TOK_NAME: |
case TOK_NAME: |
5692 |
if (pn2->pn_op != JSOP_SETNAME) { |
if (pn2->isConst()) { |
5693 |
EMIT_UINT16_IMM_OP((pn2->pn_op == JSOP_SETGVAR) |
if (PN_OP(pn2) == JSOP_CALLEE) { |
5694 |
|
if (js_Emit1(cx, cg, JSOP_CALLEE) < 0) |
5695 |
|
return JS_FALSE; |
5696 |
|
} else { |
5697 |
|
EMIT_INDEX_OP(PN_OP(pn2), atomIndex); |
5698 |
|
} |
5699 |
|
} else if (PN_OP(pn2) == JSOP_SETNAME) { |
5700 |
|
if (js_Emit1(cx, cg, JSOP_DUP) < 0) |
5701 |
|
return JS_FALSE; |
5702 |
|
EMIT_INDEX_OP(JSOP_GETXPROP, atomIndex); |
5703 |
|
} else { |
5704 |
|
EMIT_UINT16_IMM_OP((PN_OP(pn2) == JSOP_SETGVAR) |
5705 |
? JSOP_GETGVAR |
? JSOP_GETGVAR |
5706 |
: (pn2->pn_op == JSOP_SETARG) |
: (PN_OP(pn2) == JSOP_GETUPVAR) |
5707 |
|
? JSOP_GETUPVAR |
5708 |
|
: (PN_OP(pn2) == JSOP_SETARG) |
5709 |
? JSOP_GETARG |
? JSOP_GETARG |
5710 |
: JSOP_GETLOCAL, |
: JSOP_GETLOCAL, |
5711 |
atomIndex); |
atomIndex); |
|
break; |
|
5712 |
} |
} |
|
if (js_Emit1(cx, cg, JSOP_DUP) < 0) |
|
|
return JS_FALSE; |
|
|
EMIT_INDEX_OP(JSOP_GETXPROP, atomIndex); |
|
5713 |
break; |
break; |
5714 |
case TOK_DOT: |
case TOK_DOT: |
5715 |
if (js_Emit1(cx, cg, JSOP_DUP) < 0) |
if (js_Emit1(cx, cg, JSOP_DUP) < 0) |
5717 |
if (pn2->pn_atom == cx->runtime->atomState.lengthAtom) { |
if (pn2->pn_atom == cx->runtime->atomState.lengthAtom) { |
5718 |
if (js_Emit1(cx, cg, JSOP_LENGTH) < 0) |
if (js_Emit1(cx, cg, JSOP_LENGTH) < 0) |
5719 |
return JS_FALSE; |
return JS_FALSE; |
5720 |
|
} else if (pn2->pn_atom == cx->runtime->atomState.protoAtom) { |
5721 |
|
if (!EmitIndexOp(cx, JSOP_QNAMEPART, atomIndex, cg)) |
5722 |
|
return JS_FALSE; |
5723 |
|
if (js_Emit1(cx, cg, JSOP_GETELEM) < 0) |
5724 |
|
return JS_FALSE; |
5725 |
} else { |
} else { |
5726 |
EMIT_INDEX_OP(JSOP_GETPROP, atomIndex); |
EMIT_INDEX_OP(JSOP_GETPROP, atomIndex); |
5727 |
} |
} |
5749 |
/* If += etc., emit the binary operator with a decompiler note. */ |
/* If += etc., emit the binary operator with a decompiler note. */ |
5750 |
if (op != JSOP_NOP) { |
if (op != JSOP_NOP) { |
5751 |
/* |
/* |
5752 |
* Take care to avoid SRC_ASSIGNOP if the left-hand side is a |
* Take care to avoid SRC_ASSIGNOP if the left-hand side is a const |
5753 |
* const declared in a function (i.e., with non-negative pn_slot |
* declared in the current compilation unit, as in this case (just |
5754 |
* and when pn_const is true), as in this case (just a bit further |
* a bit further below) we will avoid emitting the assignment op. |
5755 |
* below) we will avoid emitting the assignment op. |
*/ |
5756 |
*/ |
if (pn2->pn_type != TOK_NAME || !pn2->isConst()) { |
|
if (pn2->pn_type != TOK_NAME || |
|
|
pn2->pn_slot < 0 || |
|
|
!pn2->pn_const) { |
|
5757 |
if (js_NewSrcNote(cx, cg, SRC_ASSIGNOP) < 0) |
if (js_NewSrcNote(cx, cg, SRC_ASSIGNOP) < 0) |
5758 |
return JS_FALSE; |
return JS_FALSE; |
5759 |
} |
} |
5774 |
/* Finally, emit the specialized assignment bytecode. */ |
/* Finally, emit the specialized assignment bytecode. */ |
5775 |
switch (pn2->pn_type) { |
switch (pn2->pn_type) { |
5776 |
case TOK_NAME: |
case TOK_NAME: |
5777 |
if (pn2->pn_slot >= 0) { |
if (pn2->isConst()) |
|
if (!pn2->pn_const) |
|
|
EMIT_UINT16_IMM_OP(PN_OP(pn2), atomIndex); |
|
5778 |
break; |
break; |
|
} |
|
5779 |
/* FALL THROUGH */ |
/* FALL THROUGH */ |
5780 |
case TOK_DOT: |
case TOK_DOT: |
5781 |
EMIT_INDEX_OP(PN_OP(pn2), atomIndex); |
EMIT_INDEX_OP(PN_OP(pn2), atomIndex); |
5937 |
|
|
5938 |
case TOK_DBLCOLON: |
case TOK_DBLCOLON: |
5939 |
if (pn->pn_arity == PN_NAME) { |
if (pn->pn_arity == PN_NAME) { |
5940 |
if (!js_EmitTree(cx, cg, pn->pn_expr)) |
if (!js_EmitTree(cx, cg, pn->expr())) |
5941 |
return JS_FALSE; |
return JS_FALSE; |
5942 |
if (!EmitAtomOp(cx, pn, PN_OP(pn), cg)) |
if (!EmitAtomOp(cx, pn, PN_OP(pn), cg)) |
5943 |
return JS_FALSE; |
return JS_FALSE; |
5949 |
* possibly including a let (a = b) ... expression. We must clear |
* possibly including a let (a = b) ... expression. We must clear |
5950 |
* TCF_IN_FOR_INIT to avoid mis-compiling such beasts. |
* TCF_IN_FOR_INIT to avoid mis-compiling such beasts. |
5951 |
*/ |
*/ |
5952 |
oldflags = cg->treeContext.flags; |
oldflags = cg->flags; |
5953 |
cg->treeContext.flags &= ~TCF_IN_FOR_INIT; |
cg->flags &= ~TCF_IN_FOR_INIT; |
5954 |
#endif |
#endif |
5955 |
|
|
5956 |
/* Binary operators that evaluate both operands unconditionally. */ |
/* Binary operators that evaluate both operands unconditionally. */ |
5959 |
if (!js_EmitTree(cx, cg, pn->pn_right)) |
if (!js_EmitTree(cx, cg, pn->pn_right)) |
5960 |
return JS_FALSE; |
return JS_FALSE; |
5961 |
#if JS_HAS_XML_SUPPORT |
#if JS_HAS_XML_SUPPORT |
5962 |
cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT; |
cg->flags |= oldflags & TCF_IN_FOR_INIT; |
5963 |
#endif |
#endif |
5964 |
if (js_Emit1(cx, cg, PN_OP(pn)) < 0) |
if (js_Emit1(cx, cg, PN_OP(pn)) < 0) |
5965 |
return JS_FALSE; |
return JS_FALSE; |
5993 |
if (pn3->pn_type != TOK_NAME) |
if (pn3->pn_type != TOK_NAME) |
5994 |
op = JSOP_TYPEOFEXPR; |
op = JSOP_TYPEOFEXPR; |
5995 |
} |
} |
5996 |
oldflags = cg->treeContext.flags; |
oldflags = cg->flags; |
5997 |
cg->treeContext.flags &= ~TCF_IN_FOR_INIT; |
cg->flags &= ~TCF_IN_FOR_INIT; |
5998 |
if (!js_EmitTree(cx, cg, pn2)) |
if (!js_EmitTree(cx, cg, pn2)) |
5999 |
return JS_FALSE; |
return JS_FALSE; |
6000 |
cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT; |
cg->flags |= oldflags & TCF_IN_FOR_INIT; |
6001 |
if (js_Emit1(cx, cg, op) < 0) |
if (js_Emit1(cx, cg, op) < 0) |
6002 |
return JS_FALSE; |
return JS_FALSE; |
6003 |
break; |
break; |
6016 |
if (!BindNameToSlot(cx, cg, pn2)) |
if (!BindNameToSlot(cx, cg, pn2)) |
6017 |
return JS_FALSE; |
return JS_FALSE; |
6018 |
op = PN_OP(pn2); |
op = PN_OP(pn2); |
6019 |
if (pn2->pn_slot >= 0) { |
if (op == JSOP_CALLEE) { |
6020 |
if (pn2->pn_const) { |
if (js_Emit1(cx, cg, op) < 0) |
6021 |
/* Incrementing a declared const: just get its value. */ |
return JS_FALSE; |
6022 |
op = (JOF_OPTYPE(op) == JOF_ATOM) |
} else if (pn2->pn_cookie != FREE_UPVAR_COOKIE) { |
6023 |
? JSOP_GETGVAR |
atomIndex = (jsatomid) pn2->pn_cookie; |
|
: JSOP_GETLOCAL; |
|
|
} |
|
|
atomIndex = (jsatomid) pn2->pn_slot; |
|
6024 |
EMIT_UINT16_IMM_OP(op, atomIndex); |
EMIT_UINT16_IMM_OP(op, atomIndex); |
6025 |
} else { |
} else { |
6026 |
|
JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM); |
6027 |
if (!EmitAtomOp(cx, pn2, op, cg)) |
if (!EmitAtomOp(cx, pn2, op, cg)) |
6028 |
return JS_FALSE; |
return JS_FALSE; |
6029 |
|
break; |
6030 |
|
} |
6031 |
|
if (pn2->isConst()) { |
6032 |
|
if (js_Emit1(cx, cg, JSOP_POS) < 0) |
6033 |
|
return JS_FALSE; |
6034 |
|
op = PN_OP(pn); |
6035 |
|
if (!(js_CodeSpec[op].format & JOF_POST)) { |
6036 |
|
if (js_Emit1(cx, cg, JSOP_ONE) < 0) |
6037 |
|
return JS_FALSE; |
6038 |
|
op = (js_CodeSpec[op].format & JOF_INC) ? JSOP_ADD : JSOP_SUB; |
6039 |
|
if (js_Emit1(cx, cg, op) < 0) |
6040 |
|
return JS_FALSE; |
6041 |
|
} |
6042 |
} |
} |
6043 |
break; |
break; |
6044 |
case TOK_DOT: |
case TOK_DOT: |
6083 |
pn2 = pn->pn_kid; |
pn2 = pn->pn_kid; |
6084 |
switch (pn2->pn_type) { |
switch (pn2->pn_type) { |
6085 |
case TOK_NAME: |
case TOK_NAME: |
|
pn2->pn_op = JSOP_DELNAME; |
|
6086 |
if (!BindNameToSlot(cx, cg, pn2)) |
if (!BindNameToSlot(cx, cg, pn2)) |
6087 |
return JS_FALSE; |
return JS_FALSE; |
6088 |
op = PN_OP(pn2); |
op = PN_OP(pn2); |
6155 |
if (jmp < 0) |
if (jmp < 0) |
6156 |
return JS_FALSE; |
return JS_FALSE; |
6157 |
top = CG_OFFSET(cg); |
top = CG_OFFSET(cg); |
6158 |
|
if (!js_Emit1(cx, cg, JSOP_LOOP)) |
6159 |
|
return JS_FALSE; |
6160 |
if (!js_EmitTree(cx, cg, pn->pn_right)) |
if (!js_EmitTree(cx, cg, pn->pn_right)) |
6161 |
return JS_FALSE; |
return JS_FALSE; |
6162 |
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); |
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); |
6224 |
default: |
default: |
6225 |
/* |
/* |
6226 |
* Push null as a placeholder for the global object, per ECMA-262 |
* Push null as a placeholder for the global object, per ECMA-262 |
6227 |
* 11.2.3 step 6. We use JSOP_NULLTHIS to distinguish this opcode |
* 11.2.3 step 6. |
|
* from JSOP_NULL (see jstracer.cpp for one use-case). |
|
6228 |
*/ |
*/ |
6229 |
if (!js_EmitTree(cx, cg, pn2)) |
if (!js_EmitTree(cx, cg, pn2)) |
6230 |
return JS_FALSE; |
return JS_FALSE; |
6231 |
if (js_Emit1(cx, cg, JSOP_NULLTHIS) < 0) |
if (js_Emit1(cx, cg, JSOP_NULL) < 0) |
6232 |
return JS_FALSE; |
return JS_FALSE; |
6233 |
} |
} |
6234 |
|
|
6240 |
* JSOP_NEW bytecode with a two-byte immediate telling how many args |
* JSOP_NEW bytecode with a two-byte immediate telling how many args |
6241 |
* were pushed on the operand stack. |
* were pushed on the operand stack. |
6242 |
*/ |
*/ |
6243 |
oldflags = cg->treeContext.flags; |
oldflags = cg->flags; |
6244 |
cg->treeContext.flags &= ~TCF_IN_FOR_INIT; |
cg->flags &= ~TCF_IN_FOR_INIT; |
6245 |
for (pn3 = pn2->pn_next; pn3; pn3 = pn3->pn_next) { |
for (pn3 = pn2->pn_next; pn3; pn3 = pn3->pn_next) { |
6246 |
if (!js_EmitTree(cx, cg, pn3)) |
if (!js_EmitTree(cx, cg, pn3)) |
6247 |
return JS_FALSE; |
return JS_FALSE; |
6248 |
} |
} |
6249 |
cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT; |
cg->flags |= oldflags & TCF_IN_FOR_INIT; |
6250 |
if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - off) < 0) |
if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - off) < 0) |
6251 |
return JS_FALSE; |
return JS_FALSE; |
6252 |
|
|
6260 |
|
|
6261 |
case TOK_LEXICALSCOPE: |
case TOK_LEXICALSCOPE: |
6262 |
{ |
{ |
6263 |
JSParsedObjectBox *pob; |
JSObjectBox *objbox; |
6264 |
uintN count; |
uintN count; |
6265 |
|
|
6266 |
pob = pn->pn_pob; |
objbox = pn->pn_objbox; |
6267 |
js_PushBlockScope(&cg->treeContext, &stmtInfo, pob->object, |
js_PushBlockScope(cg, &stmtInfo, objbox->object, CG_OFFSET(cg)); |
|
CG_OFFSET(cg)); |
|
6268 |
|
|
6269 |
/* |
/* |
6270 |
* If this lexical scope is not for a catch block, let block or let |
* If this lexical scope is not for a catch block, let block or let |
6275 |
* statements get braces by default from the decompiler. |
* statements get braces by default from the decompiler. |
6276 |
*/ |
*/ |
6277 |
noteIndex = -1; |
noteIndex = -1; |
6278 |
type = PN_TYPE(pn->pn_expr); |
type = PN_TYPE(pn->expr()); |
6279 |
if (type != TOK_CATCH && type != TOK_LET && type != TOK_FOR && |
if (type != TOK_CATCH && type != TOK_LET && type != TOK_FOR && |
6280 |
(!(stmt = stmtInfo.down) |
(!(stmt = stmtInfo.down) |
6281 |
? !(cg->treeContext.flags & TCF_IN_FUNCTION) |
? !(cg->flags & TCF_IN_FUNCTION) |
6282 |
: stmt->type == STMT_BLOCK)) { |
: stmt->type == STMT_BLOCK)) { |
6283 |
#if defined DEBUG_brendanXXX || defined DEBUG_mrbkap |
#if defined DEBUG_brendan || defined DEBUG_mrbkap |
6284 |
/* There must be no source note already output for the next op. */ |
/* There must be no source note already output for the next op. */ |
6285 |
JS_ASSERT(CG_NOTE_COUNT(cg) == 0 || |
JS_ASSERT(CG_NOTE_COUNT(cg) == 0 || |
6286 |
CG_LAST_NOTE_OFFSET(cg) != CG_OFFSET(cg) || |
CG_LAST_NOTE_OFFSET(cg) != CG_OFFSET(cg) || |
6292 |
} |
} |
6293 |
|
|
6294 |
JS_ASSERT(CG_OFFSET(cg) == top); |
JS_ASSERT(CG_OFFSET(cg) == top); |
6295 |
if (!EmitObjectOp(cx, pob, JSOP_ENTERBLOCK, cg)) |
if (!EmitEnterBlock(cx, pn, cg)) |
6296 |
return JS_FALSE; |
return JS_FALSE; |
6297 |
|
|
6298 |
if (!js_EmitTree(cx, cg, pn->pn_expr)) |
if (!js_EmitTree(cx, cg, pn->pn_expr)) |
6311 |
} |
} |
6312 |
|
|
6313 |
/* Emit the JSOP_LEAVEBLOCK or JSOP_LEAVEBLOCKEXPR opcode. */ |
/* Emit the JSOP_LEAVEBLOCK or JSOP_LEAVEBLOCKEXPR opcode. */ |
6314 |
count = OBJ_BLOCK_COUNT(cx, pob->object); |
count = OBJ_BLOCK_COUNT(cx, objbox->object); |
6315 |
EMIT_UINT16_IMM_OP(op, count); |
EMIT_UINT16_IMM_OP(op, count); |
6316 |
|
|
6317 |
ok = js_PopStatementCG(cx, cg); |
ok = js_PopStatementCG(cx, cg); |
6357 |
*/ |
*/ |
6358 |
if (!js_EmitTree(cx, cg, pn->pn_kid)) |
if (!js_EmitTree(cx, cg, pn->pn_kid)) |
6359 |
return JS_FALSE; |
return JS_FALSE; |
6360 |
slot = cg->arrayCompDepth; |
slot = AdjustBlockSlot(cx, cg, cg->arrayCompDepth); |
|
slot = AdjustBlockSlot(cx, cg, slot); |
|
6361 |
if (slot < 0) |
if (slot < 0) |
6362 |
return JS_FALSE; |
return JS_FALSE; |
6363 |
EMIT_UINT16_IMM_OP(PN_OP(pn), slot); |
EMIT_UINT16_IMM_OP(PN_OP(pn), slot); |
6370 |
case TOK_ARRAYCOMP: |
case TOK_ARRAYCOMP: |
6371 |
#endif |
#endif |
6372 |
/* |
/* |
6373 |
* Emit code for [a, b, c] of the form: |
* Emit code for [a, b, c] that is equivalent to constructing a new |
6374 |
* |
* array and in source order evaluating each element value and adding |
6375 |
* t = new Array; t[0] = a; t[1] = b; t[2] = c; t; |
* it to the array, without invoking latent setters. We use the |
6376 |
* |
* JSOP_NEWINIT and JSOP_INITELEM bytecodes to ignore setters and to |
6377 |
* but use a stack slot for t and avoid dup'ing and popping it using |
* avoid dup'ing and popping the array as each element is added, as |
6378 |
* the JSOP_NEWINIT and JSOP_INITELEM bytecodes. |
* JSOP_SETELEM/JSOP_SETPROP would do. |
6379 |
* |
* |
6380 |
* If no sharp variable is defined and the initialiser is not for an |
* If no sharp variable is defined, the initializer is not for an array |
6381 |
* array comprehension, use JSOP_NEWARRAY. |
* comprehension, the initializer is not overlarge, and the initializer |
6382 |
|
* is not in global code (whose stack growth cannot be precisely modeled |
6383 |
|
* due to the need to reserve space for global variables and regular |
6384 |
|
* expressions), use JSOP_NEWARRAY to minimize opcodes and to create the |
6385 |
|
* array using a fast, all-at-once process rather than a slow, element- |
6386 |
|
* by-element process. |
6387 |
*/ |
*/ |
|
pn2 = pn->pn_head; |
|
|
op = JSOP_NEWINIT; // FIXME: 260106 patch disabled for now |
|
|
|
|
6388 |
#if JS_HAS_SHARP_VARS |
#if JS_HAS_SHARP_VARS |
6389 |
if (pn2 && pn2->pn_type == TOK_DEFSHARP) |
sharpnum = -1; |
6390 |
op = JSOP_NEWINIT; |
do_emit_array: |
6391 |
#endif |
#endif |
6392 |
|
|
6393 |
|
op = (JS_LIKELY(pn->pn_count < JS_BIT(16)) && (cg->flags & TCF_IN_FUNCTION)) |
6394 |
|
? JSOP_NEWARRAY |
6395 |
|
: JSOP_NEWINIT; |
6396 |
|
|
6397 |
#if JS_HAS_GENERATORS |
#if JS_HAS_GENERATORS |
6398 |
if (pn->pn_type == TOK_ARRAYCOMP) |
if (pn->pn_type == TOK_ARRAYCOMP) |
6399 |
op = JSOP_NEWINIT; |
op = JSOP_NEWINIT; |
6400 |
#endif |
#endif |
6401 |
|
#if JS_HAS_SHARP_VARS |
6402 |
|
JS_ASSERT_IF(sharpnum >= 0, cg->flags & TCF_HAS_SHARPS); |
6403 |
|
if (cg->flags & TCF_HAS_SHARPS) |
6404 |
|
op = JSOP_NEWINIT; |
6405 |
|
#endif |
6406 |
|
|
6407 |
if (op == JSOP_NEWINIT && |
if (op == JSOP_NEWINIT) { |
6408 |
js_Emit2(cx, cg, op, (jsbytecode) JSProto_Array) < 0) { |
if (js_Emit2(cx, cg, op, (jsbytecode) JSProto_Array) < 0) |
6409 |
return JS_FALSE; |
return JS_FALSE; |
|
} |
|
|
|
|
6410 |
#if JS_HAS_SHARP_VARS |
#if JS_HAS_SHARP_VARS |
6411 |
if (pn2 && pn2->pn_type == TOK_DEFSHARP) { |
if (sharpnum >= 0) |
6412 |
EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid)pn2->pn_num); |
EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid) sharpnum); |
6413 |
pn2 = pn2->pn_next; |
# endif |
6414 |
} |
} |
|
#endif |
|
6415 |
|
|
6416 |
#if JS_HAS_GENERATORS |
#if JS_HAS_GENERATORS |
6417 |
if (pn->pn_type == TOK_ARRAYCOMP) { |
if (pn->pn_type == TOK_ARRAYCOMP) { |
6418 |
uintN saveDepth; |
uintN saveDepth; |
6419 |
|
|
6420 |
/* |
/* |
6421 |
* Pass the new array's stack index to the TOK_ARRAYPUSH case by |
* Pass the new array's stack index to the TOK_ARRAYPUSH case via |
6422 |
* storing it in pn->pn_extra, then simply traverse the TOK_FOR |
* cg->arrayCompDepth, then simply traverse the TOK_FOR node and |
6423 |
* node and its kids under pn2 to generate this comprehension. |
* its kids under pn2 to generate this comprehension. |
6424 |
*/ |
*/ |
6425 |
JS_ASSERT(cg->stackDepth > 0); |
JS_ASSERT(cg->stackDepth > 0); |
6426 |
saveDepth = cg->arrayCompDepth; |
saveDepth = cg->arrayCompDepth; |
6427 |
cg->arrayCompDepth = (uint32) (cg->stackDepth - 1); |
cg->arrayCompDepth = (uint32) (cg->stackDepth - 1); |
6428 |
if (!js_EmitTree(cx, cg, pn2)) |
if (!js_EmitTree(cx, cg, pn->pn_head)) |
6429 |
return JS_FALSE; |
return JS_FALSE; |
6430 |
cg->arrayCompDepth = saveDepth; |
cg->arrayCompDepth = saveDepth; |
6431 |
|
|
6436 |
} |
} |
6437 |
#endif /* JS_HAS_GENERATORS */ |
#endif /* JS_HAS_GENERATORS */ |
6438 |
|
|
6439 |
|
pn2 = pn->pn_head; |
6440 |
for (atomIndex = 0; pn2; atomIndex++, pn2 = pn2->pn_next) { |
for (atomIndex = 0; pn2; atomIndex++, pn2 = pn2->pn_next) { |
6441 |
if (op == JSOP_NEWINIT && !EmitNumberOp(cx, atomIndex, cg)) |
if (op == JSOP_NEWINIT && !EmitNumberOp(cx, atomIndex, cg)) |
6442 |
return JS_FALSE; |
return JS_FALSE; |
|
|
|
6443 |
if (pn2->pn_type == TOK_COMMA) { |
if (pn2->pn_type == TOK_COMMA) { |
6444 |
if (js_Emit1(cx, cg, JSOP_HOLE) < 0) |
if (js_Emit1(cx, cg, JSOP_HOLE) < 0) |
6445 |
return JS_FALSE; |
return JS_FALSE; |
6447 |
if (!js_EmitTree(cx, cg, pn2)) |
if (!js_EmitTree(cx, cg, pn2)) |
6448 |
return JS_FALSE; |
return JS_FALSE; |
6449 |
} |
} |
|
|
|
6450 |
if (op == JSOP_NEWINIT && js_Emit1(cx, cg, JSOP_INITELEM) < 0) |
if (op == JSOP_NEWINIT && js_Emit1(cx, cg, JSOP_INITELEM) < 0) |
6451 |
return JS_FALSE; |
return JS_FALSE; |
6452 |
} |
} |
6453 |
|
JS_ASSERT(atomIndex == pn->pn_count); |
6454 |
|
|
6455 |
if (pn->pn_extra & PNX_ENDCOMMA) { |
if (pn->pn_xflags & PNX_ENDCOMMA) { |
6456 |
/* Emit a source note so we know to decompile an extra comma. */ |
/* Emit a source note so we know to decompile an extra comma. */ |
6457 |
if (js_NewSrcNote(cx, cg, SRC_CONTINUE) < 0) |
if (js_NewSrcNote(cx, cg, SRC_CONTINUE) < 0) |
6458 |
return JS_FALSE; |
return JS_FALSE; |
6459 |
} |
} |
6460 |
|
|
6461 |
if (op == JSOP_NEWARRAY) { |
if (op == JSOP_NEWINIT) { |
6462 |
JS_ASSERT(atomIndex == pn->pn_count); |
/* |
6463 |
off = js_EmitN(cx, cg, op, 3); |
* Emit an op to finish the array and, secondarily, to aid in sharp |
6464 |
if (off < 0) |
* array cleanup (if JS_HAS_SHARP_VARS) and decompilation. |
6465 |
return JS_FALSE; |
*/ |
|
pc = CG_CODE(cg, off); |
|
|
SET_UINT24(pc, atomIndex); |
|
|
UpdateDepth(cx, cg, off); |
|
|
} else { |
|
|
/* Emit an op for sharp array cleanup and decompilation. */ |
|
6466 |
if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0) |
if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0) |
6467 |
return JS_FALSE; |
return JS_FALSE; |
6468 |
|
break; |
6469 |
} |
} |
6470 |
|
|
6471 |
|
JS_ASSERT(atomIndex < JS_BIT(16)); |
6472 |
|
EMIT_UINT16_IMM_OP(JSOP_NEWARRAY, atomIndex); |
6473 |
break; |
break; |
6474 |
|
|
6475 |
case TOK_RC: |
case TOK_RC: |
6476 |
|
#if JS_HAS_SHARP_VARS |
6477 |
|
sharpnum = -1; |
6478 |
|
do_emit_object: |
6479 |
|
#endif |
6480 |
#if JS_HAS_DESTRUCTURING_SHORTHAND |
#if JS_HAS_DESTRUCTURING_SHORTHAND |
6481 |
if (pn->pn_extra & PNX_SHORTHAND) { |
if (pn->pn_xflags & PNX_DESTRUCT) { |
6482 |
js_ReportCompileErrorNumber(cx, CG_TS(cg), pn, JSREPORT_ERROR, |
js_ReportCompileErrorNumber(cx, CG_TS(cg), pn, JSREPORT_ERROR, |
6483 |
JSMSG_BAD_OBJECT_INIT); |
JSMSG_BAD_OBJECT_INIT); |
6484 |
return JS_FALSE; |
return JS_FALSE; |
6485 |
} |
} |
6486 |
#endif |
#endif |
6487 |
/* |
/* |
6488 |
* Emit code for {p:a, '%q':b, 2:c} of the form: |
* Emit code for {p:a, '%q':b, 2:c} that is equivalent to constructing |
6489 |
* |
* a new object and in source order evaluating each property value and |
6490 |
* t = new Object; t.p = a; t['%q'] = b; t[2] = c; t; |
* adding the property to the object, without invoking latent setters. |
6491 |
* |
* We use the JSOP_NEWINIT and JSOP_INITELEM/JSOP_INITPROP bytecodes to |
6492 |
* but use a stack slot for t and avoid dup'ing and popping it via |
* ignore setters and to avoid dup'ing and popping the object as each |
6493 |
* the JSOP_NEWINIT and JSOP_INITELEM/JSOP_INITPROP bytecodes. |
* property is added, as JSOP_SETELEM/JSOP_SETPROP would do. |
6494 |
*/ |
*/ |
6495 |
if (js_Emit2(cx, cg, JSOP_NEWINIT, (jsbytecode) JSProto_Object) < 0) |
if (js_Emit2(cx, cg, JSOP_NEWINIT, (jsbytecode) JSProto_Object) < 0) |
6496 |
return JS_FALSE; |
return JS_FALSE; |
6497 |
|
|
|
pn2 = pn->pn_head; |
|
6498 |
#if JS_HAS_SHARP_VARS |
#if JS_HAS_SHARP_VARS |
6499 |
if (pn2 && pn2->pn_type == TOK_DEFSHARP) { |
if (sharpnum >= 0) |
6500 |
EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid)pn2->pn_num); |
EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid) sharpnum); |
|
pn2 = pn2->pn_next; |
|
|
} |
|
6501 |
#endif |
#endif |
6502 |
|
|
6503 |
for (; pn2; pn2 = pn2->pn_next) { |
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { |
6504 |
/* Emit an index for t[2], else map an atom for t.p or t['%q']. */ |
/* Emit an index for t[2] for later consumption by JSOP_INITELEM. */ |
6505 |
pn3 = pn2->pn_left; |
pn3 = pn2->pn_left; |
6506 |
if (pn3->pn_type == TOK_NUMBER) { |
if (pn3->pn_type == TOK_NUMBER) { |
|
#ifdef __GNUC__ |
|
|
ale = NULL; /* quell GCC overwarning */ |
|
|
#endif |
|
6507 |
if (!EmitNumberOp(cx, pn3->pn_dval, cg)) |
if (!EmitNumberOp(cx, pn3->pn_dval, cg)) |
6508 |
return JS_FALSE; |
return JS_FALSE; |
|
} else { |
|
|
JS_ASSERT(pn3->pn_type == TOK_NAME || |
|
|
pn3->pn_type == TOK_STRING); |
|
|
ale = js_IndexAtom(cx, pn3->pn_atom, &cg->atomList); |
|
|
if (!ale) |
|
|
return JS_FALSE; |
|
6509 |
} |
} |
6510 |
|
|
6511 |
/* Emit code for the property initializer. */ |
/* Emit code for the property initializer. */ |
6526 |
if (js_Emit1(cx, cg, JSOP_INITELEM) < 0) |
if (js_Emit1(cx, cg, JSOP_INITELEM) < 0) |
6527 |
return JS_FALSE; |
return JS_FALSE; |
6528 |
} else { |
} else { |
6529 |
|
JS_ASSERT(pn3->pn_type == TOK_NAME || |
6530 |
|
pn3->pn_type == TOK_STRING); |
6531 |
|
ale = cg->atomList.add(cg->compiler, pn3->pn_atom); |
6532 |
|
if (!ale) |
6533 |
|
return JS_FALSE; |
6534 |
EMIT_INDEX_OP(JSOP_INITPROP, ALE_INDEX(ale)); |
EMIT_INDEX_OP(JSOP_INITPROP, ALE_INDEX(ale)); |
6535 |
} |
} |
6536 |
} |
} |
6542 |
|
|
6543 |
#if JS_HAS_SHARP_VARS |
#if JS_HAS_SHARP_VARS |
6544 |
case TOK_DEFSHARP: |
case TOK_DEFSHARP: |
6545 |
if (!js_EmitTree(cx, cg, pn->pn_kid)) |
JS_ASSERT(cg->flags & TCF_HAS_SHARPS); |
6546 |
|
sharpnum = pn->pn_num; |
6547 |
|
pn = pn->pn_kid; |
6548 |
|
if (pn->pn_type == TOK_RB) |
6549 |
|
goto do_emit_array; |
6550 |
|
# if JS_HAS_GENERATORS |
6551 |
|
if (pn->pn_type == TOK_ARRAYCOMP) |
6552 |
|
goto do_emit_array; |
6553 |
|
# endif |
6554 |
|
if (pn->pn_type == TOK_RC) |
6555 |
|
goto do_emit_object; |
6556 |
|
|
6557 |
|
if (!js_EmitTree(cx, cg, pn)) |
6558 |
return JS_FALSE; |
return JS_FALSE; |
6559 |
EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid) pn->pn_num); |
EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid) sharpnum); |
6560 |
break; |
break; |
6561 |
|
|
6562 |
case TOK_USESHARP: |
case TOK_USESHARP: |
6563 |
|
JS_ASSERT(cg->flags & TCF_HAS_SHARPS); |
6564 |
EMIT_UINT16_IMM_OP(JSOP_USESHARP, (jsatomid) pn->pn_num); |
EMIT_UINT16_IMM_OP(JSOP_USESHARP, (jsatomid) pn->pn_num); |
6565 |
break; |
break; |
6566 |
#endif /* JS_HAS_SHARP_VARS */ |
#endif /* JS_HAS_SHARP_VARS */ |
6574 |
* assignment expressions in conditions to avoid the error correction |
* assignment expressions in conditions to avoid the error correction |
6575 |
* done by Condition (from x = y to x == y) by double-parenthesizing. |
* done by Condition (from x = y to x == y) by double-parenthesizing. |
6576 |
*/ |
*/ |
6577 |
oldflags = cg->treeContext.flags; |
oldflags = cg->flags; |
6578 |
cg->treeContext.flags &= ~TCF_IN_FOR_INIT; |
cg->flags &= ~TCF_IN_FOR_INIT; |
6579 |
if (!js_EmitTree(cx, cg, pn->pn_kid)) |
if (!js_EmitTree(cx, cg, pn->pn_kid)) |
6580 |
return JS_FALSE; |
return JS_FALSE; |
6581 |
cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT; |
cg->flags |= oldflags & TCF_IN_FOR_INIT; |
6582 |
break; |
break; |
6583 |
} |
} |
6584 |
|
|
6585 |
case TOK_NAME: |
case TOK_NAME: |
6586 |
|
/* |
6587 |
|
* Cope with a left-over function definition that was replaced by a use |
6588 |
|
* of a later function definition of the same name. See FunctionDef and |
6589 |
|
* MakeDefIntoUse in jsparse.cpp. |
6590 |
|
*/ |
6591 |
|
if (pn->pn_op == JSOP_NOP) |
6592 |
|
return JS_TRUE; |
6593 |
if (!EmitNameOp(cx, cg, pn, JS_FALSE)) |
if (!EmitNameOp(cx, cg, pn, JS_FALSE)) |
6594 |
return JS_FALSE; |
return JS_FALSE; |
6595 |
break; |
break; |
6618 |
* select JSOP_REGEXP. |
* select JSOP_REGEXP. |
6619 |
*/ |
*/ |
6620 |
JS_ASSERT(pn->pn_op == JSOP_REGEXP); |
JS_ASSERT(pn->pn_op == JSOP_REGEXP); |
6621 |
if (cg->treeContext.flags & TCF_COMPILE_N_GO) { |
if (cg->flags & TCF_COMPILE_N_GO) { |
6622 |
ok = EmitObjectOp(cx, pn->pn_pob, JSOP_OBJECT, cg); |
ok = EmitObjectOp(cx, pn->pn_objbox, JSOP_OBJECT, cg); |
6623 |
} else { |
} else { |
6624 |
ok = EmitIndexOp(cx, JSOP_REGEXP, |
ok = EmitIndexOp(cx, JSOP_REGEXP, |
6625 |
IndexParsedObject(pn->pn_pob, &cg->regexpList), |
cg->regexpList.index(pn->pn_objbox), |
6626 |
cg); |
cg); |
6627 |
} |
} |
6628 |
break; |
break; |
6646 |
case TOK_XMLELEM: |
case TOK_XMLELEM: |
6647 |
case TOK_XMLLIST: |
case TOK_XMLLIST: |
6648 |
if (pn->pn_op == JSOP_XMLOBJECT) { |
if (pn->pn_op == JSOP_XMLOBJECT) { |
6649 |
ok = EmitObjectOp(cx, pn->pn_pob, PN_OP(pn), cg); |
ok = EmitObjectOp(cx, pn->pn_objbox, PN_OP(pn), cg); |
6650 |
break; |
break; |
6651 |
} |
} |
6652 |
|
|
6653 |
JS_ASSERT(pn->pn_type == TOK_XMLLIST || pn->pn_count != 0); |
JS_ASSERT(PN_TYPE(pn) == TOK_XMLLIST || pn->pn_count != 0); |
6654 |
switch (pn->pn_head ? pn->pn_head->pn_type : TOK_XMLLIST) { |
switch (pn->pn_head ? PN_TYPE(pn->pn_head) : TOK_XMLLIST) { |
6655 |
case TOK_XMLETAGO: |
case TOK_XMLETAGO: |
6656 |
JS_ASSERT(0); |
JS_ASSERT(0); |
6657 |
/* FALL THROUGH */ |
/* FALL THROUGH */ |
6674 |
return JS_FALSE; |
return JS_FALSE; |
6675 |
} |
} |
6676 |
|
|
6677 |
if (pn->pn_extra & PNX_XMLROOT) { |
if (pn->pn_xflags & PNX_XMLROOT) { |
6678 |
if (pn->pn_count == 0) { |
if (pn->pn_count == 0) { |
6679 |
JS_ASSERT(pn->pn_type == TOK_XMLLIST); |
JS_ASSERT(pn->pn_type == TOK_XMLLIST); |
6680 |
atom = cx->runtime->atomState.emptyAtom; |
atom = cx->runtime->atomState.emptyAtom; |
6681 |
ale = js_IndexAtom(cx, atom, &cg->atomList); |
ale = cg->atomList.add(cg->compiler, atom); |
6682 |
if (!ale) |
if (!ale) |
6683 |
return JS_FALSE; |
return JS_FALSE; |
6684 |
EMIT_INDEX_OP(JSOP_STRING, ALE_INDEX(ale)); |
EMIT_INDEX_OP(JSOP_STRING, ALE_INDEX(ale)); |
6694 |
|
|
6695 |
case TOK_XMLPTAGC: |
case TOK_XMLPTAGC: |
6696 |
if (pn->pn_op == JSOP_XMLOBJECT) { |
if (pn->pn_op == JSOP_XMLOBJECT) { |
6697 |
ok = EmitObjectOp(cx, pn->pn_pob, PN_OP(pn), cg); |
ok = EmitObjectOp(cx, pn->pn_objbox, PN_OP(pn), cg); |
6698 |
break; |
break; |
6699 |
} |
} |
6700 |
/* FALL THROUGH */ |
/* FALL THROUGH */ |
6707 |
if (js_Emit1(cx, cg, JSOP_STARTXML) < 0) |
if (js_Emit1(cx, cg, JSOP_STARTXML) < 0) |
6708 |
return JS_FALSE; |
return JS_FALSE; |
6709 |
|
|
6710 |
ale = js_IndexAtom(cx, |
ale = cg->atomList.add(cg->compiler, |
6711 |
(pn->pn_type == TOK_XMLETAGO) |
(pn->pn_type == TOK_XMLETAGO) |
6712 |
? cx->runtime->atomState.etagoAtom |
? cx->runtime->atomState.etagoAtom |
6713 |
: cx->runtime->atomState.stagoAtom, |
: cx->runtime->atomState.stagoAtom); |
|
&cg->atomList); |
|
6714 |
if (!ale) |
if (!ale) |
6715 |
return JS_FALSE; |
return JS_FALSE; |
6716 |
EMIT_INDEX_OP(JSOP_STRING, ALE_INDEX(ale)); |
EMIT_INDEX_OP(JSOP_STRING, ALE_INDEX(ale)); |
6741 |
} |
} |
6742 |
} |
} |
6743 |
|
|
6744 |
ale = js_IndexAtom(cx, |
ale = cg->atomList.add(cg->compiler, |
6745 |
(pn->pn_type == TOK_XMLPTAGC) |
(pn->pn_type == TOK_XMLPTAGC) |
6746 |
? cx->runtime->atomState.ptagcAtom |
? cx->runtime->atomState.ptagcAtom |
6747 |
: cx->runtime->atomState.tagcAtom, |
: cx->runtime->atomState.tagcAtom); |
|
&cg->atomList); |
|
6748 |
if (!ale) |
if (!ale) |
6749 |
return JS_FALSE; |
return JS_FALSE; |
6750 |
EMIT_INDEX_OP(JSOP_STRING, ALE_INDEX(ale)); |
EMIT_INDEX_OP(JSOP_STRING, ALE_INDEX(ale)); |
6751 |
if (js_Emit1(cx, cg, JSOP_ADD) < 0) |
if (js_Emit1(cx, cg, JSOP_ADD) < 0) |
6752 |
return JS_FALSE; |
return JS_FALSE; |
6753 |
|
|
6754 |
if ((pn->pn_extra & PNX_XMLROOT) && js_Emit1(cx, cg, PN_OP(pn)) < 0) |
if ((pn->pn_xflags & PNX_XMLROOT) && js_Emit1(cx, cg, PN_OP(pn)) < 0) |
6755 |
return JS_FALSE; |
return JS_FALSE; |
6756 |
break; |
break; |
6757 |
} |
} |
6760 |
if (pn->pn_arity == PN_LIST) { |
if (pn->pn_arity == PN_LIST) { |
6761 |
JS_ASSERT(pn->pn_count != 0); |
JS_ASSERT(pn->pn_count != 0); |
6762 |
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { |
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { |
6763 |
|
if (pn2->pn_type == TOK_LC && |
6764 |
|
js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) { |
6765 |
|
return JS_FALSE; |
6766 |
|
} |
6767 |
if (!js_EmitTree(cx, cg, pn2)) |
if (!js_EmitTree(cx, cg, pn2)) |
6768 |
return JS_FALSE; |
return JS_FALSE; |
6769 |
if (pn2 != pn->pn_head && js_Emit1(cx, cg, JSOP_ADD) < 0) |
if (pn2 != pn->pn_head && js_Emit1(cx, cg, JSOP_ADD) < 0) |
6772 |
} else { |
} else { |
6773 |
JS_ASSERT(pn->pn_arity == PN_NULLARY); |
JS_ASSERT(pn->pn_arity == PN_NULLARY); |
6774 |
ok = (pn->pn_op == JSOP_OBJECT) |
ok = (pn->pn_op == JSOP_OBJECT) |
6775 |
? EmitObjectOp(cx, pn->pn_pob, PN_OP(pn), cg) |
? EmitObjectOp(cx, pn->pn_objbox, PN_OP(pn), cg) |
6776 |
: EmitAtomOp(cx, pn, PN_OP(pn), cg); |
: EmitAtomOp(cx, pn, PN_OP(pn), cg); |
6777 |
} |
} |
6778 |
break; |
break; |
6779 |
|
|
6780 |
case TOK_XMLPI: |
case TOK_XMLPI: |
6781 |
ale = js_IndexAtom(cx, pn->pn_atom2, &cg->atomList); |
ale = cg->atomList.add(cg->compiler, pn->pn_atom2); |
6782 |
if (!ale) |
if (!ale) |
6783 |
return JS_FALSE; |
return JS_FALSE; |
6784 |
if (!EmitIndexOp(cx, JSOP_QNAMEPART, ALE_INDEX(ale), cg)) |
if (!EmitIndexOp(cx, JSOP_QNAMEPART, ALE_INDEX(ale), cg)) |
6792 |
JS_ASSERT(0); |
JS_ASSERT(0); |
6793 |
} |
} |
6794 |
|
|
6795 |
if (ok && --cg->emitLevel == 0 && cg->spanDeps) |
if (ok && --cg->emitLevel == 0) { |
6796 |
ok = OptimizeSpanDeps(cx, cg); |
if (cg->spanDeps) |
6797 |
|
ok = OptimizeSpanDeps(cx, cg); |
6798 |
|
if (!UpdateLineNumberNotes(cx, cg, pn->pn_pos.end.lineno)) |
6799 |
|
return JS_FALSE; |
6800 |
|
} |
6801 |
|
|
6802 |
return ok; |
return ok; |
6803 |
} |
} |
7057 |
|
|
7058 |
/* |
/* |
7059 |
* Simultaneously test to see if the source note array must grow to |
* Simultaneously test to see if the source note array must grow to |
7060 |
* accomodate either the first or second byte of additional storage |
* accommodate either the first or second byte of additional storage |
7061 |
* required by this 3-byte offset. |
* required by this 3-byte offset. |
7062 |
*/ |
*/ |
7063 |
if (((CG_NOTE_COUNT(cg) + 1) & CG_NOTE_MASK(cg)) <= 1) { |
if (((CG_NOTE_COUNT(cg) + 1) & CG_NOTE_MASK(cg)) <= 1) { |
7226 |
* Since the emitter refers to each parsed object only once, for the index we |
* Since the emitter refers to each parsed object only once, for the index we |
7227 |
* use the number of already indexes objects. We also add the object to a list |
* use the number of already indexes objects. We also add the object to a list |
7228 |
* to convert the list to a fixed-size array when we complete code generation, |
* to convert the list to a fixed-size array when we complete code generation, |
7229 |
* see FinishParsedObjects bellow. |
* see JSCGObjectList::finish below. |
7230 |
* |
* |
7231 |
* Most of the objects go to JSCodeGenerator.objectList but for regexp we use |
* Most of the objects go to JSCodeGenerator.objectList but for regexp we use a |
7232 |
* a separated JSCodeGenerator.regexpList. In this way the emitted index can |
* separated JSCodeGenerator.regexpList. In this way the emitted index can be |
7233 |
* be directly used to store and fetch a reference to a cloned RegExp object |
* directly used to store and fetch a reference to a cloned RegExp object that |
7234 |
* that shares the same JSRegExp private data created for the object literal |
* shares the same JSRegExp private data created for the object literal in |
7235 |
* in pob. We need clones to hold lastIndex and other direct properties that |
* objbox. We need a cloned object to hold lastIndex and other direct properties |
7236 |
* should not be shared among threads sharing a precompiled function or |
* that should not be shared among threads sharing a precompiled function or |
7237 |
* script. |
* script. |
7238 |
* |
* |
7239 |
* If the code being compiled is function code, allocate a reserved slot in |
* If the code being compiled is function code, allocate a reserved slot in |
7245 |
* one of the slots that should be given to a regexp clone. |
* one of the slots that should be given to a regexp clone. |
7246 |
* |
* |
7247 |
* If the code being compiled is global code, the cloned regexp are stored in |
* If the code being compiled is global code, the cloned regexp are stored in |
7248 |
* fp->vars slot after cg->treeContext.ngvars and to protect regexp slots from |
* fp->vars slot after cg->ngvars and to protect regexp slots from GC we set |
7249 |
* GC we set fp->nvars to ngvars + nregexps. |
* fp->nvars to ngvars + nregexps. |
7250 |
* |
* |
7251 |
* The slots initially contain undefined or null. We populate them lazily when |
* The slots initially contain undefined or null. We populate them lazily when |
7252 |
* JSOP_REGEXP is executed for the first time. |
* JSOP_REGEXP is executed for the first time. |
7263 |
* __parent__ refer to the pre-compilation prototype and global objects, a |
* __parent__ refer to the pre-compilation prototype and global objects, a |
7264 |
* pigeon-hole problem for instanceof tests. |
* pigeon-hole problem for instanceof tests. |
7265 |
*/ |
*/ |
7266 |
static uintN |
uintN |
7267 |
IndexParsedObject(JSParsedObjectBox *pob, JSEmittedObjectList *list) |
JSCGObjectList::index(JSObjectBox *objbox) |
7268 |
{ |
{ |
7269 |
JS_ASSERT(!pob->emitLink); |
JS_ASSERT(!objbox->emitLink); |
7270 |
pob->emitLink = list->lastPob; |
objbox->emitLink = lastbox; |
7271 |
list->lastPob = pob; |
lastbox = objbox; |
7272 |
return list->length++; |
return length++; |
7273 |
} |
} |
7274 |
|
|
7275 |
void |
void |
7276 |
FinishParsedObjects(JSEmittedObjectList *emittedList, JSObjectArray *array) |
JSCGObjectList::finish(JSObjectArray *array) |
7277 |
{ |
{ |
7278 |
JSObject **cursor; |
JSObject **cursor; |
7279 |
JSParsedObjectBox *pob; |
JSObjectBox *objbox; |
7280 |
|
|
7281 |
JS_ASSERT(emittedList->length <= INDEX_LIMIT); |
JS_ASSERT(length <= INDEX_LIMIT); |
7282 |
JS_ASSERT(emittedList->length == array->length); |
JS_ASSERT(length == array->length); |
7283 |
|
|
7284 |
cursor = array->vector + array->length; |
cursor = array->vector + array->length; |
7285 |
pob = emittedList->lastPob; |
objbox = lastbox; |
7286 |
do { |
do { |
7287 |
--cursor; |
--cursor; |
7288 |
JS_ASSERT(!*cursor); |
JS_ASSERT(!*cursor); |
7289 |
*cursor = pob->object; |
*cursor = objbox->object; |
7290 |
} while ((pob = pob->emitLink) != NULL); |
} while ((objbox = objbox->emitLink) != NULL); |
7291 |
JS_ASSERT(cursor == array->vector); |
JS_ASSERT(cursor == array->vector); |
7292 |
} |
} |