Save all modification
[mozilla-1.9/m8.git] / js / src / jsinterp.c
blob8bc2d75db79523b0d901592cb7c86f301a36522f
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=78:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
25 * Contributor(s):
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
42 * JavaScript bytecode interpreter.
44 #include "jsstddef.h"
45 #include <stdio.h>
46 #include <string.h>
47 #include <math.h>
48 #include "jstypes.h"
49 #include "jsarena.h" /* Added by JSIFY */
50 #include "jsutil.h" /* Added by JSIFY */
51 #include "jsprf.h"
52 #include "jsapi.h"
53 #include "jsarray.h"
54 #include "jsatom.h"
55 #include "jsbool.h"
56 #include "jscntxt.h"
57 #include "jsconfig.h"
58 #include "jsdbgapi.h"
59 #include "jsfun.h"
60 #include "jsgc.h"
61 #include "jsinterp.h"
62 #include "jsiter.h"
63 #include "jslock.h"
64 #include "jsnum.h"
65 #include "jsobj.h"
66 #include "jsopcode.h"
67 #include "jsscan.h"
68 #include "jsscope.h"
69 #include "jsscript.h"
70 #include "jsstr.h"
72 #if JS_HAS_XML_SUPPORT
73 #include "jsxml.h"
74 #endif
77 * Stack macros and functions. These all use a local variable, jsval *sp, to
78 * point to the next free stack slot. SAVE_SP must be called before any call
79 * to a function that may invoke the interpreter. RESTORE_SP must be called
80 * only after return from js_Invoke, because only js_Invoke changes fp->sp.
82 #define PUSH(v) (*sp++ = (v))
83 #define POP() (*--sp)
84 #ifdef DEBUG
85 #define SAVE_SP(fp) \
86 (JS_ASSERT((fp)->script || !(fp)->spbase || (sp) == (fp)->spbase), \
87 (fp)->sp = sp)
88 #else
89 #define SAVE_SP(fp) ((fp)->sp = sp)
90 #endif
91 #define RESTORE_SP(fp) (sp = (fp)->sp)
94 * SAVE_SP_AND_PC commits deferred stores of interpreter registers to their
95 * homes in fp, when calling out of the interpreter loop or threaded code.
96 * RESTORE_SP_AND_PC copies the other way, to update registers after a call
97 * to a subroutine that interprets a piece of the current script.
98 * ASSERT_SAVED_SP_AND_PC checks that SAVE_SP_AND_PC was called.
100 #define SAVE_SP_AND_PC(fp) (SAVE_SP(fp), (fp)->pc = pc)
101 #define RESTORE_SP_AND_PC(fp) (RESTORE_SP(fp), pc = (fp)->pc)
102 #define ASSERT_SAVED_SP_AND_PC(fp) JS_ASSERT((fp)->sp == sp && (fp)->pc == pc);
105 * Push the generating bytecode's pc onto the parallel pc stack that runs
106 * depth slots below the operands.
108 * NB: PUSH_OPND uses sp, depth, and pc from its lexical environment. See
109 * js_Interpret for these local variables' declarations and uses.
111 #define PUSH_OPND(v) (sp[-depth] = (jsval)pc, PUSH(v))
112 #define STORE_OPND(n,v) (sp[(n)-depth] = (jsval)pc, sp[n] = (v))
113 #define POP_OPND() POP()
114 #define FETCH_OPND(n) (sp[n])
117 * Push the jsdouble d using sp, depth, and pc from the lexical environment.
118 * Try to convert d to a jsint that fits in a jsval, otherwise GC-alloc space
119 * for it and push a reference.
121 #define STORE_NUMBER(cx, n, d) \
122 JS_BEGIN_MACRO \
123 jsint i_; \
124 jsval v_; \
126 if (JSDOUBLE_IS_INT(d, i_) && INT_FITS_IN_JSVAL(i_)) { \
127 v_ = INT_TO_JSVAL(i_); \
128 } else { \
129 ok = js_NewDoubleValue(cx, d, &v_); \
130 if (!ok) \
131 goto out; \
133 STORE_OPND(n, v_); \
134 JS_END_MACRO
136 #define STORE_INT(cx, n, i) \
137 JS_BEGIN_MACRO \
138 jsval v_; \
140 if (INT_FITS_IN_JSVAL(i)) { \
141 v_ = INT_TO_JSVAL(i); \
142 } else { \
143 ok = js_NewDoubleValue(cx, (jsdouble)(i), &v_); \
144 if (!ok) \
145 goto out; \
147 STORE_OPND(n, v_); \
148 JS_END_MACRO
150 #define STORE_UINT(cx, n, u) \
151 JS_BEGIN_MACRO \
152 jsval v_; \
154 if ((u) <= JSVAL_INT_MAX) { \
155 v_ = INT_TO_JSVAL(u); \
156 } else { \
157 ok = js_NewDoubleValue(cx, (jsdouble)(u), &v_); \
158 if (!ok) \
159 goto out; \
161 STORE_OPND(n, v_); \
162 JS_END_MACRO
164 #define FETCH_NUMBER(cx, n, d) \
165 JS_BEGIN_MACRO \
166 jsval v_; \
168 v_ = FETCH_OPND(n); \
169 VALUE_TO_NUMBER(cx, v_, d); \
170 JS_END_MACRO
172 #define FETCH_INT(cx, n, i) \
173 JS_BEGIN_MACRO \
174 jsval v_ = FETCH_OPND(n); \
175 if (JSVAL_IS_INT(v_)) { \
176 i = JSVAL_TO_INT(v_); \
177 } else { \
178 SAVE_SP_AND_PC(fp); \
179 ok = js_ValueToECMAInt32(cx, v_, &i); \
180 if (!ok) \
181 goto out; \
183 JS_END_MACRO
185 #define FETCH_UINT(cx, n, ui) \
186 JS_BEGIN_MACRO \
187 jsval v_ = FETCH_OPND(n); \
188 jsint i_; \
189 if (JSVAL_IS_INT(v_) && (i_ = JSVAL_TO_INT(v_)) >= 0) { \
190 ui = (uint32) i_; \
191 } else { \
192 SAVE_SP_AND_PC(fp); \
193 ok = js_ValueToECMAUint32(cx, v_, &ui); \
194 if (!ok) \
195 goto out; \
197 JS_END_MACRO
200 * Optimized conversion macros that test for the desired type in v before
201 * homing sp and calling a conversion function.
203 #define VALUE_TO_NUMBER(cx, v, d) \
204 JS_BEGIN_MACRO \
205 if (JSVAL_IS_INT(v)) { \
206 d = (jsdouble)JSVAL_TO_INT(v); \
207 } else if (JSVAL_IS_DOUBLE(v)) { \
208 d = *JSVAL_TO_DOUBLE(v); \
209 } else { \
210 SAVE_SP_AND_PC(fp); \
211 ok = js_ValueToNumber(cx, v, &d); \
212 if (!ok) \
213 goto out; \
215 JS_END_MACRO
217 #define POP_BOOLEAN(cx, v, b) \
218 JS_BEGIN_MACRO \
219 v = FETCH_OPND(-1); \
220 if (v == JSVAL_NULL) { \
221 b = JS_FALSE; \
222 } else if (JSVAL_IS_BOOLEAN(v)) { \
223 b = JSVAL_TO_BOOLEAN(v); \
224 } else { \
225 SAVE_SP_AND_PC(fp); \
226 ok = js_ValueToBoolean(cx, v, &b); \
227 if (!ok) \
228 goto out; \
230 sp--; \
231 JS_END_MACRO
233 /* SAVE_SP_AND_PC must be already called. */
234 #define VALUE_TO_OBJECT(cx, n, v, obj) \
235 JS_BEGIN_MACRO \
236 ASSERT_SAVED_SP_AND_PC(fp); \
237 if (!JSVAL_IS_PRIMITIVE(v)) { \
238 obj = JSVAL_TO_OBJECT(v); \
239 } else { \
240 obj = js_ValueToNonNullObject(cx, v); \
241 if (!obj) { \
242 ok = JS_FALSE; \
243 goto out; \
245 STORE_OPND(n, OBJECT_TO_JSVAL(obj)); \
247 JS_END_MACRO
249 /* SAVE_SP_AND_PC must be already called. */
250 #define FETCH_OBJECT(cx, n, v, obj) \
251 JS_BEGIN_MACRO \
252 ASSERT_SAVED_SP_AND_PC(fp); \
253 v = FETCH_OPND(n); \
254 VALUE_TO_OBJECT(cx, n, v, obj); \
255 JS_END_MACRO
257 #define DEFAULT_VALUE(cx, n, hint, v) \
258 JS_BEGIN_MACRO \
259 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); \
260 JS_ASSERT(v == sp[n]); \
261 SAVE_SP_AND_PC(fp); \
262 ok = OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), hint, &sp[n]); \
263 if (!ok) \
264 goto out; \
265 v = sp[n]; \
266 JS_END_MACRO
268 JS_FRIEND_API(jsval *)
269 js_AllocRawStack(JSContext *cx, uintN nslots, void **markp)
271 jsval *sp;
273 if (markp)
274 *markp = JS_ARENA_MARK(&cx->stackPool);
275 JS_ARENA_ALLOCATE_CAST(sp, jsval *, &cx->stackPool, nslots * sizeof(jsval));
276 if (!sp) {
277 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_STACK_OVERFLOW,
278 (cx->fp && cx->fp->fun)
279 ? JS_GetFunctionName(cx->fp->fun)
280 : "script");
282 return sp;
285 JS_FRIEND_API(void)
286 js_FreeRawStack(JSContext *cx, void *mark)
288 JS_ARENA_RELEASE(&cx->stackPool, mark);
291 JS_FRIEND_API(jsval *)
292 js_AllocStack(JSContext *cx, uintN nslots, void **markp)
294 jsval *sp, *vp, *end;
295 JSArena *a;
296 JSStackHeader *sh;
297 JSStackFrame *fp;
299 /* Callers don't check for zero nslots: we do to avoid empty segments. */
300 if (nslots == 0) {
301 *markp = NULL;
302 return (jsval *) JS_ARENA_MARK(&cx->stackPool);
305 /* Allocate 2 extra slots for the stack segment header we'll likely need. */
306 sp = js_AllocRawStack(cx, 2 + nslots, markp);
307 if (!sp)
308 return NULL;
310 /* Try to avoid another header if we can piggyback on the last segment. */
311 a = cx->stackPool.current;
312 sh = cx->stackHeaders;
313 if (sh && JS_STACK_SEGMENT(sh) + sh->nslots == sp) {
314 /* Extend the last stack segment, give back the 2 header slots. */
315 sh->nslots += nslots;
316 a->avail -= 2 * sizeof(jsval);
317 } else {
319 * Need a new stack segment, so we must initialize unused slots in the
320 * current frame. See js_GC, just before marking the "operand" jsvals,
321 * where we scan from fp->spbase to fp->sp or through fp->script->depth
322 * (whichever covers fewer slots).
324 fp = cx->fp;
325 if (fp && fp->script && fp->spbase) {
326 #ifdef DEBUG
327 jsuword depthdiff = fp->script->depth * sizeof(jsval);
328 JS_ASSERT(JS_UPTRDIFF(fp->sp, fp->spbase) <= depthdiff);
329 JS_ASSERT(JS_UPTRDIFF(*markp, fp->spbase) >= depthdiff);
330 #endif
331 end = fp->spbase + fp->script->depth;
332 for (vp = fp->sp; vp < end; vp++)
333 *vp = JSVAL_VOID;
336 /* Allocate and push a stack segment header from the 2 extra slots. */
337 sh = (JSStackHeader *)sp;
338 sh->nslots = nslots;
339 sh->down = cx->stackHeaders;
340 cx->stackHeaders = sh;
341 sp += 2;
345 * Store JSVAL_NULL using memset, to let compilers optimize as they see
346 * fit, in case a caller allocates and pushes GC-things one by one, which
347 * could nest a last-ditch GC that will scan this segment.
349 memset(sp, 0, nslots * sizeof(jsval));
350 return sp;
353 JS_FRIEND_API(void)
354 js_FreeStack(JSContext *cx, void *mark)
356 JSStackHeader *sh;
357 jsuword slotdiff;
359 /* Check for zero nslots allocation special case. */
360 if (!mark)
361 return;
363 /* We can assert because js_FreeStack always balances js_AllocStack. */
364 sh = cx->stackHeaders;
365 JS_ASSERT(sh);
367 /* If mark is in the current segment, reduce sh->nslots, else pop sh. */
368 slotdiff = JS_UPTRDIFF(mark, JS_STACK_SEGMENT(sh)) / sizeof(jsval);
369 if (slotdiff < (jsuword)sh->nslots)
370 sh->nslots = slotdiff;
371 else
372 cx->stackHeaders = sh->down;
374 /* Release the stackPool space allocated since mark was set. */
375 JS_ARENA_RELEASE(&cx->stackPool, mark);
378 JSBool
379 js_GetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
381 return JS_TRUE;
384 JSBool
385 js_SetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
387 return JS_TRUE;
390 JSBool
391 js_GetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
393 return JS_TRUE;
396 JSBool
397 js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
399 return JS_TRUE;
402 JSObject *
403 js_GetScopeChain(JSContext *cx, JSStackFrame *fp)
405 JSObject *obj, *cursor, *clonedChild, *parent;
406 JSTempValueRooter tvr;
408 obj = fp->blockChain;
409 if (!obj) {
411 * Don't force a call object for a lightweight function call, but do
412 * insist that there is a call object for a heavyweight function call.
414 JS_ASSERT(!fp->fun ||
415 !(fp->fun->flags & JSFUN_HEAVYWEIGHT) ||
416 fp->callobj);
417 JS_ASSERT(fp->scopeChain);
418 return fp->scopeChain;
422 * We have one or more lexical scopes to reflect into fp->scopeChain, so
423 * make sure there's a call object at the current head of the scope chain,
424 * if this frame is a call frame.
426 if (fp->fun && !fp->callobj) {
427 JS_ASSERT(OBJ_GET_CLASS(cx, fp->scopeChain) != &js_BlockClass ||
428 JS_GetPrivate(cx, fp->scopeChain) != fp);
429 if (!js_GetCallObject(cx, fp, fp->scopeChain))
430 return NULL;
434 * Clone the block chain. To avoid recursive cloning we set the parent of
435 * the cloned child after we clone the parent. In the following loop when
436 * clonedChild is null it indicates the first iteration when no special GC
437 * rooting is necessary. On the second and the following iterations we
438 * have to protect cloned so far chain against the GC during cloning of
439 * the cursor object.
441 cursor = obj;
442 clonedChild = NULL;
443 for (;;) {
444 parent = OBJ_GET_PARENT(cx, cursor);
447 * We pass fp->scopeChain and not null even if we override the parent
448 * slot later as null triggers useless calculations of slot's value in
449 * js_NewObject that js_CloneBlockObject calls.
451 cursor = js_CloneBlockObject(cx, cursor, fp->scopeChain, fp);
452 if (!cursor) {
453 if (clonedChild)
454 JS_POP_TEMP_ROOT(cx, &tvr);
455 return NULL;
457 if (!clonedChild) {
459 * The first iteration. Check if other follow and root obj if so
460 * to protect the whole cloned chain against GC.
462 obj = cursor;
463 if (!parent)
464 break;
465 JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr);
466 } else {
468 * Avoid OBJ_SET_PARENT overhead as clonedChild cannot escape to
469 * other threads.
471 STOBJ_SET_PARENT(clonedChild, cursor);
472 if (!parent) {
473 JS_ASSERT(tvr.u.value == OBJECT_TO_JSVAL(obj));
474 JS_POP_TEMP_ROOT(cx, &tvr);
475 break;
478 clonedChild = cursor;
479 cursor = parent;
481 fp->flags |= JSFRAME_POP_BLOCKS;
482 fp->scopeChain = obj;
483 fp->blockChain = NULL;
484 return obj;
488 * Walk the scope chain looking for block scopes whose locals need to be
489 * copied from stack slots into object slots before fp goes away.
491 static JSBool
492 PutBlockObjects(JSContext *cx, JSStackFrame *fp)
494 JSBool ok;
495 JSObject *obj;
497 ok = JS_TRUE;
498 for (obj = fp->scopeChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {
499 if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) {
500 if (JS_GetPrivate(cx, obj) != fp)
501 break;
502 ok &= js_PutBlockObject(cx, obj);
505 return ok;
508 JSBool
509 js_GetPrimitiveThis(JSContext *cx, jsval *vp, JSClass *clasp, jsval *thisvp)
511 jsval v;
512 JSObject *obj;
514 v = vp[1];
515 if (JSVAL_IS_OBJECT(v)) {
516 obj = JSVAL_TO_OBJECT(v);
517 if (!JS_InstanceOf(cx, obj, clasp, vp + 2))
518 return JS_FALSE;
519 v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
521 *thisvp = v;
522 return JS_TRUE;
526 * ECMA requires "the global object", but in embeddings such as the browser,
527 * which have multiple top-level objects (windows, frames, etc. in the DOM),
528 * we prefer fun's parent. An example that causes this code to run:
530 * // in window w1
531 * function f() { return this }
532 * function g() { return f }
534 * // in window w2
535 * var h = w1.g()
536 * alert(h() == w1)
538 * The alert should display "true".
540 static JSBool
541 ComputeGlobalThis(JSContext *cx, jsval *argv)
543 JSObject *thisp;
545 if (JSVAL_IS_PRIMITIVE(argv[-2]) ||
546 !OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2]))) {
547 thisp = cx->globalObject;
548 } else {
549 jsid id;
550 jsval v;
551 uintN attrs;
552 JSObject *parent;
554 /* Walk up the parent chain. */
555 thisp = JSVAL_TO_OBJECT(argv[-2]);
556 id = ATOM_TO_JSID(cx->runtime->atomState.parentAtom);
557 for (;;) {
558 if (!OBJ_CHECK_ACCESS(cx, thisp, id, JSACC_PARENT, &v, &attrs))
559 return JS_FALSE;
560 parent = JSVAL_IS_VOID(v)
561 ? OBJ_GET_PARENT(cx, thisp)
562 : JSVAL_TO_OBJECT(v);
563 if (!parent)
564 break;
565 thisp = parent;
568 argv[-1] = OBJECT_TO_JSVAL(thisp);
569 return JS_TRUE;
572 static JSBool
573 ComputeThis(JSContext *cx, jsval *argv)
575 JSObject *thisp;
577 JS_ASSERT(!JSVAL_IS_NULL(argv[-1]));
578 if (!JSVAL_IS_OBJECT(argv[-1]))
579 return js_PrimitiveToObject(cx, &argv[-1]);
581 thisp = JSVAL_TO_OBJECT(argv[-1]);
582 if (OBJ_GET_CLASS(cx, thisp) == &js_CallClass)
583 return ComputeGlobalThis(cx, argv);
585 if (!thisp->map->ops->thisObject)
586 return JS_TRUE;
588 /* Some objects (e.g., With) delegate 'this' to another object. */
589 thisp = thisp->map->ops->thisObject(cx, thisp);
590 if (!thisp)
591 return JS_FALSE;
592 argv[-1] = OBJECT_TO_JSVAL(thisp);
593 return JS_TRUE;
596 JSBool
597 js_ComputeThis(JSContext *cx, jsval *argv)
599 if (JSVAL_IS_NULL(argv[-1]))
600 return ComputeGlobalThis(cx, argv);
601 return ComputeThis(cx, argv);
604 #if JS_HAS_NO_SUCH_METHOD
606 static JSBool
607 NoSuchMethod(JSContext *cx, JSStackFrame *fp, jsval *vp, uint32 flags,
608 uintN argc)
610 JSObject *thisp, *argsobj;
611 JSAtom *atom;
612 jsval *sp, roots[3];
613 JSTempValueRooter tvr;
614 jsid id;
615 JSBool ok;
616 jsbytecode *pc;
618 /* NB: js_ComputeThis or equivalent must have been called already. */
619 JS_ASSERT(JSVAL_IS_PRIMITIVE(vp[0]));
620 JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
621 RESTORE_SP(fp);
623 /* From here on, control must flow through label out: to return. */
624 memset(roots, 0, sizeof roots);
625 JS_PUSH_TEMP_ROOT(cx, JS_ARRAY_LENGTH(roots), roots, &tvr);
627 id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom);
628 thisp = JSVAL_TO_OBJECT(vp[1]);
629 #if JS_HAS_XML_SUPPORT
630 if (OBJECT_IS_XML(cx, thisp)) {
631 JSXMLObjectOps *ops;
633 ops = (JSXMLObjectOps *) thisp->map->ops;
634 thisp = ops->getMethod(cx, thisp, id, &roots[2]);
635 if (!thisp) {
636 ok = JS_FALSE;
637 goto out;
639 vp[1] = OBJECT_TO_JSVAL(thisp);
640 } else
641 #endif
643 ok = OBJ_GET_PROPERTY(cx, thisp, id, &roots[2]);
644 if (!ok)
645 goto out;
647 if (JSVAL_IS_PRIMITIVE(roots[2]))
648 goto not_function;
650 pc = (jsbytecode *) vp[-(intN)fp->script->depth];
651 switch ((JSOp) *pc) {
652 case JSOP_NAME:
653 case JSOP_GETPROP:
654 #if JS_HAS_XML_SUPPORT
655 case JSOP_CALLPROP:
656 #endif
657 GET_ATOM_FROM_BYTECODE(fp->script, pc, 0, atom);
658 roots[0] = ATOM_KEY(atom);
659 argsobj = js_NewArrayObject(cx, argc, vp + 2);
660 if (!argsobj) {
661 ok = JS_FALSE;
662 goto out;
664 roots[1] = OBJECT_TO_JSVAL(argsobj);
665 ok = js_InternalInvoke(cx, thisp, roots[2], flags | JSINVOKE_INTERNAL,
666 2, roots, &vp[0]);
667 break;
669 default:
670 goto not_function;
673 out:
674 JS_POP_TEMP_ROOT(cx, &tvr);
675 return ok;
677 not_function:
678 js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS);
679 ok = JS_FALSE;
680 goto out;
683 #endif /* JS_HAS_NO_SUCH_METHOD */
685 #ifdef DUMP_CALL_TABLE
687 #include "jsclist.h"
688 #include "jshash.h"
689 #include "jsdtoa.h"
691 typedef struct CallKey {
692 jsval callee; /* callee value */
693 const char *filename; /* function filename or null */
694 uintN lineno; /* function lineno or 0 */
695 } CallKey;
697 /* Compensate for typeof null == "object" brain damage. */
698 #define JSTYPE_NULL JSTYPE_LIMIT
699 #define TYPEOF(cx,v) (JSVAL_IS_NULL(v) ? JSTYPE_NULL : JS_TypeOfValue(cx,v))
700 #define TYPENAME(t) (((t) == JSTYPE_NULL) ? js_null_str : JS_TYPE_STR(t))
701 #define NTYPEHIST (JSTYPE_LIMIT + 1)
703 typedef struct CallValue {
704 uint32 total; /* total call count */
705 uint32 recycled; /* LRU-recycled calls lost */
706 uint16 minargc; /* minimum argument count */
707 uint16 maxargc; /* maximum argument count */
708 struct ArgInfo {
709 uint32 typeHist[NTYPEHIST]; /* histogram by type */
710 JSCList lruList; /* top 10 values LRU list */
711 struct ArgValCount {
712 JSCList lruLink; /* LRU list linkage */
713 jsval value; /* recently passed value */
714 uint32 count; /* number of times passed */
715 char strbuf[112]; /* string conversion buffer */
716 } topValCounts[10]; /* top 10 value storage */
717 } argInfo[8];
718 } CallValue;
720 typedef struct CallEntry {
721 JSHashEntry entry;
722 CallKey key;
723 CallValue value;
724 char name[32]; /* function name copy */
725 } CallEntry;
727 static void *
728 AllocCallTable(void *pool, size_t size)
730 return malloc(size);
733 static void
734 FreeCallTable(void *pool, void *item)
736 free(item);
739 static JSHashEntry *
740 AllocCallEntry(void *pool, const void *key)
742 return (JSHashEntry*) calloc(1, sizeof(CallEntry));
745 static void
746 FreeCallEntry(void *pool, JSHashEntry *he, uintN flag)
748 JS_ASSERT(flag == HT_FREE_ENTRY);
749 free(he);
752 static JSHashAllocOps callTableAllocOps = {
753 AllocCallTable, FreeCallTable,
754 AllocCallEntry, FreeCallEntry
757 JS_STATIC_DLL_CALLBACK(JSHashNumber)
758 js_hash_call_key(const void *key)
760 CallKey *ck = (CallKey *) key;
761 JSHashNumber hash = (jsuword)ck->callee >> 3;
763 if (ck->filename) {
764 hash = (hash << 4) ^ JS_HashString(ck->filename);
765 hash = (hash << 4) ^ ck->lineno;
767 return hash;
770 JS_STATIC_DLL_CALLBACK(intN)
771 js_compare_call_keys(const void *k1, const void *k2)
773 CallKey *ck1 = (CallKey *)k1, *ck2 = (CallKey *)k2;
775 return ck1->callee == ck2->callee &&
776 ((ck1->filename && ck2->filename)
777 ? strcmp(ck1->filename, ck2->filename) == 0
778 : ck1->filename == ck2->filename) &&
779 ck1->lineno == ck2->lineno;
782 JSHashTable *js_CallTable;
783 size_t js_LogCallToSourceLimit;
785 JS_STATIC_DLL_CALLBACK(intN)
786 CallTableDumper(JSHashEntry *he, intN k, void *arg)
788 CallEntry *ce = (CallEntry *)he;
789 FILE *fp = (FILE *)arg;
790 uintN argc, i, n;
791 struct ArgInfo *ai;
792 JSType save, type;
793 JSCList *cl;
794 struct ArgValCount *avc;
795 jsval argval;
797 if (ce->key.filename) {
798 /* We're called at the end of the mark phase, so mark our filenames. */
799 js_MarkScriptFilename(ce->key.filename);
800 fprintf(fp, "%s:%u ", ce->key.filename, ce->key.lineno);
801 } else {
802 fprintf(fp, "@%p ", (void *) ce->key.callee);
805 if (ce->name[0])
806 fprintf(fp, "name %s ", ce->name);
807 fprintf(fp, "calls %lu (%lu) argc %u/%u\n",
808 (unsigned long) ce->value.total,
809 (unsigned long) ce->value.recycled,
810 ce->value.minargc, ce->value.maxargc);
812 argc = JS_MIN(ce->value.maxargc, 8);
813 for (i = 0; i < argc; i++) {
814 ai = &ce->value.argInfo[i];
816 n = 0;
817 save = -1;
818 for (type = JSTYPE_VOID; type <= JSTYPE_LIMIT; type++) {
819 if (ai->typeHist[type]) {
820 save = type;
821 ++n;
824 if (n == 1) {
825 fprintf(fp, " arg %u type %s: %lu\n",
826 i, TYPENAME(save), (unsigned long) ai->typeHist[save]);
827 } else {
828 fprintf(fp, " arg %u type histogram:\n", i);
829 for (type = JSTYPE_VOID; type <= JSTYPE_LIMIT; type++) {
830 fprintf(fp, " %9s: %8lu ",
831 TYPENAME(type), (unsigned long) ai->typeHist[type]);
832 for (n = (uintN) JS_HOWMANY(ai->typeHist[type], 10); n > 0; --n)
833 fputc('*', fp);
834 fputc('\n', fp);
838 fprintf(fp, " arg %u top 10 values:\n", i);
839 n = 1;
840 for (cl = ai->lruList.prev; cl != &ai->lruList; cl = cl->prev) {
841 avc = (struct ArgValCount *)cl;
842 if (!avc->count)
843 break;
844 argval = avc->value;
845 fprintf(fp, " %9u: %8lu %.*s (%#lx)\n",
846 n, (unsigned long) avc->count,
847 (int) sizeof avc->strbuf, avc->strbuf,
848 argval);
849 ++n;
853 return HT_ENUMERATE_NEXT;
856 void
857 js_DumpCallTable(JSContext *cx)
859 char name[24];
860 FILE *fp;
861 static uintN dumpCount;
863 if (!js_CallTable)
864 return;
866 JS_snprintf(name, sizeof name, "/tmp/calltable.dump.%u", dumpCount & 7);
867 dumpCount++;
868 fp = fopen(name, "w");
869 if (!fp)
870 return;
872 JS_HashTableEnumerateEntries(js_CallTable, CallTableDumper, fp);
873 fclose(fp);
876 static void
877 LogCall(JSContext *cx, jsval callee, uintN argc, jsval *argv)
879 CallKey key;
880 const char *name, *cstr;
881 JSFunction *fun;
882 JSHashNumber keyHash;
883 JSHashEntry **hep, *he;
884 CallEntry *ce;
885 uintN i, j;
886 jsval argval;
887 JSType type;
888 struct ArgInfo *ai;
889 struct ArgValCount *avc;
890 JSString *str;
892 if (!js_CallTable) {
893 js_CallTable = JS_NewHashTable(1024, js_hash_call_key,
894 js_compare_call_keys, NULL,
895 &callTableAllocOps, NULL);
896 if (!js_CallTable)
897 return;
900 key.callee = callee;
901 key.filename = NULL;
902 key.lineno = 0;
903 name = "";
904 if (VALUE_IS_FUNCTION(cx, callee)) {
905 fun = (JSFunction *) OBJ_GET_PRIVATE(cx, JSVAL_TO_OBJECT(callee));
906 if (fun->atom)
907 name = js_AtomToPrintableString(cx, fun->atom);
908 if (FUN_INTERPRETED(fun)) {
909 key.filename = fun->u.i.script->filename;
910 key.lineno = fun->u.i.script->lineno;
913 keyHash = js_hash_call_key(&key);
915 hep = JS_HashTableRawLookup(js_CallTable, keyHash, &key);
916 he = *hep;
917 if (he) {
918 ce = (CallEntry *) he;
919 JS_ASSERT(strncmp(ce->name, name, sizeof ce->name) == 0);
920 } else {
921 he = JS_HashTableRawAdd(js_CallTable, hep, keyHash, &key, NULL);
922 if (!he)
923 return;
924 ce = (CallEntry *) he;
925 ce->entry.key = &ce->key;
926 ce->entry.value = &ce->value;
927 ce->key = key;
928 for (i = 0; i < 8; i++) {
929 ai = &ce->value.argInfo[i];
930 JS_INIT_CLIST(&ai->lruList);
931 for (j = 0; j < 10; j++)
932 JS_APPEND_LINK(&ai->topValCounts[j].lruLink, &ai->lruList);
934 strncpy(ce->name, name, sizeof ce->name);
937 ++ce->value.total;
938 if (ce->value.minargc < argc)
939 ce->value.minargc = argc;
940 if (ce->value.maxargc < argc)
941 ce->value.maxargc = argc;
942 if (argc > 8)
943 argc = 8;
944 for (i = 0; i < argc; i++) {
945 ai = &ce->value.argInfo[i];
946 argval = argv[i];
947 type = TYPEOF(cx, argval);
948 ++ai->typeHist[type];
950 for (j = 0; ; j++) {
951 if (j == 10) {
952 avc = (struct ArgValCount *) ai->lruList.next;
953 ce->value.recycled += avc->count;
954 avc->value = argval;
955 avc->count = 1;
956 break;
958 avc = &ai->topValCounts[j];
959 if (avc->value == argval) {
960 ++avc->count;
961 break;
965 /* Move avc to the back of the LRU list. */
966 JS_REMOVE_LINK(&avc->lruLink);
967 JS_APPEND_LINK(&avc->lruLink, &ai->lruList);
969 str = NULL;
970 cstr = "";
971 switch (TYPEOF(cx, argval)) {
972 case JSTYPE_VOID:
973 cstr = js_undefined_str;
974 break;
975 case JSTYPE_NULL:
976 cstr = js_null_str;
977 break;
978 case JSTYPE_BOOLEAN:
979 cstr = JS_BOOLEAN_STR(JSVAL_TO_BOOLEAN(argval));
980 break;
981 case JSTYPE_NUMBER:
982 if (JSVAL_IS_INT(argval)) {
983 JS_snprintf(avc->strbuf, sizeof avc->strbuf, "%ld",
984 JSVAL_TO_INT(argval));
985 } else {
986 JS_dtostr(avc->strbuf, sizeof avc->strbuf, DTOSTR_STANDARD, 0,
987 *JSVAL_TO_DOUBLE(argval));
989 continue;
990 case JSTYPE_STRING:
991 str = js_QuoteString(cx, JSVAL_TO_STRING(argval), (jschar)'"');
992 break;
993 case JSTYPE_FUNCTION:
994 if (VALUE_IS_FUNCTION(cx, argval)) {
995 fun = (JSFunction *) OBJ_GET_PRIVATE(cx, JSVAL_TO_OBJECT(argval));
996 if (fun && fun->atom) {
997 str = ATOM_TO_STRING(fun->atom);
998 break;
1001 /* FALL THROUGH */
1002 case JSTYPE_OBJECT:
1003 js_LogCallToSourceLimit = sizeof avc->strbuf;
1004 cx->options |= JSOPTION_LOGCALL_TOSOURCE;
1005 str = js_ValueToSource(cx, argval);
1006 cx->options &= ~JSOPTION_LOGCALL_TOSOURCE;
1007 break;
1009 if (str)
1010 js_PutEscapedString(avc->strbuf, sizeof avc->strbuf, str, 0);
1011 else
1012 strncpy(avc->strbuf, cstr, sizeof avc->strbuf);
1016 #endif /* DUMP_CALL_TABLE */
1019 * Conditional assert to detect failure to clear a pending exception that is
1020 * suppressed (or unintentional suppression of a wanted exception).
1022 #if defined DEBUG_brendan || defined DEBUG_mrbkap || defined DEBUG_shaver
1023 # define DEBUG_NOT_THROWING 1
1024 #endif
1026 #ifdef DEBUG_NOT_THROWING
1027 # define ASSERT_NOT_THROWING(cx) JS_ASSERT(!(cx)->throwing)
1028 #else
1029 # define ASSERT_NOT_THROWING(cx) /* nothing */
1030 #endif
1032 #define START_FAST_CALL(fp) (JS_ASSERT(!((fp)->flags & JSFRAME_IN_FAST_CALL)),\
1033 (fp)->flags |= JSFRAME_IN_FAST_CALL)
1034 #define END_FAST_CALL(fp) (JS_ASSERT((fp)->flags & JSFRAME_IN_FAST_CALL), \
1035 (fp)->flags &= ~JSFRAME_IN_FAST_CALL)
1038 * We check if the function accepts a primitive value as |this|. For that we
1039 * use a table that maps value's tag into the corresponding function flag.
1041 JS_STATIC_ASSERT(JSVAL_INT == 1);
1042 JS_STATIC_ASSERT(JSVAL_DOUBLE == 2);
1043 JS_STATIC_ASSERT(JSVAL_STRING == 4);
1044 JS_STATIC_ASSERT(JSVAL_BOOLEAN == 6);
1046 static const uint16 PrimitiveTestFlags[] = {
1047 JSFUN_THISP_NUMBER, /* INT */
1048 JSFUN_THISP_NUMBER, /* DOUBLE */
1049 JSFUN_THISP_NUMBER, /* INT */
1050 JSFUN_THISP_STRING, /* STRING */
1051 JSFUN_THISP_NUMBER, /* INT */
1052 JSFUN_THISP_BOOLEAN, /* BOOLEAN */
1053 JSFUN_THISP_NUMBER /* INT */
1056 #define PRIMITIVE_THIS_TEST(fun,thisv) \
1057 (JS_ASSERT(thisv != JSVAL_VOID), \
1058 JSFUN_THISP_TEST(JSFUN_THISP_FLAGS((fun)->flags), \
1059 PrimitiveTestFlags[JSVAL_TAG(thisv) - 1]))
1062 * Find a function reference and its 'this' object implicit first parameter
1063 * under argc arguments on cx's stack, and call the function. Push missing
1064 * required arguments, allocate declared local variables, and pop everything
1065 * when done. Then push the return value.
1067 JS_FRIEND_API(JSBool)
1068 js_Invoke(JSContext *cx, uintN argc, uintN flags)
1070 void *mark;
1071 JSStackFrame *fp, frame;
1072 jsval *sp, *newsp, *limit;
1073 jsval *vp, v;
1074 JSObject *funobj, *parent;
1075 JSBool ok;
1076 JSClass *clasp;
1077 JSObjectOps *ops;
1078 JSNative native;
1079 JSFunction *fun;
1080 JSScript *script;
1081 uintN nslots, nvars, nalloc, surplus;
1082 JSInterpreterHook hook;
1083 void *hookData;
1085 /* Mark the top of stack and load frequently-used registers. */
1086 mark = JS_ARENA_MARK(&cx->stackPool);
1087 fp = cx->fp;
1088 sp = fp->sp;
1091 * Set vp to the callee value's stack slot (it's where rval goes).
1092 * Once vp is set, control should flow through label out2: to return.
1093 * Set frame.rval early so native class and object ops can throw and
1094 * return false, causing a goto out2 with ok set to false.
1096 vp = sp - (2 + argc);
1097 v = *vp;
1098 frame.rval = JSVAL_VOID;
1101 * A callee must be an object reference, unless its 'this' parameter
1102 * implements the __noSuchMethod__ method, in which case that method will
1103 * be called like so:
1105 * this.__noSuchMethod__(id, args)
1107 * where id is the name of the method that this invocation attempted to
1108 * call by name, and args is an Array containing this invocation's actual
1109 * parameters.
1111 if (JSVAL_IS_PRIMITIVE(v)) {
1112 #if JS_HAS_NO_SUCH_METHOD
1113 if (fp->script && !(flags & JSINVOKE_INTERNAL)) {
1114 ok = NoSuchMethod(cx, fp, vp, flags, argc);
1115 if (ok)
1116 frame.rval = *vp;
1117 goto out2;
1119 #endif
1120 goto bad;
1123 funobj = JSVAL_TO_OBJECT(v);
1124 parent = OBJ_GET_PARENT(cx, funobj);
1125 clasp = OBJ_GET_CLASS(cx, funobj);
1126 if (clasp != &js_FunctionClass) {
1127 /* Function is inlined, all other classes use object ops. */
1128 ops = funobj->map->ops;
1131 * XXX this makes no sense -- why convert to function if clasp->call?
1132 * XXX better to call that hook without converting
1133 * XXX the only thing that needs fixing is liveconnect
1135 * Try converting to function, for closure and API compatibility.
1136 * We attempt the conversion under all circumstances for 1.2, but
1137 * only if there is a call op defined otherwise.
1139 if ((ops == &js_ObjectOps) ? clasp->call : ops->call) {
1140 ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v);
1141 if (!ok)
1142 goto out2;
1144 if (VALUE_IS_FUNCTION(cx, v)) {
1145 /* Make vp refer to funobj to keep it available as argv[-2]. */
1146 *vp = v;
1147 funobj = JSVAL_TO_OBJECT(v);
1148 parent = OBJ_GET_PARENT(cx, funobj);
1149 goto have_fun;
1152 fun = NULL;
1153 script = NULL;
1154 nslots = nvars = 0;
1156 /* Try a call or construct native object op. */
1157 if (flags & JSINVOKE_CONSTRUCT) {
1158 if (!JSVAL_IS_OBJECT(vp[1])) {
1159 ok = js_PrimitiveToObject(cx, &vp[1]);
1160 if (!ok)
1161 goto out2;
1163 native = ops->construct;
1164 } else {
1165 native = ops->call;
1167 if (!native)
1168 goto bad;
1169 } else {
1170 have_fun:
1171 /* Get private data and set derived locals from it. */
1172 fun = (JSFunction *) OBJ_GET_PRIVATE(cx, funobj);
1173 nalloc = FUN_MINARGS(fun);
1174 nslots = (nalloc > argc) ? nalloc - argc : 0;
1175 if (FUN_INTERPRETED(fun)) {
1176 native = NULL;
1177 script = fun->u.i.script;
1178 nvars = fun->u.i.nvars;
1179 } else {
1180 native = fun->u.n.native;
1181 script = NULL;
1182 nvars = 0;
1183 nslots += fun->u.n.extra;
1186 if (JSFUN_BOUND_METHOD_TEST(fun->flags)) {
1187 /* Handle bound method special case. */
1188 vp[1] = OBJECT_TO_JSVAL(parent);
1189 } else if (!JSVAL_IS_OBJECT(vp[1])) {
1190 JS_ASSERT(!(flags & JSINVOKE_CONSTRUCT));
1191 if (PRIMITIVE_THIS_TEST(fun, vp[1]))
1192 goto init_frame;
1196 if (flags & JSINVOKE_CONSTRUCT) {
1197 /* Default return value for a constructor is the new object. */
1198 JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
1199 frame.rval = vp[1];
1200 } else {
1202 * We must call js_ComputeThis in case we are not called from the
1203 * interpreter, where a prior bytecode has computed an appropriate
1204 * |this| already.
1206 ok = js_ComputeThis(cx, vp + 2);
1207 if (!ok)
1208 goto out2;
1211 init_frame:
1213 * Initialize the rest of frame, except for sp (set by SAVE_SP later).
1215 * To set thisp we use an explicit cast and not JSVAL_TO_OBJECT, as vp[1]
1216 * can be a primitive value here for those native functions specified with
1217 * JSFUN_THISP_(NUMBER|STRING|BOOLEAN) flags.
1219 frame.thisp = (JSObject *)vp[1];
1220 frame.varobj = NULL;
1221 frame.callobj = frame.argsobj = NULL;
1222 frame.script = script;
1223 frame.callee = funobj;
1224 frame.fun = fun;
1225 frame.argc = argc;
1226 frame.argv = sp - argc;
1227 frame.nvars = nvars;
1228 frame.vars = sp;
1229 frame.down = fp;
1230 frame.annotation = NULL;
1231 frame.scopeChain = NULL; /* set below for real, after cx->fp is set */
1232 frame.pc = NULL;
1233 frame.spbase = NULL;
1234 frame.sharpDepth = 0;
1235 frame.sharpArray = NULL;
1236 frame.flags = flags;
1237 frame.dormantNext = NULL;
1238 frame.xmlNamespace = NULL;
1239 frame.blockChain = NULL;
1241 /* From here on, control must flow through label out: to return. */
1242 cx->fp = &frame;
1244 /* Init these now in case we goto out before first hook call. */
1245 hook = cx->debugHooks->callHook;
1246 hookData = NULL;
1248 /* Check for argument slots required by the function. */
1249 if (nslots) {
1250 /* All arguments must be contiguous, so we may have to copy actuals. */
1251 nalloc = nslots;
1252 limit = (jsval *) cx->stackPool.current->limit;
1253 JS_ASSERT((jsval *) cx->stackPool.current->base <= sp && sp <= limit);
1254 if (sp + nslots > limit) {
1255 /* Hit end of arena: we have to copy argv[-2..(argc+nslots-1)]. */
1256 nalloc += 2 + argc;
1257 } else {
1258 /* Take advantage of surplus slots in the caller's frame depth. */
1259 JS_ASSERT((jsval *)mark >= sp);
1260 surplus = (jsval *)mark - sp;
1261 nalloc -= surplus;
1264 /* Check whether we have enough space in the caller's frame. */
1265 if ((intN)nalloc > 0) {
1266 /* Need space for actuals plus missing formals minus surplus. */
1267 newsp = js_AllocRawStack(cx, nalloc, NULL);
1268 if (!newsp) {
1269 ok = JS_FALSE;
1270 goto out;
1273 /* If we couldn't allocate contiguous args, copy actuals now. */
1274 if (newsp != mark) {
1275 JS_ASSERT(sp + nslots > limit);
1276 JS_ASSERT(2 + argc + nslots == nalloc);
1277 *newsp++ = vp[0];
1278 *newsp++ = vp[1];
1279 if (argc)
1280 memcpy(newsp, frame.argv, argc * sizeof(jsval));
1281 frame.argv = newsp;
1282 sp = frame.vars = newsp + argc;
1286 /* Advance frame.vars to make room for the missing args. */
1287 frame.vars += nslots;
1289 /* Push void to initialize missing args. */
1290 do {
1291 PUSH(JSVAL_VOID);
1292 } while (--nslots != 0);
1294 JS_ASSERT(nslots == 0);
1296 /* Now allocate stack space for local variables. */
1297 if (nvars) {
1298 JS_ASSERT((jsval *)cx->stackPool.current->avail >= frame.vars);
1299 surplus = (jsval *)cx->stackPool.current->avail - frame.vars;
1300 if (surplus < nvars) {
1301 newsp = js_AllocRawStack(cx, nvars, NULL);
1302 if (!newsp) {
1303 ok = JS_FALSE;
1304 goto out;
1306 if (newsp != sp) {
1307 /* NB: Discontinuity between argv and vars. */
1308 sp = frame.vars = newsp;
1312 /* Push void to initialize local variables. */
1313 do {
1314 PUSH(JSVAL_VOID);
1315 } while (--nvars != 0);
1317 JS_ASSERT(nvars == 0);
1319 /* Store the current sp in frame before calling fun. */
1320 SAVE_SP(&frame);
1322 /* call the hook if present */
1323 if (hook && (native || script))
1324 hookData = hook(cx, &frame, JS_TRUE, 0, cx->debugHooks->callHookData);
1326 /* Call the function, either a native method or an interpreted script. */
1327 if (native) {
1328 #ifdef DEBUG_NOT_THROWING
1329 JSBool alreadyThrowing = cx->throwing;
1330 #endif
1332 #if JS_HAS_LVALUE_RETURN
1333 /* Set by JS_SetCallReturnValue2, used to return reference types. */
1334 cx->rval2set = JS_FALSE;
1335 #endif
1337 /* If native, use caller varobj and scopeChain for eval. */
1338 frame.varobj = fp->varobj;
1339 frame.scopeChain = fp->scopeChain;
1341 /* But ensure that we have a scope chain. */
1342 if (!frame.scopeChain)
1343 frame.scopeChain = parent;
1345 if (fun && (fun->flags & JSFUN_FAST_NATIVE)) {
1347 * Note the lack of START/END_FAST_CALL bracketing here. Unlike
1348 * the other JSFastNative call (see the JSOP_CALL special case in
1349 * js_Interpret), we have a full stack frame for this call.
1351 ok = ((JSFastNative) native)(cx, argc, frame.argv - 2);
1352 frame.rval = frame.argv[-2];
1353 } else {
1354 #ifdef DEBUG_brendan
1355 static FILE *fp;
1356 if (!fp) {
1357 fp = fopen("/tmp/slow-natives.dump", "w");
1358 if (fp)
1359 setlinebuf(fp);
1361 if (fp) {
1362 fprintf(fp, "%p %s.%s\n",
1363 native,
1364 JSVAL_IS_OBJECT(vp[1])
1365 ? ((OBJ_GET_CLASS(cx, frame.thisp) == &js_FunctionClass)
1366 ? JS_GetFunctionName(JS_GetPrivate(cx, frame.thisp))
1367 : OBJ_GET_CLASS(cx, frame.thisp)->name)
1368 : JSVAL_IS_BOOLEAN(vp[1])
1369 ? js_BooleanClass.name
1370 : JSVAL_IS_STRING(vp[1])
1371 ? js_StringClass.name
1372 : js_NumberClass.name,
1373 fun && fun->atom
1374 ? JS_GetFunctionName(fun)
1375 : "???");
1377 #endif
1378 ok = native(cx, frame.thisp, argc, frame.argv, &frame.rval);
1381 JS_RUNTIME_METER(cx->runtime, nativeCalls);
1382 #ifdef DEBUG_NOT_THROWING
1383 if (ok && !alreadyThrowing)
1384 ASSERT_NOT_THROWING(cx);
1385 #endif
1386 } else if (script) {
1387 #ifdef DUMP_CALL_TABLE
1388 LogCall(cx, *vp, argc, frame.argv);
1389 #endif
1390 /* Use parent scope so js_GetCallObject can find the right "Call". */
1391 frame.scopeChain = parent;
1392 if (JSFUN_HEAVYWEIGHT_TEST(fun->flags)) {
1393 /* Scope with a call object parented by the callee's parent. */
1394 if (!js_GetCallObject(cx, &frame, parent)) {
1395 ok = JS_FALSE;
1396 goto out;
1399 ok = js_Interpret(cx, script->code, &v);
1400 } else {
1401 /* fun might be onerror trying to report a syntax error in itself. */
1402 frame.scopeChain = NULL;
1403 ok = JS_TRUE;
1406 out:
1407 if (hookData) {
1408 hook = cx->debugHooks->callHook;
1409 if (hook)
1410 hook(cx, &frame, JS_FALSE, &ok, hookData);
1413 /* If frame has a call object, sync values and clear back-pointer. */
1414 if (frame.callobj)
1415 ok &= js_PutCallObject(cx, &frame);
1417 /* If frame has an arguments object, sync values and clear back-pointer. */
1418 if (frame.argsobj)
1419 ok &= js_PutArgsObject(cx, &frame);
1421 /* Restore cx->fp now that we're done releasing frame objects. */
1422 cx->fp = fp;
1424 out2:
1425 /* Pop everything we may have allocated off the stack. */
1426 JS_ARENA_RELEASE(&cx->stackPool, mark);
1428 /* Store the return value and restore sp just above it. */
1429 *vp = frame.rval;
1430 fp->sp = vp + 1;
1433 * Store the location of the JSOP_CALL or JSOP_EVAL that generated the
1434 * return value, but only if this is an external (compiled from script
1435 * source) call that has stack budget for the generating pc.
1437 if (fp->script && !(flags & JSINVOKE_INTERNAL))
1438 vp[-(intN)fp->script->depth] = (jsval)fp->pc;
1439 return ok;
1441 bad:
1442 js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS);
1443 ok = JS_FALSE;
1444 goto out2;
1447 JSBool
1448 js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags,
1449 uintN argc, jsval *argv, jsval *rval)
1451 JSStackFrame *fp, *oldfp, frame;
1452 jsval *oldsp, *sp;
1453 void *mark;
1454 uintN i;
1455 JSBool ok;
1457 fp = oldfp = cx->fp;
1458 if (!fp) {
1459 memset(&frame, 0, sizeof frame);
1460 cx->fp = fp = &frame;
1462 oldsp = fp->sp;
1463 sp = js_AllocStack(cx, 2 + argc, &mark);
1464 if (!sp) {
1465 ok = JS_FALSE;
1466 goto out;
1469 PUSH(fval);
1470 PUSH(OBJECT_TO_JSVAL(obj));
1471 for (i = 0; i < argc; i++)
1472 PUSH(argv[i]);
1473 SAVE_SP(fp);
1474 ok = js_Invoke(cx, argc, flags | JSINVOKE_INTERNAL);
1475 if (ok) {
1476 RESTORE_SP(fp);
1479 * Store *rval in the a scoped local root if a scope is open, else in
1480 * the lastInternalResult pigeon-hole GC root, solely so users of
1481 * js_InternalInvoke and its direct and indirect (js_ValueToString for
1482 * example) callers do not need to manage roots for local, temporary
1483 * references to such results.
1485 *rval = POP_OPND();
1486 if (JSVAL_IS_GCTHING(*rval) && *rval != JSVAL_NULL) {
1487 if (cx->localRootStack) {
1488 if (js_PushLocalRoot(cx, cx->localRootStack, *rval) < 0)
1489 ok = JS_FALSE;
1490 } else {
1491 cx->weakRoots.lastInternalResult = *rval;
1496 js_FreeStack(cx, mark);
1497 out:
1498 fp->sp = oldsp;
1499 if (oldfp != fp)
1500 cx->fp = oldfp;
1502 return ok;
1505 JSBool
1506 js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval,
1507 JSAccessMode mode, uintN argc, jsval *argv, jsval *rval)
1509 int stackDummy;
1512 * js_InternalInvoke could result in another try to get or set the same id
1513 * again, see bug 355497.
1515 if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
1516 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1517 JSMSG_OVER_RECURSED);
1518 return JS_FALSE;
1522 * Check general (not object-ops/class-specific) access from the running
1523 * script to obj.id only if id has a scripted getter or setter that we're
1524 * about to invoke. If we don't check this case, nothing else will -- no
1525 * other native code has the chance to check.
1527 * Contrast this non-native (scripted) case with native getter and setter
1528 * accesses, where the native itself must do an access check, if security
1529 * policies requires it. We make a checkAccess or checkObjectAccess call
1530 * back to the embedding program only in those cases where we're not going
1531 * to call an embedding-defined native function, getter, setter, or class
1532 * hook anyway. Where we do call such a native, there's no need for the
1533 * engine to impose a separate access check callback on all embeddings --
1534 * many embeddings have no security policy at all.
1536 JS_ASSERT(mode == JSACC_READ || mode == JSACC_WRITE);
1537 if (cx->runtime->checkObjectAccess &&
1538 VALUE_IS_FUNCTION(cx, fval) &&
1539 FUN_INTERPRETED((JSFunction *)
1540 JS_GetPrivate(cx, JSVAL_TO_OBJECT(fval))) &&
1541 !cx->runtime->checkObjectAccess(cx, obj, ID_TO_VALUE(id), mode,
1542 &fval)) {
1543 return JS_FALSE;
1546 return js_InternalCall(cx, obj, fval, argc, argv, rval);
1549 JSBool
1550 js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
1551 JSStackFrame *down, uintN flags, jsval *result)
1553 JSInterpreterHook hook;
1554 void *hookData, *mark;
1555 JSStackFrame *oldfp, frame;
1556 JSObject *obj, *tmp;
1557 JSBool ok;
1559 hook = cx->debugHooks->executeHook;
1560 hookData = mark = NULL;
1561 oldfp = cx->fp;
1562 frame.script = script;
1563 if (down) {
1564 /* Propagate arg/var state for eval and the debugger API. */
1565 frame.callobj = down->callobj;
1566 frame.argsobj = down->argsobj;
1567 frame.varobj = down->varobj;
1568 frame.callee = down->callee;
1569 frame.fun = down->fun;
1570 frame.thisp = down->thisp;
1571 frame.argc = down->argc;
1572 frame.argv = down->argv;
1573 frame.nvars = down->nvars;
1574 frame.vars = down->vars;
1575 frame.annotation = down->annotation;
1576 frame.sharpArray = down->sharpArray;
1577 } else {
1578 frame.callobj = frame.argsobj = NULL;
1579 obj = chain;
1580 if (cx->options & JSOPTION_VAROBJFIX) {
1581 while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
1582 obj = tmp;
1584 frame.varobj = obj;
1585 frame.callee = NULL;
1586 frame.fun = NULL;
1587 frame.thisp = chain;
1588 frame.argc = 0;
1589 frame.argv = NULL;
1590 frame.nvars = script->ngvars;
1591 if (script->regexpsOffset != 0)
1592 frame.nvars += JS_SCRIPT_REGEXPS(script)->length;
1593 if (frame.nvars != 0) {
1594 frame.vars = js_AllocRawStack(cx, frame.nvars, &mark);
1595 if (!frame.vars)
1596 return JS_FALSE;
1597 memset(frame.vars, 0, frame.nvars * sizeof(jsval));
1598 } else {
1599 frame.vars = NULL;
1601 frame.annotation = NULL;
1602 frame.sharpArray = NULL;
1604 frame.rval = JSVAL_VOID;
1605 frame.down = down;
1606 frame.scopeChain = chain;
1607 frame.pc = NULL;
1608 frame.sp = oldfp ? oldfp->sp : NULL;
1609 frame.spbase = NULL;
1610 frame.sharpDepth = 0;
1611 frame.flags = flags;
1612 frame.dormantNext = NULL;
1613 frame.xmlNamespace = NULL;
1614 frame.blockChain = NULL;
1617 * Here we wrap the call to js_Interpret with code to (conditionally)
1618 * save and restore the old stack frame chain into a chain of 'dormant'
1619 * frame chains. Since we are replacing cx->fp, we were running into
1620 * the problem that if GC was called under this frame, some of the GC
1621 * things associated with the old frame chain (available here only in
1622 * the C variable 'oldfp') were not rooted and were being collected.
1624 * So, now we preserve the links to these 'dormant' frame chains in cx
1625 * before calling js_Interpret and cleanup afterwards. The GC walks
1626 * these dormant chains and marks objects in the same way that it marks
1627 * objects in the primary cx->fp chain.
1629 if (oldfp && oldfp != down) {
1630 JS_ASSERT(!oldfp->dormantNext);
1631 oldfp->dormantNext = cx->dormantFrameChain;
1632 cx->dormantFrameChain = oldfp;
1635 cx->fp = &frame;
1636 if (hook) {
1637 hookData = hook(cx, &frame, JS_TRUE, 0,
1638 cx->debugHooks->executeHookData);
1642 * Use frame.rval, not result, so the last result stays rooted across any
1643 * GC activations nested within this js_Interpret.
1645 ok = js_Interpret(cx, script->code, &frame.rval);
1646 *result = frame.rval;
1648 if (hookData) {
1649 hook = cx->debugHooks->executeHook;
1650 if (hook)
1651 hook(cx, &frame, JS_FALSE, &ok, hookData);
1653 if (mark)
1654 js_FreeRawStack(cx, mark);
1655 cx->fp = oldfp;
1657 if (oldfp && oldfp != down) {
1658 JS_ASSERT(cx->dormantFrameChain == oldfp);
1659 cx->dormantFrameChain = oldfp->dormantNext;
1660 oldfp->dormantNext = NULL;
1663 return ok;
1666 #if JS_HAS_EXPORT_IMPORT
1668 * If id is JSVAL_VOID, import all exported properties from obj.
1670 static JSBool
1671 ImportProperty(JSContext *cx, JSObject *obj, jsid id)
1673 JSBool ok;
1674 JSIdArray *ida;
1675 JSProperty *prop;
1676 JSObject *obj2, *target, *funobj, *closure;
1677 uintN attrs;
1678 jsint i;
1679 jsval value;
1681 if (JSVAL_IS_VOID(id)) {
1682 ida = JS_Enumerate(cx, obj);
1683 if (!ida)
1684 return JS_FALSE;
1685 ok = JS_TRUE;
1686 if (ida->length == 0)
1687 goto out;
1688 } else {
1689 ida = NULL;
1690 if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
1691 return JS_FALSE;
1692 if (!prop) {
1693 js_ReportValueError(cx, JSMSG_NOT_DEFINED,
1694 JSDVG_IGNORE_STACK, ID_TO_VALUE(id), NULL);
1695 return JS_FALSE;
1697 ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs);
1698 OBJ_DROP_PROPERTY(cx, obj2, prop);
1699 if (!ok)
1700 return JS_FALSE;
1701 if (!(attrs & JSPROP_EXPORTED)) {
1702 js_ReportValueError(cx, JSMSG_NOT_EXPORTED,
1703 JSDVG_IGNORE_STACK, ID_TO_VALUE(id), NULL);
1704 return JS_FALSE;
1708 target = cx->fp->varobj;
1709 i = 0;
1710 do {
1711 if (ida) {
1712 id = ida->vector[i];
1713 ok = OBJ_GET_ATTRIBUTES(cx, obj, id, NULL, &attrs);
1714 if (!ok)
1715 goto out;
1716 if (!(attrs & JSPROP_EXPORTED))
1717 continue;
1719 ok = OBJ_CHECK_ACCESS(cx, obj, id, JSACC_IMPORT, &value, &attrs);
1720 if (!ok)
1721 goto out;
1722 if (VALUE_IS_FUNCTION(cx, value)) {
1723 funobj = JSVAL_TO_OBJECT(value);
1724 closure = js_CloneFunctionObject(cx, funobj, obj);
1725 if (!closure) {
1726 ok = JS_FALSE;
1727 goto out;
1729 value = OBJECT_TO_JSVAL(closure);
1733 * Handle the case of importing a property that refers to a local
1734 * variable or formal parameter of a function activation. These
1735 * properties are accessed by opcodes using stack slot numbers
1736 * generated by the compiler rather than runtime name-lookup. These
1737 * local references, therefore, bypass the normal scope chain lookup.
1738 * So, instead of defining a new property in the activation object,
1739 * modify the existing value in the stack slot.
1741 if (OBJ_GET_CLASS(cx, target) == &js_CallClass) {
1742 ok = OBJ_LOOKUP_PROPERTY(cx, target, id, &obj2, &prop);
1743 if (!ok)
1744 goto out;
1745 } else {
1746 prop = NULL;
1748 if (prop && target == obj2) {
1749 ok = OBJ_SET_PROPERTY(cx, target, id, &value);
1750 } else {
1751 ok = OBJ_DEFINE_PROPERTY(cx, target, id, value, NULL, NULL,
1752 attrs & ~(JSPROP_EXPORTED |
1753 JSPROP_GETTER |
1754 JSPROP_SETTER),
1755 NULL);
1757 if (prop)
1758 OBJ_DROP_PROPERTY(cx, obj2, prop);
1759 if (!ok)
1760 goto out;
1761 } while (ida && ++i < ida->length);
1763 out:
1764 if (ida)
1765 JS_DestroyIdArray(cx, ida);
1766 return ok;
1768 #endif /* JS_HAS_EXPORT_IMPORT */
1770 #define JSPROP_INITIALIZER 0x100 /* NB: Not a valid property attribute. */
1772 JSBool
1773 js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
1774 JSObject **objp, JSProperty **propp)
1776 JSObject *obj2;
1777 JSProperty *prop;
1778 uintN oldAttrs, report;
1779 JSBool isFunction;
1780 jsval value;
1781 const char *type, *name;
1783 if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
1784 return JS_FALSE;
1785 if (propp) {
1786 *objp = obj2;
1787 *propp = prop;
1789 if (!prop)
1790 return JS_TRUE;
1793 * Use prop as a speedup hint to OBJ_GET_ATTRIBUTES, but drop it on error.
1794 * An assertion at label bad: will insist that it is null.
1796 if (!OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs)) {
1797 OBJ_DROP_PROPERTY(cx, obj2, prop);
1798 #ifdef DEBUG
1799 prop = NULL;
1800 #endif
1801 goto bad;
1805 * From here, return true, or else goto bad on failure to null out params.
1806 * If our caller doesn't want prop, drop it (we don't need it any longer).
1808 if (!propp) {
1809 OBJ_DROP_PROPERTY(cx, obj2, prop);
1810 prop = NULL;
1813 if (attrs == JSPROP_INITIALIZER) {
1814 /* Allow the new object to override properties. */
1815 if (obj2 != obj)
1816 return JS_TRUE;
1817 report = JSREPORT_WARNING | JSREPORT_STRICT;
1818 } else {
1819 /* We allow redeclaring some non-readonly properties. */
1820 if (((oldAttrs | attrs) & JSPROP_READONLY) == 0) {
1822 * Allow redeclaration of variables and functions, but insist that
1823 * the new value is not a getter if the old value was, ditto for
1824 * setters -- unless prop is impermanent (in which case anyone
1825 * could delete it and redefine it, willy-nilly).
1827 if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
1828 return JS_TRUE;
1829 if ((~(oldAttrs ^ attrs) & (JSPROP_GETTER | JSPROP_SETTER)) == 0)
1830 return JS_TRUE;
1831 if (!(oldAttrs & JSPROP_PERMANENT))
1832 return JS_TRUE;
1835 report = JSREPORT_ERROR;
1836 isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0;
1837 if (!isFunction) {
1838 if (!OBJ_GET_PROPERTY(cx, obj, id, &value))
1839 goto bad;
1840 isFunction = VALUE_IS_FUNCTION(cx, value);
1844 type = (attrs == JSPROP_INITIALIZER)
1845 ? "property"
1846 : (oldAttrs & attrs & JSPROP_GETTER)
1847 ? js_getter_str
1848 : (oldAttrs & attrs & JSPROP_SETTER)
1849 ? js_setter_str
1850 : (oldAttrs & JSPROP_READONLY)
1851 ? js_const_str
1852 : isFunction
1853 ? js_function_str
1854 : js_var_str;
1855 name = js_ValueToPrintableString(cx, ID_TO_VALUE(id));
1856 if (!name)
1857 goto bad;
1858 return JS_ReportErrorFlagsAndNumber(cx, report,
1859 js_GetErrorMessage, NULL,
1860 JSMSG_REDECLARED_VAR,
1861 type, name);
1863 bad:
1864 if (propp) {
1865 *objp = NULL;
1866 *propp = NULL;
1868 JS_ASSERT(!prop);
1869 return JS_FALSE;
1872 JSBool
1873 js_StrictlyEqual(jsval lval, jsval rval)
1875 jsval ltag = JSVAL_TAG(lval), rtag = JSVAL_TAG(rval);
1876 jsdouble ld, rd;
1878 if (ltag == rtag) {
1879 if (ltag == JSVAL_STRING) {
1880 JSString *lstr = JSVAL_TO_STRING(lval),
1881 *rstr = JSVAL_TO_STRING(rval);
1882 return js_EqualStrings(lstr, rstr);
1884 if (ltag == JSVAL_DOUBLE) {
1885 ld = *JSVAL_TO_DOUBLE(lval);
1886 rd = *JSVAL_TO_DOUBLE(rval);
1887 return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
1889 return lval == rval;
1891 if (ltag == JSVAL_DOUBLE && JSVAL_IS_INT(rval)) {
1892 ld = *JSVAL_TO_DOUBLE(lval);
1893 rd = JSVAL_TO_INT(rval);
1894 return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
1896 if (JSVAL_IS_INT(lval) && rtag == JSVAL_DOUBLE) {
1897 ld = JSVAL_TO_INT(lval);
1898 rd = *JSVAL_TO_DOUBLE(rval);
1899 return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
1901 return lval == rval;
1904 JSBool
1905 js_InvokeConstructor(JSContext *cx, jsval *vp, uintN argc)
1907 JSFunction *fun;
1908 JSObject *obj, *obj2, *proto, *parent;
1909 jsval lval, rval;
1910 JSClass *clasp, *funclasp;
1912 fun = NULL;
1913 obj2 = NULL;
1914 lval = *vp;
1915 if (!JSVAL_IS_OBJECT(lval) ||
1916 (obj2 = JSVAL_TO_OBJECT(lval)) == NULL ||
1917 /* XXX clean up to avoid special cases above ObjectOps layer */
1918 OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass ||
1919 !obj2->map->ops->construct)
1921 fun = js_ValueToFunction(cx, vp, JSV2F_CONSTRUCT);
1922 if (!fun)
1923 return JS_FALSE;
1926 clasp = &js_ObjectClass;
1927 if (!obj2) {
1928 proto = parent = NULL;
1929 fun = NULL;
1930 } else {
1932 * Get the constructor prototype object for this function.
1933 * Use the nominal 'this' parameter slot, vp[1], as a local
1934 * root to protect this prototype, in case it has no other
1935 * strong refs.
1937 if (!OBJ_GET_PROPERTY(cx, obj2,
1938 ATOM_TO_JSID(cx->runtime->atomState
1939 .classPrototypeAtom),
1940 &vp[1])) {
1941 return JS_FALSE;
1943 rval = vp[1];
1944 proto = JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : NULL;
1945 parent = OBJ_GET_PARENT(cx, obj2);
1947 if (OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass) {
1948 funclasp = ((JSFunction *) OBJ_GET_PRIVATE(cx, obj2))->clasp;
1949 if (funclasp)
1950 clasp = funclasp;
1953 obj = js_NewObject(cx, clasp, proto, parent);
1954 if (!obj)
1955 return JS_FALSE;
1957 /* Now we have an object with a constructor method; call it. */
1958 vp[1] = OBJECT_TO_JSVAL(obj);
1959 if (!js_Invoke(cx, argc, JSINVOKE_CONSTRUCT)) {
1960 cx->weakRoots.newborn[GCX_OBJECT] = NULL;
1961 return JS_FALSE;
1964 /* Check the return value and if it's primitive, force it to be obj. */
1965 rval = *vp;
1966 if (JSVAL_IS_PRIMITIVE(rval)) {
1967 if (!fun) {
1968 /* native [[Construct]] returning primitive is error */
1969 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1970 JSMSG_BAD_NEW_RESULT,
1971 js_ValueToPrintableString(cx, rval));
1972 return JS_FALSE;
1974 *vp = OBJECT_TO_JSVAL(obj);
1977 JS_RUNTIME_METER(cx->runtime, constructs);
1978 return JS_TRUE;
1981 static JSBool
1982 InternNonIntElementId(JSContext *cx, JSObject *obj, jsval idval, jsid *idp)
1984 JSAtom *atom;
1986 JS_ASSERT(!JSVAL_IS_INT(idval));
1988 #if JS_HAS_XML_SUPPORT
1989 if (!JSVAL_IS_PRIMITIVE(idval)) {
1990 if (OBJECT_IS_XML(cx, obj)) {
1991 *idp = OBJECT_JSVAL_TO_JSID(idval);
1992 return JS_TRUE;
1994 if (!js_IsFunctionQName(cx, JSVAL_TO_OBJECT(idval), idp))
1995 return JS_FALSE;
1996 if (*idp != 0)
1997 return JS_TRUE;
1999 #endif
2001 atom = js_ValueToStringAtom(cx, idval);
2002 if (!atom)
2003 return JS_FALSE;
2004 *idp = ATOM_TO_JSID(atom);
2005 return JS_TRUE;
2009 * Threaded interpretation via computed goto appears to be well-supported by
2010 * GCC 3 and higher. IBM's C compiler when run with the right options (e.g.,
2011 * -qlanglvl=extended) also supports threading. Ditto the SunPro C compiler.
2012 * Currently it's broken for JS_VERSION < 160, though this isn't worth fixing.
2013 * Add your compiler support macros here.
2015 #ifndef JS_THREADED_INTERP
2016 # if JS_VERSION >= 160 && ( \
2017 __GNUC__ >= 3 || \
2018 (__IBMC__ >= 700 && defined __IBM_COMPUTED_GOTO) || \
2019 __SUNPRO_C >= 0x570)
2020 # define JS_THREADED_INTERP 1
2021 # else
2022 # define JS_THREADED_INTERP 0
2023 # endif
2024 #endif
2027 * Define JS_OPMETER to instrument bytecode succession, generating a .dot file
2028 * on shutdown that shows the graph of significant predecessor/successor pairs
2029 * executed, where the edge labels give the succession counts. The .dot file
2030 * is named by the JS_OPMETER_FILE envariable, and defaults to /tmp/ops.dot.
2032 * Bonus feature: JS_OPMETER also enables counters for stack-addressing ops
2033 * such as JSOP_GETVAR, JSOP_INCARG, via METER_SLOT_OP. The resulting counts
2034 * are written to JS_OPMETER_HIST, defaulting to /tmp/ops.hist.
2036 #ifndef JS_OPMETER
2037 # define METER_OP_INIT(op) /* nothing */
2038 # define METER_OP_PAIR(op1,op2) /* nothing */
2039 # define METER_SLOT_OP(op,slot) /* nothing */
2040 #else
2042 # include <stdlib.h>
2045 * The second dimension is hardcoded at 256 because we know that many bits fit
2046 * in a byte, and mainly to optimize away multiplying by JSOP_LIMIT to address
2047 * any particular row.
2049 # define METER_OP_INIT(op) ((op) = JSOP_STOP)
2050 # define METER_OP_PAIR(op1,op2) ((op1) != JSOP_STOP && ++succeeds[op1][op2])
2051 # define HIST_NSLOTS 8
2052 # define METER_SLOT_OP(op,slot) ((slot) < HIST_NSLOTS && ++slot_ops[op][slot])
2054 static uint32 succeeds[JSOP_LIMIT][256];
2055 static uint32 slot_ops[JSOP_LIMIT][HIST_NSLOTS];
2057 typedef struct Edge {
2058 const char *from;
2059 const char *to;
2060 uint32 count;
2061 } Edge;
2063 static int
2064 compare_edges(const void *a, const void *b)
2066 const Edge *ea = (const Edge *) a;
2067 const Edge *eb = (const Edge *) b;
2069 return (int32)eb->count - (int32)ea->count;
2072 void
2073 js_DumpOpMeters()
2075 const char *name, *from, *style;
2076 FILE *fp;
2077 uint32 total, count;
2078 uint32 i, j, nedges;
2079 Edge *graph;
2081 name = getenv("JS_OPMETER_FILE");
2082 if (!name)
2083 name = "/tmp/ops.dot";
2084 fp = fopen(name, "w");
2085 if (!fp) {
2086 perror(name);
2087 return;
2090 total = nedges = 0;
2091 for (i = 0; i < JSOP_LIMIT; i++) {
2092 for (j = 0; j < JSOP_LIMIT; j++) {
2093 count = succeeds[i][j];
2094 if (count != 0) {
2095 total += count;
2096 ++nedges;
2101 # define SIGNIFICANT(count,total) (200. * (count) >= (total))
2103 graph = (Edge *) calloc(nedges, sizeof graph[0]);
2104 for (i = nedges = 0; i < JSOP_LIMIT; i++) {
2105 from = js_CodeSpec[i].name;
2106 for (j = 0; j < JSOP_LIMIT; j++) {
2107 count = succeeds[i][j];
2108 if (count != 0 && SIGNIFICANT(count, total)) {
2109 graph[nedges].from = from;
2110 graph[nedges].to = js_CodeSpec[j].name;
2111 graph[nedges].count = count;
2112 ++nedges;
2116 qsort(graph, nedges, sizeof(Edge), compare_edges);
2118 # undef SIGNIFICANT
2120 fputs("digraph {\n", fp);
2121 for (i = 0, style = NULL; i < nedges; i++) {
2122 JS_ASSERT(i == 0 || graph[i-1].count >= graph[i].count);
2123 if (!style || graph[i-1].count != graph[i].count) {
2124 style = (i > nedges * .75) ? "dotted" :
2125 (i > nedges * .50) ? "dashed" :
2126 (i > nedges * .25) ? "solid" : "bold";
2128 fprintf(fp, " %s -> %s [label=\"%lu\" style=%s]\n",
2129 graph[i].from, graph[i].to,
2130 (unsigned long)graph[i].count, style);
2132 free(graph);
2133 fputs("}\n", fp);
2134 fclose(fp);
2136 name = getenv("JS_OPMETER_HIST");
2137 if (!name)
2138 name = "/tmp/ops.hist";
2139 fp = fopen(name, "w");
2140 if (!fp) {
2141 perror(name);
2142 return;
2144 fputs("bytecode", fp);
2145 for (j = 0; j < HIST_NSLOTS; j++)
2146 fprintf(fp, " slot %1u", (unsigned)j);
2147 putc('\n', fp);
2148 fputs("========", fp);
2149 for (j = 0; j < HIST_NSLOTS; j++)
2150 fputs(" =======", fp);
2151 putc('\n', fp);
2152 for (i = 0; i < JSOP_LIMIT; i++) {
2153 for (j = 0; j < HIST_NSLOTS; j++) {
2154 if (slot_ops[i][j] != 0) {
2155 /* Reuse j in the next loop, since we break after. */
2156 fprintf(fp, "%-8.8s", js_CodeSpec[i].name);
2157 for (j = 0; j < HIST_NSLOTS; j++)
2158 fprintf(fp, " %7lu", (unsigned long)slot_ops[i][j]);
2159 putc('\n', fp);
2160 break;
2164 fclose(fp);
2167 #endif /* JS_OPSMETER */
2170 * Ensure that the intrepreter switch can close call-bytecode cases in the
2171 * same way as non-call bytecodes.
2173 JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH);
2174 JS_STATIC_ASSERT(JSOP_GETGVAR_LENGTH == JSOP_CALLGVAR_LENGTH);
2175 JS_STATIC_ASSERT(JSOP_GETVAR_LENGTH == JSOP_CALLVAR_LENGTH);
2176 JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH);
2177 JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH);
2178 JS_STATIC_ASSERT(JSOP_XMLNAME_LENGTH == JSOP_CALLXMLNAME_LENGTH);
2180 JSBool
2181 js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result)
2183 JSRuntime *rt;
2184 JSStackFrame *fp;
2185 JSScript *script;
2186 uintN inlineCallCount;
2187 JSAtom **atoms;
2188 JSObject *obj, *obj2, *parent;
2189 JSVersion currentVersion, originalVersion;
2190 JSBool ok, cond;
2191 JSTrapHandler interruptHandler;
2192 jsint depth, len;
2193 jsval *sp, *newsp;
2194 void *mark;
2195 jsbytecode *endpc, *pc2;
2196 JSOp op, op2;
2197 jsatomid index;
2198 JSAtom *atom;
2199 uintN argc, attrs, flags, slot;
2200 jsval *vp, lval, rval, ltmp, rtmp;
2201 jsid id;
2202 JSObject *withobj, *iterobj;
2203 JSProperty *prop;
2204 JSScopeProperty *sprop;
2205 JSString *str, *str2;
2206 jsint i, j;
2207 jsdouble d, d2;
2208 JSClass *clasp;
2209 JSFunction *fun;
2210 JSType type;
2211 #if !JS_THREADED_INTERP && defined DEBUG
2212 FILE *tracefp = NULL;
2213 #endif
2214 #if JS_HAS_EXPORT_IMPORT
2215 JSIdArray *ida;
2216 #endif
2217 jsint low, high, off, npairs;
2218 JSBool match;
2219 #if JS_HAS_GETTER_SETTER
2220 JSPropertyOp getter, setter;
2221 #endif
2222 int stackDummy;
2224 #ifdef __GNUC__
2225 # define JS_EXTENSION __extension__
2226 # define JS_EXTENSION_(s) __extension__ ({ s; })
2227 #else
2228 # define JS_EXTENSION
2229 # define JS_EXTENSION_(s) s
2230 #endif
2232 #if JS_THREADED_INTERP
2233 static void *normalJumpTable[] = {
2234 # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
2235 JS_EXTENSION &&L_##op,
2236 # include "jsopcode.tbl"
2237 # undef OPDEF
2240 static void *interruptJumpTable[] = {
2241 # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
2242 JS_EXTENSION &&interrupt,
2243 # include "jsopcode.tbl"
2244 # undef OPDEF
2247 register void **jumpTable = normalJumpTable;
2249 METER_OP_INIT(op); /* to nullify first METER_OP_PAIR */
2251 # define DO_OP() JS_EXTENSION_(goto *jumpTable[op])
2252 # define DO_NEXT_OP(n) do { METER_OP_PAIR(op, pc[n]); \
2253 op = (JSOp) *(pc += (n)); \
2254 DO_OP(); } while (0)
2255 # define BEGIN_CASE(OP) L_##OP:
2256 # define END_CASE(OP) DO_NEXT_OP(OP##_LENGTH);
2257 # define END_VARLEN_CASE DO_NEXT_OP(len);
2258 # define EMPTY_CASE(OP) BEGIN_CASE(OP) op = (JSOp) *++pc; DO_OP();
2259 #else
2260 # define DO_OP() goto do_op
2261 # define DO_NEXT_OP(n) goto advance_pc
2262 # define BEGIN_CASE(OP) case OP:
2263 # define END_CASE(OP) break;
2264 # define END_VARLEN_CASE break;
2265 # define EMPTY_CASE(OP) BEGIN_CASE(OP) END_CASE(OP)
2266 #endif
2268 *result = JSVAL_VOID;
2269 rt = cx->runtime;
2271 /* Set registerized frame pointer and derived script pointer. */
2272 fp = cx->fp;
2273 script = fp->script;
2274 JS_ASSERT(script->length != 0);
2276 /* Count of JS function calls that nest in this C js_Interpret frame. */
2277 inlineCallCount = 0;
2280 * Initialize the index segment register used by LOAD_ATOM and
2281 * GET_FULL_INDEX macros bellow. As a register we use a pointer based on
2282 * the atom map to turn frequently executed LOAD_ATOM into simple array
2283 * access. For less frequent object and regexp loads we have to recover
2284 * the segment from atoms pointer first.
2286 atoms = script->atomMap.vector;
2288 #define LOAD_ATOM(PCOFF) \
2289 JS_BEGIN_MACRO \
2290 JS_ASSERT((size_t)(atoms - script->atomMap.vector) < \
2291 (size_t)(script->atomMap.length - GET_INDEX(pc + PCOFF))); \
2292 atom = atoms[GET_INDEX(pc + PCOFF)]; \
2293 JS_END_MACRO
2295 #define GET_FULL_INDEX(PCOFF) \
2296 (atoms - script->atomMap.vector + GET_INDEX(pc + PCOFF))
2298 #define LOAD_OBJECT(PCOFF) \
2299 JS_GET_SCRIPT_OBJECT(script, GET_FULL_INDEX(PCOFF), obj)
2301 #define LOAD_FUNCTION(PCOFF) \
2302 JS_BEGIN_MACRO \
2303 LOAD_OBJECT(PCOFF); \
2304 JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_FunctionClass); \
2305 JS_END_MACRO
2308 * Optimized Get and SetVersion for proper script language versioning.
2310 * If any native method or JSClass/JSObjectOps hook calls js_SetVersion
2311 * and changes cx->version, the effect will "stick" and we will stop
2312 * maintaining currentVersion. This is relied upon by testsuites, for
2313 * the most part -- web browsers select version before compiling and not
2314 * at run-time.
2316 currentVersion = (JSVersion) script->version;
2317 originalVersion = (JSVersion) cx->version;
2318 if (currentVersion != originalVersion)
2319 js_SetVersion(cx, currentVersion);
2321 #ifdef __GNUC__
2322 flags = 0; /* suppress gcc warnings */
2323 id = 0;
2324 #endif
2327 * Prepare to call a user-supplied branch handler, and abort the script
2328 * if it returns false.
2330 #define CHECK_BRANCH(len) \
2331 JS_BEGIN_MACRO \
2332 if (len <= 0 && cx->branchCallback) { \
2333 SAVE_SP_AND_PC(fp); \
2334 if (!(ok = cx->branchCallback(cx, script))) \
2335 goto out; \
2337 JS_END_MACRO
2340 * Load the debugger's interrupt hook here and after calling out to native
2341 * functions (but not to getters, setters, or other native hooks), so we do
2342 * not have to reload it each time through the interpreter loop -- we hope
2343 * the compiler can keep it in a register when it is non-null.
2345 #if JS_THREADED_INTERP
2346 # define LOAD_JUMP_TABLE() \
2347 (jumpTable = interruptHandler ? interruptJumpTable : normalJumpTable)
2348 #else
2349 # define LOAD_JUMP_TABLE() /* nothing */
2350 #endif
2352 #define LOAD_INTERRUPT_HANDLER(cx) \
2353 JS_BEGIN_MACRO \
2354 interruptHandler = (cx)->debugHooks->interruptHandler; \
2355 LOAD_JUMP_TABLE(); \
2356 JS_END_MACRO
2358 LOAD_INTERRUPT_HANDLER(cx);
2360 /* Check for too much js_Interpret nesting, or too deep a C stack. */
2361 ++cx->interpLevel;
2362 if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
2363 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
2364 ok = JS_FALSE;
2365 goto out2;
2369 * Allocate operand and pc stack slots for the script's worst-case depth,
2370 * unless we're called to interpret a part of an already active script, a
2371 * filtering predicate expression for example.
2373 depth = (jsint) script->depth;
2374 if (JS_LIKELY(!fp->spbase)) {
2375 newsp = js_AllocRawStack(cx, (uintN)(2 * depth), &mark);
2376 if (!newsp) {
2377 ok = JS_FALSE;
2378 goto out2;
2380 sp = newsp + depth;
2381 fp->spbase = sp;
2382 SAVE_SP(fp);
2383 } else {
2384 sp = fp->sp;
2385 JS_ASSERT(JS_UPTRDIFF(sp, fp->spbase) <= depth * sizeof(jsval));
2386 newsp = fp->spbase - depth;
2387 mark = NULL;
2391 * To support generator_throw and to catch ignored exceptions, fail right
2392 * away if cx->throwing is set. If no exception is pending, null obj in
2393 * case a callable object is being sent into a yield expression, and the
2394 * yield's result is invoked.
2396 ok = !cx->throwing;
2397 if (!ok) {
2398 #ifdef DEBUG_NOT_THROWING
2399 printf("JS INTERPRETER CALLED WITH PENDING EXCEPTION %lx\n",
2400 (unsigned long) cx->exception);
2401 #endif
2402 goto out;
2405 #if JS_THREADED_INTERP
2408 * This is a loop, but it does not look like a loop. The loop-closing
2409 * jump is distributed throughout interruptJumpTable, and comes back to
2410 * the interrupt label. The dispatch on op is through normalJumpTable.
2411 * The trick is LOAD_INTERRUPT_HANDLER setting jumpTable appropriately.
2413 * It is important that "op" be initialized before the interrupt label
2414 * because it is possible for "op" to be specially assigned during the
2415 * normally processing of an opcode while looping (in particular, this
2416 * happens in JSOP_TRAP while debugging). We rely on DO_NEXT_OP to
2417 * correctly manage "op" in all other cases.
2419 op = (JSOp) *pc;
2420 if (interruptHandler) {
2421 interrupt:
2422 SAVE_SP_AND_PC(fp);
2423 switch (interruptHandler(cx, script, pc, &rval,
2424 cx->debugHooks->interruptHandlerData)) {
2425 case JSTRAP_ERROR:
2426 ok = JS_FALSE;
2427 goto out;
2428 case JSTRAP_CONTINUE:
2429 break;
2430 case JSTRAP_RETURN:
2431 fp->rval = rval;
2432 goto out;
2433 case JSTRAP_THROW:
2434 cx->throwing = JS_TRUE;
2435 cx->exception = rval;
2436 ok = JS_FALSE;
2437 goto out;
2438 default:;
2440 LOAD_INTERRUPT_HANDLER(cx);
2443 JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT);
2444 JS_EXTENSION_(goto *normalJumpTable[op]);
2446 #else /* !JS_THREADED_INTERP */
2448 for (;;) {
2449 op = (JSOp) *pc;
2450 do_op:
2451 len = js_CodeSpec[op].length;
2453 #ifdef DEBUG
2454 tracefp = (FILE *) cx->tracefp;
2455 if (tracefp) {
2456 intN nuses, n;
2458 fprintf(tracefp, "%4u: ", js_PCToLineNumber(cx, script, pc));
2459 js_Disassemble1(cx, script, pc,
2460 PTRDIFF(pc, script->code, jsbytecode), JS_FALSE,
2461 tracefp);
2462 nuses = js_CodeSpec[op].nuses;
2463 if (nuses) {
2464 SAVE_SP_AND_PC(fp);
2465 for (n = -nuses; n < 0; n++) {
2466 char *bytes = js_DecompileValueGenerator(cx, n, sp[n],
2467 NULL);
2468 if (bytes) {
2469 fprintf(tracefp, "%s %s",
2470 (n == -nuses) ? " inputs:" : ",",
2471 bytes);
2472 JS_free(cx, bytes);
2475 fprintf(tracefp, " @ %d\n", sp - fp->spbase);
2478 #endif /* DEBUG */
2480 if (interruptHandler) {
2481 SAVE_SP_AND_PC(fp);
2482 switch (interruptHandler(cx, script, pc, &rval,
2483 cx->debugHooks->interruptHandlerData)) {
2484 case JSTRAP_ERROR:
2485 ok = JS_FALSE;
2486 goto out;
2487 case JSTRAP_CONTINUE:
2488 break;
2489 case JSTRAP_RETURN:
2490 fp->rval = rval;
2491 goto out;
2492 case JSTRAP_THROW:
2493 cx->throwing = JS_TRUE;
2494 cx->exception = rval;
2495 ok = JS_FALSE;
2496 goto out;
2497 default:;
2499 LOAD_INTERRUPT_HANDLER(cx);
2502 switch (op) {
2504 #endif /* !JS_THREADED_INTERP */
2506 BEGIN_CASE(JSOP_STOP)
2507 goto out;
2509 EMPTY_CASE(JSOP_NOP)
2511 EMPTY_CASE(JSOP_GROUP)
2513 BEGIN_CASE(JSOP_PUSH)
2514 PUSH_OPND(JSVAL_VOID);
2515 END_CASE(JSOP_PUSH)
2517 BEGIN_CASE(JSOP_POP)
2518 sp--;
2519 END_CASE(JSOP_POP)
2521 BEGIN_CASE(JSOP_POPN)
2522 sp -= GET_UINT16(pc);
2523 #ifdef DEBUG
2524 JS_ASSERT(fp->spbase <= sp);
2525 obj = fp->blockChain;
2526 JS_ASSERT(!obj ||
2527 fp->spbase + OBJ_BLOCK_DEPTH(cx, obj)
2528 + OBJ_BLOCK_COUNT(cx, obj)
2529 <= sp);
2530 for (obj = fp->scopeChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {
2531 clasp = OBJ_GET_CLASS(cx, obj);
2532 if (clasp != &js_BlockClass && clasp != &js_WithClass)
2533 continue;
2534 if (JS_GetPrivate(cx, obj) != fp)
2535 break;
2536 JS_ASSERT(fp->spbase + OBJ_BLOCK_DEPTH(cx, obj)
2537 + ((clasp == &js_BlockClass)
2538 ? OBJ_BLOCK_COUNT(cx, obj)
2539 : 1)
2540 <= sp);
2542 #endif
2543 END_CASE(JSOP_POPN)
2545 BEGIN_CASE(JSOP_SWAP)
2546 vp = sp - depth; /* swap generating pc's for the decompiler */
2547 ltmp = vp[-1];
2548 vp[-1] = vp[-2];
2549 sp[-2] = ltmp;
2550 rtmp = sp[-1];
2551 sp[-1] = sp[-2];
2552 sp[-2] = rtmp;
2553 END_CASE(JSOP_SWAP)
2555 BEGIN_CASE(JSOP_POPV)
2556 *result = POP_OPND();
2557 END_CASE(JSOP_POPV)
2559 BEGIN_CASE(JSOP_ENTERWITH)
2560 SAVE_SP_AND_PC(fp);
2561 FETCH_OBJECT(cx, -1, rval, obj);
2562 OBJ_TO_INNER_OBJECT(cx, obj);
2563 if (!obj || !(obj2 = js_GetScopeChain(cx, fp))) {
2564 ok = JS_FALSE;
2565 goto out;
2567 withobj = js_NewWithObject(cx, obj, obj2, sp - fp->spbase - 1);
2568 if (!withobj) {
2569 ok = JS_FALSE;
2570 goto out;
2572 fp->scopeChain = withobj;
2573 STORE_OPND(-1, OBJECT_TO_JSVAL(withobj));
2574 END_CASE(JSOP_ENTERWITH)
2576 BEGIN_CASE(JSOP_LEAVEWITH)
2577 rval = POP_OPND();
2578 JS_ASSERT(JSVAL_IS_OBJECT(rval));
2579 withobj = JSVAL_TO_OBJECT(rval);
2580 JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass);
2581 fp->scopeChain = OBJ_GET_PARENT(cx, withobj);
2582 JS_SetPrivate(cx, withobj, NULL);
2583 END_CASE(JSOP_LEAVEWITH)
2585 BEGIN_CASE(JSOP_SETRVAL)
2586 ASSERT_NOT_THROWING(cx);
2587 fp->rval = POP_OPND();
2588 END_CASE(JSOP_SETRVAL)
2590 BEGIN_CASE(JSOP_RETURN)
2591 CHECK_BRANCH(-1);
2592 fp->rval = POP_OPND();
2593 /* FALL THROUGH */
2595 BEGIN_CASE(JSOP_RETRVAL) /* fp->rval already set */
2596 ASSERT_NOT_THROWING(cx);
2597 if (inlineCallCount)
2598 inline_return:
2600 JSInlineFrame *ifp = (JSInlineFrame *) fp;
2601 void *hookData = ifp->hookData;
2604 * If fp has blocks on its scope chain, home their locals now,
2605 * before calling any debugger hook, and before freeing stack.
2606 * This matches the order of block putting and hook calling in
2607 * the "out-of-line" return code at the bottom of js_Interpret
2608 * and in js_Invoke.
2610 if (fp->flags & JSFRAME_POP_BLOCKS) {
2611 SAVE_SP_AND_PC(fp);
2612 ok &= PutBlockObjects(cx, fp);
2615 if (hookData) {
2616 JSInterpreterHook hook = cx->debugHooks->callHook;
2617 if (hook) {
2618 SAVE_SP_AND_PC(fp);
2619 hook(cx, fp, JS_FALSE, &ok, hookData);
2620 LOAD_INTERRUPT_HANDLER(cx);
2625 * If fp has a call object, sync values and clear the back-
2626 * pointer. This can happen for a lightweight function if it
2627 * calls eval unexpectedly (in a way that is hidden from the
2628 * compiler). See bug 325540.
2630 if (fp->callobj) {
2631 SAVE_SP_AND_PC(fp);
2632 ok &= js_PutCallObject(cx, fp);
2635 if (fp->argsobj) {
2636 SAVE_SP_AND_PC(fp);
2637 ok &= js_PutArgsObject(cx, fp);
2640 /* Restore context version only if callee hasn't set version. */
2641 if (JS_LIKELY(cx->version == currentVersion)) {
2642 currentVersion = ifp->callerVersion;
2643 if (currentVersion != cx->version)
2644 js_SetVersion(cx, currentVersion);
2647 /* Store the return value in the caller's operand frame. */
2648 vp = ifp->rvp;
2649 *vp = fp->rval;
2651 /* Restore cx->fp and release the inline frame's space. */
2652 cx->fp = fp = fp->down;
2653 JS_ARENA_RELEASE(&cx->stackPool, ifp->mark);
2655 /* Restore sp to point just above the return value. */
2656 fp->sp = vp + 1;
2657 RESTORE_SP(fp);
2659 /* Restore the calling script's interpreter registers. */
2660 script = fp->script;
2661 depth = (jsint) script->depth;
2662 atoms = script->atomMap.vector;
2663 pc = fp->pc;
2664 #if !JS_THREADED_INTERP
2665 endpc = script->code + script->length;
2666 #endif
2668 /* Store the generating pc for the return value. */
2669 vp[-depth] = (jsval)pc;
2671 /* Resume execution in the calling frame. */
2672 inlineCallCount--;
2673 if (JS_LIKELY(ok)) {
2674 JS_ASSERT(js_CodeSpec[*pc].length == JSOP_CALL_LENGTH);
2675 len = JSOP_CALL_LENGTH;
2676 DO_NEXT_OP(len);
2679 goto out;
2681 BEGIN_CASE(JSOP_DEFAULT)
2682 (void) POP();
2683 /* FALL THROUGH */
2684 BEGIN_CASE(JSOP_GOTO)
2685 len = GET_JUMP_OFFSET(pc);
2686 CHECK_BRANCH(len);
2687 END_VARLEN_CASE
2689 BEGIN_CASE(JSOP_IFEQ)
2690 POP_BOOLEAN(cx, rval, cond);
2691 if (cond == JS_FALSE) {
2692 len = GET_JUMP_OFFSET(pc);
2693 CHECK_BRANCH(len);
2694 DO_NEXT_OP(len);
2696 END_CASE(JSOP_IFEQ)
2698 BEGIN_CASE(JSOP_IFNE)
2699 POP_BOOLEAN(cx, rval, cond);
2700 if (cond != JS_FALSE) {
2701 len = GET_JUMP_OFFSET(pc);
2702 CHECK_BRANCH(len);
2703 DO_NEXT_OP(len);
2705 END_CASE(JSOP_IFNE)
2707 BEGIN_CASE(JSOP_OR)
2708 POP_BOOLEAN(cx, rval, cond);
2709 if (cond == JS_TRUE) {
2710 len = GET_JUMP_OFFSET(pc);
2711 PUSH_OPND(rval);
2712 DO_NEXT_OP(len);
2714 END_CASE(JSOP_OR)
2716 BEGIN_CASE(JSOP_AND)
2717 POP_BOOLEAN(cx, rval, cond);
2718 if (cond == JS_FALSE) {
2719 len = GET_JUMP_OFFSET(pc);
2720 PUSH_OPND(rval);
2721 DO_NEXT_OP(len);
2723 END_CASE(JSOP_AND)
2725 BEGIN_CASE(JSOP_DEFAULTX)
2726 (void) POP();
2727 /* FALL THROUGH */
2728 BEGIN_CASE(JSOP_GOTOX)
2729 len = GET_JUMPX_OFFSET(pc);
2730 CHECK_BRANCH(len);
2731 END_VARLEN_CASE
2733 BEGIN_CASE(JSOP_IFEQX)
2734 POP_BOOLEAN(cx, rval, cond);
2735 if (cond == JS_FALSE) {
2736 len = GET_JUMPX_OFFSET(pc);
2737 CHECK_BRANCH(len);
2738 DO_NEXT_OP(len);
2740 END_CASE(JSOP_IFEQX)
2742 BEGIN_CASE(JSOP_IFNEX)
2743 POP_BOOLEAN(cx, rval, cond);
2744 if (cond != JS_FALSE) {
2745 len = GET_JUMPX_OFFSET(pc);
2746 CHECK_BRANCH(len);
2747 DO_NEXT_OP(len);
2749 END_CASE(JSOP_IFNEX)
2751 BEGIN_CASE(JSOP_ORX)
2752 POP_BOOLEAN(cx, rval, cond);
2753 if (cond == JS_TRUE) {
2754 len = GET_JUMPX_OFFSET(pc);
2755 PUSH_OPND(rval);
2756 DO_NEXT_OP(len);
2758 END_CASE(JSOP_ORX)
2760 BEGIN_CASE(JSOP_ANDX)
2761 POP_BOOLEAN(cx, rval, cond);
2762 if (cond == JS_FALSE) {
2763 len = GET_JUMPX_OFFSET(pc);
2764 PUSH_OPND(rval);
2765 DO_NEXT_OP(len);
2767 END_CASE(JSOP_ANDX)
2770 * If the index value at sp[n] is not an int that fits in a jsval, it could
2771 * be an object (an XML QName, AttributeName, or AnyName), but only if we are
2772 * compiling with JS_HAS_XML_SUPPORT. Otherwise convert the index value to a
2773 * string atom id.
2775 * SAVE_SP_AND_PC must be already called.
2777 #define FETCH_ELEMENT_ID(obj, n, id) \
2778 JS_BEGIN_MACRO \
2779 jsval idval_ = FETCH_OPND(n); \
2780 if (JSVAL_IS_INT(idval_)) { \
2781 id = INT_JSVAL_TO_JSID(idval_); \
2782 } else { \
2783 ok = InternNonIntElementId(cx, obj, idval_, &id); \
2784 if (!ok) \
2785 goto out; \
2787 JS_END_MACRO
2789 BEGIN_CASE(JSOP_IN)
2790 rval = FETCH_OPND(-1);
2791 SAVE_SP_AND_PC(fp);
2792 if (JSVAL_IS_PRIMITIVE(rval)) {
2793 js_ReportValueError(cx, JSMSG_IN_NOT_OBJECT, -1, rval, NULL);
2794 ok = JS_FALSE;
2795 goto out;
2797 obj = JSVAL_TO_OBJECT(rval);
2798 FETCH_ELEMENT_ID(obj, -2, id);
2799 ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
2800 if (!ok)
2801 goto out;
2802 sp--;
2803 STORE_OPND(-1, BOOLEAN_TO_JSVAL(prop != NULL));
2804 if (prop)
2805 OBJ_DROP_PROPERTY(cx, obj2, prop);
2806 END_CASE(JSOP_IN)
2808 BEGIN_CASE(JSOP_FOREACH)
2809 flags = JSITER_ENUMERATE | JSITER_FOREACH;
2810 goto value_to_iter;
2812 #if JS_HAS_DESTRUCTURING
2813 BEGIN_CASE(JSOP_FOREACHKEYVAL)
2814 flags = JSITER_ENUMERATE | JSITER_FOREACH | JSITER_KEYVALUE;
2815 goto value_to_iter;
2816 #endif
2818 BEGIN_CASE(JSOP_FORIN)
2820 * Set JSITER_ENUMERATE to indicate that for-in loop should use
2821 * the enumeration protocol's iterator for compatibility if an
2822 * explicit iterator is not given via the optional __iterator__
2823 * method.
2825 flags = JSITER_ENUMERATE;
2827 value_to_iter:
2828 JS_ASSERT(sp > fp->spbase);
2829 SAVE_SP_AND_PC(fp);
2830 ok = js_ValueToIterator(cx, flags, &sp[-1]);
2831 if (!ok)
2832 goto out;
2833 JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[-1]));
2834 JS_ASSERT(JSOP_FORIN_LENGTH == js_CodeSpec[op].length);
2835 END_CASE(JSOP_FORIN)
2837 BEGIN_CASE(JSOP_FORPROP)
2839 * Handle JSOP_FORPROP first, so the cost of the goto do_forinloop
2840 * is not paid for the more common cases.
2842 LOAD_ATOM(0);
2843 id = ATOM_TO_JSID(atom);
2844 i = -2;
2845 goto do_forinloop;
2847 BEGIN_CASE(JSOP_FORNAME)
2848 LOAD_ATOM(0);
2849 id = ATOM_TO_JSID(atom);
2850 /* FALL THROUGH */
2852 BEGIN_CASE(JSOP_FORARG)
2853 BEGIN_CASE(JSOP_FORVAR)
2854 BEGIN_CASE(JSOP_FORCONST)
2855 BEGIN_CASE(JSOP_FORLOCAL)
2857 * JSOP_FORARG and JSOP_FORVAR don't require any lval computation
2858 * here, because they address slots on the stack (in fp->args and
2859 * fp->vars, respectively). Same applies to JSOP_FORLOCAL, which
2860 * addresses fp->spbase.
2862 /* FALL THROUGH */
2864 BEGIN_CASE(JSOP_FORELEM)
2866 * JSOP_FORELEM simply initializes or updates the iteration state
2867 * and leaves the index expression evaluation and assignment to the
2868 * enumerator until after the next property has been acquired, via
2869 * a JSOP_ENUMELEM bytecode.
2871 i = -1;
2873 do_forinloop:
2875 * Reach under the top of stack to find our property iterator, a
2876 * JSObject that contains the iteration state.
2878 JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[i]));
2879 iterobj = JSVAL_TO_OBJECT(sp[i]);
2881 SAVE_SP_AND_PC(fp);
2882 ok = js_CallIteratorNext(cx, iterobj, &rval);
2883 if (!ok)
2884 goto out;
2885 if (rval == JSVAL_HOLE) {
2886 rval = JSVAL_FALSE;
2887 goto end_forinloop;
2890 switch (op) {
2891 case JSOP_FORARG:
2892 slot = GET_ARGNO(pc);
2893 JS_ASSERT(slot < fp->fun->nargs);
2894 fp->argv[slot] = rval;
2895 break;
2897 case JSOP_FORVAR:
2898 slot = GET_VARNO(pc);
2899 JS_ASSERT(slot < fp->fun->u.i.nvars);
2900 fp->vars[slot] = rval;
2901 break;
2903 case JSOP_FORCONST:
2904 /* Don't update the const slot. */
2905 break;
2907 case JSOP_FORLOCAL:
2908 slot = GET_UINT16(pc);
2909 JS_ASSERT(slot < (uintN)depth);
2910 vp = &fp->spbase[slot];
2911 GC_POKE(cx, *vp);
2912 *vp = rval;
2913 break;
2915 case JSOP_FORELEM:
2916 /* FORELEM is not a SET operation, it's more like BINDNAME. */
2917 PUSH_OPND(rval);
2918 break;
2920 case JSOP_FORPROP:
2922 * We fetch object here to ensure that the iterator is called
2923 * even if lval is null or undefined that throws in
2924 * FETCH_OBJECT. See bug 372331.
2926 FETCH_OBJECT(cx, -1, lval, obj);
2927 goto set_for_property;
2929 case JSOP_FORNAME:
2931 * We find property here after the iterator call to ensure
2932 * that we take into account side effects of the iterator
2933 * call. See bug 372331.
2936 ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
2937 if (!ok)
2938 goto out;
2939 if (prop)
2940 OBJ_DROP_PROPERTY(cx, obj2, prop);
2942 set_for_property:
2943 /* Set the variable obj[id] to refer to rval. */
2944 fp->flags |= JSFRAME_ASSIGNING;
2945 ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
2946 fp->flags &= ~JSFRAME_ASSIGNING;
2947 if (!ok)
2948 goto out;
2949 break;
2951 default:
2952 JS_ASSERT(0);
2953 break;
2956 /* Push true to keep looping through properties. */
2957 rval = JSVAL_TRUE;
2959 end_forinloop:
2960 sp += i + 1;
2961 PUSH_OPND(rval);
2962 len = js_CodeSpec[op].length;
2963 DO_NEXT_OP(len);
2965 BEGIN_CASE(JSOP_DUP)
2966 JS_ASSERT(sp > fp->spbase);
2967 vp = sp - 1; /* address top of stack */
2968 rval = *vp;
2969 vp -= depth; /* address generating pc */
2970 vp[1] = *vp;
2971 PUSH(rval);
2972 END_CASE(JSOP_DUP)
2974 BEGIN_CASE(JSOP_DUP2)
2975 JS_ASSERT(sp - 2 >= fp->spbase);
2976 vp = sp - 1; /* address top of stack */
2977 lval = vp[-1];
2978 rval = *vp;
2979 vp -= depth; /* address generating pc */
2980 vp[1] = vp[2] = *vp;
2981 PUSH(lval);
2982 PUSH(rval);
2983 END_CASE(JSOP_DUP2)
2985 #define PROPERTY_OP(n, call) \
2986 JS_BEGIN_MACRO \
2987 /* Fetch the left part and resolve it to a non-null object. */ \
2988 SAVE_SP_AND_PC(fp); \
2989 FETCH_OBJECT(cx, n, lval, obj); \
2991 /* Get or set the property, set ok false if error, true if success. */\
2992 call; \
2993 if (!ok) \
2994 goto out; \
2995 JS_END_MACRO
2997 #define ELEMENT_OP(n, call) \
2998 JS_BEGIN_MACRO \
2999 /* Fetch the left part and resolve it to a non-null object. */ \
3000 SAVE_SP_AND_PC(fp); \
3001 FETCH_OBJECT(cx, n - 1, lval, obj); \
3003 /* Fetch index and convert it to id suitable for use with obj. */ \
3004 FETCH_ELEMENT_ID(obj, n, id); \
3006 /* Get or set the element, set ok false if error, true if success. */ \
3007 call; \
3008 if (!ok) \
3009 goto out; \
3010 JS_END_MACRO
3012 #define NATIVE_GET(cx,obj,pobj,sprop,vp) \
3013 JS_BEGIN_MACRO \
3014 if (SPROP_HAS_STUB_GETTER(sprop)) { \
3015 /* Fast path for Object instance properties. */ \
3016 JS_ASSERT((sprop)->slot != SPROP_INVALID_SLOT || \
3017 !SPROP_HAS_STUB_SETTER(sprop)); \
3018 *vp = ((sprop)->slot != SPROP_INVALID_SLOT) \
3019 ? LOCKED_OBJ_GET_SLOT(pobj, (sprop)->slot) \
3020 : JSVAL_VOID; \
3021 } else { \
3022 SAVE_SP_AND_PC(fp); \
3023 ok = js_NativeGet(cx, obj, pobj, sprop, vp); \
3024 if (!ok) \
3025 goto out; \
3027 JS_END_MACRO
3029 BEGIN_CASE(JSOP_SETCONST)
3030 LOAD_ATOM(0);
3031 obj = fp->varobj;
3032 rval = FETCH_OPND(-1);
3033 SAVE_SP_AND_PC(fp);
3034 ok = OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), rval,
3035 NULL, NULL,
3036 JSPROP_ENUMERATE | JSPROP_PERMANENT |
3037 JSPROP_READONLY,
3038 NULL);
3039 if (!ok)
3040 goto out;
3041 STORE_OPND(-1, rval);
3042 END_CASE(JSOP_SETCONST)
3044 #if JS_HAS_DESTRUCTURING
3045 BEGIN_CASE(JSOP_ENUMCONSTELEM)
3046 rval = FETCH_OPND(-3);
3047 SAVE_SP_AND_PC(fp);
3048 FETCH_OBJECT(cx, -2, lval, obj);
3049 FETCH_ELEMENT_ID(obj, -1, id);
3050 ok = OBJ_DEFINE_PROPERTY(cx, obj, id, rval, NULL, NULL,
3051 JSPROP_ENUMERATE | JSPROP_PERMANENT |
3052 JSPROP_READONLY,
3053 NULL);
3054 if (!ok)
3055 goto out;
3056 sp -= 3;
3057 END_CASE(JSOP_ENUMCONSTELEM)
3058 #endif
3060 BEGIN_CASE(JSOP_BINDNAME)
3061 LOAD_ATOM(0);
3062 id = ATOM_TO_JSID(atom);
3063 SAVE_SP_AND_PC(fp);
3064 obj = js_FindIdentifierBase(cx, id);
3065 if (!obj) {
3066 ok = JS_FALSE;
3067 goto out;
3069 PUSH_OPND(OBJECT_TO_JSVAL(obj));
3070 END_CASE(JSOP_BINDNAME)
3072 BEGIN_CASE(JSOP_SETNAME)
3073 LOAD_ATOM(0);
3074 id = ATOM_TO_JSID(atom);
3075 rval = FETCH_OPND(-1);
3076 lval = FETCH_OPND(-2);
3077 JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval));
3078 obj = JSVAL_TO_OBJECT(lval);
3079 SAVE_SP_AND_PC(fp);
3080 ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
3081 if (!ok)
3082 goto out;
3083 sp--;
3084 STORE_OPND(-1, rval);
3085 END_CASE(JSOP_SETNAME)
3087 #define INTEGER_OP(OP, EXTRA_CODE) \
3088 JS_BEGIN_MACRO \
3089 FETCH_INT(cx, -1, j); \
3090 FETCH_INT(cx, -2, i); \
3091 EXTRA_CODE \
3092 i = i OP j; \
3093 sp--; \
3094 STORE_INT(cx, -1, i); \
3095 JS_END_MACRO
3097 #define BITWISE_OP(OP) INTEGER_OP(OP, (void) 0;)
3098 #define SIGNED_SHIFT_OP(OP) INTEGER_OP(OP, j &= 31;)
3100 BEGIN_CASE(JSOP_BITOR)
3101 BITWISE_OP(|);
3102 END_CASE(JSOP_BITOR)
3104 BEGIN_CASE(JSOP_BITXOR)
3105 BITWISE_OP(^);
3106 END_CASE(JSOP_BITXOR)
3108 BEGIN_CASE(JSOP_BITAND)
3109 BITWISE_OP(&);
3110 END_CASE(JSOP_BITAND)
3112 #define RELATIONAL_OP(OP) \
3113 JS_BEGIN_MACRO \
3114 rval = FETCH_OPND(-1); \
3115 lval = FETCH_OPND(-2); \
3116 /* Optimize for two int-tagged operands (typical loop control). */ \
3117 if ((lval & rval) & JSVAL_INT) { \
3118 ltmp = lval ^ JSVAL_VOID; \
3119 rtmp = rval ^ JSVAL_VOID; \
3120 if (ltmp && rtmp) { \
3121 cond = JSVAL_TO_INT(lval) OP JSVAL_TO_INT(rval); \
3122 } else { \
3123 d = ltmp ? JSVAL_TO_INT(lval) : *rt->jsNaN; \
3124 d2 = rtmp ? JSVAL_TO_INT(rval) : *rt->jsNaN; \
3125 cond = JSDOUBLE_COMPARE(d, OP, d2, JS_FALSE); \
3127 } else { \
3128 if (!JSVAL_IS_PRIMITIVE(lval)) \
3129 DEFAULT_VALUE(cx, -2, JSTYPE_NUMBER, lval); \
3130 if (!JSVAL_IS_PRIMITIVE(rval)) \
3131 DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval); \
3132 if (JSVAL_IS_STRING(lval) && JSVAL_IS_STRING(rval)) { \
3133 str = JSVAL_TO_STRING(lval); \
3134 str2 = JSVAL_TO_STRING(rval); \
3135 cond = js_CompareStrings(str, str2) OP 0; \
3136 } else { \
3137 VALUE_TO_NUMBER(cx, lval, d); \
3138 VALUE_TO_NUMBER(cx, rval, d2); \
3139 cond = JSDOUBLE_COMPARE(d, OP, d2, JS_FALSE); \
3142 sp--; \
3143 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \
3144 JS_END_MACRO
3147 * NB: These macros can't use JS_BEGIN_MACRO/JS_END_MACRO around their bodies
3148 * because they begin if/else chains, so callers must not put semicolons after
3149 * the call expressions!
3151 #if JS_HAS_XML_SUPPORT
3152 #define XML_EQUALITY_OP(OP) \
3153 if ((ltmp == JSVAL_OBJECT && \
3154 (obj2 = JSVAL_TO_OBJECT(lval)) && \
3155 OBJECT_IS_XML(cx, obj2)) || \
3156 (rtmp == JSVAL_OBJECT && \
3157 (obj2 = JSVAL_TO_OBJECT(rval)) && \
3158 OBJECT_IS_XML(cx, obj2))) { \
3159 JSXMLObjectOps *ops; \
3161 ops = (JSXMLObjectOps *) obj2->map->ops; \
3162 if (obj2 == JSVAL_TO_OBJECT(rval)) \
3163 rval = lval; \
3164 SAVE_SP_AND_PC(fp); \
3165 ok = ops->equality(cx, obj2, rval, &cond); \
3166 if (!ok) \
3167 goto out; \
3168 cond = cond OP JS_TRUE; \
3169 } else
3171 #define EXTENDED_EQUALITY_OP(OP) \
3172 if (ltmp == JSVAL_OBJECT && \
3173 (obj2 = JSVAL_TO_OBJECT(lval)) && \
3174 ((clasp = OBJ_GET_CLASS(cx, obj2))->flags & JSCLASS_IS_EXTENDED)) { \
3175 JSExtendedClass *xclasp; \
3177 xclasp = (JSExtendedClass *) clasp; \
3178 SAVE_SP_AND_PC(fp); \
3179 ok = xclasp->equality(cx, obj2, rval, &cond); \
3180 if (!ok) \
3181 goto out; \
3182 cond = cond OP JS_TRUE; \
3183 } else
3184 #else
3185 #define XML_EQUALITY_OP(OP) /* nothing */
3186 #define EXTENDED_EQUALITY_OP(OP) /* nothing */
3187 #endif
3189 #define EQUALITY_OP(OP, IFNAN) \
3190 JS_BEGIN_MACRO \
3191 rval = FETCH_OPND(-1); \
3192 lval = FETCH_OPND(-2); \
3193 ltmp = JSVAL_TAG(lval); \
3194 rtmp = JSVAL_TAG(rval); \
3195 XML_EQUALITY_OP(OP) \
3196 if (ltmp == rtmp) { \
3197 if (ltmp == JSVAL_STRING) { \
3198 str = JSVAL_TO_STRING(lval); \
3199 str2 = JSVAL_TO_STRING(rval); \
3200 cond = js_EqualStrings(str, str2) OP JS_TRUE; \
3201 } else if (ltmp == JSVAL_DOUBLE) { \
3202 d = *JSVAL_TO_DOUBLE(lval); \
3203 d2 = *JSVAL_TO_DOUBLE(rval); \
3204 cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \
3205 } else { \
3206 EXTENDED_EQUALITY_OP(OP) \
3207 /* Handle all undefined (=>NaN) and int combinations. */ \
3208 cond = lval OP rval; \
3210 } else { \
3211 if (JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval)) { \
3212 cond = (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) OP 1; \
3213 } else if (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) { \
3214 cond = 1 OP 0; \
3215 } else { \
3216 if (ltmp == JSVAL_OBJECT) { \
3217 DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval); \
3218 ltmp = JSVAL_TAG(lval); \
3219 } else if (rtmp == JSVAL_OBJECT) { \
3220 DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval); \
3221 rtmp = JSVAL_TAG(rval); \
3223 if (ltmp == JSVAL_STRING && rtmp == JSVAL_STRING) { \
3224 str = JSVAL_TO_STRING(lval); \
3225 str2 = JSVAL_TO_STRING(rval); \
3226 cond = js_EqualStrings(str, str2) OP JS_TRUE; \
3227 } else { \
3228 VALUE_TO_NUMBER(cx, lval, d); \
3229 VALUE_TO_NUMBER(cx, rval, d2); \
3230 cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \
3234 sp--; \
3235 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \
3236 JS_END_MACRO
3238 BEGIN_CASE(JSOP_EQ)
3239 EQUALITY_OP(==, JS_FALSE);
3240 END_CASE(JSOP_EQ)
3242 BEGIN_CASE(JSOP_NE)
3243 EQUALITY_OP(!=, JS_TRUE);
3244 END_CASE(JSOP_NE)
3246 #define STRICT_EQUALITY_OP(OP) \
3247 JS_BEGIN_MACRO \
3248 rval = FETCH_OPND(-1); \
3249 lval = FETCH_OPND(-2); \
3250 cond = js_StrictlyEqual(lval, rval) OP JS_TRUE; \
3251 sp--; \
3252 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \
3253 JS_END_MACRO
3255 BEGIN_CASE(JSOP_STRICTEQ)
3256 STRICT_EQUALITY_OP(==);
3257 END_CASE(JSOP_STRICTEQ)
3259 BEGIN_CASE(JSOP_STRICTNE)
3260 STRICT_EQUALITY_OP(!=);
3261 END_CASE(JSOP_STRICTNE)
3263 BEGIN_CASE(JSOP_CASE)
3264 pc2 = (jsbytecode *) sp[-2-depth];
3265 STRICT_EQUALITY_OP(==);
3266 (void) POP();
3267 if (cond) {
3268 len = GET_JUMP_OFFSET(pc);
3269 CHECK_BRANCH(len);
3270 DO_NEXT_OP(len);
3272 sp[-depth] = (jsval)pc2;
3273 PUSH(lval);
3274 END_CASE(JSOP_CASE)
3276 BEGIN_CASE(JSOP_CASEX)
3277 pc2 = (jsbytecode *) sp[-2-depth];
3278 STRICT_EQUALITY_OP(==);
3279 (void) POP();
3280 if (cond) {
3281 len = GET_JUMPX_OFFSET(pc);
3282 CHECK_BRANCH(len);
3283 DO_NEXT_OP(len);
3285 sp[-depth] = (jsval)pc2;
3286 PUSH(lval);
3287 END_CASE(JSOP_CASEX)
3289 BEGIN_CASE(JSOP_LT)
3290 RELATIONAL_OP(<);
3291 END_CASE(JSOP_LT)
3293 BEGIN_CASE(JSOP_LE)
3294 RELATIONAL_OP(<=);
3295 END_CASE(JSOP_LE)
3297 BEGIN_CASE(JSOP_GT)
3298 RELATIONAL_OP(>);
3299 END_CASE(JSOP_GT)
3301 BEGIN_CASE(JSOP_GE)
3302 RELATIONAL_OP(>=);
3303 END_CASE(JSOP_GE)
3305 #undef EQUALITY_OP
3306 #undef RELATIONAL_OP
3308 BEGIN_CASE(JSOP_LSH)
3309 SIGNED_SHIFT_OP(<<);
3310 END_CASE(JSOP_LSH)
3312 BEGIN_CASE(JSOP_RSH)
3313 SIGNED_SHIFT_OP(>>);
3314 END_CASE(JSOP_RSH)
3316 BEGIN_CASE(JSOP_URSH)
3318 uint32 u;
3320 FETCH_INT(cx, -1, j);
3321 FETCH_UINT(cx, -2, u);
3322 u >>= j & 31;
3323 sp--;
3324 STORE_UINT(cx, -1, u);
3326 END_CASE(JSOP_URSH)
3328 #undef INTEGER_OP
3329 #undef BITWISE_OP
3330 #undef SIGNED_SHIFT_OP
3332 BEGIN_CASE(JSOP_ADD)
3333 rval = FETCH_OPND(-1);
3334 lval = FETCH_OPND(-2);
3335 #if JS_HAS_XML_SUPPORT
3336 if (!JSVAL_IS_PRIMITIVE(lval) &&
3337 (obj2 = JSVAL_TO_OBJECT(lval), OBJECT_IS_XML(cx, obj2)) &&
3338 VALUE_IS_XML(cx, rval)) {
3339 JSXMLObjectOps *ops;
3341 ops = (JSXMLObjectOps *) obj2->map->ops;
3342 SAVE_SP_AND_PC(fp);
3343 ok = ops->concatenate(cx, obj2, rval, &rval);
3344 if (!ok)
3345 goto out;
3346 sp--;
3347 STORE_OPND(-1, rval);
3348 } else
3349 #endif
3351 if (!JSVAL_IS_PRIMITIVE(lval))
3352 DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval);
3353 if (!JSVAL_IS_PRIMITIVE(rval))
3354 DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval);
3355 if ((cond = JSVAL_IS_STRING(lval)) || JSVAL_IS_STRING(rval)) {
3356 SAVE_SP_AND_PC(fp);
3357 if (cond) {
3358 str = JSVAL_TO_STRING(lval);
3359 ok = (str2 = js_ValueToString(cx, rval)) != NULL;
3360 if (!ok)
3361 goto out;
3362 sp[-1] = STRING_TO_JSVAL(str2);
3363 } else {
3364 str2 = JSVAL_TO_STRING(rval);
3365 ok = (str = js_ValueToString(cx, lval)) != NULL;
3366 if (!ok)
3367 goto out;
3368 sp[-2] = STRING_TO_JSVAL(str);
3370 str = js_ConcatStrings(cx, str, str2);
3371 if (!str) {
3372 ok = JS_FALSE;
3373 goto out;
3375 sp--;
3376 STORE_OPND(-1, STRING_TO_JSVAL(str));
3377 } else {
3378 VALUE_TO_NUMBER(cx, lval, d);
3379 VALUE_TO_NUMBER(cx, rval, d2);
3380 d += d2;
3381 sp--;
3382 STORE_NUMBER(cx, -1, d);
3385 END_CASE(JSOP_ADD)
3387 #define BINARY_OP(OP) \
3388 JS_BEGIN_MACRO \
3389 FETCH_NUMBER(cx, -1, d2); \
3390 FETCH_NUMBER(cx, -2, d); \
3391 d = d OP d2; \
3392 sp--; \
3393 STORE_NUMBER(cx, -1, d); \
3394 JS_END_MACRO
3396 BEGIN_CASE(JSOP_SUB)
3397 BINARY_OP(-);
3398 END_CASE(JSOP_SUB)
3400 BEGIN_CASE(JSOP_MUL)
3401 BINARY_OP(*);
3402 END_CASE(JSOP_MUL)
3404 BEGIN_CASE(JSOP_DIV)
3405 FETCH_NUMBER(cx, -1, d2);
3406 FETCH_NUMBER(cx, -2, d);
3407 sp--;
3408 if (d2 == 0) {
3409 #ifdef XP_WIN
3410 /* XXX MSVC miscompiles such that (NaN == 0) */
3411 if (JSDOUBLE_IS_NaN(d2))
3412 rval = DOUBLE_TO_JSVAL(rt->jsNaN);
3413 else
3414 #endif
3415 if (d == 0 || JSDOUBLE_IS_NaN(d))
3416 rval = DOUBLE_TO_JSVAL(rt->jsNaN);
3417 else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31)
3418 rval = DOUBLE_TO_JSVAL(rt->jsNegativeInfinity);
3419 else
3420 rval = DOUBLE_TO_JSVAL(rt->jsPositiveInfinity);
3421 STORE_OPND(-1, rval);
3422 } else {
3423 d /= d2;
3424 STORE_NUMBER(cx, -1, d);
3426 END_CASE(JSOP_DIV)
3428 BEGIN_CASE(JSOP_MOD)
3429 FETCH_NUMBER(cx, -1, d2);
3430 FETCH_NUMBER(cx, -2, d);
3431 sp--;
3432 if (d2 == 0) {
3433 STORE_OPND(-1, DOUBLE_TO_JSVAL(rt->jsNaN));
3434 } else {
3435 #ifdef XP_WIN
3436 /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */
3437 if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2)))
3438 #endif
3439 d = fmod(d, d2);
3440 STORE_NUMBER(cx, -1, d);
3442 END_CASE(JSOP_MOD)
3444 BEGIN_CASE(JSOP_NOT)
3445 POP_BOOLEAN(cx, rval, cond);
3446 PUSH_OPND(BOOLEAN_TO_JSVAL(!cond));
3447 END_CASE(JSOP_NOT)
3449 BEGIN_CASE(JSOP_BITNOT)
3450 FETCH_INT(cx, -1, i);
3451 i = ~i;
3452 STORE_INT(cx, -1, i);
3453 END_CASE(JSOP_BITNOT)
3455 BEGIN_CASE(JSOP_NEG)
3457 * Optimize the case of an int-tagged operand by noting that
3458 * INT_FITS_IN_JSVAL(i) => INT_FITS_IN_JSVAL(-i) unless i is 0
3459 * when -i is the negative zero which is jsdouble.
3461 rval = FETCH_OPND(-1);
3462 if (JSVAL_IS_INT(rval) && (i = JSVAL_TO_INT(rval)) != 0) {
3463 i = -i;
3464 JS_ASSERT(INT_FITS_IN_JSVAL(i));
3465 rval = INT_TO_JSVAL(i);
3466 } else {
3467 if (JSVAL_IS_DOUBLE(rval)) {
3468 d = *JSVAL_TO_DOUBLE(rval);
3469 } else {
3470 SAVE_SP_AND_PC(fp);
3471 ok = js_ValueToNumber(cx, rval, &d);
3472 if (!ok)
3473 goto out;
3475 #ifdef HPUX
3477 * Negation of a zero doesn't produce a negative
3478 * zero on HPUX. Perform the operation by bit
3479 * twiddling.
3481 JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT;
3482 #else
3483 d = -d;
3484 #endif
3485 ok = js_NewNumberValue(cx, d, &rval);
3486 if (!ok)
3487 goto out;
3489 STORE_OPND(-1, rval);
3490 END_CASE(JSOP_NEG)
3492 BEGIN_CASE(JSOP_POS)
3493 rval = FETCH_OPND(-1);
3494 if (!JSVAL_IS_NUMBER(rval)) {
3495 SAVE_SP_AND_PC(fp);
3496 ok = js_ValueToNumber(cx, rval, &d);
3497 if (!ok)
3498 goto out;
3499 ok = js_NewNumberValue(cx, d, &rval);
3500 if (!ok)
3501 goto out;
3502 sp[-1] = rval;
3504 sp[-1-depth] = (jsval)pc;
3505 END_CASE(JSOP_POS)
3507 BEGIN_CASE(JSOP_NEW)
3508 /* Get immediate argc and find the constructor function. */
3509 argc = GET_ARGC(pc);
3511 do_new:
3512 SAVE_SP_AND_PC(fp);
3513 vp = sp - (2 + argc);
3514 JS_ASSERT(vp >= fp->spbase);
3516 ok = js_InvokeConstructor(cx, vp, argc);
3517 if (!ok)
3518 goto out;
3519 RESTORE_SP(fp);
3520 LOAD_INTERRUPT_HANDLER(cx);
3521 obj = JSVAL_TO_OBJECT(*vp);
3522 len = js_CodeSpec[op].length;
3523 DO_NEXT_OP(len);
3525 BEGIN_CASE(JSOP_DELNAME)
3526 LOAD_ATOM(0);
3527 id = ATOM_TO_JSID(atom);
3529 SAVE_SP_AND_PC(fp);
3530 ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
3531 if (!ok)
3532 goto out;
3534 /* ECMA says to return true if name is undefined or inherited. */
3535 rval = JSVAL_TRUE;
3536 if (prop) {
3537 OBJ_DROP_PROPERTY(cx, obj2, prop);
3538 ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval);
3539 if (!ok)
3540 goto out;
3542 PUSH_OPND(rval);
3543 END_CASE(JSOP_DELNAME)
3545 BEGIN_CASE(JSOP_DELPROP)
3546 LOAD_ATOM(0);
3547 id = ATOM_TO_JSID(atom);
3548 PROPERTY_OP(-1, ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval));
3549 STORE_OPND(-1, rval);
3550 END_CASE(JSOP_DELPROP)
3552 BEGIN_CASE(JSOP_DELELEM)
3553 ELEMENT_OP(-1, ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval));
3554 sp--;
3555 STORE_OPND(-1, rval);
3556 END_CASE(JSOP_DELELEM)
3558 BEGIN_CASE(JSOP_TYPEOFEXPR)
3559 BEGIN_CASE(JSOP_TYPEOF)
3560 rval = FETCH_OPND(-1);
3561 SAVE_SP_AND_PC(fp);
3562 type = JS_TypeOfValue(cx, rval);
3563 atom = rt->atomState.typeAtoms[type];
3564 STORE_OPND(-1, ATOM_KEY(atom));
3565 END_CASE(JSOP_TYPEOF)
3567 BEGIN_CASE(JSOP_VOID)
3568 (void) POP_OPND();
3569 PUSH_OPND(JSVAL_VOID);
3570 END_CASE(JSOP_VOID)
3572 BEGIN_CASE(JSOP_INCELEM)
3573 BEGIN_CASE(JSOP_DECELEM)
3574 BEGIN_CASE(JSOP_ELEMINC)
3575 BEGIN_CASE(JSOP_ELEMDEC)
3577 * Delay fetching of id until we have the object to ensure
3578 * the proper evaluation oder. See bug 372331.
3580 id = 0;
3581 i = -2;
3582 goto fetch_incop_obj;
3584 BEGIN_CASE(JSOP_INCPROP)
3585 BEGIN_CASE(JSOP_DECPROP)
3586 BEGIN_CASE(JSOP_PROPINC)
3587 BEGIN_CASE(JSOP_PROPDEC)
3588 LOAD_ATOM(0);
3589 id = ATOM_TO_JSID(atom);
3590 i = -1;
3592 fetch_incop_obj:
3593 SAVE_SP_AND_PC(fp);
3594 FETCH_OBJECT(cx, i, lval, obj);
3595 STORE_OPND(i, OBJECT_TO_JSVAL(obj));
3596 if (id == 0)
3597 FETCH_ELEMENT_ID(obj, -1, id);
3598 goto do_incop;
3600 BEGIN_CASE(JSOP_INCNAME)
3601 BEGIN_CASE(JSOP_DECNAME)
3602 BEGIN_CASE(JSOP_NAMEINC)
3603 BEGIN_CASE(JSOP_NAMEDEC)
3604 LOAD_ATOM(0);
3605 id = ATOM_TO_JSID(atom);
3607 SAVE_SP_AND_PC(fp);
3608 ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
3609 if (!ok)
3610 goto out;
3611 if (!prop)
3612 goto atom_not_defined;
3614 OBJ_DROP_PROPERTY(cx, obj2, prop);
3615 lval = OBJECT_TO_JSVAL(obj);
3616 i = 0;
3618 do_incop:
3620 const JSCodeSpec *cs;
3622 /* The operand must contain a number. */
3623 ok = OBJ_GET_PROPERTY(cx, obj, id, &rval);
3624 if (!ok)
3625 goto out;
3627 /* Preload for use in the if/else immediately below. */
3628 cs = &js_CodeSpec[op];
3630 /* The expression result goes in rtmp, the updated value in rval. */
3631 if (JSVAL_IS_INT(rval) &&
3632 rval != INT_TO_JSVAL(JSVAL_INT_MIN) &&
3633 rval != INT_TO_JSVAL(JSVAL_INT_MAX)) {
3634 if (cs->format & JOF_POST) {
3635 rtmp = rval;
3636 (cs->format & JOF_INC) ? (rval += 2) : (rval -= 2);
3637 } else {
3638 (cs->format & JOF_INC) ? (rval += 2) : (rval -= 2);
3639 rtmp = rval;
3641 } else {
3644 * Initially, rval contains the value to increment or decrement, which is not
3645 * yet converted. As above, the expression result goes in rtmp, the updated
3646 * value goes in rval. Our caller must set vp to point at a GC-rooted jsval
3647 * in which we home rtmp, to protect it from GC in case the unconverted rval
3648 * is not a number.
3650 #define NONINT_INCREMENT_OP_MIDDLE() \
3651 JS_BEGIN_MACRO \
3652 VALUE_TO_NUMBER(cx, rval, d); \
3653 if (cs->format & JOF_POST) { \
3654 rtmp = rval; \
3655 if (!JSVAL_IS_NUMBER(rtmp)) { \
3656 ok = js_NewNumberValue(cx, d, &rtmp); \
3657 if (!ok) \
3658 goto out; \
3660 *vp = rtmp; \
3661 (cs->format & JOF_INC) ? d++ : d--; \
3662 ok = js_NewNumberValue(cx, d, &rval); \
3663 } else { \
3664 (cs->format & JOF_INC) ? ++d : --d; \
3665 ok = js_NewNumberValue(cx, d, &rval); \
3666 rtmp = rval; \
3668 if (!ok) \
3669 goto out; \
3670 JS_END_MACRO
3672 if (cs->format & JOF_POST) {
3674 * We must push early to protect the postfix increment
3675 * or decrement result, if converted to a jsdouble from
3676 * a non-number value, from GC nesting in the setter.
3678 vp = sp;
3679 PUSH(JSVAL_VOID);
3680 SAVE_SP(fp);
3681 --i;
3683 #ifdef __GNUC__
3684 else vp = NULL; /* suppress bogus gcc warnings */
3685 #endif
3687 NONINT_INCREMENT_OP_MIDDLE();
3690 fp->flags |= JSFRAME_ASSIGNING;
3691 ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
3692 fp->flags &= ~JSFRAME_ASSIGNING;
3693 if (!ok)
3694 goto out;
3695 sp += i;
3696 PUSH_OPND(rtmp);
3697 len = js_CodeSpec[op].length;
3698 DO_NEXT_OP(len);
3701 /* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */
3702 #define FAST_INCREMENT_OP(SLOT,COUNT,BASE,PRE,OPEQ,MINMAX) \
3703 slot = SLOT; \
3704 JS_ASSERT(slot < fp->fun->COUNT); \
3705 METER_SLOT_OP(op, slot); \
3706 vp = fp->BASE + slot; \
3707 rval = *vp; \
3708 if (!JSVAL_IS_INT(rval) || rval == INT_TO_JSVAL(JSVAL_INT_##MINMAX)) \
3709 goto do_nonint_fast_incop; \
3710 PRE = rval; \
3711 rval OPEQ 2; \
3712 *vp = rval; \
3713 PUSH_OPND(PRE); \
3714 goto end_nonint_fast_incop
3716 BEGIN_CASE(JSOP_INCARG)
3717 FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rval, +=, MAX);
3718 BEGIN_CASE(JSOP_DECARG)
3719 FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rval, -=, MIN);
3720 BEGIN_CASE(JSOP_ARGINC)
3721 FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rtmp, +=, MAX);
3722 BEGIN_CASE(JSOP_ARGDEC)
3723 FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rtmp, -=, MIN);
3725 BEGIN_CASE(JSOP_INCVAR)
3726 FAST_INCREMENT_OP(GET_VARNO(pc), u.i.nvars, vars, rval, +=, MAX);
3727 BEGIN_CASE(JSOP_DECVAR)
3728 FAST_INCREMENT_OP(GET_VARNO(pc), u.i.nvars, vars, rval, -=, MIN);
3729 BEGIN_CASE(JSOP_VARINC)
3730 FAST_INCREMENT_OP(GET_VARNO(pc), u.i.nvars, vars, rtmp, +=, MAX);
3731 BEGIN_CASE(JSOP_VARDEC)
3732 FAST_INCREMENT_OP(GET_VARNO(pc), u.i.nvars, vars, rtmp, -=, MIN);
3734 end_nonint_fast_incop:
3735 len = JSOP_INCARG_LENGTH; /* all fast incops are same length */
3736 DO_NEXT_OP(len);
3738 #undef FAST_INCREMENT_OP
3740 do_nonint_fast_incop:
3742 const JSCodeSpec *cs = &js_CodeSpec[op];
3744 NONINT_INCREMENT_OP_MIDDLE();
3745 *vp = rval;
3746 PUSH_OPND(rtmp);
3747 len = cs->length;
3748 DO_NEXT_OP(len);
3751 /* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */
3752 #define FAST_GLOBAL_INCREMENT_OP(SLOWOP,PRE,OPEQ,MINMAX) \
3753 slot = GET_VARNO(pc); \
3754 JS_ASSERT(slot < fp->nvars); \
3755 METER_SLOT_OP(op, slot); \
3756 lval = fp->vars[slot]; \
3757 if (JSVAL_IS_NULL(lval)) { \
3758 op = SLOWOP; \
3759 DO_OP(); \
3761 slot = JSVAL_TO_INT(lval); \
3762 obj = fp->varobj; \
3763 rval = OBJ_GET_SLOT(cx, obj, slot); \
3764 if (!JSVAL_IS_INT(rval) || rval == INT_TO_JSVAL(JSVAL_INT_##MINMAX)) \
3765 goto do_nonint_fast_global_incop; \
3766 PRE = rval; \
3767 rval OPEQ 2; \
3768 OBJ_SET_SLOT(cx, obj, slot, rval); \
3769 PUSH_OPND(PRE); \
3770 goto end_nonint_fast_global_incop
3772 BEGIN_CASE(JSOP_INCGVAR)
3773 FAST_GLOBAL_INCREMENT_OP(JSOP_INCNAME, rval, +=, MAX);
3774 BEGIN_CASE(JSOP_DECGVAR)
3775 FAST_GLOBAL_INCREMENT_OP(JSOP_DECNAME, rval, -=, MIN);
3776 BEGIN_CASE(JSOP_GVARINC)
3777 FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEINC, rtmp, +=, MAX);
3778 BEGIN_CASE(JSOP_GVARDEC)
3779 FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEDEC, rtmp, -=, MIN);
3781 end_nonint_fast_global_incop:
3782 len = JSOP_INCGVAR_LENGTH; /* all gvar incops are same length */
3783 JS_ASSERT(len == js_CodeSpec[op].length);
3784 DO_NEXT_OP(len);
3786 #undef FAST_GLOBAL_INCREMENT_OP
3788 do_nonint_fast_global_incop:
3790 const JSCodeSpec *cs = &js_CodeSpec[op];
3792 vp = sp++;
3793 SAVE_SP(fp);
3794 NONINT_INCREMENT_OP_MIDDLE();
3795 OBJ_SET_SLOT(cx, obj, slot, rval);
3796 STORE_OPND(-1, rtmp);
3797 len = cs->length;
3798 DO_NEXT_OP(len);
3801 BEGIN_CASE(JSOP_GETTHISPROP)
3802 LOAD_ATOM(0);
3803 id = ATOM_TO_JSID(atom);
3804 obj = fp->thisp;
3805 SAVE_SP_AND_PC(fp);
3806 ok = OBJ_GET_PROPERTY(cx, obj, id, &rval);
3807 if (!ok)
3808 goto out;
3809 PUSH_OPND(rval);
3810 END_CASE(JSOP_GETTHISPROP)
3812 BEGIN_CASE(JSOP_GETARGPROP)
3813 LOAD_ATOM(ARGNO_LEN);
3814 slot = GET_ARGNO(pc);
3815 JS_ASSERT(slot < fp->fun->nargs);
3816 PUSH_OPND(fp->argv[slot]);
3817 len = JSOP_GETARGPROP_LENGTH;
3818 goto do_getprop_body;
3820 BEGIN_CASE(JSOP_GETVARPROP)
3821 LOAD_ATOM(VARNO_LEN);
3822 slot = GET_VARNO(pc);
3823 JS_ASSERT(slot < fp->fun->u.i.nvars);
3824 PUSH_OPND(fp->vars[slot]);
3825 len = JSOP_GETVARPROP_LENGTH;
3826 goto do_getprop_body;
3828 BEGIN_CASE(JSOP_GETLOCALPROP)
3829 LOAD_ATOM(2);
3830 slot = GET_UINT16(pc);
3831 JS_ASSERT(slot < (uintN)depth);
3832 PUSH_OPND(fp->spbase[slot]);
3833 len = JSOP_GETLOCALPROP_LENGTH;
3834 goto do_getprop_body;
3836 BEGIN_CASE(JSOP_GETPROP)
3837 BEGIN_CASE(JSOP_GETXPROP)
3838 /* Get an immediate atom naming the property. */
3839 LOAD_ATOM(0);
3840 len = JSOP_GETPROP_LENGTH;
3842 do_getprop_body:
3843 lval = FETCH_OPND(-1);
3844 if (JSVAL_IS_STRING(lval) && atom == rt->atomState.lengthAtom) {
3845 str = JSVAL_TO_STRING(lval);
3846 rval = INT_TO_JSVAL(JSSTRING_LENGTH(str));
3847 } else {
3848 id = ATOM_TO_JSID(atom);
3849 SAVE_SP_AND_PC(fp);
3850 VALUE_TO_OBJECT(cx, -1, lval, obj);
3851 ok = OBJ_GET_PROPERTY(cx, obj, id, &rval);
3852 if (!ok)
3853 goto out;
3855 STORE_OPND(-1, rval);
3856 END_VARLEN_CASE
3858 BEGIN_CASE(JSOP_SETPROP)
3859 /* Get an immediate atom naming the property. */
3860 LOAD_ATOM(0);
3861 id = ATOM_TO_JSID(atom);
3863 /* Pop the right-hand side into rval for OBJ_SET_PROPERTY. */
3864 rval = FETCH_OPND(-1);
3865 PROPERTY_OP(-2, ok = OBJ_SET_PROPERTY(cx, obj, id, &rval));
3866 sp--;
3867 STORE_OPND(-1, rval);
3868 END_CASE(JSOP_SETPROP)
3870 BEGIN_CASE(JSOP_GETELEM)
3871 ELEMENT_OP(-1, ok = OBJ_GET_PROPERTY(cx, obj, id, &rval));
3872 sp--;
3873 STORE_OPND(-1, rval);
3874 END_CASE(JSOP_GETELEM)
3876 BEGIN_CASE(JSOP_CALLELEM)
3878 * FIXME: JSOP_CALLELEM should call getMethod on XML objects as
3879 * CALLPROP does. See bug 362910.
3881 ELEMENT_OP(-1, ok = OBJ_GET_PROPERTY(cx, obj, id, &rval));
3882 STORE_OPND(-2, rval);
3883 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
3884 END_CASE(JSOP_CALLELEM)
3886 BEGIN_CASE(JSOP_SETELEM)
3887 rval = FETCH_OPND(-1);
3888 ELEMENT_OP(-2, ok = OBJ_SET_PROPERTY(cx, obj, id, &rval));
3889 sp -= 2;
3890 STORE_OPND(-1, rval);
3891 END_CASE(JSOP_SETELEM)
3893 BEGIN_CASE(JSOP_ENUMELEM)
3894 /* Funky: the value to set is under the [obj, id] pair. */
3895 rval = FETCH_OPND(-3);
3896 SAVE_SP_AND_PC(fp);
3897 FETCH_OBJECT(cx, -2, lval, obj);
3898 FETCH_ELEMENT_ID(obj, -1, id);
3899 ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
3900 if (!ok)
3901 goto out;
3902 sp -= 3;
3903 END_CASE(JSOP_ENUMELEM)
3905 BEGIN_CASE(JSOP_CALL)
3906 BEGIN_CASE(JSOP_EVAL)
3907 argc = GET_ARGC(pc);
3908 vp = sp - (argc + 2);
3909 lval = *vp;
3910 SAVE_SP_AND_PC(fp);
3911 if (VALUE_IS_FUNCTION(cx, lval)) {
3912 obj = JSVAL_TO_OBJECT(lval);
3913 fun = (JSFunction *) OBJ_GET_PRIVATE(cx, obj);
3915 if (fun->flags & JSFUN_INTERPRETED) {
3916 uintN nframeslots, nvars, nslots, missing;
3917 JSArena *a;
3918 jsuword avail, nbytes;
3919 JSBool overflow;
3920 void *newmark;
3921 jsval *rvp;
3922 JSInlineFrame *newifp;
3923 JSInterpreterHook hook;
3925 /* Compute the total number of stack slots needed by fun. */
3926 nframeslots = JS_HOWMANY(sizeof(JSInlineFrame),
3927 sizeof(jsval));
3928 nvars = fun->u.i.nvars;
3929 script = fun->u.i.script;
3930 depth = (jsint) script->depth;
3931 atoms = script->atomMap.vector;
3932 nslots = nframeslots + nvars + 2 * depth;
3934 /* Allocate missing expected args adjacent to actuals. */
3935 missing = (fun->nargs > argc) ? fun->nargs - argc : 0;
3936 a = cx->stackPool.current;
3937 avail = a->avail;
3938 newmark = (void *) avail;
3939 if (missing) {
3940 newsp = sp + missing;
3941 overflow = (jsuword) newsp > a->limit;
3942 if (overflow)
3943 nslots += 2 + argc + missing;
3944 else if ((jsuword) newsp > avail)
3945 avail = a->avail = (jsuword) newsp;
3947 #ifdef __GNUC__
3948 else overflow = JS_FALSE; /* suppress bogus gcc warnings */
3949 #endif
3951 /* Allocate the inline frame with its vars and operands. */
3952 newsp = (jsval *) avail;
3953 nbytes = nslots * sizeof(jsval);
3954 avail += nbytes;
3955 if (avail <= a->limit) {
3956 a->avail = avail;
3957 } else {
3958 JS_ARENA_ALLOCATE_CAST(newsp, jsval *, &cx->stackPool,
3959 nbytes);
3960 if (!newsp) {
3961 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3962 JSMSG_STACK_OVERFLOW,
3963 (fp && fp->fun)
3964 ? JS_GetFunctionName(fp->fun)
3965 : "script");
3966 goto bad_inline_call;
3971 * Move args if missing overflow arena a, then push any
3972 * missing args.
3974 rvp = vp;
3975 if (missing) {
3976 if (overflow) {
3977 memcpy(newsp, vp, (2 + argc) * sizeof(jsval));
3978 vp = newsp;
3979 sp = vp + 2 + argc;
3980 newsp = sp + missing;
3982 do {
3983 PUSH(JSVAL_VOID);
3984 } while (--missing != 0);
3987 /* Claim space for the stack frame and initialize it. */
3988 newifp = (JSInlineFrame *) newsp;
3989 newsp += nframeslots;
3990 newifp->frame.callobj = NULL;
3991 newifp->frame.argsobj = NULL;
3992 newifp->frame.varobj = NULL;
3993 newifp->frame.script = script;
3994 newifp->frame.callee = obj;
3995 newifp->frame.fun = fun;
3996 newifp->frame.argc = argc;
3997 newifp->frame.argv = vp + 2;
3998 newifp->frame.rval = JSVAL_VOID;
3999 newifp->frame.nvars = nvars;
4000 newifp->frame.vars = newsp;
4001 newifp->frame.down = fp;
4002 newifp->frame.annotation = NULL;
4003 newifp->frame.scopeChain = parent = OBJ_GET_PARENT(cx, obj);
4004 newifp->frame.sharpDepth = 0;
4005 newifp->frame.sharpArray = NULL;
4006 newifp->frame.flags = 0;
4007 newifp->frame.dormantNext = NULL;
4008 newifp->frame.xmlNamespace = NULL;
4009 newifp->frame.blockChain = NULL;
4010 newifp->rvp = rvp;
4011 newifp->mark = newmark;
4013 /* Compute the 'this' parameter now that argv is set. */
4014 JS_ASSERT(!JSFUN_BOUND_METHOD_TEST(fun->flags));
4015 JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
4016 newifp->frame.thisp = (JSObject *)vp[1];
4017 #ifdef DUMP_CALL_TABLE
4018 LogCall(cx, *vp, argc, vp + 2);
4019 #endif
4021 /* Push void to initialize local variables. */
4022 sp = newsp;
4023 while (nvars--)
4024 PUSH(JSVAL_VOID);
4025 sp += depth;
4026 newifp->frame.spbase = sp;
4027 SAVE_SP(&newifp->frame);
4029 /* Call the debugger hook if present. */
4030 hook = cx->debugHooks->callHook;
4031 if (hook) {
4032 newifp->frame.pc = NULL;
4033 newifp->hookData = hook(cx, &newifp->frame, JS_TRUE, 0,
4034 cx->debugHooks->callHookData);
4035 LOAD_INTERRUPT_HANDLER(cx);
4036 } else {
4037 newifp->hookData = NULL;
4040 /* Scope with a call object parented by callee's parent. */
4041 if (JSFUN_HEAVYWEIGHT_TEST(fun->flags) &&
4042 !js_GetCallObject(cx, &newifp->frame, parent)) {
4043 goto bad_inline_call;
4046 /* Switch version if currentVersion wasn't overridden. */
4047 newifp->callerVersion = (JSVersion) cx->version;
4048 if (JS_LIKELY(cx->version == currentVersion)) {
4049 currentVersion = (JSVersion) script->version;
4050 if (currentVersion != cx->version)
4051 js_SetVersion(cx, currentVersion);
4054 /* Push the frame and set interpreter registers. */
4055 cx->fp = fp = &newifp->frame;
4056 pc = script->code;
4057 #if !JS_THREADED_INTERP
4058 endpc = pc + script->length;
4059 #endif
4060 inlineCallCount++;
4061 JS_RUNTIME_METER(rt, inlineCalls);
4063 /* Load first op and dispatch it (safe since JSOP_STOP). */
4064 op = (JSOp) *pc;
4065 DO_OP();
4067 bad_inline_call:
4068 RESTORE_SP(fp);
4069 JS_ASSERT(fp->pc == pc);
4070 script = fp->script;
4071 depth = (jsint) script->depth;
4072 atoms = script->atomMap.vector;
4073 js_FreeRawStack(cx, newmark);
4074 ok = JS_FALSE;
4075 goto out;
4078 if (fun->flags & JSFUN_FAST_NATIVE) {
4079 uintN nargs = JS_MAX(argc, fun->u.n.minargs);
4081 nargs += fun->u.n.extra;
4082 if (argc < nargs) {
4084 * If we can't fit missing args and local roots in
4085 * this frame's operand stack, take the slow path.
4087 nargs -= argc;
4088 if (sp + nargs > fp->spbase + depth)
4089 goto do_invoke;
4090 do {
4091 PUSH(JSVAL_VOID);
4092 } while (--nargs != 0);
4093 SAVE_SP(fp);
4096 JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]) ||
4097 PRIMITIVE_THIS_TEST(fun, vp[1]));
4099 START_FAST_CALL(fp);
4100 ok = ((JSFastNative) fun->u.n.native)(cx, argc, vp);
4101 END_FAST_CALL(fp);
4102 if (!ok)
4103 goto out;
4104 sp = vp + 1;
4105 vp[-depth] = (jsval)pc;
4106 goto end_call;
4110 do_invoke:
4111 ok = js_Invoke(cx, argc, 0);
4112 RESTORE_SP(fp);
4113 LOAD_INTERRUPT_HANDLER(cx);
4114 if (!ok)
4115 goto out;
4116 JS_RUNTIME_METER(rt, nonInlineCalls);
4118 end_call:
4119 #if JS_HAS_LVALUE_RETURN
4120 if (cx->rval2set) {
4122 * Use the stack depth we didn't claim in our budget, but that
4123 * we know is there on account of [fun, this] already having
4124 * been pushed, at a minimum (if no args). Those two slots
4125 * have been popped and [rval] has been pushed, which leaves
4126 * one more slot for rval2 before we might overflow.
4128 * NB: rval2 must be the property identifier, and rval the
4129 * object from which to get the property. The pair form an
4130 * ECMA "reference type", which can be used on the right- or
4131 * left-hand side of assignment ops. Note well: only native
4132 * methods can return reference types. See JSOP_SETCALL just
4133 * below for the left-hand-side case.
4135 PUSH_OPND(cx->rval2);
4136 ELEMENT_OP(-1, ok = OBJ_GET_PROPERTY(cx, obj, id, &rval));
4138 sp--;
4139 STORE_OPND(-1, rval);
4140 cx->rval2set = JS_FALSE;
4142 #endif /* JS_HAS_LVALUE_RETURN */
4143 END_CASE(JSOP_CALL)
4145 #if JS_HAS_LVALUE_RETURN
4146 BEGIN_CASE(JSOP_SETCALL)
4147 argc = GET_ARGC(pc);
4148 SAVE_SP_AND_PC(fp);
4149 ok = js_Invoke(cx, argc, 0);
4150 RESTORE_SP(fp);
4151 LOAD_INTERRUPT_HANDLER(cx);
4152 if (!ok)
4153 goto out;
4154 if (!cx->rval2set) {
4155 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4156 JSMSG_BAD_LEFTSIDE_OF_ASS);
4157 ok = JS_FALSE;
4158 goto out;
4160 PUSH_OPND(cx->rval2);
4161 cx->rval2set = JS_FALSE;
4162 END_CASE(JSOP_SETCALL)
4163 #endif
4165 BEGIN_CASE(JSOP_NAME)
4166 BEGIN_CASE(JSOP_CALLNAME)
4167 LOAD_ATOM(0);
4168 id = ATOM_TO_JSID(atom);
4170 SAVE_SP_AND_PC(fp);
4171 ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
4172 if (!ok)
4173 goto out;
4174 if (!prop) {
4175 /* Kludge to allow (typeof foo == "undefined") tests. */
4176 len = JSOP_NAME_LENGTH;
4177 endpc = script->code + script->length;
4178 for (pc2 = pc + len; pc2 < endpc; pc2++) {
4179 op2 = (JSOp)*pc2;
4180 if (op2 == JSOP_TYPEOF) {
4181 PUSH_OPND(JSVAL_VOID);
4182 DO_NEXT_OP(len);
4184 if (op2 != JSOP_GROUP)
4185 break;
4187 goto atom_not_defined;
4190 /* Take the slow path if prop was not found in a native object. */
4191 if (!OBJ_IS_NATIVE(obj) || !OBJ_IS_NATIVE(obj2)) {
4192 OBJ_DROP_PROPERTY(cx, obj2, prop);
4193 ok = OBJ_GET_PROPERTY(cx, obj, id, &rval);
4194 if (!ok)
4195 goto out;
4196 } else {
4197 sprop = (JSScopeProperty *)prop;
4198 NATIVE_GET(cx, obj, obj2, sprop, &rval);
4199 OBJ_DROP_PROPERTY(cx, obj2, prop);
4201 PUSH_OPND(rval);
4202 if (op == JSOP_CALLNAME) {
4203 PUSH_OPND(OBJECT_TO_JSVAL(obj));
4204 SAVE_SP(fp);
4205 ok = ComputeThis(cx, sp);
4206 if (!ok)
4207 goto out;
4209 END_CASE(JSOP_NAME)
4211 BEGIN_CASE(JSOP_UINT16)
4212 i = (jsint) GET_UINT16(pc);
4213 rval = INT_TO_JSVAL(i);
4214 PUSH_OPND(rval);
4215 END_CASE(JSOP_UINT16)
4217 BEGIN_CASE(JSOP_UINT24)
4218 i = (jsint) GET_UINT24(pc);
4219 rval = INT_TO_JSVAL(i);
4220 PUSH_OPND(rval);
4221 END_CASE(JSOP_UINT24)
4223 BEGIN_CASE(JSOP_INT8)
4224 i = GET_INT8(pc);
4225 rval = INT_TO_JSVAL(i);
4226 PUSH_OPND(rval);
4227 END_CASE(JSOP_INT8)
4229 BEGIN_CASE(JSOP_INT32)
4230 i = GET_INT32(pc);
4231 rval = INT_TO_JSVAL(i);
4232 PUSH_OPND(rval);
4233 END_CASE(JSOP_INT32)
4235 BEGIN_CASE(JSOP_INDEXBASE)
4237 * Here atoms can exceed script->atomMap.length as we use atoms
4238 * as a segment register for object literals as well.
4240 atoms += GET_INDEXBASE(pc);
4241 END_CASE(JSOP_INDEXBASE)
4243 BEGIN_CASE(JSOP_INDEXBASE1)
4244 BEGIN_CASE(JSOP_INDEXBASE2)
4245 BEGIN_CASE(JSOP_INDEXBASE3)
4246 atoms += (op - JSOP_INDEXBASE1 + 1) << 16;
4247 END_CASE(JSOP_INDEXBASE3)
4249 BEGIN_CASE(JSOP_RESETBASE0)
4250 BEGIN_CASE(JSOP_RESETBASE)
4251 atoms = script->atomMap.vector;
4252 END_CASE(JSOP_RESETBASE)
4254 BEGIN_CASE(JSOP_DOUBLE)
4255 BEGIN_CASE(JSOP_STRING)
4256 LOAD_ATOM(0);
4257 PUSH_OPND(ATOM_KEY(atom));
4258 END_CASE(JSOP_DOUBLE)
4260 BEGIN_CASE(JSOP_OBJECT)
4261 LOAD_OBJECT(0);
4262 PUSH_OPND(OBJECT_TO_JSVAL(obj));
4263 END_CASE(JSOP_OBJECT)
4265 BEGIN_CASE(JSOP_REGEXP)
4267 JSObject *funobj;
4270 * Push a regexp object for the atom mapped by the bytecode at pc,
4271 * cloning the literal's regexp object if necessary, to simulate in
4272 * the pre-compile/execute-later case what ECMA specifies for the
4273 * compile-and-go case: that scanning each regexp literal creates
4274 * a single corresponding RegExp object.
4276 * To support pre-compilation transparently, we must handle the
4277 * case where a regexp object literal is used in a different global
4278 * at execution time from the global with which it was scanned at
4279 * compile time. We do this by re-wrapping the JSRegExp private
4280 * data struct with a cloned object having the right prototype and
4281 * parent, and having its own lastIndex property value storage.
4283 * Unlike JSOP_DEFFUN and other prolog bytecodes that may clone
4284 * literal objects, we don't want to pay a script prolog execution
4285 * price for all regexp literals in a script (many may not be used
4286 * by a particular execution of that script, depending on control
4287 * flow), so we initialize lazily here.
4289 * XXX This code is specific to regular expression objects. If we
4290 * need a similar op for other kinds of object literals, we should
4291 * push cloning down under JSObjectOps and reuse code here.
4293 index = GET_FULL_INDEX(0);
4294 JS_ASSERT(index < JS_SCRIPT_REGEXPS(script)->length);
4296 slot = index;
4297 if (fp->fun) {
4299 * We're in function code, not global or eval code (in eval
4300 * code, JSOP_REGEXP is never emitted). The cloned funobj
4301 * contains script->regexps->nregexps reserved slot for the
4302 * cloned regexps, see fun_reserveSlots, jsfun.c.
4304 funobj = fp->callee;
4305 slot += JSCLASS_RESERVED_SLOTS(&js_FunctionClass);
4306 if (!JS_GetReservedSlot(cx, funobj, slot, &rval))
4307 return JS_FALSE;
4308 if (JSVAL_IS_VOID(rval))
4309 rval = JSVAL_NULL;
4310 } else {
4312 * We're in global code. The code generator already arranged
4313 * via script->nregexps to reserve a global variable slot
4314 * at cloneIndex. All global variable slots are initialized
4315 * to null, not void, for faster testing in JSOP_*GVAR cases.
4317 slot += script->ngvars;
4318 rval = fp->vars[slot];
4319 #ifdef __GNUC__
4320 funobj = NULL; /* suppress bogus gcc warnings */
4321 #endif
4324 if (JSVAL_IS_NULL(rval)) {
4325 /* Compute the current global object in obj2. */
4326 obj2 = fp->scopeChain;
4327 while ((parent = OBJ_GET_PARENT(cx, obj2)) != NULL)
4328 obj2 = parent;
4331 * We must home sp here, because either js_CloneRegExpObject
4332 * or JS_SetReservedSlot could nest a last-ditch GC. We home
4333 * pc as well, in case js_CloneRegExpObject has to lookup the
4334 * "RegExp" class in the global object, which could entail a
4335 * JSNewResolveOp call.
4337 SAVE_SP_AND_PC(fp);
4340 * If obj's parent is not obj2, we must clone obj so that it
4341 * has the right parent, and therefore, the right prototype.
4343 * Yes, this means we assume that the correct RegExp.prototype
4344 * to which regexp instances (including literals) delegate can
4345 * be distinguished solely by the instance's parent, which was
4346 * set to the parent of the RegExp constructor function object
4347 * when the instance was created. In other words,
4349 * (/x/.__parent__ == RegExp.__parent__) implies
4350 * (/x/.__proto__ == RegExp.prototype)
4352 * (unless you assign a different object to RegExp.prototype
4353 * at runtime, in which case, ECMA doesn't specify operation,
4354 * and you get what you deserve).
4356 * This same coupling between instance parent and constructor
4357 * parent turns up everywhere (see jsobj.c's FindClassObject,
4358 * js_ConstructObject, and js_NewObject). It's fundamental to
4359 * the design of the language when you consider multiple global
4360 * objects and separate compilation and execution, even though
4361 * it is not specified fully in ECMA.
4363 JS_GET_SCRIPT_REGEXP(script, index, obj);
4364 if (OBJ_GET_PARENT(cx, obj) != obj2) {
4365 obj = js_CloneRegExpObject(cx, obj, obj2);
4366 if (!obj) {
4367 ok = JS_FALSE;
4368 goto out;
4371 rval = OBJECT_TO_JSVAL(obj);
4373 /* Store the regexp object value in its cloneIndex slot. */
4374 if (fp->fun) {
4375 if (!JS_SetReservedSlot(cx, funobj, slot, rval))
4376 return JS_FALSE;
4377 } else {
4378 fp->vars[slot] = rval;
4382 PUSH_OPND(rval);
4384 END_CASE(JSOP_REGEXP)
4386 BEGIN_CASE(JSOP_ZERO)
4387 PUSH_OPND(JSVAL_ZERO);
4388 END_CASE(JSOP_ZERO)
4390 BEGIN_CASE(JSOP_ONE)
4391 PUSH_OPND(JSVAL_ONE);
4392 END_CASE(JSOP_ONE)
4394 BEGIN_CASE(JSOP_NULL)
4395 PUSH_OPND(JSVAL_NULL);
4396 END_CASE(JSOP_NULL)
4398 BEGIN_CASE(JSOP_THIS)
4399 obj = fp->thisp;
4400 clasp = OBJ_GET_CLASS(cx, obj);
4401 if (clasp->flags & JSCLASS_IS_EXTENDED) {
4402 JSExtendedClass *xclasp;
4404 xclasp = (JSExtendedClass *) clasp;
4405 if (xclasp->outerObject) {
4406 obj = xclasp->outerObject(cx, obj);
4407 if (!obj) {
4408 ok = JS_FALSE;
4409 goto out;
4414 PUSH_OPND(OBJECT_TO_JSVAL(obj));
4415 END_CASE(JSOP_THIS)
4417 BEGIN_CASE(JSOP_FALSE)
4418 PUSH_OPND(JSVAL_FALSE);
4419 END_CASE(JSOP_FALSE)
4421 BEGIN_CASE(JSOP_TRUE)
4422 PUSH_OPND(JSVAL_TRUE);
4423 END_CASE(JSOP_TRUE)
4425 BEGIN_CASE(JSOP_TABLESWITCH)
4426 pc2 = pc;
4427 len = GET_JUMP_OFFSET(pc2);
4430 * ECMAv2+ forbids conversion of discriminant, so we will skip to
4431 * the default case if the discriminant isn't already an int jsval.
4432 * (This opcode is emitted only for dense jsint-domain switches.)
4434 rval = POP_OPND();
4435 if (!JSVAL_IS_INT(rval))
4436 DO_NEXT_OP(len);
4437 i = JSVAL_TO_INT(rval);
4439 pc2 += JUMP_OFFSET_LEN;
4440 low = GET_JUMP_OFFSET(pc2);
4441 pc2 += JUMP_OFFSET_LEN;
4442 high = GET_JUMP_OFFSET(pc2);
4444 i -= low;
4445 if ((jsuint)i < (jsuint)(high - low + 1)) {
4446 pc2 += JUMP_OFFSET_LEN + JUMP_OFFSET_LEN * i;
4447 off = (jsint) GET_JUMP_OFFSET(pc2);
4448 if (off)
4449 len = off;
4451 END_VARLEN_CASE
4453 BEGIN_CASE(JSOP_TABLESWITCHX)
4454 pc2 = pc;
4455 len = GET_JUMPX_OFFSET(pc2);
4458 * ECMAv2+ forbids conversion of discriminant, so we will skip to
4459 * the default case if the discriminant isn't already an int jsval.
4460 * (This opcode is emitted only for dense jsint-domain switches.)
4462 rval = POP_OPND();
4463 if (!JSVAL_IS_INT(rval))
4464 DO_NEXT_OP(len);
4465 i = JSVAL_TO_INT(rval);
4467 pc2 += JUMPX_OFFSET_LEN;
4468 low = GET_JUMP_OFFSET(pc2);
4469 pc2 += JUMP_OFFSET_LEN;
4470 high = GET_JUMP_OFFSET(pc2);
4472 i -= low;
4473 if ((jsuint)i < (jsuint)(high - low + 1)) {
4474 pc2 += JUMP_OFFSET_LEN + JUMPX_OFFSET_LEN * i;
4475 off = (jsint) GET_JUMPX_OFFSET(pc2);
4476 if (off)
4477 len = off;
4479 END_VARLEN_CASE
4481 BEGIN_CASE(JSOP_LOOKUPSWITCHX)
4482 off = JUMPX_OFFSET_LEN;
4483 goto do_lookup_switch;
4485 BEGIN_CASE(JSOP_LOOKUPSWITCH)
4486 off = JUMP_OFFSET_LEN;
4488 do_lookup_switch:
4490 * JSOP_LOOKUPSWITCH and JSOP_LOOKUPSWITCHX are never used if
4491 * any atom index in it would exceed 64K limit.
4493 JS_ASSERT(atoms == script->atomMap.vector);
4494 pc2 = pc;
4495 lval = POP_OPND();
4497 if (!JSVAL_IS_NUMBER(lval) &&
4498 !JSVAL_IS_STRING(lval) &&
4499 !JSVAL_IS_BOOLEAN(lval)) {
4500 goto end_lookup_switch;
4503 pc2 += off;
4504 npairs = (jsint) GET_UINT16(pc2);
4505 pc2 += UINT16_LEN;
4506 JS_ASSERT(npairs); /* empty switch uses JSOP_TABLESWITCH */
4508 #define SEARCH_PAIRS(MATCH_CODE) \
4509 for (;;) { \
4510 JS_ASSERT(GET_INDEX(pc2) < script->atomMap.length); \
4511 atom = atoms[GET_INDEX(pc2)]; \
4512 rval = ATOM_KEY(atom); \
4513 MATCH_CODE \
4514 pc2 += INDEX_LEN; \
4515 if (match) \
4516 break; \
4517 pc2 += off; \
4518 if (--npairs == 0) { \
4519 pc2 = pc; \
4520 break; \
4523 if (JSVAL_IS_STRING(lval)) {
4524 str = JSVAL_TO_STRING(lval);
4525 SEARCH_PAIRS(
4526 match = (JSVAL_IS_STRING(rval) &&
4527 ((str2 = JSVAL_TO_STRING(rval)) == str ||
4528 js_EqualStrings(str2, str)));
4530 } else if (JSVAL_IS_DOUBLE(lval)) {
4531 d = *JSVAL_TO_DOUBLE(lval);
4532 SEARCH_PAIRS(
4533 match = (JSVAL_IS_DOUBLE(rval) &&
4534 *JSVAL_TO_DOUBLE(rval) == d);
4536 } else {
4537 SEARCH_PAIRS(
4538 match = (lval == rval);
4541 #undef SEARCH_PAIRS
4543 end_lookup_switch:
4544 len = (op == JSOP_LOOKUPSWITCH)
4545 ? GET_JUMP_OFFSET(pc2)
4546 : GET_JUMPX_OFFSET(pc2);
4547 END_VARLEN_CASE
4549 EMPTY_CASE(JSOP_CONDSWITCH)
4551 #if JS_HAS_EXPORT_IMPORT
4552 BEGIN_CASE(JSOP_EXPORTALL)
4553 obj = fp->varobj;
4554 SAVE_SP_AND_PC(fp);
4555 ida = JS_Enumerate(cx, obj);
4556 if (!ida) {
4557 ok = JS_FALSE;
4558 } else {
4559 for (i = 0, j = ida->length; i < j; i++) {
4560 id = ida->vector[i];
4561 ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
4562 if (!ok)
4563 break;
4564 if (!prop)
4565 continue;
4566 ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs);
4567 if (ok) {
4568 attrs |= JSPROP_EXPORTED;
4569 ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs);
4571 OBJ_DROP_PROPERTY(cx, obj2, prop);
4572 if (!ok)
4573 break;
4575 JS_DestroyIdArray(cx, ida);
4577 END_CASE(JSOP_EXPORTALL)
4579 BEGIN_CASE(JSOP_EXPORTNAME)
4580 LOAD_ATOM(0);
4581 id = ATOM_TO_JSID(atom);
4582 obj = fp->varobj;
4583 SAVE_SP_AND_PC(fp);
4584 ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
4585 if (!ok)
4586 goto out;
4587 if (!prop) {
4588 ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL,
4589 JSPROP_EXPORTED, NULL);
4590 } else {
4591 ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs);
4592 if (ok) {
4593 attrs |= JSPROP_EXPORTED;
4594 ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs);
4596 OBJ_DROP_PROPERTY(cx, obj2, prop);
4598 if (!ok)
4599 goto out;
4600 END_CASE(JSOP_EXPORTNAME)
4602 BEGIN_CASE(JSOP_IMPORTALL)
4603 id = (jsid) JSVAL_VOID;
4604 PROPERTY_OP(-1, ok = ImportProperty(cx, obj, id));
4605 sp--;
4606 END_CASE(JSOP_IMPORTALL)
4608 BEGIN_CASE(JSOP_IMPORTPROP)
4609 /* Get an immediate atom naming the property. */
4610 LOAD_ATOM(0);
4611 id = ATOM_TO_JSID(atom);
4612 PROPERTY_OP(-1, ok = ImportProperty(cx, obj, id));
4613 sp--;
4614 END_CASE(JSOP_IMPORTPROP)
4616 BEGIN_CASE(JSOP_IMPORTELEM)
4617 ELEMENT_OP(-1, ok = ImportProperty(cx, obj, id));
4618 sp -= 2;
4619 END_CASE(JSOP_IMPORTELEM)
4620 #endif /* JS_HAS_EXPORT_IMPORT */
4622 BEGIN_CASE(JSOP_TRAP)
4623 SAVE_SP_AND_PC(fp);
4624 switch (JS_HandleTrap(cx, script, pc, &rval)) {
4625 case JSTRAP_ERROR:
4626 ok = JS_FALSE;
4627 goto out;
4628 case JSTRAP_CONTINUE:
4629 JS_ASSERT(JSVAL_IS_INT(rval));
4630 op = (JSOp) JSVAL_TO_INT(rval);
4631 JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT);
4632 LOAD_INTERRUPT_HANDLER(cx);
4633 DO_OP();
4634 case JSTRAP_RETURN:
4635 fp->rval = rval;
4636 goto out;
4637 case JSTRAP_THROW:
4638 cx->throwing = JS_TRUE;
4639 cx->exception = rval;
4640 ok = JS_FALSE;
4641 goto out;
4642 default:;
4644 LOAD_INTERRUPT_HANDLER(cx);
4645 END_CASE(JSOP_TRAP)
4647 BEGIN_CASE(JSOP_ARGUMENTS)
4648 SAVE_SP_AND_PC(fp);
4649 ok = js_GetArgsValue(cx, fp, &rval);
4650 if (!ok)
4651 goto out;
4652 PUSH_OPND(rval);
4653 END_CASE(JSOP_ARGUMENTS)
4655 BEGIN_CASE(JSOP_ARGSUB)
4656 id = INT_TO_JSID(GET_ARGNO(pc));
4657 SAVE_SP_AND_PC(fp);
4658 ok = js_GetArgsProperty(cx, fp, id, &rval);
4659 if (!ok)
4660 goto out;
4661 PUSH_OPND(rval);
4662 END_CASE(JSOP_ARGSUB)
4664 BEGIN_CASE(JSOP_ARGCNT)
4665 id = ATOM_TO_JSID(rt->atomState.lengthAtom);
4666 SAVE_SP_AND_PC(fp);
4667 ok = js_GetArgsProperty(cx, fp, id, &rval);
4668 if (!ok)
4669 goto out;
4670 PUSH_OPND(rval);
4671 END_CASE(JSOP_ARGCNT)
4673 #define PUSH_GLOBAL_THIS(cx,sp) \
4674 JS_BEGIN_MACRO \
4675 PUSH_OPND(JSVAL_NULL); \
4676 SAVE_SP_AND_PC(fp); \
4677 ok = ComputeGlobalThis(cx, sp); \
4678 if (!ok) \
4679 goto out; \
4680 JS_END_MACRO
4682 BEGIN_CASE(JSOP_GLOBALTHIS)
4683 PUSH_GLOBAL_THIS(cx, sp);
4684 END_CASE(JSOP_GLOBALTHIS)
4686 BEGIN_CASE(JSOP_GETARG)
4687 BEGIN_CASE(JSOP_CALLARG)
4688 slot = GET_ARGNO(pc);
4689 JS_ASSERT(slot < fp->fun->nargs);
4690 METER_SLOT_OP(op, slot);
4691 PUSH_OPND(fp->argv[slot]);
4692 if (op == JSOP_CALLARG)
4693 PUSH_GLOBAL_THIS(cx, sp);
4694 END_CASE(JSOP_GETARG)
4696 BEGIN_CASE(JSOP_SETARG)
4697 slot = GET_ARGNO(pc);
4698 JS_ASSERT(slot < fp->fun->nargs);
4699 METER_SLOT_OP(op, slot);
4700 vp = &fp->argv[slot];
4701 GC_POKE(cx, *vp);
4702 *vp = FETCH_OPND(-1);
4703 END_CASE(JSOP_SETARG)
4705 BEGIN_CASE(JSOP_GETVAR)
4706 BEGIN_CASE(JSOP_CALLVAR)
4707 slot = GET_VARNO(pc);
4708 JS_ASSERT(slot < fp->fun->u.i.nvars);
4709 METER_SLOT_OP(op, slot);
4710 PUSH_OPND(fp->vars[slot]);
4711 if (op == JSOP_CALLVAR)
4712 PUSH_GLOBAL_THIS(cx, sp);
4713 END_CASE(JSOP_GETVAR)
4715 BEGIN_CASE(JSOP_SETVAR)
4716 slot = GET_VARNO(pc);
4717 JS_ASSERT(slot < fp->fun->u.i.nvars);
4718 METER_SLOT_OP(op, slot);
4719 vp = &fp->vars[slot];
4720 GC_POKE(cx, *vp);
4721 *vp = FETCH_OPND(-1);
4722 END_CASE(JSOP_SETVAR)
4724 BEGIN_CASE(JSOP_GETGVAR)
4725 BEGIN_CASE(JSOP_CALLGVAR)
4726 slot = GET_VARNO(pc);
4727 JS_ASSERT(slot < fp->nvars);
4728 METER_SLOT_OP(op, slot);
4729 lval = fp->vars[slot];
4730 if (JSVAL_IS_NULL(lval)) {
4731 op = (op == JSOP_GETGVAR) ? JSOP_NAME : JSOP_CALLNAME;
4732 DO_OP();
4734 slot = JSVAL_TO_INT(lval);
4735 obj = fp->varobj;
4736 rval = OBJ_GET_SLOT(cx, obj, slot);
4737 PUSH_OPND(rval);
4738 if (op == JSOP_CALLGVAR)
4739 PUSH_OPND(OBJECT_TO_JSVAL(obj));
4740 END_CASE(JSOP_GETGVAR)
4742 BEGIN_CASE(JSOP_SETGVAR)
4743 slot = GET_VARNO(pc);
4744 JS_ASSERT(slot < fp->nvars);
4745 METER_SLOT_OP(op, slot);
4746 rval = FETCH_OPND(-1);
4747 lval = fp->vars[slot];
4748 obj = fp->varobj;
4749 if (JSVAL_IS_NULL(lval)) {
4751 * Inline-clone and specialize JSOP_SETNAME code here because
4752 * JSOP_SETGVAR has arity 1: [rval], not arity 2: [obj, rval]
4753 * as JSOP_SETNAME does, where [obj] is due to JSOP_BINDNAME.
4755 LOAD_ATOM(0);
4756 id = ATOM_TO_JSID(atom);
4757 SAVE_SP_AND_PC(fp);
4758 ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
4759 if (!ok)
4760 goto out;
4761 STORE_OPND(-1, rval);
4762 } else {
4763 slot = JSVAL_TO_INT(lval);
4764 GC_POKE(cx, STOBJ_GET_SLOT(obj, slot));
4765 OBJ_SET_SLOT(cx, obj, slot, rval);
4767 END_CASE(JSOP_SETGVAR)
4769 BEGIN_CASE(JSOP_DEFCONST)
4770 BEGIN_CASE(JSOP_DEFVAR)
4771 index = GET_INDEX(pc);
4772 atom = atoms[index];
4775 * index is relative to atoms at this point but for global var
4776 * code below we need the absolute value.
4778 index += atoms - script->atomMap.vector;
4779 obj = fp->varobj;
4780 attrs = JSPROP_ENUMERATE;
4781 if (!(fp->flags & JSFRAME_EVAL))
4782 attrs |= JSPROP_PERMANENT;
4783 if (op == JSOP_DEFCONST)
4784 attrs |= JSPROP_READONLY;
4786 /* Lookup id in order to check for redeclaration problems. */
4787 id = ATOM_TO_JSID(atom);
4788 SAVE_SP_AND_PC(fp);
4789 ok = js_CheckRedeclaration(cx, obj, id, attrs, &obj2, &prop);
4790 if (!ok)
4791 goto out;
4793 /* Bind a variable only if it's not yet defined. */
4794 if (!prop) {
4795 ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL,
4796 attrs, &prop);
4797 if (!ok)
4798 goto out;
4799 JS_ASSERT(prop);
4800 obj2 = obj;
4804 * Try to optimize a property we either just created, or found
4805 * directly in the global object, that is permanent, has a slot,
4806 * and has stub getter and setter, into a "fast global" accessed
4807 * by the JSOP_*GVAR opcodes.
4809 if (index < script->ngvars &&
4810 (attrs & JSPROP_PERMANENT) &&
4811 obj2 == obj &&
4812 OBJ_IS_NATIVE(obj)) {
4813 sprop = (JSScopeProperty *) prop;
4814 if (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)) &&
4815 SPROP_HAS_STUB_GETTER(sprop) &&
4816 SPROP_HAS_STUB_SETTER(sprop)) {
4818 * Fast globals use fp->vars to map the global name's
4819 * atom index to the permanent fp->varobj slot number,
4820 * tagged as a jsval. The atom index for the global's
4821 * name literal is identical to its fp->vars index.
4823 fp->vars[index] = INT_TO_JSVAL(sprop->slot);
4827 OBJ_DROP_PROPERTY(cx, obj2, prop);
4828 END_CASE(JSOP_DEFVAR)
4830 BEGIN_CASE(JSOP_DEFFUN)
4831 LOAD_FUNCTION(0);
4832 fun = (JSFunction *) OBJ_GET_PRIVATE(cx, obj);
4833 id = ATOM_TO_JSID(fun->atom);
4836 * We must be at top-level (either outermost block that forms a
4837 * function's body, or a global) scope, not inside an expression
4838 * (JSOP_{ANON,NAMED}FUNOBJ) or compound statement (JSOP_CLOSURE)
4839 * in the same compilation unit (ECMA Program).
4841 * However, we could be in a Program being eval'd from inside a
4842 * with statement, so we need to distinguish scope chain head from
4843 * variables object. Hence the obj2 vs. parent distinction below.
4844 * First we make sure the function object we're defining has the
4845 * right scope chain. Then we define its name in fp->varobj.
4847 * If static link is not current scope, clone fun's object to link
4848 * to the current scope via parent. This clause exists to enable
4849 * sharing of compiled functions among multiple equivalent scopes,
4850 * splitting the cost of compilation evenly among the scopes and
4851 * amortizing it over a number of executions. Examples include XUL
4852 * scripts and event handlers shared among Mozilla chrome windows,
4853 * and server-side JS user-defined functions shared among requests.
4855 * NB: The Script object exposes compile and exec in the language,
4856 * such that this clause introduces an incompatible change from old
4857 * JS versions that supported Script. Such a JS version supported
4858 * executing a script that defined and called functions scoped by
4859 * the compile-time static link, not by the exec-time scope chain.
4861 * We sacrifice compatibility, breaking such scripts, in order to
4862 * promote compile-cost sharing and amortizing, and because Script
4863 * is not and will not be standardized.
4865 JS_ASSERT(!fp->blockChain);
4866 obj2 = fp->scopeChain;
4867 if (OBJ_GET_PARENT(cx, obj) != obj2) {
4868 obj = js_CloneFunctionObject(cx, obj, obj2);
4869 if (!obj) {
4870 ok = JS_FALSE;
4871 goto out;
4876 * Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY. All
4877 * paths from here must flow through the "Restore fp->scopeChain"
4878 * code below the OBJ_DEFINE_PROPERTY call.
4880 fp->scopeChain = obj;
4881 rval = OBJECT_TO_JSVAL(obj);
4884 * ECMA requires functions defined when entering Global code to be
4885 * permanent, and functions defined when entering Eval code to be
4886 * impermanent.
4888 attrs = JSPROP_ENUMERATE;
4889 if (!(fp->flags & JSFRAME_EVAL))
4890 attrs |= JSPROP_PERMANENT;
4893 * Load function flags that are also property attributes. Getters
4894 * and setters do not need a slot, their value is stored elsewhere
4895 * in the property itself, not in obj slots.
4897 flags = JSFUN_GSFLAG2ATTR(fun->flags);
4898 if (flags) {
4899 attrs |= flags | JSPROP_SHARED;
4900 rval = JSVAL_VOID;
4904 * Check for a const property of the same name -- or any kind
4905 * of property if executing with the strict option. We check
4906 * here at runtime as well as at compile-time, to handle eval
4907 * as well as multiple HTML script tags.
4909 parent = fp->varobj;
4910 SAVE_SP_AND_PC(fp);
4911 ok = js_CheckRedeclaration(cx, parent, id, attrs, NULL, NULL);
4912 if (ok) {
4913 ok = OBJ_DEFINE_PROPERTY(cx, parent, id, rval,
4914 (flags & JSPROP_GETTER)
4915 ? JS_EXTENSION (JSPropertyOp) obj
4916 : NULL,
4917 (flags & JSPROP_SETTER)
4918 ? JS_EXTENSION (JSPropertyOp) obj
4919 : NULL,
4920 attrs,
4921 &prop);
4924 /* Restore fp->scopeChain now that obj is defined in fp->varobj. */
4925 fp->scopeChain = obj2;
4926 if (!ok)
4927 goto out;
4928 OBJ_DROP_PROPERTY(cx, parent, prop);
4929 END_CASE(JSOP_DEFFUN)
4931 BEGIN_CASE(JSOP_DEFLOCALFUN)
4932 LOAD_FUNCTION(VARNO_LEN);
4935 * Define a local function (i.e., one nested at the top level of
4936 * another function), parented by the current scope chain, and
4937 * stored in a local variable slot that the compiler allocated.
4938 * This is an optimization over JSOP_DEFFUN that avoids requiring
4939 * a call object for the outer function's activation.
4941 slot = GET_VARNO(pc);
4943 JS_ASSERT(!fp->blockChain);
4944 if (!(fp->flags & JSFRAME_POP_BLOCKS)) {
4946 * If the compiler-created function object (obj) is scoped by a
4947 * let-induced body block, temporarily update fp->blockChain so
4948 * that js_GetScopeChain will clone the block into the runtime
4949 * scope needed to parent the function object's clone.
4951 parent = OBJ_GET_PARENT(cx, obj);
4952 if (OBJ_GET_CLASS(cx, parent) == &js_BlockClass)
4953 fp->blockChain = parent;
4954 parent = js_GetScopeChain(cx, fp);
4955 } else {
4957 * We have already emulated JSOP_ENTERBLOCK for the enclosing
4958 * body block, for a prior JSOP_DEFLOCALFUN in the prolog, so
4959 * we just load fp->scopeChain into parent.
4961 * In typical execution scenarios, the prolog bytecodes that
4962 * include this JSOP_DEFLOCALFUN run, then come main bytecodes
4963 * including JSOP_ENTERBLOCK for the outermost (body) block.
4964 * JSOP_ENTERBLOCK will detect that it need not do anything if
4965 * the body block was entered above due to a local function.
4966 * Finally the matching JSOP_LEAVEBLOCK runs.
4968 * If the matching JSOP_LEAVEBLOCK for the body block does not
4969 * run for some reason, the body block will be properly "put"
4970 * (via js_PutBlockObject) by the PutBlockObjects call at the
4971 * bottom of js_Interpret.
4973 parent = fp->scopeChain;
4974 JS_ASSERT(OBJ_GET_CLASS(cx, parent) == &js_BlockClass);
4975 JS_ASSERT(OBJ_GET_PROTO(cx, parent) == OBJ_GET_PARENT(cx, obj));
4976 JS_ASSERT(OBJ_GET_CLASS(cx, OBJ_GET_PARENT(cx, parent))
4977 == &js_CallClass);
4980 /* If re-parenting, store a clone of the function object. */
4981 if (OBJ_GET_PARENT(cx, obj) != parent) {
4982 SAVE_SP_AND_PC(fp);
4983 obj = js_CloneFunctionObject(cx, obj, parent);
4984 if (!obj) {
4985 ok = JS_FALSE;
4986 goto out;
4990 fp->vars[slot] = OBJECT_TO_JSVAL(obj);
4991 END_CASE(JSOP_DEFLOCALFUN)
4993 BEGIN_CASE(JSOP_ANONFUNOBJ)
4994 /* Load the specified function object literal. */
4995 LOAD_FUNCTION(0);
4997 /* If re-parenting, push a clone of the function object. */
4998 SAVE_SP_AND_PC(fp);
4999 parent = js_GetScopeChain(cx, fp);
5000 if (!parent) {
5001 ok = JS_FALSE;
5002 goto out;
5004 if (OBJ_GET_PARENT(cx, obj) != parent) {
5005 obj = js_CloneFunctionObject(cx, obj, parent);
5006 if (!obj) {
5007 ok = JS_FALSE;
5008 goto out;
5011 PUSH_OPND(OBJECT_TO_JSVAL(obj));
5012 END_CASE(JSOP_ANONFUNOBJ)
5014 BEGIN_CASE(JSOP_NAMEDFUNOBJ)
5015 /* ECMA ed. 3 FunctionExpression: function Identifier [etc.]. */
5016 LOAD_FUNCTION(0);
5017 rval = OBJECT_TO_JSVAL(obj);
5020 * 1. Create a new object as if by the expression new Object().
5021 * 2. Add Result(1) to the front of the scope chain.
5023 * Step 2 is achieved by making the new object's parent be the
5024 * current scope chain, and then making the new object the parent
5025 * of the Function object clone.
5027 SAVE_SP_AND_PC(fp);
5028 obj2 = js_GetScopeChain(cx, fp);
5029 if (!obj2) {
5030 ok = JS_FALSE;
5031 goto out;
5033 parent = js_NewObject(cx, &js_ObjectClass, NULL, obj2);
5034 if (!parent) {
5035 ok = JS_FALSE;
5036 goto out;
5040 * 3. Create a new Function object as specified in section 13.2
5041 * with [parameters and body specified by the function expression
5042 * that was parsed by the compiler into a Function object, and
5043 * saved in the script's atom map].
5045 * Protect parent from GC after js_CloneFunctionObject calls into
5046 * js_NewObject, which displaces the newborn object root in cx by
5047 * allocating the clone, then runs a last-ditch GC while trying
5048 * to allocate the clone's slots vector. Another, multi-threaded
5049 * path: js_CloneFunctionObject => js_NewObject => OBJ_GET_CLASS
5050 * which may suspend the current request in ClaimScope, with the
5051 * newborn displaced as in the first scenario.
5053 fp->scopeChain = parent;
5054 obj = js_CloneFunctionObject(cx, JSVAL_TO_OBJECT(rval), parent);
5055 if (!obj) {
5056 ok = JS_FALSE;
5057 goto out;
5061 * Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY. All
5062 * paths from here must flow through the "Restore fp->scopeChain"
5063 * code below the OBJ_DEFINE_PROPERTY call.
5065 fp->scopeChain = obj;
5066 rval = OBJECT_TO_JSVAL(obj);
5069 * 4. Create a property in the object Result(1). The property's
5070 * name is [fun->atom, the identifier parsed by the compiler],
5071 * value is Result(3), and attributes are { DontDelete, ReadOnly }.
5073 fun = (JSFunction *) OBJ_GET_PRIVATE(cx, obj);
5074 attrs = JSFUN_GSFLAG2ATTR(fun->flags);
5075 if (attrs) {
5076 attrs |= JSPROP_SHARED;
5077 rval = JSVAL_VOID;
5079 ok = OBJ_DEFINE_PROPERTY(cx, parent, ATOM_TO_JSID(fun->atom), rval,
5080 (attrs & JSPROP_GETTER)
5081 ? JS_EXTENSION (JSPropertyOp) obj
5082 : NULL,
5083 (attrs & JSPROP_SETTER)
5084 ? JS_EXTENSION (JSPropertyOp) obj
5085 : NULL,
5086 attrs |
5087 JSPROP_ENUMERATE | JSPROP_PERMANENT |
5088 JSPROP_READONLY,
5089 NULL);
5091 /* Restore fp->scopeChain now that obj is defined in parent. */
5092 fp->scopeChain = obj2;
5093 if (!ok) {
5094 cx->weakRoots.newborn[GCX_OBJECT] = NULL;
5095 goto out;
5099 * 5. Remove Result(1) from the front of the scope chain [no-op].
5100 * 6. Return Result(3).
5102 PUSH_OPND(OBJECT_TO_JSVAL(obj));
5103 END_CASE(JSOP_NAMEDFUNOBJ)
5105 BEGIN_CASE(JSOP_CLOSURE)
5107 * ECMA ed. 3 extension: a named function expression in a compound
5108 * statement (not at the top statement level of global code, or at
5109 * the top level of a function body).
5111 LOAD_FUNCTION(0);
5114 * Clone the function object with the current scope chain as the
5115 * clone's parent. The original function object is the prototype
5116 * of the clone. Do this only if re-parenting; the compiler may
5117 * have seen the right parent already and created a sufficiently
5118 * well-scoped function object.
5120 SAVE_SP_AND_PC(fp);
5121 obj2 = js_GetScopeChain(cx, fp);
5122 if (!obj2) {
5123 ok = JS_FALSE;
5124 goto out;
5126 if (OBJ_GET_PARENT(cx, obj) != obj2) {
5127 obj = js_CloneFunctionObject(cx, obj, obj2);
5128 if (!obj) {
5129 ok = JS_FALSE;
5130 goto out;
5135 * Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY. All
5136 * paths from here must flow through the "Restore fp->scopeChain"
5137 * code below the OBJ_DEFINE_PROPERTY call.
5139 fp->scopeChain = obj;
5140 rval = OBJECT_TO_JSVAL(obj);
5143 * Make a property in fp->varobj with id fun->atom and value obj,
5144 * unless fun is a getter or setter (in which case, obj is cast to
5145 * a JSPropertyOp and passed accordingly).
5147 fun = (JSFunction *) OBJ_GET_PRIVATE(cx, obj);
5148 attrs = JSFUN_GSFLAG2ATTR(fun->flags);
5149 if (attrs) {
5150 attrs |= JSPROP_SHARED;
5151 rval = JSVAL_VOID;
5153 parent = fp->varobj;
5154 ok = OBJ_DEFINE_PROPERTY(cx, parent, ATOM_TO_JSID(fun->atom), rval,
5155 (attrs & JSPROP_GETTER)
5156 ? JS_EXTENSION (JSPropertyOp) obj
5157 : NULL,
5158 (attrs & JSPROP_SETTER)
5159 ? JS_EXTENSION (JSPropertyOp) obj
5160 : NULL,
5161 attrs | JSPROP_ENUMERATE
5162 | JSPROP_PERMANENT,
5163 &prop);
5165 /* Restore fp->scopeChain now that obj is defined in fp->varobj. */
5166 fp->scopeChain = obj2;
5167 if (!ok) {
5168 cx->weakRoots.newborn[GCX_OBJECT] = NULL;
5169 goto out;
5171 OBJ_DROP_PROPERTY(cx, parent, prop);
5172 END_CASE(JSOP_CLOSURE)
5174 #if JS_HAS_GETTER_SETTER
5175 BEGIN_CASE(JSOP_GETTER)
5176 BEGIN_CASE(JSOP_SETTER)
5177 do_getter_setter:
5178 op2 = (JSOp) *++pc;
5179 switch (op2) {
5180 case JSOP_INDEXBASE:
5181 atoms += GET_INDEXBASE(pc);
5182 pc += JSOP_INDEXBASE_LENGTH - 1;
5183 goto do_getter_setter;
5184 case JSOP_INDEXBASE1:
5185 case JSOP_INDEXBASE2:
5186 case JSOP_INDEXBASE3:
5187 atoms += (op2 - JSOP_INDEXBASE1 + 1) << 16;
5188 goto do_getter_setter;
5190 case JSOP_SETNAME:
5191 case JSOP_SETPROP:
5192 LOAD_ATOM(0);
5193 id = ATOM_TO_JSID(atom);
5194 rval = FETCH_OPND(-1);
5195 i = -1;
5196 goto gs_pop_lval;
5198 case JSOP_SETELEM:
5199 rval = FETCH_OPND(-1);
5200 id = 0;
5201 i = -2;
5202 gs_pop_lval:
5203 SAVE_SP_AND_PC(fp);
5204 FETCH_OBJECT(cx, i - 1, lval, obj);
5205 break;
5207 case JSOP_INITPROP:
5208 JS_ASSERT(sp - fp->spbase >= 2);
5209 rval = FETCH_OPND(-1);
5210 i = -1;
5211 LOAD_ATOM(0);
5212 id = ATOM_TO_JSID(atom);
5213 goto gs_get_lval;
5215 case JSOP_INITELEM:
5216 JS_ASSERT(sp - fp->spbase >= 3);
5217 rval = FETCH_OPND(-1);
5218 id = 0;
5219 i = -2;
5220 gs_get_lval:
5221 lval = FETCH_OPND(i-1);
5222 JS_ASSERT(JSVAL_IS_OBJECT(lval));
5223 obj = JSVAL_TO_OBJECT(lval);
5224 SAVE_SP_AND_PC(fp);
5225 break;
5227 default:
5228 JS_ASSERT(0);
5231 /* Ensure that id has a type suitable for use with obj. */
5232 if (id == 0)
5233 FETCH_ELEMENT_ID(obj, i, id);
5235 if (JS_TypeOfValue(cx, rval) != JSTYPE_FUNCTION) {
5236 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5237 JSMSG_BAD_GETTER_OR_SETTER,
5238 (op == JSOP_GETTER)
5239 ? js_getter_str
5240 : js_setter_str);
5241 ok = JS_FALSE;
5242 goto out;
5246 * Getters and setters are just like watchpoints from an access
5247 * control point of view.
5249 ok = OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &rtmp, &attrs);
5250 if (!ok)
5251 goto out;
5253 if (op == JSOP_GETTER) {
5254 getter = JS_EXTENSION (JSPropertyOp) JSVAL_TO_OBJECT(rval);
5255 setter = NULL;
5256 attrs = JSPROP_GETTER;
5257 } else {
5258 getter = NULL;
5259 setter = JS_EXTENSION (JSPropertyOp) JSVAL_TO_OBJECT(rval);
5260 attrs = JSPROP_SETTER;
5262 attrs |= JSPROP_ENUMERATE | JSPROP_SHARED;
5264 /* Check for a readonly or permanent property of the same name. */
5265 ok = js_CheckRedeclaration(cx, obj, id, attrs, NULL, NULL);
5266 if (!ok)
5267 goto out;
5269 ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, getter, setter,
5270 attrs, NULL);
5271 if (!ok)
5272 goto out;
5274 sp += i;
5275 if (js_CodeSpec[op2].ndefs)
5276 STORE_OPND(-1, rval);
5277 len = js_CodeSpec[op2].length;
5278 DO_NEXT_OP(len);
5279 #endif /* JS_HAS_GETTER_SETTER */
5281 BEGIN_CASE(JSOP_NEWINIT)
5282 argc = 0;
5283 fp->sharpDepth++;
5284 goto do_new;
5286 BEGIN_CASE(JSOP_ENDINIT)
5287 if (--fp->sharpDepth == 0)
5288 fp->sharpArray = NULL;
5290 /* Re-set the newborn root to the top of this object tree. */
5291 JS_ASSERT(sp - fp->spbase >= 1);
5292 lval = FETCH_OPND(-1);
5293 JS_ASSERT(JSVAL_IS_OBJECT(lval));
5294 cx->weakRoots.newborn[GCX_OBJECT] =
5295 (JSGCThing *)JSVAL_TO_GCTHING(lval);
5296 END_CASE(JSOP_ENDINIT)
5298 BEGIN_CASE(JSOP_INITPROP)
5299 /* Get the immediate property name into id. */
5300 LOAD_ATOM(0);
5301 id = ATOM_TO_JSID(atom);
5302 /* Pop the property's value into rval. */
5303 JS_ASSERT(sp - fp->spbase >= 2);
5304 rval = FETCH_OPND(-1);
5305 i = -1;
5306 SAVE_SP_AND_PC(fp);
5307 goto do_init;
5309 BEGIN_CASE(JSOP_INITELEM)
5310 /* Pop the element's value into rval. */
5311 JS_ASSERT(sp - fp->spbase >= 3);
5312 rval = FETCH_OPND(-1);
5313 i = -2;
5314 SAVE_SP_AND_PC(fp);
5315 FETCH_ELEMENT_ID(obj, -2, id);
5317 do_init:
5318 /* Find the object being initialized at top of stack. */
5319 lval = FETCH_OPND(i-1);
5320 JS_ASSERT(JSVAL_IS_OBJECT(lval));
5321 obj = JSVAL_TO_OBJECT(lval);
5323 /* Set the property named by obj[id] to rval. */
5324 ok = js_CheckRedeclaration(cx, obj, id, JSPROP_INITIALIZER, NULL,
5325 NULL);
5326 if (!ok)
5327 goto out;
5328 ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
5329 if (!ok)
5330 goto out;
5331 sp += i;
5332 len = js_CodeSpec[op].length;
5333 DO_NEXT_OP(len);
5335 #if JS_HAS_SHARP_VARS
5336 BEGIN_CASE(JSOP_DEFSHARP)
5337 SAVE_SP_AND_PC(fp);
5338 obj = fp->sharpArray;
5339 if (!obj) {
5340 obj = js_NewArrayObject(cx, 0, NULL);
5341 if (!obj) {
5342 ok = JS_FALSE;
5343 goto out;
5345 fp->sharpArray = obj;
5347 i = (jsint) GET_UINT16(pc);
5348 id = INT_TO_JSID(i);
5349 rval = FETCH_OPND(-1);
5350 if (JSVAL_IS_PRIMITIVE(rval)) {
5351 char numBuf[12];
5352 JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i);
5353 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5354 JSMSG_BAD_SHARP_DEF, numBuf);
5355 ok = JS_FALSE;
5356 goto out;
5358 ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
5359 if (!ok)
5360 goto out;
5361 END_CASE(JSOP_DEFSHARP)
5363 BEGIN_CASE(JSOP_USESHARP)
5364 i = (jsint) GET_UINT16(pc);
5365 id = INT_TO_JSID(i);
5366 obj = fp->sharpArray;
5367 if (!obj) {
5368 rval = JSVAL_VOID;
5369 } else {
5370 SAVE_SP_AND_PC(fp);
5371 ok = OBJ_GET_PROPERTY(cx, obj, id, &rval);
5372 if (!ok)
5373 goto out;
5375 if (!JSVAL_IS_OBJECT(rval)) {
5376 char numBuf[12];
5377 JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i);
5379 SAVE_SP_AND_PC(fp);
5380 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5381 JSMSG_BAD_SHARP_USE, numBuf);
5382 ok = JS_FALSE;
5383 goto out;
5385 PUSH_OPND(rval);
5386 END_CASE(JSOP_USESHARP)
5387 #endif /* JS_HAS_SHARP_VARS */
5389 /* No-ops for ease of decompilation and jit'ing. */
5390 EMPTY_CASE(JSOP_TRY)
5391 EMPTY_CASE(JSOP_FINALLY)
5393 BEGIN_CASE(JSOP_GOSUB)
5394 PUSH(JSVAL_FALSE);
5395 i = PTRDIFF(pc, script->main, jsbytecode) + JSOP_GOSUB_LENGTH;
5396 len = GET_JUMP_OFFSET(pc);
5397 PUSH(INT_TO_JSVAL(i));
5398 END_VARLEN_CASE
5400 BEGIN_CASE(JSOP_GOSUBX)
5401 PUSH(JSVAL_FALSE);
5402 i = PTRDIFF(pc, script->main, jsbytecode) + JSOP_GOSUBX_LENGTH;
5403 len = GET_JUMPX_OFFSET(pc);
5404 PUSH(INT_TO_JSVAL(i));
5405 END_VARLEN_CASE
5407 BEGIN_CASE(JSOP_RETSUB)
5408 rval = POP();
5409 lval = POP();
5410 JS_ASSERT(JSVAL_IS_BOOLEAN(lval));
5411 if (JSVAL_TO_BOOLEAN(lval)) {
5413 * Exception was pending during finally, throw it *before* we
5414 * adjust pc, because pc indexes into script->trynotes. This
5415 * turns out not to be necessary, but it seems clearer. And
5416 * it points out a FIXME: 350509, due to Igor Bukanov.
5418 cx->throwing = JS_TRUE;
5419 cx->exception = rval;
5420 ok = JS_FALSE;
5421 goto out;
5423 JS_ASSERT(JSVAL_IS_INT(rval));
5424 len = JSVAL_TO_INT(rval);
5425 pc = script->main;
5426 END_VARLEN_CASE
5428 BEGIN_CASE(JSOP_EXCEPTION)
5429 JS_ASSERT(cx->throwing);
5430 PUSH(cx->exception);
5431 cx->throwing = JS_FALSE;
5432 END_CASE(JSOP_EXCEPTION)
5434 BEGIN_CASE(JSOP_THROWING)
5435 JS_ASSERT(!cx->throwing);
5436 cx->throwing = JS_TRUE;
5437 cx->exception = POP_OPND();
5438 END_CASE(JSOP_THROWING)
5440 BEGIN_CASE(JSOP_THROW)
5441 JS_ASSERT(!cx->throwing);
5442 cx->throwing = JS_TRUE;
5443 cx->exception = POP_OPND();
5444 ok = JS_FALSE;
5445 /* let the code at out try to catch the exception. */
5446 goto out;
5448 BEGIN_CASE(JSOP_SETLOCALPOP)
5450 * The stack must have a block with at least one local slot below
5451 * the exception object.
5453 JS_ASSERT(sp - fp->spbase >= 2);
5454 slot = GET_UINT16(pc);
5455 JS_ASSERT(slot + 1 < (uintN)depth);
5456 fp->spbase[slot] = POP_OPND();
5457 END_CASE(JSOP_SETLOCALPOP)
5459 BEGIN_CASE(JSOP_INSTANCEOF)
5460 SAVE_SP_AND_PC(fp);
5461 rval = FETCH_OPND(-1);
5462 if (JSVAL_IS_PRIMITIVE(rval) ||
5463 !(obj = JSVAL_TO_OBJECT(rval))->map->ops->hasInstance) {
5464 js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
5465 -1, rval, NULL);
5466 ok = JS_FALSE;
5467 goto out;
5469 lval = FETCH_OPND(-2);
5470 cond = JS_FALSE;
5471 ok = obj->map->ops->hasInstance(cx, obj, lval, &cond);
5472 if (!ok)
5473 goto out;
5474 sp--;
5475 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));
5476 END_CASE(JSOP_INSTANCEOF)
5478 #if JS_HAS_DEBUGGER_KEYWORD
5479 BEGIN_CASE(JSOP_DEBUGGER)
5481 JSTrapHandler handler = cx->debugHooks->debuggerHandler;
5482 if (handler) {
5483 SAVE_SP_AND_PC(fp);
5484 switch (handler(cx, script, pc, &rval,
5485 cx->debugHooks->debuggerHandlerData)) {
5486 case JSTRAP_ERROR:
5487 ok = JS_FALSE;
5488 goto out;
5489 case JSTRAP_CONTINUE:
5490 break;
5491 case JSTRAP_RETURN:
5492 fp->rval = rval;
5493 goto out;
5494 case JSTRAP_THROW:
5495 cx->throwing = JS_TRUE;
5496 cx->exception = rval;
5497 ok = JS_FALSE;
5498 goto out;
5499 default:;
5501 LOAD_INTERRUPT_HANDLER(cx);
5504 END_CASE(JSOP_DEBUGGER)
5505 #endif /* JS_HAS_DEBUGGER_KEYWORD */
5507 #if JS_HAS_XML_SUPPORT
5508 BEGIN_CASE(JSOP_DEFXMLNS)
5509 rval = POP();
5510 SAVE_SP_AND_PC(fp);
5511 ok = js_SetDefaultXMLNamespace(cx, rval);
5512 if (!ok)
5513 goto out;
5514 END_CASE(JSOP_DEFXMLNS)
5516 BEGIN_CASE(JSOP_ANYNAME)
5517 SAVE_SP_AND_PC(fp);
5518 ok = js_GetAnyName(cx, &rval);
5519 if (!ok)
5520 goto out;
5521 PUSH_OPND(rval);
5522 END_CASE(JSOP_ANYNAME)
5524 BEGIN_CASE(JSOP_QNAMEPART)
5525 LOAD_ATOM(0);
5526 PUSH_OPND(ATOM_KEY(atom));
5527 END_CASE(JSOP_QNAMEPART)
5529 BEGIN_CASE(JSOP_QNAMECONST)
5530 LOAD_ATOM(0);
5531 rval = ATOM_KEY(atom);
5532 lval = FETCH_OPND(-1);
5533 SAVE_SP_AND_PC(fp);
5534 obj = js_ConstructXMLQNameObject(cx, lval, rval);
5535 if (!obj) {
5536 ok = JS_FALSE;
5537 goto out;
5539 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
5540 END_CASE(JSOP_QNAMECONST)
5542 BEGIN_CASE(JSOP_QNAME)
5543 rval = FETCH_OPND(-1);
5544 lval = FETCH_OPND(-2);
5545 SAVE_SP_AND_PC(fp);
5546 obj = js_ConstructXMLQNameObject(cx, lval, rval);
5547 if (!obj) {
5548 ok = JS_FALSE;
5549 goto out;
5551 sp--;
5552 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
5553 END_CASE(JSOP_QNAME)
5555 BEGIN_CASE(JSOP_TOATTRNAME)
5556 rval = FETCH_OPND(-1);
5557 SAVE_SP_AND_PC(fp);
5558 ok = js_ToAttributeName(cx, &rval);
5559 if (!ok)
5560 goto out;
5561 STORE_OPND(-1, rval);
5562 END_CASE(JSOP_TOATTRNAME)
5564 BEGIN_CASE(JSOP_TOATTRVAL)
5565 rval = FETCH_OPND(-1);
5566 JS_ASSERT(JSVAL_IS_STRING(rval));
5567 SAVE_SP_AND_PC(fp);
5568 str = js_EscapeAttributeValue(cx, JSVAL_TO_STRING(rval));
5569 if (!str) {
5570 ok = JS_FALSE;
5571 goto out;
5573 STORE_OPND(-1, STRING_TO_JSVAL(str));
5574 END_CASE(JSOP_TOATTRVAL)
5576 BEGIN_CASE(JSOP_ADDATTRNAME)
5577 BEGIN_CASE(JSOP_ADDATTRVAL)
5578 rval = FETCH_OPND(-1);
5579 lval = FETCH_OPND(-2);
5580 str = JSVAL_TO_STRING(lval);
5581 str2 = JSVAL_TO_STRING(rval);
5582 SAVE_SP_AND_PC(fp);
5583 str = js_AddAttributePart(cx, op == JSOP_ADDATTRNAME, str, str2);
5584 if (!str) {
5585 ok = JS_FALSE;
5586 goto out;
5588 sp--;
5589 STORE_OPND(-1, STRING_TO_JSVAL(str));
5590 END_CASE(JSOP_ADDATTRNAME)
5592 BEGIN_CASE(JSOP_BINDXMLNAME)
5593 lval = FETCH_OPND(-1);
5594 SAVE_SP_AND_PC(fp);
5595 ok = js_FindXMLProperty(cx, lval, &obj, &id);
5596 if (!ok)
5597 goto out;
5598 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
5599 PUSH_OPND(ID_TO_VALUE(id));
5600 END_CASE(JSOP_BINDXMLNAME)
5602 BEGIN_CASE(JSOP_SETXMLNAME)
5603 obj = JSVAL_TO_OBJECT(FETCH_OPND(-3));
5604 rval = FETCH_OPND(-1);
5605 SAVE_SP_AND_PC(fp);
5606 FETCH_ELEMENT_ID(obj, -2, id);
5607 ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
5608 if (!ok)
5609 goto out;
5610 sp -= 2;
5611 STORE_OPND(-1, rval);
5612 END_CASE(JSOP_SETXMLNAME)
5614 BEGIN_CASE(JSOP_CALLXMLNAME)
5615 BEGIN_CASE(JSOP_XMLNAME)
5616 lval = FETCH_OPND(-1);
5617 SAVE_SP_AND_PC(fp);
5618 ok = js_FindXMLProperty(cx, lval, &obj, &id);
5619 if (!ok)
5620 goto out;
5621 ok = OBJ_GET_PROPERTY(cx, obj, id, &rval);
5622 if (!ok)
5623 goto out;
5624 STORE_OPND(-1, rval);
5625 if (op == JSOP_CALLXMLNAME) {
5626 PUSH_OPND(OBJECT_TO_JSVAL(obj));
5627 SAVE_SP(fp);
5628 ok = ComputeThis(cx, sp);
5629 if (!ok)
5630 goto out;
5632 END_CASE(JSOP_XMLNAME)
5634 BEGIN_CASE(JSOP_DESCENDANTS)
5635 BEGIN_CASE(JSOP_DELDESC)
5636 SAVE_SP_AND_PC(fp);
5637 FETCH_OBJECT(cx, -2, lval, obj);
5638 rval = FETCH_OPND(-1);
5639 ok = js_GetXMLDescendants(cx, obj, rval, &rval);
5640 if (!ok)
5641 goto out;
5643 if (op == JSOP_DELDESC) {
5644 sp[-1] = rval; /* set local root */
5645 ok = js_DeleteXMLListElements(cx, JSVAL_TO_OBJECT(rval));
5646 if (!ok)
5647 goto out;
5648 rval = JSVAL_TRUE; /* always succeed */
5651 sp--;
5652 STORE_OPND(-1, rval);
5653 END_CASE(JSOP_DESCENDANTS)
5655 BEGIN_CASE(JSOP_FILTER)
5656 len = GET_JUMP_OFFSET(pc);
5657 SAVE_SP_AND_PC(fp);
5658 FETCH_OBJECT(cx, -1, lval, obj);
5659 ok = js_FilterXMLList(cx, obj, pc + js_CodeSpec[op].length, &rval);
5660 if (!ok)
5661 goto out;
5662 JS_ASSERT(fp->sp == sp);
5663 STORE_OPND(-1, rval);
5664 END_VARLEN_CASE
5666 BEGIN_CASE(JSOP_ENDFILTER)
5667 *result = POP_OPND();
5668 goto out;
5670 EMPTY_CASE(JSOP_STARTXML)
5671 EMPTY_CASE(JSOP_STARTXMLEXPR)
5673 BEGIN_CASE(JSOP_TOXML)
5674 rval = FETCH_OPND(-1);
5675 SAVE_SP_AND_PC(fp);
5676 obj = js_ValueToXMLObject(cx, rval);
5677 if (!obj) {
5678 ok = JS_FALSE;
5679 goto out;
5681 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
5682 END_CASE(JSOP_TOXML)
5684 BEGIN_CASE(JSOP_TOXMLLIST)
5685 rval = FETCH_OPND(-1);
5686 SAVE_SP_AND_PC(fp);
5687 obj = js_ValueToXMLListObject(cx, rval);
5688 if (!obj) {
5689 ok = JS_FALSE;
5690 goto out;
5692 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
5693 END_CASE(JSOP_TOXMLLIST)
5695 BEGIN_CASE(JSOP_XMLTAGEXPR)
5696 rval = FETCH_OPND(-1);
5697 SAVE_SP_AND_PC(fp);
5698 str = js_ValueToString(cx, rval);
5699 if (!str) {
5700 ok = JS_FALSE;
5701 goto out;
5703 STORE_OPND(-1, STRING_TO_JSVAL(str));
5704 END_CASE(JSOP_XMLTAGEXPR)
5706 BEGIN_CASE(JSOP_XMLELTEXPR)
5707 rval = FETCH_OPND(-1);
5708 SAVE_SP_AND_PC(fp);
5709 if (VALUE_IS_XML(cx, rval)) {
5710 str = js_ValueToXMLString(cx, rval);
5711 } else {
5712 str = js_ValueToString(cx, rval);
5713 if (str)
5714 str = js_EscapeElementValue(cx, str);
5716 if (!str) {
5717 ok = JS_FALSE;
5718 goto out;
5720 STORE_OPND(-1, STRING_TO_JSVAL(str));
5721 END_CASE(JSOP_XMLELTEXPR)
5723 BEGIN_CASE(JSOP_XMLOBJECT)
5724 LOAD_OBJECT(0);
5725 SAVE_SP_AND_PC(fp);
5726 obj = js_CloneXMLObject(cx, obj);
5727 if (!obj) {
5728 ok = JS_FALSE;
5729 goto out;
5731 PUSH_OPND(OBJECT_TO_JSVAL(obj));
5732 END_CASE(JSOP_XMLOBJECT)
5734 BEGIN_CASE(JSOP_XMLCDATA)
5735 LOAD_ATOM(0);
5736 str = ATOM_TO_STRING(atom);
5737 obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_TEXT, NULL, str);
5738 if (!obj) {
5739 ok = JS_FALSE;
5740 goto out;
5742 PUSH_OPND(OBJECT_TO_JSVAL(obj));
5743 END_CASE(JSOP_XMLCDATA)
5745 BEGIN_CASE(JSOP_XMLCOMMENT)
5746 LOAD_ATOM(0);
5747 str = ATOM_TO_STRING(atom);
5748 obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_COMMENT, NULL, str);
5749 if (!obj) {
5750 ok = JS_FALSE;
5751 goto out;
5753 PUSH_OPND(OBJECT_TO_JSVAL(obj));
5754 END_CASE(JSOP_XMLCOMMENT)
5756 BEGIN_CASE(JSOP_XMLPI)
5757 LOAD_ATOM(0);
5758 str = ATOM_TO_STRING(atom);
5759 rval = FETCH_OPND(-1);
5760 str2 = JSVAL_TO_STRING(rval);
5761 SAVE_SP_AND_PC(fp);
5762 obj = js_NewXMLSpecialObject(cx,
5763 JSXML_CLASS_PROCESSING_INSTRUCTION,
5764 str, str2);
5765 if (!obj) {
5766 ok = JS_FALSE;
5767 goto out;
5769 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
5770 END_CASE(JSOP_XMLPI)
5772 BEGIN_CASE(JSOP_CALLPROP)
5773 /* Get an immediate atom naming the property. */
5774 LOAD_ATOM(0);
5775 id = ATOM_TO_JSID(atom);
5776 PUSH(JSVAL_NULL);
5777 SAVE_SP_AND_PC(fp);
5778 lval = FETCH_OPND(-2);
5779 if (!JSVAL_IS_PRIMITIVE(lval)) {
5780 obj = JSVAL_TO_OBJECT(lval);
5782 /* Special-case XML object method lookup, per ECMA-357. */
5783 if (OBJECT_IS_XML(cx, obj)) {
5784 JSXMLObjectOps *ops;
5786 ops = (JSXMLObjectOps *) obj->map->ops;
5787 obj = ops->getMethod(cx, obj, id, &rval);
5788 if (!obj)
5789 ok = JS_FALSE;
5790 } else {
5791 ok = OBJ_GET_PROPERTY(cx, obj, id, &rval);
5793 if (!ok)
5794 goto out;
5795 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
5796 STORE_OPND(-2, rval);
5797 ok = ComputeThis(cx, sp);
5798 if (!ok)
5799 goto out;
5800 } else {
5801 if (JSVAL_IS_STRING(lval)) {
5802 i = JSProto_String;
5803 } else if (JSVAL_IS_NUMBER(lval)) {
5804 i = JSProto_Number;
5805 } else if (JSVAL_IS_BOOLEAN(lval)) {
5806 i = JSProto_Boolean;
5807 } else {
5808 JS_ASSERT(JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval));
5809 js_ReportValueError(cx, JSMSG_NO_PROPERTIES, -2, lval,
5810 NULL);
5811 ok = JS_FALSE;
5812 goto out;
5815 ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(i), &obj);
5816 if (!ok)
5817 goto out;
5818 JS_ASSERT(obj);
5819 ok = OBJ_GET_PROPERTY(cx, obj, id, &rval);
5820 if (!ok)
5821 goto out;
5822 STORE_OPND(-1, lval);
5823 STORE_OPND(-2, rval);
5825 /* Wrap primitive lval in object clothing if necessary. */
5826 if (!VALUE_IS_FUNCTION(cx, rval) ||
5827 (obj = JSVAL_TO_OBJECT(rval),
5828 fun = (JSFunction *) OBJ_GET_PRIVATE(cx, obj),
5829 !PRIMITIVE_THIS_TEST(fun, lval))) {
5830 ok = js_PrimitiveToObject(cx, &sp[-1]);
5831 if (!ok)
5832 goto out;
5835 END_CASE(JSOP_CALLPROP)
5837 BEGIN_CASE(JSOP_GETFUNNS)
5838 SAVE_SP_AND_PC(fp);
5839 ok = js_GetFunctionNamespace(cx, &rval);
5840 if (!ok)
5841 goto out;
5842 PUSH_OPND(rval);
5843 END_CASE(JSOP_GETFUNNS)
5844 #endif /* JS_HAS_XML_SUPPORT */
5846 BEGIN_CASE(JSOP_ENTERBLOCK)
5847 LOAD_OBJECT(0);
5848 JS_ASSERT(fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp);
5849 vp = sp + OBJ_BLOCK_COUNT(cx, obj);
5850 JS_ASSERT(vp <= fp->spbase + depth);
5851 while (sp < vp) {
5852 STORE_OPND(0, JSVAL_VOID);
5853 sp++;
5857 * If this frame had to reflect the compile-time block chain into
5858 * the runtime scope chain, we can't optimize block scopes out of
5859 * runtime any longer, because an outer block that parents obj has
5860 * been cloned onto the scope chain. To avoid re-cloning such a
5861 * parent and accumulating redundant clones via js_GetScopeChain,
5862 * we must clone each block eagerly on entry, and push it on the
5863 * scope chain, until this frame pops.
5865 if (fp->flags & JSFRAME_POP_BLOCKS) {
5866 JS_ASSERT(!fp->blockChain);
5869 * Check whether JSOP_DEFLOCALFUN emulated JSOP_ENTERBLOCK for
5870 * the body block in order to correctly scope the local cloned
5871 * function object it creates.
5873 parent = fp->scopeChain;
5874 if (OBJ_GET_PROTO(cx, parent) == obj) {
5875 JS_ASSERT(OBJ_GET_CLASS(cx, parent) == &js_BlockClass);
5876 } else {
5877 obj = js_CloneBlockObject(cx, obj, parent, fp);
5878 if (!obj) {
5879 ok = JS_FALSE;
5880 goto out;
5882 fp->scopeChain = obj;
5884 } else {
5885 JS_ASSERT(!fp->blockChain ||
5886 OBJ_GET_PARENT(cx, obj) == fp->blockChain);
5887 fp->blockChain = obj;
5889 END_CASE(JSOP_ENTERBLOCK)
5891 BEGIN_CASE(JSOP_LEAVEBLOCKEXPR)
5892 BEGIN_CASE(JSOP_LEAVEBLOCK)
5894 JSObject **chainp;
5896 /* Grab the result of the expression. */
5897 if (op == JSOP_LEAVEBLOCKEXPR)
5898 rval = FETCH_OPND(-1);
5900 chainp = &fp->blockChain;
5901 obj = *chainp;
5902 if (!obj) {
5903 chainp = &fp->scopeChain;
5904 obj = *chainp;
5907 * This block was cloned, so clear its private data and sync
5908 * its locals to their property slots.
5910 SAVE_SP_AND_PC(fp);
5911 ok = js_PutBlockObject(cx, obj);
5912 if (!ok)
5913 goto out;
5916 sp -= GET_UINT16(pc);
5917 JS_ASSERT(fp->spbase <= sp && sp <= fp->spbase + depth);
5919 /* Store the result into the topmost stack slot. */
5920 if (op == JSOP_LEAVEBLOCKEXPR)
5921 STORE_OPND(-1, rval);
5923 JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass);
5924 JS_ASSERT(op == JSOP_LEAVEBLOCKEXPR
5925 ? fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp - 1
5926 : fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp);
5928 *chainp = OBJ_GET_PARENT(cx, obj);
5929 JS_ASSERT(chainp != &fp->blockChain ||
5930 !*chainp ||
5931 OBJ_GET_CLASS(cx, *chainp) == &js_BlockClass);
5933 END_CASE(JSOP_LEAVEBLOCK)
5935 BEGIN_CASE(JSOP_GETLOCAL)
5936 BEGIN_CASE(JSOP_CALLLOCAL)
5937 slot = GET_UINT16(pc);
5938 JS_ASSERT(slot < (uintN)depth);
5939 PUSH_OPND(fp->spbase[slot]);
5940 if (op == JSOP_CALLLOCAL)
5941 PUSH_GLOBAL_THIS(cx, sp);
5942 END_CASE(JSOP_GETLOCAL)
5944 BEGIN_CASE(JSOP_SETLOCAL)
5945 slot = GET_UINT16(pc);
5946 JS_ASSERT(slot < (uintN)depth);
5947 vp = &fp->spbase[slot];
5948 GC_POKE(cx, *vp);
5949 *vp = FETCH_OPND(-1);
5950 END_CASE(JSOP_SETLOCAL)
5952 /* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */
5953 #define FAST_LOCAL_INCREMENT_OP(PRE,OPEQ,MINMAX) \
5954 slot = GET_UINT16(pc); \
5955 JS_ASSERT(slot < (uintN)depth); \
5956 vp = fp->spbase + slot; \
5957 rval = *vp; \
5958 if (!JSVAL_IS_INT(rval) || rval == INT_TO_JSVAL(JSVAL_INT_##MINMAX)) \
5959 goto do_nonint_fast_incop; \
5960 PRE = rval; \
5961 rval OPEQ 2; \
5962 *vp = rval; \
5963 PUSH_OPND(PRE)
5965 BEGIN_CASE(JSOP_INCLOCAL)
5966 FAST_LOCAL_INCREMENT_OP(rval, +=, MAX);
5967 END_CASE(JSOP_INCLOCAL)
5969 BEGIN_CASE(JSOP_DECLOCAL)
5970 FAST_LOCAL_INCREMENT_OP(rval, -=, MIN);
5971 END_CASE(JSOP_DECLOCAL)
5973 BEGIN_CASE(JSOP_LOCALINC)
5974 FAST_LOCAL_INCREMENT_OP(rtmp, +=, MAX);
5975 END_CASE(JSOP_LOCALINC)
5977 BEGIN_CASE(JSOP_LOCALDEC)
5978 FAST_LOCAL_INCREMENT_OP(rtmp, -=, MIN);
5979 END_CASE(JSOP_LOCALDEC)
5981 #undef FAST_LOCAL_INCREMENT_OP
5983 BEGIN_CASE(JSOP_ENDITER)
5985 * Decrease the stack pointer even when !ok, see comments in the
5986 * exception capturing code for details.
5988 SAVE_SP_AND_PC(fp);
5989 ok = js_CloseIterator(cx, sp[-1]);
5990 --sp;
5991 if (!ok)
5992 goto out;
5993 END_CASE(JSOP_ENDITER)
5995 #if JS_HAS_GENERATORS
5996 BEGIN_CASE(JSOP_GENERATOR)
5997 pc += JSOP_GENERATOR_LENGTH;
5998 SAVE_SP_AND_PC(fp);
5999 obj = js_NewGenerator(cx, fp);
6000 if (!obj) {
6001 ok = JS_FALSE;
6002 } else {
6003 JS_ASSERT(!fp->callobj && !fp->argsobj);
6004 fp->rval = OBJECT_TO_JSVAL(obj);
6006 goto out;
6008 BEGIN_CASE(JSOP_YIELD)
6009 ASSERT_NOT_THROWING(cx);
6010 if (fp->flags & JSFRAME_FILTERING) {
6011 /* FIXME: bug 309894 -- fix to eliminate this error. */
6012 JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL,
6013 JSMSG_YIELD_FROM_FILTER);
6014 ok = JS_FALSE;
6015 goto out;
6017 if (FRAME_TO_GENERATOR(fp)->state == JSGEN_CLOSING) {
6018 js_ReportValueError(cx, JSMSG_BAD_GENERATOR_YIELD,
6019 JSDVG_SEARCH_STACK, fp->argv[-2], NULL);
6020 ok = JS_FALSE;
6021 goto out;
6023 fp->rval = FETCH_OPND(-1);
6024 fp->flags |= JSFRAME_YIELDING;
6025 pc += JSOP_YIELD_LENGTH;
6026 SAVE_SP_AND_PC(fp);
6027 goto out;
6029 BEGIN_CASE(JSOP_ARRAYPUSH)
6030 slot = GET_UINT16(pc);
6031 JS_ASSERT(slot < (uintN)depth);
6032 lval = fp->spbase[slot];
6033 obj = JSVAL_TO_OBJECT(lval);
6034 JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ArrayClass);
6035 rval = FETCH_OPND(-1);
6038 * We know that the array is created with only a 'length' private
6039 * data slot at JSSLOT_ARRAY_LENGTH, and that previous iterations
6040 * of the comprehension have added the only properties directly in
6041 * the array object.
6043 lval = obj->fslots[JSSLOT_ARRAY_LENGTH];
6044 JS_ASSERT(JSVAL_IS_INT(lval));
6045 i = JSVAL_TO_INT(lval);
6046 if (i == ARRAY_INIT_LIMIT) {
6047 JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL,
6048 JSMSG_ARRAY_INIT_TOO_BIG);
6049 ok = JS_FALSE;
6050 goto out;
6052 id = INT_TO_JSID(i);
6054 SAVE_SP_AND_PC(fp);
6055 ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
6056 if (!ok)
6057 goto out;
6058 --sp;
6059 END_CASE(JSOP_ARRAYPUSH)
6060 #endif /* JS_HAS_GENERATORS */
6062 #if !JS_HAS_GENERATORS
6063 L_JSOP_GENERATOR:
6064 L_JSOP_YIELD:
6065 L_JSOP_ARRAYPUSH:
6066 #endif
6068 #if !JS_HAS_DESTRUCTURING
6069 L_JSOP_FOREACHKEYVAL:
6070 L_JSOP_ENUMCONSTELEM:
6071 #endif
6073 #if JS_THREADED_INTERP
6074 L_JSOP_BACKPATCH:
6075 L_JSOP_BACKPATCH_POP:
6076 #else
6077 default:
6078 #endif
6080 char numBuf[12];
6081 JS_snprintf(numBuf, sizeof numBuf, "%d", op);
6082 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
6083 JSMSG_BAD_BYTECODE, numBuf);
6084 ok = JS_FALSE;
6085 goto out;
6088 #if !JS_THREADED_INTERP
6090 } /* switch (op) */
6092 advance_pc:
6093 pc += len;
6095 #ifdef DEBUG
6096 if (tracefp) {
6097 intN ndefs, n;
6098 jsval *siter;
6100 ndefs = js_CodeSpec[op].ndefs;
6101 if (ndefs) {
6102 SAVE_SP_AND_PC(fp);
6103 if (op == JSOP_FORELEM && sp[-1] == JSVAL_FALSE)
6104 --ndefs;
6105 for (n = -ndefs; n < 0; n++) {
6106 char *bytes = js_DecompileValueGenerator(cx, n, sp[n],
6107 NULL);
6108 if (bytes) {
6109 fprintf(tracefp, "%s %s",
6110 (n == -ndefs) ? " output:" : ",",
6111 bytes);
6112 JS_free(cx, bytes);
6115 fprintf(tracefp, " @ %d\n", sp - fp->spbase);
6117 fprintf(tracefp, " stack: ");
6118 for (siter = fp->spbase; siter < sp; siter++) {
6119 str = js_ValueToString(cx, *siter);
6120 if (!str)
6121 fputs("<null>", tracefp);
6122 else
6123 js_FileEscapedString(tracefp, str, 0);
6124 fputc(' ', tracefp);
6126 fputc('\n', tracefp);
6128 #endif /* DEBUG */
6130 #endif /* !JS_THREADED_INTERP */
6132 out:
6133 JS_ASSERT((size_t)(pc - script->code) < script->length);
6134 if (!ok && cx->throwing && !(fp->flags & JSFRAME_FILTERING)) {
6136 * An exception has been raised and we are not in an XML filtering
6137 * predicate expression. The latter check is necessary to avoid
6138 * catching exceptions within the filtering predicate, such as this
6139 * example taken from tests/e4x/Regress/regress-301596.js:
6141 * try {
6142 * <xml/>.(@a == 1);
6143 * throw 5;
6144 * } catch (e) {
6147 * The inner interpreter activation executing the predicate bytecode
6148 * will throw "reference to undefined XML name @a" (or 5, in older
6149 * versions that followed the first edition of ECMA-357 and evaluated
6150 * unbound identifiers to undefined), and the exception must not be
6151 * caught until control unwinds to the outer interpreter activation.
6153 * Otherwise, the wrong stack depth will be restored by JSOP_SETSP,
6154 * and the catch will move into the filtering predicate expression,
6155 * leading to double catch execution if it rethrows.
6157 * FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=309894
6159 JSTrapHandler handler;
6160 JSTryNote *tn, *tnlimit;
6161 uint32 offset;
6164 * Call debugger throw hook if set (XXX thread safety?).
6166 handler = cx->debugHooks->throwHook;
6167 if (handler) {
6168 SAVE_SP_AND_PC(fp);
6169 switch (handler(cx, script, pc, &rval,
6170 cx->debugHooks->throwHookData)) {
6171 case JSTRAP_ERROR:
6172 cx->throwing = JS_FALSE;
6173 goto no_catch;
6174 case JSTRAP_RETURN:
6175 ok = JS_TRUE;
6176 cx->throwing = JS_FALSE;
6177 fp->rval = rval;
6178 goto no_catch;
6179 case JSTRAP_THROW:
6180 cx->exception = rval;
6181 case JSTRAP_CONTINUE:
6182 default:;
6184 LOAD_INTERRUPT_HANDLER(cx);
6188 * Look for a try block in script that can catch this exception.
6190 if (script->trynotesOffset == 0)
6191 goto no_catch;
6193 offset = (uint32)(pc - script->main);
6194 tn = JS_SCRIPT_TRYNOTES(script)->vector;
6195 tnlimit = tn + JS_SCRIPT_TRYNOTES(script)->length;
6196 do {
6197 if (offset - tn->start >= tn->length)
6198 continue;
6201 * We have a note that covers the exception pc but we must check
6202 * whether the interpreter has already executed the corresponding
6203 * handler. This is possible when the executed bytecode
6204 * implements break or return from inside a for-in loop.
6206 * In this case the emitter generates additional [enditer] and
6207 * [gosub] opcodes to close all outstanding iterators and execute
6208 * the finally blocks. If such an [enditer] throws an exception,
6209 * its pc can still be inside several nested for-in loops and
6210 * try-finally statements even if we have already closed the
6211 * corresponding iterators and invoked the finally blocks.
6213 * To address this, we make [enditer] always decrease the stack
6214 * even when its implementation throws an exception. Thus already
6215 * executed [enditer] and [gosub] opcodes will have try notes
6216 * with the stack depth exceeding the current one and this
6217 * condition is what we use to filter them out.
6219 if (tn->stackDepth > sp - fp->spbase)
6220 continue;
6223 * Prepare to execute the try note handler and unwind the block
6224 * and scope chains until we match the stack depth of the try
6225 * note. Note that we set sp after we call js_PutBlockObject to
6226 * avoid potential GC hazards.
6228 ok = JS_TRUE;
6229 i = tn->stackDepth;
6230 for (obj = fp->blockChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {
6231 JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass);
6232 if (OBJ_BLOCK_DEPTH(cx, obj) < i)
6233 break;
6235 fp->blockChain = obj;
6237 JS_ASSERT(ok);
6238 for (obj = fp->scopeChain; ; obj = OBJ_GET_PARENT(cx, obj)) {
6239 clasp = OBJ_GET_CLASS(cx, obj);
6240 if (clasp != &js_WithClass && clasp != &js_BlockClass)
6241 break;
6242 if (JS_GetPrivate(cx, obj) != fp ||
6243 OBJ_BLOCK_DEPTH(cx, obj) < i) {
6244 break;
6246 if (clasp == &js_BlockClass) {
6247 /* Don't fail until after we've updated all stacks. */
6248 ok &= js_PutBlockObject(cx, obj);
6249 } else {
6250 JS_SetPrivate(cx, obj, NULL);
6254 fp->scopeChain = obj;
6255 sp = fp->spbase + i;
6258 * Set pc to the first bytecode after the the try note to point
6259 * to the beginning of catch or finally or to [enditer] closing
6260 * the for-in loop.
6262 * We do it before checking for ok so, when failing during the
6263 * scope recovery, we restart the exception search with the
6264 * updated stack and pc avoiding calling the handler again.
6266 offset = tn->start + tn->length;
6267 pc = (script)->main + offset;
6268 if (!ok)
6269 goto out;
6271 switch (tn->kind) {
6272 case JSTN_CATCH:
6273 JS_ASSERT(*pc == JSOP_ENTERBLOCK);
6275 #if JS_HAS_GENERATORS
6276 /* Catch cannot intercept the closing of a generator. */
6277 if (JS_UNLIKELY(cx->exception == JSVAL_ARETURN))
6278 break;
6279 #endif
6282 * Don't clear cx->throwing to save cx->exception from GC
6283 * until it is pushed to the stack via [exception] in the
6284 * catch block.
6286 len = 0;
6287 DO_NEXT_OP(len);
6289 case JSTN_FINALLY:
6291 * Push (true, exception) pair for finally to indicate that
6292 * [retsub] should rethrow the exception.
6294 PUSH(JSVAL_TRUE);
6295 PUSH(cx->exception);
6296 cx->throwing = JS_FALSE;
6297 len = 0;
6298 DO_NEXT_OP(len);
6300 case JSTN_ITER:
6302 * This is similar to JSOP_ENDITER in the interpreter loop
6303 * except the code now uses a reserved stack slot to save and
6304 * restore the exception.
6306 JS_ASSERT(*pc == JSOP_ENDITER);
6307 PUSH(cx->exception);
6308 cx->throwing = JS_FALSE;
6309 SAVE_SP_AND_PC(fp);
6310 ok = js_CloseIterator(cx, sp[-2]);
6311 sp -= 2;
6312 if (!ok) {
6314 * close generated a new exception error or an error,
6315 * restart the handler search to properly notify the
6316 * debugger.
6318 goto out;
6320 cx->throwing = JS_TRUE;
6321 cx->exception = sp[1];
6324 * Reset ok to false so, if this is the last try note, the
6325 * exception will be propagated outside the function or
6326 * script.
6328 ok = JS_FALSE;
6329 break;
6331 } while (++tn != tnlimit);
6333 no_catch:;
6334 #if JS_HAS_GENERATORS
6335 if (JS_UNLIKELY(cx->throwing && cx->exception == JSVAL_ARETURN)) {
6336 cx->throwing = JS_FALSE;
6337 ok = JS_TRUE;
6338 fp->rval = JSVAL_VOID;
6340 #endif
6344 * Check whether control fell off the end of a lightweight function, or an
6345 * exception thrown under such a function was not caught by it. If so, go
6346 * to the inline code under JSOP_RETURN.
6348 if (inlineCallCount)
6349 goto inline_return;
6352 * Reset sp before freeing stack slots, because our caller may GC soon.
6353 * Clear spbase to indicate that we've popped the 2 * depth operand slots.
6354 * Restore the previous frame's execution state.
6356 if (JS_LIKELY(mark != NULL)) {
6357 /* If fp has blocks on its scope chain, home their locals now. */
6358 if (fp->flags & JSFRAME_POP_BLOCKS) {
6359 SAVE_SP_AND_PC(fp);
6360 ok &= PutBlockObjects(cx, fp);
6363 fp->sp = fp->spbase;
6364 fp->spbase = NULL;
6365 js_FreeRawStack(cx, mark);
6366 } else {
6367 SAVE_SP(fp);
6370 out2:
6371 if (cx->version == currentVersion && currentVersion != originalVersion)
6372 js_SetVersion(cx, originalVersion);
6373 cx->interpLevel--;
6374 return ok;
6376 atom_not_defined:
6378 const char *printable = js_AtomToPrintableString(cx, atom);
6379 if (printable)
6380 js_ReportIsNotDefined(cx, printable);
6381 ok = JS_FALSE;
6382 goto out;