467
* Recursive helper to convert a compile-time block chain to a runtime block
468
* scope chain prefix. Each cloned block object is safe from GC by virtue of
469
* the object newborn root. This root cannot be displaced by arbitrary code
470
* called from within js_NewObject, because we pass non-null proto and parent
471
* arguments (so js_NewObject won't call js_GetClassPrototype).
474
CloneBlockChain(JSContext *cx, JSStackFrame *fp, JSObject *obj)
478
parent = OBJ_GET_PARENT(cx, obj);
480
parent = fp->scopeChain;
482
parent = CloneBlockChain(cx, fp, parent);
485
fp->scopeChain = parent;
487
return js_CloneBlockObject(cx, obj, parent, fp);
491
467
js_GetScopeChain(JSContext *cx, JSStackFrame *fp)
469
JSObject *obj, *cursor, *clonedChild, *parent;
470
JSTempValueRooter tvr;
495
472
obj = fp->blockChain;
497
obj = CloneBlockChain(cx, fp, obj);
474
JS_ASSERT(fp->scopeChain);
475
return fp->scopeChain;
479
* Clone the block chain. To avoid recursive cloning we set the parent of
480
* the cloned child after we clone the parent. In the following loop when
481
* clonedChild is null it indicates the first iteration when no special GC
482
* rooting is necessary. On the second and the following iterations we
483
* have to protect clonedChild against the GC during cloning of the parent.
488
parent = OBJ_GET_PARENT(cx, cursor);
491
* We pass fp->scopeChain and not null even if we override the parent
492
* slot later as null triggers useless calculations of slot's value in
493
* js_NewObject that js_CloneBlockObject calls.
495
cursor = js_CloneBlockObject(cx, cursor, fp->scopeChain, fp);
498
JS_POP_TEMP_ROOT(cx, &tvr);
500
fp->flags |= JSFRAME_POP_BLOCKS;
501
fp->scopeChain = obj;
502
fp->blockChain = NULL;
504
JS_PUSH_SINGLE_TEMP_ROOT(cx, cursor, &tvr);
507
* Avoid OBJ_SET_PARENT overhead as clonedChild cannot escape to
510
clonedChild->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(cursor);
511
JS_ASSERT(tvr.u.value == OBJECT_TO_JSVAL(clonedChild));
512
tvr.u.value = OBJECT_TO_JSVAL(cursor);
516
JS_POP_TEMP_ROOT(cx, &tvr);
519
clonedChild = cursor;
505
JS_ASSERT(fp->scopeChain);
506
return fp->scopeChain;
522
fp->flags |= JSFRAME_POP_BLOCKS;
523
fp->scopeChain = obj;
524
fp->blockChain = NULL;
520
539
for (obj = fp->scopeChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {
521
if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass)
540
if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) {
541
if (JS_GetPrivate(cx, obj) != fp)
522
543
ok &= js_PutBlockObject(cx, obj);
1261
1283
/* All arguments must be contiguous, so we may have to copy actuals. */
1262
1284
nalloc = nslots;
1263
1285
limit = (jsval *) cx->stackPool.current->limit;
1286
JS_ASSERT((jsval *) cx->stackPool.current->base <= sp && sp <= limit);
1264
1287
if (sp + nslots > limit) {
1265
1288
/* Hit end of arena: we have to copy argv[-2..(argc+nslots-1)]. */
1266
1289
nalloc += 2 + argc;
1380
1403
hook(cx, &frame, JS_FALSE, &ok, hookData);
1383
/* If frame has block objects on its scope chain, cut them loose. */
1384
if (frame.flags & JSFRAME_POP_BLOCKS)
1385
ok &= PutBlockObjects(cx, &frame);
1387
1406
/* If frame has a call object, sync values and clear back-pointer. */
1388
1407
if (frame.callobj)
1389
1408
ok &= js_PutCallObject(cx, &frame);
1711
1730
ok = OBJ_SET_PROPERTY(cx, target, id, &value);
1713
1732
ok = OBJ_DEFINE_PROPERTY(cx, target, id, value, NULL, NULL,
1714
attrs & ~JSPROP_EXPORTED,
1733
attrs & ~(JSPROP_EXPORTED |
1748
1769
return JS_TRUE;
1750
/* From here, return true, or goto bad on failure to drop prop. */
1751
if (!OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs))
1772
* Use prop as a speedup hint to OBJ_GET_ATTRIBUTES, but drop it on error.
1773
* An assertion at label bad: will insist that it is null.
1775
if (!OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs)) {
1776
OBJ_DROP_PROPERTY(cx, obj2, prop);
1784
* From here, return true, or else goto bad on failure to null out params.
1785
* If our caller doesn't want prop, drop it (we don't need it any longer).
1788
OBJ_DROP_PROPERTY(cx, obj2, prop);
1754
1792
/* If either property is readonly, we have an error. */
1755
1793
report = ((oldAttrs | attrs) & JSPROP_READONLY)
1981
2019
# undef JS_THREADED_INTERP
1984
typedef enum JSOpLength {
1985
#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
1986
op##_LENGTH = length,
1987
#include "jsopcode.tbl"
1993
2023
js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result)
2194
2224
* jump is distributed throughout interruptJumpTable, and comes back to
2195
2225
* the interrupt label. The dispatch on op is through normalJumpTable.
2196
2226
* The trick is LOAD_INTERRUPT_HANDLER setting jumpTable appropriately.
2228
* It is important that "op" be initialized before the interrupt label
2229
* because it is possible for "op" to be specially assigned during the
2230
* normally processing of an opcode while looping (in particular, this
2231
* happens in JSOP_TRAP while debugging). We rely on DO_NEXT_OP to
2232
* correctly manage "op" in all other cases.
2198
2235
if (interruptHandler) {
2200
2237
SAVE_SP_AND_PC(fp);
2355
2395
JSInlineFrame *ifp = (JSInlineFrame *) fp;
2356
2396
void *hookData = ifp->hookData;
2399
* If fp has blocks on its scope chain, home their locals now,
2400
* before calling any debugger hook, and before freeing stack.
2401
* This matches the order of block putting and hook calling in
2402
* the "out-of-line" return code at the bottom of js_Interpret
2405
if (fp->flags & JSFRAME_POP_BLOCKS) {
2407
ok &= PutBlockObjects(cx, fp);
2358
2410
if (hookData) {
2359
2411
JSInterpreterHook hook = cx->runtime->callHook;
2367
/* If fp has blocks on its scope chain, cut them loose. */
2368
if (fp->flags & JSFRAME_POP_BLOCKS) {
2370
ok &= PutBlockObjects(cx, fp);
2374
2420
* If fp has a call object, sync values and clear the back-
2375
2421
* pointer. This can happen for a lightweight function if it
2675
2723
/* Is this the first iteration ? */
2676
2724
if (JSVAL_IS_NULL(rval)) {
2678
* Yes, and because rval is null we know JSOP_STARTITER stored
2679
* that slot, and we must use the new iteration protocol.
2725
/* Yes, use the new iteration protocol. */
2681
2726
fp->pc = (jsbytecode *) sp[i-depth];
2682
2727
iterobj = js_ValueToIterator(cx, OBJECT_TO_JSVAL(obj), flags);
2904
2947
BEGIN_CASE(JSOP_DUP)
2905
2948
JS_ASSERT(sp > fp->spbase);
2949
vp = sp - 1; /* address top of stack */
2951
vp -= depth; /* address generating pc */
2908
2954
END_CASE(JSOP_DUP)
2910
2956
BEGIN_CASE(JSOP_DUP2)
2911
JS_ASSERT(sp - 1 > fp->spbase);
2912
lval = FETCH_OPND(-2);
2913
rval = FETCH_OPND(-1);
2957
JS_ASSERT(sp - 2 >= fp->spbase);
2958
vp = sp - 1; /* address top of stack */
2961
vp -= depth; /* address generating pc */
2962
vp[1] = vp[2] = *vp;
2916
2965
END_CASE(JSOP_DUP2)
2918
2967
#define PROPERTY_OP(n, call) \
4256
4310
case JSOP_GETMETHOD: goto do_JSOP_GETMETHOD;
4257
4311
case JSOP_SETMETHOD: goto do_JSOP_SETMETHOD;
4259
case JSOP_INITCATCHVAR: goto do_JSOP_INITCATCHVAR;
4260
4313
case JSOP_NAMEDFUNOBJ: goto do_JSOP_NAMEDFUNOBJ;
4261
4314
case JSOP_NUMBER: goto do_JSOP_NUMBER;
4262
4315
case JSOP_OBJECT: goto do_JSOP_OBJECT;
4922
4973
JS_ASSERT(!fp->blockChain);
4923
4974
obj2 = fp->scopeChain;
4924
JS_ASSERT(OBJ_GET_CLASS(cx, obj2) != &js_BlockClass);
4925
4975
if (OBJ_GET_PARENT(cx, obj) != obj2) {
4926
4976
obj = js_CloneFunctionObject(cx, obj, obj2);
5013
5063
/* If re-parenting, store a clone of the function object. */
5014
5064
JS_ASSERT(!fp->blockChain);
5015
5065
parent = fp->scopeChain;
5016
JS_ASSERT(OBJ_GET_CLASS(cx, parent) != &js_BlockClass);
5017
5066
if (OBJ_GET_PARENT(cx, obj) != parent) {
5018
5067
SAVE_SP_AND_PC(fp);
5019
5068
obj = js_CloneFunctionObject(cx, obj, parent);
5030
5079
obj = ATOM_TO_OBJECT(atom);
5032
5081
/* If re-parenting, push a clone of the function object. */
5033
5083
parent = js_GetScopeChain(cx, fp);
5038
5088
if (OBJ_GET_PARENT(cx, obj) != parent) {
5040
5089
obj = js_CloneFunctionObject(cx, obj, parent);
5271
5320
/* Ensure that id has a type suitable for use with obj. */
5272
5321
CHECK_ELEMENT_ID(obj, id);
5274
5324
if (JS_TypeOfValue(cx, rval) != JSTYPE_FUNCTION) {
5275
5325
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5276
5326
JSMSG_BAD_GETTER_OR_SETTER,
5467
5516
END_CASE(JSOP_SETSP)
5469
5518
BEGIN_CASE(JSOP_GOSUB)
5519
JS_ASSERT(cx->exception != JSVAL_HOLE);
5520
if (!cx->throwing) {
5523
lval = cx->exception;
5524
cx->throwing = JS_FALSE;
5470
5527
i = PTRDIFF(pc, script->main, jsbytecode) + JSOP_GOSUB_LENGTH;
5471
5528
len = GET_JUMP_OFFSET(pc);
5472
5529
PUSH(INT_TO_JSVAL(i));
5473
5530
END_VARLEN_CASE
5475
5532
BEGIN_CASE(JSOP_GOSUBX)
5533
JS_ASSERT(cx->exception != JSVAL_HOLE);
5534
lval = cx->throwing ? cx->exception : JSVAL_HOLE;
5476
5536
i = PTRDIFF(pc, script->main, jsbytecode) + JSOP_GOSUBX_LENGTH;
5477
5537
len = GET_JUMPX_OFFSET(pc);
5478
5538
PUSH(INT_TO_JSVAL(i));
5481
5541
BEGIN_CASE(JSOP_RETSUB)
5483
5543
JS_ASSERT(JSVAL_IS_INT(rval));
5545
if (lval != JSVAL_HOLE) {
5547
* Exception was pending during finally, throw it *before* we
5548
* adjust pc, because pc indexes into script->trynotes. This
5549
* turns out not to be necessary, but it seems clearer. And
5550
* it points out a FIXME: 350509, due to Igor Bukanov.
5552
cx->throwing = JS_TRUE;
5553
cx->exception = lval;
5484
5557
len = JSVAL_TO_INT(rval);
5485
5558
pc = script->main;
5486
5559
END_VARLEN_CASE
5497
5575
/* let the code at out try to catch the exception. */
5500
BEGIN_LITOPX_CASE(JSOP_INITCATCHVAR, 0)
5501
/* Load the value into rval, while keeping it live on stack. */
5502
JS_ASSERT(sp - fp->spbase >= 2);
5503
rval = FETCH_OPND(-1);
5505
/* Get the immediate catch variable name into id. */
5506
id = ATOM_TO_JSID(atom);
5508
/* Find the object being initialized at top of stack. */
5509
lval = FETCH_OPND(-2);
5510
JS_ASSERT(JSVAL_IS_OBJECT(lval));
5511
obj = JSVAL_TO_OBJECT(lval);
5578
BEGIN_CASE(JSOP_SETLOCALPOP)
5516
* It's possible for an evil script to substitute a random object
5517
* for the new object. Check to make sure that we don't override a
5518
* readonly property with the below OBJ_DEFINE_PROPERTY.
5580
* The stack must have a block with at least one local slot below
5581
* the exception object.
5520
ok = OBJ_GET_ATTRIBUTES(cx, obj, id, NULL, &attrs);
5523
if (!(attrs & (JSPROP_READONLY | JSPROP_PERMANENT |
5524
JSPROP_GETTER | JSPROP_SETTER))) {
5525
/* Define obj[id] to contain rval and to be permanent. */
5526
ok = OBJ_DEFINE_PROPERTY(cx, obj, id, rval, NULL, NULL,
5527
JSPROP_PERMANENT, NULL);
5532
/* Now that we're done with rval, pop it. */
5534
END_LITOPX_CASE(JSOP_INITCATCHVAR)
5583
JS_ASSERT(sp - fp->spbase >= 2);
5584
slot = GET_UINT16(pc);
5585
JS_ASSERT(slot + 1 < (uintN)depth);
5586
fp->spbase[slot] = POP_OPND();
5587
END_CASE(JSOP_SETLOCALPOP)
5536
5589
BEGIN_CASE(JSOP_INSTANCEOF)
5537
5590
SAVE_SP_AND_PC(fp);
5994
6046
JS_ASSERT(op == JSOP_LEAVEBLOCKEXPR
5995
6047
? fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp - 1
5996
6048
: fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp);
5997
6050
*chainp = OBJ_GET_PARENT(cx, obj);
6051
JS_ASSERT(chainp != &fp->blockChain ||
6053
OBJ_GET_CLASS(cx, *chainp) == &js_BlockClass);
5999
6055
END_CASE(JSOP_LEAVEBLOCK)
6046
6102
#undef FAST_LOCAL_INCREMENT_OP
6048
#endif /* JS_HAS_BLOCK_SCOPE */
6050
6104
#if JS_HAS_GENERATORS
6051
6105
BEGIN_CASE(JSOP_STARTITER)
6053
* Start of a for-in or for-each-in loop: clear flags and push two
6054
* nulls. If this is a for-each-in loop, JSOP_FOREACH will follow
6055
* and set flags = JSITER_FOREACH. Push null instead of undefined
6056
* so that code at do_forinloop: can tell that this opcode pushed
6057
* the iterator slot, rather than a backward compatible JSOP_PUSH
6058
* that was emitted prior to the introduction of the new iteration
6107
* Start of a for-in or for-each-in loop: push two nulls. Push
6108
* null instead of undefined so that code at do_forinloop: can
6109
* tell that this opcode pushed the iterator slot, rather than a
6110
* backward compatible JSOP_PUSH that was emitted prior to the
6111
* introduction of the new iteration protocol.
6062
6113
sp[0] = sp[1] = JSVAL_NULL;
6064
6115
END_CASE(JSOP_STARTITER)
6096
6147
BEGIN_CASE(JSOP_YIELD)
6097
6148
ASSERT_NOT_THROWING(cx);
6149
if (fp->flags & JSFRAME_FILTERING) {
6150
/* FIXME: bug 309894 -- fix to eliminate this error. */
6151
JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL,
6152
JSMSG_YIELD_FROM_FILTER);
6156
if (FRAME_TO_GENERATOR(fp)->state == JSGEN_CLOSING) {
6157
str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK,
6158
fp->argv[-2], NULL);
6160
JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL,
6161
JSMSG_BAD_GENERATOR_YIELD,
6162
JSSTRING_CHARS(str));
6098
6167
fp->rval = FETCH_OPND(-1);
6099
6168
fp->flags |= JSFRAME_YIELDING;
6100
6169
pc += JSOP_YIELD_LENGTH;
6256
6311
* Look for a try block in script that can catch this exception.
6313
#if JS_HAS_GENERATORS
6314
if (JS_LIKELY(cx->exception != JSVAL_ARETURN)) {
6315
SCRIPT_FIND_CATCH_START(script, pc, pc);
6319
pc = js_FindFinallyHandler(script, pc);
6321
cx->throwing = JS_FALSE;
6323
fp->rval = JSVAL_VOID;
6258
6328
SCRIPT_FIND_CATCH_START(script, pc, pc);
6260
/* Don't clear cx->throwing to save cx->exception from GC. */
6333
/* Don't clear cx->throwing to save cx->exception from GC. */
6280
6352
* Restore the previous frame's execution state.
6282
6354
if (JS_LIKELY(mark != NULL)) {
6355
/* If fp has blocks on its scope chain, home their locals now. */
6356
if (fp->flags & JSFRAME_POP_BLOCKS) {
6358
ok &= PutBlockObjects(cx, fp);
6283
6361
fp->sp = fp->spbase;
6284
6362
fp->spbase = NULL;
6285
6363
js_FreeRawStack(cx, mark);