зеркало из https://github.com/mozilla/gecko-dev.git
Bug 349326: for-in loop now always closes iterator objects. r=brendan
This commit is contained in:
Родитель
7d3de0c8ce
Коммит
5414afce47
|
@ -1070,8 +1070,9 @@ Notes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|||
|
||||
JS_STATIC_ASSERT(JSTN_CATCH == 0);
|
||||
JS_STATIC_ASSERT(JSTN_FINALLY == 1);
|
||||
JS_STATIC_ASSERT(JSTN_ITER == 2);
|
||||
|
||||
static const char* const TryNoteNames[] = { "catch", "finally" };
|
||||
static const char* const TryNoteNames[] = { "catch", "finally", "iter" };
|
||||
|
||||
static JSBool
|
||||
TryNotes(JSContext *cx, JSScript *script)
|
||||
|
@ -1086,7 +1087,7 @@ TryNotes(JSContext *cx, JSScript *script)
|
|||
fprintf(gOutFile, "\nException table:\n"
|
||||
"kind stack start end\n");
|
||||
do {
|
||||
JS_ASSERT(tn->kind == JSTN_CATCH || tn->kind == JSTN_FINALLY);
|
||||
JS_ASSERT(tn->kind < JS_ARRAY_LENGTH(TryNoteNames));
|
||||
fprintf(gOutFile, " %-7s %6u %8u %8u\n",
|
||||
TryNoteNames[tn->kind], tn->stackDepth,
|
||||
tn->start, tn->start + tn->length);
|
||||
|
|
142
js/src/jsemit.c
142
js/src/jsemit.c
|
@ -75,6 +75,10 @@
|
|||
#define SRCNOTE_SIZE(n) ((n) * sizeof(jssrcnote))
|
||||
#define TRYNOTE_SIZE(n) ((n) * sizeof(JSTryNote))
|
||||
|
||||
static JSBool
|
||||
NewTryNote(JSContext *cx, JSCodeGenerator *cg, JSTryNoteKind kind,
|
||||
uintN stackDepth, size_t start, size_t end);
|
||||
|
||||
JS_FRIEND_API(JSBool)
|
||||
js_InitCodeGenerator(JSContext *cx, JSCodeGenerator *cg,
|
||||
JSArenaPool *codePool, JSArenaPool *notePool,
|
||||
|
@ -756,7 +760,7 @@ OptimizeSpanDeps(JSContext *cx, JSCodeGenerator *cg)
|
|||
jssrcnote *sn, *snlimit;
|
||||
JSSrcNoteSpec *spec;
|
||||
uintN i, n, noteIndex;
|
||||
JSTryNote *tn, *tnlimit;
|
||||
JSTryNode *tryNode;
|
||||
#ifdef DEBUG_brendan
|
||||
int passes = 0;
|
||||
#endif
|
||||
|
@ -1064,25 +1068,27 @@ OptimizeSpanDeps(JSContext *cx, JSCodeGenerator *cg)
|
|||
* Fix try/catch notes (O(numTryNotes * log2(numSpanDeps)), but it's
|
||||
* not clear how we can beat that).
|
||||
*/
|
||||
for (tn = cg->tryBase, tnlimit = cg->tryNext; tn < tnlimit; tn++) {
|
||||
for (tryNode = cg->lastTryNode; tryNode; tryNode = tryNode->prev) {
|
||||
/*
|
||||
* First, look for the nearest span dependency at/above tn->start.
|
||||
* There may not be any such spandep, in which case the guard will
|
||||
* be returned.
|
||||
*/
|
||||
offset = tn->start;
|
||||
offset = tryNode->note.start;
|
||||
sd = FindNearestSpanDep(cg, offset, 0, &guard);
|
||||
delta = sd->offset - sd->before;
|
||||
tn->start = offset + delta;
|
||||
tryNode->note.start = offset + delta;
|
||||
|
||||
/*
|
||||
* Next, find the nearest spandep at/above tn->start + tn->length.
|
||||
* Use its delta minus tn->start's delta to increase tn->length.
|
||||
*/
|
||||
length = tn->length;
|
||||
length = tryNode->note.length;
|
||||
sd2 = FindNearestSpanDep(cg, offset + length, sd - sdbase, &guard);
|
||||
if (sd2 != sd)
|
||||
tn->length = length + sd2->offset - sd2->before - delta;
|
||||
if (sd2 != sd) {
|
||||
tryNode->note.length =
|
||||
length + sd2->offset - sd2->before - delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1330,19 +1336,21 @@ EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
|
|||
ptrdiff_t jmp;
|
||||
|
||||
/*
|
||||
* Return from within a try block that has a finally clause must be split
|
||||
* into two ops: JSOP_SETRVAL, to pop the r.v. and store it in fp->rval;
|
||||
* and JSOP_RETRVAL, which makes control flow go back to the caller, who
|
||||
* picks up fp->rval as usual. Otherwise, the stack will be unbalanced
|
||||
* when executing the finally clause.
|
||||
* Return from a try block that has a finally clause or from a for-in loop
|
||||
* must be split into two ops: JSOP_SETRVAL, to pop the r.v. and store it
|
||||
* in fp->rval; and JSOP_RETRVAL, which makes control flow go back to the
|
||||
* caller, who picks up fp->rval as usual. Otherwise, the stack will be
|
||||
* unbalanced when executing the finally clause.
|
||||
*
|
||||
* We mutate *returnop once only if we find an enclosing try-block (viz,
|
||||
* STMT_FINALLY) to ensure that we emit just one JSOP_SETRVAL before one
|
||||
* or more JSOP_GOSUBs and other fixup opcodes emitted by this function.
|
||||
* STMT_FINALLY) or a for-in loop to ensure that we emit just one
|
||||
* JSOP_SETRVAL before one or more JSOP_GOSUBs/JSOP_ENDITERs and other
|
||||
* fixup opcodes emitted by this function.
|
||||
*
|
||||
* Our caller (the TOK_RETURN case of js_EmitTree) then emits *returnop.
|
||||
* The fixup opcodes and gosubs must interleave in the proper order, from
|
||||
* inner statement to outer, so that finally clauses run at the correct
|
||||
* stack depth.
|
||||
* The fixup opcodes and gosubs/enditers must interleave in the proper
|
||||
* order, from inner statement to outer, so that finally clauses run at
|
||||
* the correct stack depth.
|
||||
*/
|
||||
if (returnop) {
|
||||
JS_ASSERT(*returnop == JSOP_RETURN);
|
||||
|
@ -1350,7 +1358,8 @@ EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
|
|||
stmt = stmt->down) {
|
||||
if (stmt->type == STMT_FINALLY ||
|
||||
((cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT) &&
|
||||
STMT_MAYBE_SCOPE(stmt))) {
|
||||
STMT_MAYBE_SCOPE(stmt)) ||
|
||||
stmt->type == STMT_FOR_IN_LOOP) {
|
||||
if (js_Emit1(cx, cg, JSOP_SETRVAL) < 0)
|
||||
return JS_FALSE;
|
||||
*returnop = JSOP_RETRVAL;
|
||||
|
@ -3225,9 +3234,6 @@ bad:
|
|||
JSBool
|
||||
js_EmitFunctionBytecode(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body)
|
||||
{
|
||||
if (!js_AllocTryNotes(cx, cg))
|
||||
return JS_FALSE;
|
||||
|
||||
if (cg->treeContext.flags & TCF_FUN_IS_GENERATOR) {
|
||||
if (js_Emit1(cx, cg, JSOP_GENERATOR) < 0)
|
||||
return JS_FALSE;
|
||||
|
@ -4063,7 +4069,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
return JS_FALSE;
|
||||
}
|
||||
cg2->treeContext.flags = (uint16) (pn->pn_flags | TCF_IN_FUNCTION);
|
||||
cg2->treeContext.tryCount = pn->pn_tryCount;
|
||||
cg2->parent = cg;
|
||||
fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(pn->pn_funAtom));
|
||||
if (!js_EmitFunctionBody(cx, cg2, pn->pn_body, fun))
|
||||
|
@ -4734,8 +4739,16 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
return JS_FALSE;
|
||||
|
||||
if (pn2->pn_type == TOK_IN) {
|
||||
if (js_Emit1(cx, cg, JSOP_ENDITER) < 0)
|
||||
/*
|
||||
* JSOP_ENDITER needs a slot to save an exception thrown from the
|
||||
* body of for-in loop when closing the iterator object.
|
||||
*/
|
||||
JS_ASSERT(js_CodeSpec[JSOP_ENDITER].format & JOF_TMPSLOT);
|
||||
if (!NewTryNote(cx, cg, JSTN_ITER, cg->stackDepth, top,
|
||||
CG_OFFSET(cg)) ||
|
||||
js_Emit1(cx, cg, JSOP_ENDITER) < 0) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -5057,7 +5070,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
* (first to last for a given nesting level, inner to outer by level).
|
||||
*/
|
||||
if (pn->pn_kid2 &&
|
||||
!js_NewTryNote(cx, cg, JSTN_CATCH, depth, tryStart, tryEnd)) {
|
||||
!NewTryNote(cx, cg, JSTN_CATCH, depth, tryStart, tryEnd)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
|
@ -5067,8 +5080,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
* for the try{}finally{} case.
|
||||
*/
|
||||
if (pn->pn_kid3 &&
|
||||
!js_NewTryNote(cx, cg, JSTN_FINALLY, depth, tryStart,
|
||||
finallyStart)) {
|
||||
!NewTryNote(cx, cg, JSTN_FINALLY, depth, tryStart, finallyStart)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
break;
|
||||
|
@ -6834,69 +6846,43 @@ js_FinishTakingSrcNotes(JSContext *cx, JSCodeGenerator *cg, jssrcnote *notes)
|
|||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg)
|
||||
{
|
||||
size_t size, incr;
|
||||
ptrdiff_t delta;
|
||||
|
||||
size = TRYNOTE_SIZE(cg->treeContext.tryCount);
|
||||
if (size <= cg->tryNoteSpace)
|
||||
return JS_TRUE;
|
||||
|
||||
/*
|
||||
* Allocate trynotes from cx->tempPool.
|
||||
* XXX Too much growing and we bloat, as other tempPool allocators block
|
||||
* in-place growth, and we never recycle old free space in an arena.
|
||||
* YYY But once we consume an entire arena, we'll realloc it, letting the
|
||||
* malloc heap recycle old space, while still freeing _en masse_ via the
|
||||
* arena pool.
|
||||
*/
|
||||
if (!cg->tryBase) {
|
||||
size = JS_ROUNDUP(size, TRYNOTE_SIZE(TRYNOTE_CHUNK));
|
||||
JS_ARENA_ALLOCATE_CAST(cg->tryBase, JSTryNote *, &cx->tempPool, size);
|
||||
if (!cg->tryBase)
|
||||
return JS_FALSE;
|
||||
cg->tryNoteSpace = size;
|
||||
cg->tryNext = cg->tryBase;
|
||||
} else {
|
||||
delta = PTRDIFF((char *)cg->tryNext, (char *)cg->tryBase, char);
|
||||
incr = size - cg->tryNoteSpace;
|
||||
incr = JS_ROUNDUP(incr, TRYNOTE_SIZE(TRYNOTE_CHUNK));
|
||||
size = cg->tryNoteSpace;
|
||||
JS_ARENA_GROW_CAST(cg->tryBase, JSTryNote *, &cx->tempPool, size, incr);
|
||||
if (!cg->tryBase)
|
||||
return JS_FALSE;
|
||||
cg->tryNoteSpace = size + incr;
|
||||
cg->tryNext = (JSTryNote *)((char *)cg->tryBase + delta);
|
||||
}
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSTryNote *
|
||||
js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, JSTryNoteKind kind,
|
||||
static JSBool
|
||||
NewTryNote(JSContext *cx, JSCodeGenerator *cg, JSTryNoteKind kind,
|
||||
uintN stackDepth, size_t start, size_t end)
|
||||
{
|
||||
JSTryNote *tn;
|
||||
JSTryNode *tryNode;
|
||||
|
||||
JS_ASSERT(cg->tryBase <= cg->tryNext);
|
||||
JS_ASSERT(kind == JSTN_FINALLY || kind == JSTN_CATCH);
|
||||
JS_ASSERT((uintN)(uint16)stackDepth == stackDepth);
|
||||
JS_ASSERT(start <= end);
|
||||
JS_ASSERT((size_t)(uint32)start == start);
|
||||
JS_ASSERT((size_t)(uint32)end == end);
|
||||
tn = cg->tryNext++;
|
||||
tn->kind = kind;
|
||||
tn->stackDepth = (uint16)stackDepth;
|
||||
tn->start = (uint32)start;
|
||||
tn->length = (uint32)(end - start);
|
||||
return tn;
|
||||
|
||||
JS_ARENA_ALLOCATE_TYPE(tryNode, JSTryNode, &cx->tempPool);
|
||||
if (!tryNode)
|
||||
return JS_FALSE;
|
||||
|
||||
tryNode->note.kind = kind;
|
||||
tryNode->note.stackDepth = (uint16)stackDepth;
|
||||
tryNode->note.start = (uint32)start;
|
||||
tryNode->note.length = (uint32)(end - start);
|
||||
tryNode->prev = cg->lastTryNode;
|
||||
cg->lastTryNode = tryNode;
|
||||
cg->ntrynotes++;
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg,
|
||||
JSTryNoteArray *array)
|
||||
{
|
||||
JS_ASSERT(cg->tryNext - cg->tryBase == (ptrdiff_t) array->length);
|
||||
memcpy(array->notes, cg->tryBase, TRYNOTE_SIZE(array->length));
|
||||
JSTryNode *tryNode;
|
||||
JSTryNote *tn;
|
||||
|
||||
JS_ASSERT(array->length > 0 && array->length == cg->ntrynotes);
|
||||
tn = array->notes + array->length;
|
||||
tryNode = cg->lastTryNode;
|
||||
do {
|
||||
*--tn = tryNode->note;
|
||||
} while ((tryNode = tryNode->prev) != NULL);
|
||||
JS_ASSERT(tn == array->notes);
|
||||
}
|
||||
|
|
|
@ -157,7 +157,6 @@ struct JSStmtInfo {
|
|||
struct JSTreeContext { /* tree context for semantic checks */
|
||||
uint16 flags; /* statement state flags, see below */
|
||||
uint16 numGlobalVars; /* max. no. of global variables/regexps */
|
||||
uint32 tryCount; /* total count of try statements parsed */
|
||||
uint32 globalUses; /* optimizable global var uses in total */
|
||||
uint32 loopyGlobalUses;/* optimizable global var uses in loops */
|
||||
JSStmtInfo *topStmt; /* top of statement info stack */
|
||||
|
@ -188,7 +187,7 @@ struct JSTreeContext { /* tree context for semantic checks */
|
|||
|
||||
#define TREE_CONTEXT_INIT(tc) \
|
||||
((tc)->flags = (tc)->numGlobalVars = 0, \
|
||||
(tc)->tryCount = (tc)->globalUses = (tc)->loopyGlobalUses = 0, \
|
||||
(tc)->globalUses = (tc)->loopyGlobalUses = 0, \
|
||||
(tc)->topStmt = (tc)->topScopeStmt = NULL, \
|
||||
(tc)->blockChain = NULL, \
|
||||
ATOM_LIST_INIT(&(tc)->decls), \
|
||||
|
@ -258,6 +257,13 @@ struct JSJumpTarget {
|
|||
? JT_CLR_TAG((sd)->target)->offset - (pivot) \
|
||||
: 0)
|
||||
|
||||
typedef struct JSTryNode JSTryNode;
|
||||
|
||||
struct JSTryNode {
|
||||
JSTryNote note;
|
||||
JSTryNode *prev;
|
||||
};
|
||||
|
||||
struct JSCodeGenerator {
|
||||
JSTreeContext treeContext; /* base state: statement info stack, etc. */
|
||||
|
||||
|
@ -286,9 +292,8 @@ struct JSCodeGenerator {
|
|||
intN stackDepth; /* current stack depth in script frame */
|
||||
uintN maxStackDepth; /* maximum stack depth so far */
|
||||
|
||||
JSTryNote *tryBase; /* first exception handling note */
|
||||
JSTryNote *tryNext; /* next available note */
|
||||
size_t tryNoteSpace; /* # of bytes allocated at tryBase */
|
||||
uintN ntrynotes; /* number of allocated so far try notes */
|
||||
JSTryNode *lastTryNode; /* the last allocated try node */
|
||||
|
||||
JSSpanDep *spanDeps; /* span dependent instruction records */
|
||||
JSJumpTarget *jumpTargets; /* AVL tree of jump target offsets */
|
||||
|
@ -707,21 +712,6 @@ js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index,
|
|||
extern JSBool
|
||||
js_FinishTakingSrcNotes(JSContext *cx, JSCodeGenerator *cg, jssrcnote *notes);
|
||||
|
||||
/*
|
||||
* Allocate cg->treeContext.tryCount notes (plus one for the end sentinel)
|
||||
* from cx->tempPool and set up cg->tryBase/tryNext for exactly tryCount
|
||||
* js_NewTryNote calls. The storage is freed by js_FinishCodeGenerator.
|
||||
*/
|
||||
extern JSBool
|
||||
js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg);
|
||||
|
||||
/*
|
||||
* Grab the next trynote slot in cg, filling it in appropriately.
|
||||
*/
|
||||
extern JSTryNote *
|
||||
js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, JSTryNoteKind kind,
|
||||
uintN stackDepth, size_t start, size_t end);
|
||||
|
||||
extern void
|
||||
js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg,
|
||||
JSTryNoteArray *array);
|
||||
|
|
|
@ -989,7 +989,7 @@ js_RegisterCloseableIterator(JSContext *cx, JSObject *obj)
|
|||
}
|
||||
|
||||
static void
|
||||
CloseIteratorStates(JSContext *cx)
|
||||
CloseNativeIterators(JSContext *cx)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
size_t count, newCount, i;
|
||||
|
@ -1004,7 +1004,7 @@ CloseIteratorStates(JSContext *cx)
|
|||
for (i = 0; i != count; ++i) {
|
||||
obj = (JSObject *)array[i];
|
||||
if (js_IsAboutToBeFinalized(cx, obj))
|
||||
js_CloseIteratorState(cx, obj);
|
||||
js_CloseNativeIterator(cx, obj);
|
||||
else
|
||||
array[newCount++] = obj;
|
||||
}
|
||||
|
@ -1321,7 +1321,7 @@ js_RunCloseHooks(JSContext *cx)
|
|||
METER(deferCount++);
|
||||
continue;
|
||||
}
|
||||
ok = js_CloseGeneratorObject(cx, gen);
|
||||
ok = js_CloseGenerator(cx, gen->obj);
|
||||
|
||||
/*
|
||||
* Unlink the generator after closing it to make sure it always stays
|
||||
|
@ -2778,7 +2778,7 @@ restart:
|
|||
rt->gcMarkingTracer = NULL;
|
||||
|
||||
/* Finalize iterator states before the objects they iterate over. */
|
||||
CloseIteratorStates(cx);
|
||||
CloseNativeIterators(cx);
|
||||
|
||||
#ifdef DUMP_CALL_TABLE
|
||||
/*
|
||||
|
|
|
@ -5829,18 +5829,15 @@ interrupt:
|
|||
#undef FAST_LOCAL_INCREMENT_OP
|
||||
|
||||
BEGIN_CASE(JSOP_ENDITER)
|
||||
JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[-1]));
|
||||
iterobj = JSVAL_TO_OBJECT(sp[-1]);
|
||||
|
||||
/*
|
||||
* js_CloseNativeIterator checks whether the iterator is not
|
||||
* native, and also detects the case of a native iterator that
|
||||
* has already escaped, even though a for-in loop caused it to
|
||||
* be created. See jsiter.c.
|
||||
* Decrease the stack pointer even when !ok, see comments in the
|
||||
* exception capturing code for details.
|
||||
*/
|
||||
SAVE_SP_AND_PC(fp);
|
||||
js_CloseNativeIterator(cx, iterobj);
|
||||
*--sp = JSVAL_NULL;
|
||||
ok = js_CloseIterator(cx, sp[-1]);
|
||||
--sp;
|
||||
if (!ok)
|
||||
goto out;
|
||||
END_CASE(JSOP_ENDITER)
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
|
@ -5970,12 +5967,12 @@ interrupt:
|
|||
|
||||
out:
|
||||
JS_ASSERT((size_t)(pc - script->code) < script->length);
|
||||
if (!ok) {
|
||||
if (!ok && cx->throwing && !(fp->flags & JSFRAME_FILTERING)) {
|
||||
/*
|
||||
* Has an exception been raised? Also insist that we are not in an
|
||||
* XML filtering predicate expression, to avoid catching exceptions
|
||||
* within the filtering predicate, such as this example taken from
|
||||
* tests/e4x/Regress/regress-301596.js:
|
||||
* An exception has been raised and we are not in an XML filtering
|
||||
* predicate expression. The latter check is necessary to avoid
|
||||
* catching exceptions within the filtering predicate, such as this
|
||||
* example taken from tests/e4x/Regress/regress-301596.js:
|
||||
*
|
||||
* try {
|
||||
* <xml/>.(@a == 1);
|
||||
|
@ -5995,132 +5992,183 @@ out:
|
|||
*
|
||||
* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=309894
|
||||
*/
|
||||
if (cx->throwing && !(fp->flags & JSFRAME_FILTERING)) {
|
||||
JSTrapHandler handler;
|
||||
JSTryNote *tn, *tnlimit;
|
||||
uint32 offset;
|
||||
JSTrapHandler handler;
|
||||
JSTryNote *tn, *tnlimit;
|
||||
uint32 offset;
|
||||
|
||||
/*
|
||||
* Call debugger throw hook if set (XXX thread safety?).
|
||||
*/
|
||||
handler = cx->debugHooks->throwHook;
|
||||
if (handler) {
|
||||
SAVE_SP_AND_PC(fp);
|
||||
switch (handler(cx, script, pc, &rval,
|
||||
cx->debugHooks->throwHookData)) {
|
||||
case JSTRAP_ERROR:
|
||||
cx->throwing = JS_FALSE;
|
||||
goto no_catch;
|
||||
case JSTRAP_RETURN:
|
||||
ok = JS_TRUE;
|
||||
cx->throwing = JS_FALSE;
|
||||
fp->rval = rval;
|
||||
goto no_catch;
|
||||
case JSTRAP_THROW:
|
||||
cx->exception = rval;
|
||||
case JSTRAP_CONTINUE:
|
||||
default:;
|
||||
}
|
||||
LOAD_INTERRUPT_HANDLER(cx);
|
||||
}
|
||||
/*
|
||||
* Call debugger throw hook if set (XXX thread safety?).
|
||||
*/
|
||||
handler = cx->debugHooks->throwHook;
|
||||
if (handler) {
|
||||
SAVE_SP_AND_PC(fp);
|
||||
switch (handler(cx, script, pc, &rval,
|
||||
cx->debugHooks->throwHookData)) {
|
||||
case JSTRAP_ERROR:
|
||||
cx->throwing = JS_FALSE;
|
||||
goto no_catch;
|
||||
case JSTRAP_RETURN:
|
||||
ok = JS_TRUE;
|
||||
cx->throwing = JS_FALSE;
|
||||
fp->rval = rval;
|
||||
goto no_catch;
|
||||
case JSTRAP_THROW:
|
||||
cx->exception = rval;
|
||||
case JSTRAP_CONTINUE:
|
||||
default:;
|
||||
}
|
||||
LOAD_INTERRUPT_HANDLER(cx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Look for a try block in script that can catch this exception.
|
||||
*/
|
||||
if (!script->trynotes)
|
||||
goto no_catch;
|
||||
/*
|
||||
* Look for a try block in script that can catch this exception.
|
||||
*/
|
||||
if (!script->trynotes)
|
||||
goto no_catch;
|
||||
|
||||
offset = (uint32)(pc - script->main);
|
||||
tn = script->trynotes->notes;
|
||||
tnlimit = tn + script->trynotes->length;
|
||||
do {
|
||||
if (offset - tn->start >= tn->length)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* We have a note that covers the exception pc but we must check
|
||||
* whether the interpreter has already executed the corresponding
|
||||
* handler. This is possible when the executed bytecode
|
||||
* implements break or return from inside a for-in loop.
|
||||
*
|
||||
* In this case the emitter generates additional [enditer] and
|
||||
* [gosub] opcodes to close all outstanding iterators and execute
|
||||
* the finally blocks. If such an [enditer] throws an exception,
|
||||
* its pc can still be inside several nested for-in loops and
|
||||
* try-finally statements even if we have already closed the
|
||||
* corresponding iterators and invoked the finally blocks.
|
||||
*
|
||||
* To address this, we make [enditer] always decrease the stack
|
||||
* even when its implementation throws an exception. Thus already
|
||||
* executed [enditer] and [gosub] opcodes will have try notes
|
||||
* with the stack depth exceeding the current one and this
|
||||
* condition is what we use to filter them out.
|
||||
*/
|
||||
if (tn->stackDepth > sp - fp->spbase)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Prepare to execute the try note handler and unwind the block
|
||||
* and scope chains until we match the stack depth of the try
|
||||
* note. Note that we set sp after we call js_PutBlockObject to
|
||||
* avoid potential GC hazards.
|
||||
*/
|
||||
ok = JS_TRUE;
|
||||
i = tn->stackDepth;
|
||||
for (obj = fp->blockChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {
|
||||
JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass);
|
||||
if (OBJ_BLOCK_DEPTH(cx, obj) < i)
|
||||
break;
|
||||
}
|
||||
fp->blockChain = obj;
|
||||
|
||||
JS_ASSERT(ok);
|
||||
for (obj = fp->scopeChain; ; obj = OBJ_GET_PARENT(cx, obj)) {
|
||||
clasp = OBJ_GET_CLASS(cx, obj);
|
||||
if (clasp != &js_WithClass && clasp != &js_BlockClass)
|
||||
break;
|
||||
if (JS_GetPrivate(cx, obj) != fp ||
|
||||
OBJ_BLOCK_DEPTH(cx, obj) < i) {
|
||||
break;
|
||||
}
|
||||
if (clasp == &js_BlockClass) {
|
||||
/* Don't fail until after we've updated all stacks. */
|
||||
ok &= js_PutBlockObject(cx, obj);
|
||||
} else {
|
||||
JS_SetPrivate(cx, obj, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
fp->scopeChain = obj;
|
||||
sp = fp->spbase + i;
|
||||
|
||||
/*
|
||||
* Set pc to the first bytecode after the the try note to point
|
||||
* to the beginning of catch or finally or to [enditer] closing
|
||||
* the for-in loop.
|
||||
*
|
||||
* We do it before checking for ok so, when failing during the
|
||||
* scope recovery, we restart the exception search with the
|
||||
* updated stack and pc avoiding calling the handler again.
|
||||
*/
|
||||
offset = tn->start + tn->length;
|
||||
pc = (script)->main + offset;
|
||||
if (!ok)
|
||||
goto out;
|
||||
|
||||
switch (tn->kind) {
|
||||
case JSTN_CATCH:
|
||||
JS_ASSERT(*pc == JSOP_ENTERBLOCK);
|
||||
|
||||
offset = (uint32)(pc - script->main);
|
||||
tn = script->trynotes->notes;
|
||||
tnlimit = tn + script->trynotes->length;
|
||||
for (;;) {
|
||||
if (offset - tn->start < tn->length) {
|
||||
if (tn->kind == JSTN_FINALLY)
|
||||
break;
|
||||
JS_ASSERT(tn->kind == JSTN_CATCH);
|
||||
#if JS_HAS_GENERATORS
|
||||
/* Catch can not intercept closing of a generator. */
|
||||
if (JS_LIKELY(cx->exception != JSVAL_ARETURN))
|
||||
break;
|
||||
#else
|
||||
break;
|
||||
/* Catch cannot intercept the closing of a generator. */
|
||||
if (JS_UNLIKELY(cx->exception == JSVAL_ARETURN))
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
if (++tn == tnlimit)
|
||||
goto no_catch;
|
||||
}
|
||||
|
||||
ok = JS_TRUE;
|
||||
/*
|
||||
* Don't clear cx->throwing to save cx->exception from GC
|
||||
* until it is pushed to the stack via [exception] in the
|
||||
* catch block.
|
||||
*/
|
||||
len = 0;
|
||||
DO_NEXT_OP(len);
|
||||
|
||||
/*
|
||||
* Unwind the block and scope chains until we match the stack
|
||||
* depth of the try note.
|
||||
*/
|
||||
i = tn->stackDepth;
|
||||
for (obj = fp->blockChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {
|
||||
JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass);
|
||||
if (OBJ_BLOCK_DEPTH(cx, obj) < i)
|
||||
break;
|
||||
}
|
||||
fp->blockChain = obj;
|
||||
case JSTN_FINALLY:
|
||||
/*
|
||||
* Push (true, exception) pair for finally to indicate that
|
||||
* [retsub] should rethrow the exception.
|
||||
*/
|
||||
PUSH(JSVAL_TRUE);
|
||||
PUSH(cx->exception);
|
||||
cx->throwing = JS_FALSE;
|
||||
len = 0;
|
||||
DO_NEXT_OP(len);
|
||||
|
||||
JS_ASSERT(ok);
|
||||
for (obj = fp->scopeChain;
|
||||
(clasp = OBJ_GET_CLASS(cx, obj)) == &js_WithClass ||
|
||||
clasp == &js_BlockClass;
|
||||
obj = OBJ_GET_PARENT(cx, obj)) {
|
||||
if (JS_GetPrivate(cx, obj) != fp ||
|
||||
OBJ_BLOCK_DEPTH(cx, obj) < i) {
|
||||
break;
|
||||
}
|
||||
if (clasp == &js_BlockClass) {
|
||||
/* Don't fail until after we've updated all stacks. */
|
||||
ok &= js_PutBlockObject(cx, obj);
|
||||
} else {
|
||||
JS_SetPrivate(cx, obj, NULL);
|
||||
}
|
||||
}
|
||||
case JSTN_ITER:
|
||||
/*
|
||||
* This is similar to JSOP_ENDITER in the interpreter loop
|
||||
* except the code now uses a reserved stack slot to save and
|
||||
* restore the exception.
|
||||
*/
|
||||
JS_ASSERT(*pc == JSOP_ENDITER);
|
||||
PUSH(cx->exception);
|
||||
cx->throwing = JS_FALSE;
|
||||
SAVE_SP_AND_PC(fp);
|
||||
ok = js_CloseIterator(cx, sp[-2]);
|
||||
sp -= 2;
|
||||
if (!ok) {
|
||||
/*
|
||||
* close generated a new exception error or an error,
|
||||
* restart the handler search to properly notify the
|
||||
* debugger.
|
||||
*/
|
||||
goto out;
|
||||
}
|
||||
cx->throwing = JS_TRUE;
|
||||
cx->exception = sp[1];
|
||||
|
||||
fp->scopeChain = obj;
|
||||
/*
|
||||
* Reset ok to false so, if this is the last try note, the
|
||||
* exception will be propagated outside the function or
|
||||
* script.
|
||||
*/
|
||||
ok = JS_FALSE;
|
||||
break;
|
||||
}
|
||||
} while (++tn != tnlimit);
|
||||
|
||||
/* Set sp after js_PutBlockObject to avoid potential GC hazards. */
|
||||
sp = fp->spbase + i;
|
||||
|
||||
/* The catch or finally begins right after the code they protect. */
|
||||
pc = (script)->main + tn->start + tn->length;
|
||||
|
||||
/*
|
||||
* When failing during the scope recovery, restart the exception
|
||||
* search with the updated stack and pc.
|
||||
*/
|
||||
if (!ok)
|
||||
goto out;
|
||||
|
||||
JS_ASSERT(cx->exception != JSVAL_HOLE);
|
||||
if (tn->kind == JSTN_FINALLY) {
|
||||
/*
|
||||
* Push (false, exception) pair for finally to indicate that
|
||||
* [retsub] should rethrow the exception.
|
||||
*/
|
||||
PUSH(JSVAL_TRUE);
|
||||
PUSH(cx->exception);
|
||||
cx->throwing = JS_FALSE;
|
||||
} else {
|
||||
/*
|
||||
* Don't clear cx->throwing to save cx->exception from GC
|
||||
* until it is pushed to the stack via [exception] in the
|
||||
* catch block.
|
||||
*/
|
||||
}
|
||||
|
||||
len = 0;
|
||||
ok = JS_TRUE;
|
||||
DO_NEXT_OP(len);
|
||||
}
|
||||
|
||||
no_catch:;
|
||||
no_catch:;
|
||||
#if JS_HAS_GENERATORS
|
||||
if (JS_UNLIKELY(cx->exception == JSVAL_ARETURN)) {
|
||||
if (JS_UNLIKELY(cx->throwing && cx->exception == JSVAL_ARETURN)) {
|
||||
cx->throwing = JS_FALSE;
|
||||
ok = JS_TRUE;
|
||||
fp->rval = JSVAL_VOID;
|
||||
|
|
|
@ -82,12 +82,12 @@ extern const char js_throw_str[]; /* from jsscan.h */
|
|||
* when GC detects that the iterator is no longer reachable.
|
||||
*/
|
||||
void
|
||||
js_CloseIteratorState(JSContext *cx, JSObject *iterobj)
|
||||
js_CloseNativeIterator(JSContext *cx, JSObject *iterobj)
|
||||
{
|
||||
jsval state;
|
||||
JSObject *iterable;
|
||||
|
||||
JS_ASSERT(JS_InstanceOf(cx, iterobj, &js_IteratorClass, NULL));
|
||||
JS_ASSERT(STOBJ_GET_CLASS(iterobj) == &js_IteratorClass);
|
||||
|
||||
/* Avoid double work if js_CloseNativeIterator was called on obj. */
|
||||
state = STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_STATE);
|
||||
|
@ -315,29 +315,6 @@ js_GetNativeIteratorFlags(JSContext *cx, JSObject *iterobj)
|
|||
return JSVAL_TO_INT(OBJ_GET_SLOT(cx, iterobj, JSSLOT_ITER_FLAGS));
|
||||
}
|
||||
|
||||
void
|
||||
js_CloseNativeIterator(JSContext *cx, JSObject *iterobj)
|
||||
{
|
||||
uintN flags;
|
||||
|
||||
/*
|
||||
* If this iterator is not an instance of the native default iterator
|
||||
* class, leave it to be GC'ed.
|
||||
*/
|
||||
if (!JS_InstanceOf(cx, iterobj, &js_IteratorClass, NULL))
|
||||
return;
|
||||
|
||||
/*
|
||||
* If this iterator was not created by js_ValueToIterator called from the
|
||||
* for-in loop code in js_Interpret, leave it to be GC'ed.
|
||||
*/
|
||||
flags = JSVAL_TO_INT(OBJ_GET_SLOT(cx, iterobj, JSSLOT_ITER_FLAGS));
|
||||
if (!(flags & JSITER_ENUMERATE))
|
||||
return;
|
||||
|
||||
js_CloseIteratorState(cx, iterobj);
|
||||
}
|
||||
|
||||
/*
|
||||
* Call ToObject(v).__iterator__(keyonly) if ToObject(v).__iterator__ exists.
|
||||
* Otherwise construct the defualt iterator.
|
||||
|
@ -438,6 +415,28 @@ js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp)
|
|||
goto out;
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_CloseIterator(JSContext *cx, jsval v)
|
||||
{
|
||||
JSObject *obj;
|
||||
JSClass *clasp;
|
||||
|
||||
JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
|
||||
obj = JSVAL_TO_OBJECT(v);
|
||||
clasp = OBJ_GET_CLASS(cx, obj);
|
||||
|
||||
if (clasp == &js_IteratorClass) {
|
||||
js_CloseNativeIterator(cx, obj);
|
||||
}
|
||||
#if JS_HAS_GENERATORS
|
||||
else if (clasp == &js_GeneratorClass) {
|
||||
if (!js_CloseGenerator(cx, obj))
|
||||
return JS_FALSE;
|
||||
}
|
||||
#endif
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
CallEnumeratorNext(JSContext *cx, JSObject *iterobj, uintN flags, jsval *rval)
|
||||
{
|
||||
|
@ -911,10 +910,23 @@ SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj,
|
|||
* unreachable.
|
||||
*/
|
||||
JSBool
|
||||
js_CloseGeneratorObject(JSContext *cx, JSGenerator *gen)
|
||||
js_CloseGenerator(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JSGenerator *gen;
|
||||
|
||||
JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_GeneratorClass);
|
||||
gen = (JSGenerator *) JS_GetPrivate(cx, obj);
|
||||
if (!gen) {
|
||||
/* Generator prototype object. */
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JS_ASSERT(gen->state != JSGEN_RUNNING && gen->state != JSGEN_CLOSING);
|
||||
if (gen->state == JSGEN_CLOSED)
|
||||
return JS_TRUE;
|
||||
|
||||
/* We pass null as rval since SendToGenerator never uses it with CLOSE. */
|
||||
return SendToGenerator(cx, JSGENOP_CLOSE, gen->obj, gen, JSVAL_VOID, NULL);
|
||||
return SendToGenerator(cx, JSGENOP_CLOSE, obj, gen, JSVAL_VOID, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -49,12 +49,6 @@
|
|||
#define JSITER_FOREACH 0x2 /* return [key, value] pair rather than key */
|
||||
#define JSITER_KEYVALUE 0x4 /* destructuring for-in wants [key, value] */
|
||||
|
||||
extern void
|
||||
js_CloseNativeIterator(JSContext *cx, JSObject *iterobj);
|
||||
|
||||
extern void
|
||||
js_CloseIteratorState(JSContext *cx, JSObject *iterobj);
|
||||
|
||||
/*
|
||||
* Convert the value stored in *vp to its iteration object. The flags should
|
||||
* contain JSITER_ENUMERATE if js_ValueToIterator is called when enumerating
|
||||
|
@ -64,6 +58,9 @@ js_CloseIteratorState(JSContext *cx, JSObject *iterobj);
|
|||
extern JSBool
|
||||
js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp);
|
||||
|
||||
extern JSBool
|
||||
js_CloseIterator(JSContext *cx, jsval v);
|
||||
|
||||
/*
|
||||
* Given iterobj, call iterobj.next(). If the iterator stopped, set *rval to
|
||||
* JSVAL_HOLE. Otherwise set it to the result of the next call.
|
||||
|
@ -71,6 +68,12 @@ js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp);
|
|||
extern JSBool
|
||||
js_CallIteratorNext(JSContext *cx, JSObject *iterobj, jsval *rval);
|
||||
|
||||
/*
|
||||
* Close iterobj, whose class must be js_IteratorClass.
|
||||
*/
|
||||
extern void
|
||||
js_CloseNativeIterator(JSContext *cx, JSObject *iterobj);
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
|
||||
/*
|
||||
|
@ -100,7 +103,7 @@ extern JSObject *
|
|||
js_NewGenerator(JSContext *cx, JSStackFrame *fp);
|
||||
|
||||
extern JSBool
|
||||
js_CloseGeneratorObject(JSContext *cx, JSGenerator *gen);
|
||||
js_CloseGenerator(JSContext *cx, JSObject *obj);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -465,7 +465,7 @@ OPDEF(JSOP_FORLOCAL, 207,"forlocal", NULL, 3, 0, 1, 19, JOF_LOCAL|
|
|||
* Iterator, generator, and array comprehension support.
|
||||
*/
|
||||
OPDEF(JSOP_FORCONST, 208,"forconst", NULL, 3, 0, 1, 19, JOF_QVAR|JOF_NAME|JOF_FOR)
|
||||
OPDEF(JSOP_ENDITER, 209,"enditer", NULL, 1, 1, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_ENDITER, 209,"enditer", NULL, 1, 1, 0, 0, JOF_BYTE|JOF_TMPSLOT)
|
||||
OPDEF(JSOP_GENERATOR, 210,"generator", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_YIELD, 211,"yield", NULL, 1, 1, 1, 1, JOF_BYTE)
|
||||
OPDEF(JSOP_ARRAYPUSH, 212,"arraypush", NULL, 3, 1, 0, 3, JOF_LOCAL)
|
||||
|
|
|
@ -1474,7 +1474,6 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
pn->pn_op = op;
|
||||
pn->pn_body = body;
|
||||
pn->pn_flags = funtc.flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS);
|
||||
pn->pn_tryCount = funtc.tryCount;
|
||||
TREE_CONTEXT_FINISH(&funtc);
|
||||
return result;
|
||||
}
|
||||
|
@ -1549,7 +1548,6 @@ Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
|||
tc->flags &= ~TCF_RETURN_EXPR;
|
||||
}
|
||||
if (!js_FoldConstants(cx, pn2, tc) ||
|
||||
!js_AllocTryNotes(cx, (JSCodeGenerator *)tc) ||
|
||||
!js_EmitTree(cx, (JSCodeGenerator *)tc, pn2)) {
|
||||
tt = TOK_ERROR;
|
||||
break;
|
||||
|
@ -3185,7 +3183,6 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
|||
pn->pn_kid2 = catchList;
|
||||
|
||||
if (tt == TOK_FINALLY) {
|
||||
tc->tryCount++;
|
||||
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
|
||||
js_PushStatement(tc, &stmtInfo, STMT_FINALLY, -1);
|
||||
pn->pn_kid3 = Statements(cx, ts, tc);
|
||||
|
@ -3201,7 +3198,6 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
|||
JSMSG_CATCH_OR_FINALLY);
|
||||
return NULL;
|
||||
}
|
||||
tc->tryCount++;
|
||||
return pn;
|
||||
}
|
||||
|
||||
|
@ -4294,6 +4290,7 @@ ComprehensionTail(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
|||
JSTokenType tt;
|
||||
JSAtom *atom;
|
||||
|
||||
JS_ASSERT(type == TOK_SEMI || type == TOK_ARRAYPUSH);
|
||||
JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_FOR);
|
||||
|
||||
/*
|
||||
|
|
|
@ -68,7 +68,6 @@ JS_BEGIN_EXTERN_C
|
|||
* pn_body: TOK_LC node for function body statements
|
||||
* pn_flags: TCF_FUN_* flags (see jsemit.h) collected
|
||||
* while parsing the function's body
|
||||
* pn_tryCount: of try statements in function
|
||||
*
|
||||
* <Statements>
|
||||
* TOK_LC list pn_head: list of pn_count statements
|
||||
|
@ -281,7 +280,6 @@ struct JSParseNode {
|
|||
JSAtom *funAtom; /* atomized function object */
|
||||
JSParseNode *body; /* TOK_LC list of statements */
|
||||
uint32 flags; /* accumulated tree context flags */
|
||||
uint32 tryCount; /* count of try statements in body */
|
||||
} func;
|
||||
struct { /* list of next-linked nodes */
|
||||
JSParseNode *head; /* first node in list */
|
||||
|
@ -323,7 +321,6 @@ struct JSParseNode {
|
|||
#define pn_funAtom pn_u.func.funAtom
|
||||
#define pn_body pn_u.func.body
|
||||
#define pn_flags pn_u.func.flags
|
||||
#define pn_tryCount pn_u.func.tryCount
|
||||
#define pn_head pn_u.list.head
|
||||
#define pn_tail pn_u.list.tail
|
||||
#define pn_count pn_u.list.count
|
||||
|
|
|
@ -1398,15 +1398,15 @@ js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 ntrynotes)
|
|||
JS_FRIEND_API(JSScript *)
|
||||
js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun)
|
||||
{
|
||||
uint32 mainLength, prologLength, nsrcnotes, ntrynotes;
|
||||
uint32 mainLength, prologLength, nsrcnotes;
|
||||
JSScript *script;
|
||||
const char *filename;
|
||||
|
||||
mainLength = CG_OFFSET(cg);
|
||||
prologLength = CG_PROLOG_OFFSET(cg);
|
||||
CG_COUNT_FINAL_SRCNOTES(cg, nsrcnotes);
|
||||
ntrynotes = (uint32)(cg->tryNext - cg->tryBase);
|
||||
script = js_NewScript(cx, prologLength + mainLength, nsrcnotes, ntrynotes);
|
||||
script = js_NewScript(cx, prologLength + mainLength, nsrcnotes,
|
||||
cg->ntrynotes);
|
||||
if (!script)
|
||||
return NULL;
|
||||
|
||||
|
|
|
@ -50,11 +50,13 @@
|
|||
JS_BEGIN_EXTERN_C
|
||||
|
||||
/*
|
||||
* Type of try note associated with each catch block or finally block.
|
||||
* Type of try note associated with each catch or finally block or with for-in
|
||||
* loop.
|
||||
*/
|
||||
typedef enum JSTryNoteKind {
|
||||
JSTN_CATCH,
|
||||
JSTN_FINALLY
|
||||
JSTN_FINALLY,
|
||||
JSTN_ITER
|
||||
} JSTryNoteKind;
|
||||
|
||||
/*
|
||||
|
@ -64,9 +66,9 @@ struct JSTryNote {
|
|||
uint8 kind; /* one of JSTryNoteKind */
|
||||
uint8 padding; /* explicit padding on uint16 boundary */
|
||||
uint16 stackDepth; /* stack depth upon exception handler entry */
|
||||
uint32 start; /* start of the try statement relative to
|
||||
script->main */
|
||||
uint32 length; /* length of the try statement */
|
||||
uint32 start; /* start of the try statement or for-in loop
|
||||
relative to script->main */
|
||||
uint32 length; /* length of the try statement or for-in loop */
|
||||
};
|
||||
|
||||
typedef struct JSTryNoteArray {
|
||||
|
|
Загрузка…
Ссылка в новой задаче